Skip to content
VLSI Mentor

DDR · Module 22

Signal Integrity

Three mechanisms eat the same margin budget and interact through the data pattern. Data bus inversion is the first digital mitigation here — and it changes what the channel carries rather than improving the channel.

Chapter 22.2 explained one mechanism completely: energy turns around at an impedance discontinuity, returns after a round trip, and lands on a bit k unit intervals later than the one that launched it. That single fact accounted for the data dependence, the pattern sensitivity, the rank scaling and the fragmentation of a pass region.

It is one mechanism. A receiver's margin is eaten by three, they are not independent, and the thing that couples them is the data itself.

This chapter names all three, explains how they interact, and then introduces the first genuinely digital mitigation in the module — one whose registers and encoding are specified precisely enough to build, and which is routinely described as “opening the eye” when what it actually does is change what the channel is asked to carry.

The module's central law, with this chapter's qualification:

A DDR signal arrives degraded by effects that are electrical, not logical. Termination, encoding and protection do not remove those effects — they bound them, detect them, or trade one for another.

Data bus inversion is the clearest case of trading one for another in the whole module. It improves nothing electrically. It re-encodes the data so the channel sees a less demanding pattern, and it pays for that by consuming a pin that had another job.

1. Three Mechanisms, One Budget

The three mechanisms are usually taught as a list. The list is less useful than the thing they have in common:

They all consume the same quantity — the margin at the receiver's decision point — and Chapter 20.5 already published how much of that there is.

MechanismWhere the energy comes fromArrives
Inter-symbol interferenceThis line's own earlier bitsDelayed, per 22.2 §3
CrosstalkA neighbouring line's activityRoughly simultaneously
Frequency-dependent lossNowhere — energy is removedNow, as reduced amplitude

That third row is the one that breaks the pattern, and noticing why is worth more than memorising the list. ISI and crosstalk are additive — they put energy where it was not wanted. Loss is subtractive — it takes energy away from where it was wanted. So the three cannot be reasoned about as one lumped “noise” term, because two of them grow the disturbance and one of them shrinks the signal, and the ratio of the two is what a receiver actually resolves.

Chapter 20.5 §3 listed the contributors to the window and made the point that they cannot simply be added up — worst cases do not co-occur, some are statistical, some are correlated. This chapter supplies the causes behind three of those rows, and §5 explains the correlation that chapter could only assert.

2. Inter-Symbol Interference Is a Name for Something Already Explained

ISI is the effect of a symbol on the symbols around it. Chapter 22.2 §3 described it completely without using the term:

Energy from bit N arrives during bit N+k. That is inter-symbol interference. The reflection mechanism of 22.2 is one way it happens, and it is the dominant way on a DDR channel with impedance discontinuities.

There is a second contributor that 22.2 did not cover, and it belongs here because it comes from §4's loss rather than from a reflection.

A lossy channel smears a transition in time. High-frequency content is attenuated more than low-frequency content (§4), and a sharp edge is mostly high-frequency content. So an edge that left the driver crisply arrives at the receiver as a slower transition — and if the transition has not finished before the next bit begins, the previous bit's level is still partly present.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   launched              arrives after loss

   ┌───┐   ┌───┐         ╭──╮    ╭──╮
   │   │   │   │        ╱    ╲  ╱    ╲
   ┘   └───┘   └        ╯      ╲╯      ╲
   
   crisp, settles        still settling when
   within the UI         the next bit starts
                         -> the previous bit's
                            level bleeds in

So ISI has two distinct sources with different remedies, and conflating them wastes effort:

ISI sourceMechanismRemedy
ReflectionsEnergy returns after a round trip — 22.2 §3Termination at the junction (22.1)
Loss-induced smearingHigh frequencies attenuated, edges slowedEqualisation (22.4)

Terminating a channel whose ISI is loss-dominated helps very little. Equalising a channel whose ISI is reflection-dominated helps only for round trips within the equaliser's reach — 22.2 §3's table, where k of four or less is addressable and five or more is not. Diagnosing which source dominates is therefore a prerequisite to choosing a remedy, and §17's debugging table is built around that distinction.

3. Crosstalk — Energy From a Neighbour

Crosstalk is energy coupled from an adjacent line. A DDR data bus has many lines running in parallel for their whole length, switching at the same rate, so the coupling is structural rather than incidental.

Three properties distinguish it from ISI and each one changes how it behaves.

It arrives roughly simultaneously, not delayed. The aggressor's transition and the victim's disturbance are close in time, because the coupling is along the shared path rather than via a round trip. So crosstalk does not have 22.2 §3's k offset, and an equaliser that corrects post-cursor ISI does nothing for it.

Its magnitude depends on how many neighbours switch at once, and in which direction. One neighbour switching the same way as the victim helps or hurts depending on the coupling's sign; many neighbours switching together produce a much larger disturbance than one. This is the simultaneous switching problem, and it is why §9's monitor counts what it counts.

The worst case is a pattern, and it is constructible. The victim holds still while every neighbour switches together in the direction that hurts. That pattern is not rare or adversarial — it occurs naturally in real data.

4. Frequency-Dependent Loss

A conductor is not lossless. Two mechanisms remove energy, and both are stronger at higher frequencies.

Conductor loss. Current at high frequency crowds toward the surface of a conductor rather than filling it, so the effective cross-section falls and the resistance rises with frequency.

Dielectric loss. The insulating material between conductor and return path absorbs energy, and its absorption rises with frequency.

The consequence is not a uniform reduction in amplitude. The channel is a low-pass filter, so it treats a signal's components differently:

Signal contentFrequencyAttenuated
A long run of identical bitsLowLittle
Rapid alternationHighSubstantially
The sharp edges of any transitionHighestMost

Two consequences follow, and the second is the one that connects loss to everything else in the chapter.

Amplitude becomes pattern-dependent. A run of identical bits arrives close to full amplitude, because its content is low-frequency. An alternating pattern arrives attenuated. So the same channel delivers different amplitudes depending on what is being sent — and the receiver's threshold does not move to follow.

Loss produces ISI. §2's second source. Attenuating a transition's high-frequency content slows the transition, and a transition that has not settled by the end of its unit interval leaves residue in the next one.

Loss gets worse with rate for two compounding reasons, which is why it is the mechanism that comes to dominate at high speed. The signal's content moves to higher frequencies where attenuation is greater, and the unit interval shrinks so there is less time for a slowed transition to settle. Chapter 22.4 is where that compounding becomes the binding constraint.

5. Why They Interact

Chapter 20.5 §3 stated that the window's contributors cannot be added up because some of them are correlated, and left the correlation unexplained. Here is the explanation, and it is a single shared variable.

All three mechanisms are functions of the data pattern, so they are correlated through it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
                        the data pattern

              ┌────────────────┼────────────────┐
              ▼                ▼                ▼
        run length      simultaneous      frequency
        and history       switching        content
              │                │                │
              ▼                ▼                ▼
             ISI           crosstalk           loss
              │                │                │
              └────────────────┼────────────────┘

                    margin at the decision point

Which means the worst cases are not independent events that happen to coincide occasionally. A pattern that maximises one mechanism often moves another, and sometimes in the same direction:

PatternISICrosstalkLoss
Long run of identical bitsReinforces reflections of the same polarityFew transitions, so littleLow-frequency — little attenuation
Rapid alternationReflections partly cancelMany transitionsHigh-frequency — most attenuation
Victim static, neighbours switchingLittle on the victim's own lineMaximumLittle on the victim
A run followed by a transitionAccumulated reflection meets a fresh edgeModerateThe edge is most attenuated

The last row is the one that matters and it is why worst-case analysis is hard. The worst moment is not the worst pattern for any single mechanism; it is a transition arriving on top of accumulated history. The run built up a reflection and delivered full amplitude; then the transition has to fight both the accumulated energy and its own attenuation.

This is also, finally, the honest reason 20.5 §2 found that the published data-valid window exceeds the subtraction of tQH and tDQSQ. Those limits are characterised separately, their worst cases involve different patterns, and the composite window is measured rather than computed — because the mechanisms are correlated through a variable the arithmetic does not contain.

6. The Pattern Is the Variable

Three mechanisms, one shared input. That makes the data pattern the single most important experimental variable in signal integrity, and it has a direct consequence for everything Module 21 did.

A training result is only as good as the pattern it was measured with.

Chapter 21.2 §5 established that the four MPR default patterns have transition counts of 7, 3, 1 and 0 across an eight-beat burst. That is a graded set of frequency content, and §4 says the channel treats each differently. So:

Training patternExercisesMisses
01010101 — 7 transitionsMaximum frequency content; loss and edge-rate limitsLong-run reflection build-up
00001111 — 1 transitionRun-length effects; reflection reinforcementHigh-frequency attenuation
00000000 — 0 transitionsNothing in time at allEverything — 21.2 §5's trap
Real trafficWhatever it happens to containWhatever it happens not to

A pass region measured with one pattern is not the pass region for another, and the difference is not noise — it is the three mechanisms responding to different inputs. This is why 21.2 §16 had to say a wide region is evidence rather than measurement, and this chapter is the reason.

7. What a Measured Grid Is Not

Chapter 21.4 §5 established this discipline and this chapter must restate it, because the word “eye” appears in every discussion of signal integrity and the thing usually drawn is not one.

A pass/fail grid measured by sweeping a sampling point and a reference level is not an eye diagram.

An eye diagram is an analog superposition of many bit transitions, measured in volts against time, on instrumentation with the bandwidth to resolve them. A grid is a set of pass/fail outcomes indexed by two integer code axes. They look similar drawn on a page and they are different objects:

Neither grid axis has a known scale. Chapter 21.1 §4 established that delay codes have no defined size; 21.4 §4 established that the VrefDQ code step is specified only as a 0.5%-to-0.8% band with one value per design. So a grid's aspect ratio is meaningless and its area is not an area.

A grid has no error-rate dimension. A cell failed because a comparison failed — once, for one pattern, at one temperature. An eye's boundary is a statistical contour at some bit error rate. There is no axis in a grid along which that could even be expressed.

And the specification says as much about its own construct. JESD79-4 describes a DQ receiver compliance mask and states plainly that it is not the valid data-eye. If the standard's own mask is not the eye, a pass/fail grid measured through a whole capture path certainly is not.

8. Data Bus Inversion

Now the mitigation, and it is the first one in this module that is genuinely digital, genuinely algorithmic, and specified precisely enough to build.

The encoding is verified and simple. DBI_n is an input/output that identifies whether to store or output the true or the inverted data. If DBI_n is LOW, the data is stored or output after inversion inside the device; it is not inverted if DBI_n is HIGH.

The registers, all in MR5:

FieldMeaning
A12Read DBI — 0 = Disable, 1 = Enable
A11Write DBI — 0 = Disable, 1 = Enable
A10Data Mask — 0 = Disable, 1 = Enable

Those three bits sitting together is not a coincidence, and it is the chapter's most important architectural point. DM is multiplexed with the DBI function by the MR5 A10, A11 and A12 setting — they share a pin. So:

Enabling DBI means giving up data masking. The pin that carried DM_n carries DBI_n instead. A system that needs byte-granular write masking (6.11) and wants DBI on writes has a conflict that no configuration resolves, because there is one pin.

That is the trade the central law predicts, in its sharpest form in the module: DBI does not improve the channel. It re-encodes the data, and it pays for the encoding with a pin that had another job.

9. What the Policy Is For, and Its Fixed Point

Since JEDEC specifies the encoding and not the policy, the policy is a design choice — and the natural one follows from what the channel finds expensive.

A DDR4 data pin is terminated toward a supply rail (22.1 §12). Holding a pin at the level away from that rail draws current through the termination. So the number of pins held at the expensive level, per beat, is a real cost — and it is also a proxy for simultaneous switching, because a beat with many pins at one level is followed by a transition on many pins at once (§3).

The natural policy is therefore: invert when more than half the bits in the group would otherwise be at the expensive level. For an eight-bit group with the expensive level taken as zero, that is “invert when more than four bits are zero.”

Here is that policy on eight beats, with every count recomputed:

BeatRawZerosInvert?DBI_nOn the wireZeros on wire
00x008yes00xFF0
10x017yes00xFE1
20x0F4no10x0F4
30xFF0no10xFF0
40x554no10x554
50x036yes00xFC2
60x075yes00xF83
70xF04no10xF04

Total zeros on the wire falls from 38 to 18. And more usefully than the total: the worst single beat falls from 8 to 4.

That second number is the guarantee, and it is worth stating as one. For an eight-bit group, the policy ensures no beat ever presents more than four bits at the expensive level — because any beat that would have is inverted, and inverting a beat with five or more zeros leaves three or fewer.

The fixed point is the interesting part. Beats 2, 4 and 7 all have exactly four zeros and are not inverted, and could not be improved by inverting: a four-four split maps to a four-four split. So the policy's guarantee is tight — four is both the bound and an attainable worst case, and no threshold policy on a symmetric encoding can do better.

10. The Mechanisms and the Mitigation

How the three degradation mechanisms and the one digital mitigation relate, in four rows. The top row holds inter-symbol interference, crosstalk, frequency-dependent loss, and the shared margin budget that all three consume. The second row holds the data pattern, which is the single input all three mechanisms depend on, the net itself, and the neighbouring lines that couple into it. The third row holds the data bus inversion encoder, the data mask pin that DBI takes over and therefore gives up, and the receiver. The bottom row is one bit finally resolved. Edges run from the data pattern into inter-symbol interference and into the encoder, from the neighbouring lines into crosstalk, from all three mechanisms into the shared budget, from the budget into the receiver, from the encoder onto the net and from the net into the receiver, from the encoder to the data mask pin it displaces, and from the receiver to the resolved bit. The structure shows that the mitigation acts on the pattern rather than on any of the three mechanisms.ISIown earlier bitsCrosstalkneighbour's energyLossenergy REMOVEDMargin budget20.5 published itData patternthe shared input — §5The net22.2's junctionsNeighbour linesswitch togetherDBI encoderchanges the patternDM pingiven up for DBIReceiverone thresholdOne bit resolvedno quality report12

The structure makes §8's claim visible. The encoder's edge goes to the pattern and to the net — not to any of the three mechanism nodes. DBI does not touch ISI, crosstalk or loss. It changes what the pattern is, and the three mechanisms then respond to a different input. That is the whole of what an encoding can do.

And the edge from the encoder to the DM pin is the cost, drawn rather than described.

11. A Burst Through the Encoder

DBI decisions across eight beats — counts, not volts

8 cycles
Eight beats of write data passing through a data-bus-inversion encoder, showing counts rather than any analog quantity. The clock runs throughout. The raw data row carries the bytes zero, one, fifteen, two hundred and fifty five, eighty five, three, seven and two hundred and forty. The zeros-in row gives the number of bits at the expensive level in each raw beat: eight, seven, four, zero, four, six, five and four. The DBI_n row is the encoder's decision, low meaning invert: it is low for beats zero, one, five and six, where more than four bits were at the expensive level, and high for the others. The transmitted row shows what actually goes on the wire after any inversion. The zeros-out row shows the resulting count per beat: zero, one, four, zero, four, two, three and four, so no beat exceeds four. The worst-so-far row tracks the running maximum and settles at four. Nothing in this diagram represents a voltage, an eye, or a margin; every row is a count of bits.worst case 8 zeros, inverted to 0worst case 8 zeros,inverted to 0a 4/4 split — DBI cannot improve ita 4/4 split — DBI cannotimprove itworst on the wire capped at 4 of 8worst on the wire capped at4 of 8CKraw_data00010FFF550307F0zeros_in87404654dbi_ntx_dataFFFE0FFF55FCF8F0zeros_outworst_seen01444444t0t1t2t3t4t5t6t7

Compare zeros_in with zeros_out and the policy's guarantee is visible: the input reaches 8, the output never exceeds 4. Compare beats 2, 4 and 7 and the fixed point is visible: a four-four split passes through untouched because inverting it would change nothing.

Every row here is a count of bits. There is no voltage, no margin and no eye in this figure, by the discipline of §7.

12. The DBI Codec

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// dbi_codec -- data bus inversion encode and decode.
//
// CLASSIFICATION: educational, synthesisable. Implements the encoding
// JESD79-4 specifies (DBI_n LOW => inverted) with a POLICY-selectable
// decision rule, because the standard specifies the encoding and NOT
// the decision (§8's verified callout).
//
// WHAT IT DOES NOT MODEL:
//   - any voltage, current, power, margin, or eye
//   - crosstalk, ISI, or loss. It changes the PATTERN; §10's diagram
//     shows the encoder touching none of the three mechanisms.
//   - whether the re-encoded pattern is actually better for a given
//     channel. That is a channel property.
//
// COUNTING BITS IS NOT OPENING AN EYE.
// ---------------------------------------------------------------------
module dbi_codec #(
  // Bits per DBI group. One DBI_n accompanies each group.
  parameter int GROUP_W = 8,
  // Which level is "expensive" -- the one whose count we minimise.
  // For a pin terminated toward a rail, holding it away from that rail
  // draws current (§9). Which level that is depends on the termination
  // scheme, so it is a parameter and not an assumption.
  parameter bit EXPENSIVE_LEVEL = 1'b0,
  // Invert when the expensive count EXCEEDS this. GROUP_W/2 is the
  // natural choice and gives §9's tight bound. POLICY, not JEDEC.
  parameter int THRESHOLD = GROUP_W/2,
  // A COUNT of bits up to GROUP_W needs clog2(GROUP_W+1).
  parameter int CNT_W = $clog2(GROUP_W + 1)
) (
  // ── Encode side (write DBI: the controller decides).
  input  logic [GROUP_W-1:0] enc_in,
  output logic [GROUP_W-1:0] enc_out,
  output logic               enc_dbi_n,     // LOW = inverted
  output logic [CNT_W-1:0]   enc_cnt_in,    // expensive bits before
  output logic [CNT_W-1:0]   enc_cnt_out,   // expensive bits after

  // ── Decode side (read DBI: the device decided, we undo it).
  input  logic [GROUP_W-1:0] dec_in,
  input  logic               dec_dbi_n,
  output logic [GROUP_W-1:0] dec_out,

  // ── Observability.
  output logic               enc_at_fixed_point,  // §9: cannot improve
  output logic               err_no_improvement
);

  initial begin
    if (GROUP_W < 2)
      $fatal(1, "dbi_codec: GROUP_W must be at least 2");
    if (THRESHOLD < 0 || THRESHOLD > GROUP_W)
      $fatal(1, "dbi_codec: THRESHOLD must be 0..GROUP_W");
    // A threshold below half makes the encoder invert beats that were
    // already good, which increases the expensive count. Legal to
    // build, never correct to ship -- so it is a hard stop.
    if (THRESHOLD * 2 < GROUP_W)
      $fatal(1, "dbi_codec: THRESHOLD (%0d) below half of GROUP_W (%0d)",
             THRESHOLD, GROUP_W);
  end

  // ── Count the expensive level. $countones counts ones, so invert
  //    first when the expensive level is zero.
  logic [GROUP_W-1:0] enc_masked;
  assign enc_masked = EXPENSIVE_LEVEL ? enc_in : ~enc_in;

  logic [CNT_W-1:0] n_exp;
  assign n_exp = CNT_W'($countones(enc_masked));

  // ── The decision. DBI_n is LOW when we invert (JESD79-4's polarity).
  logic invert;
  assign invert    = (n_exp > CNT_W'(THRESHOLD));
  assign enc_dbi_n = ~invert;
  assign enc_out   = invert ? ~enc_in : enc_in;

  // ── The resulting count, computed from the OUTPUT rather than
  //    derived as (GROUP_W - n_exp). Computing it independently is what
  //    lets §15's P3 check the two against each other instead of
  //    checking an identity against itself.
  logic [GROUP_W-1:0] out_masked;
  assign out_masked  = EXPENSIVE_LEVEL ? enc_out : ~enc_out;
  assign enc_cnt_in  = n_exp;
  assign enc_cnt_out = CNT_W'($countones(out_masked));

  // ── §9's fixed point: an exactly-balanced group. Inverting it would
  //    change nothing, so the encoder leaves it alone and the bound is
  //    attained rather than merely approached.
  assign enc_at_fixed_point = (GROUP_W % 2 == 0)
                            && (n_exp == CNT_W'(GROUP_W/2));
  // The encoder made things worse. Impossible for THRESHOLD >= half,
  // which the elaboration guard enforces; published so the guard's
  // necessity is observable rather than assumed.
  assign err_no_improvement = (enc_cnt_out > enc_cnt_in);

  // ── Decode is the same inversion, driven by the received flag. It is
  //    deliberately NOT a copy of the encoder: the receiver does not
  //    re-derive the decision, it obeys the transmitted one. A decoder
  //    that recomputed the policy would corrupt data whenever the
  //    transmitter's policy differed from its own -- which is legal,
  //    since JEDEC does not specify the policy (§8).
  assign dec_out = dec_dbi_n ? dec_in : ~dec_in;

endmodule

The comment on dec_out is the most important line in the block. The decoder must obey the transmitted flag and must not recompute the decision. Because JEDEC specifies the encoding and not the policy, a controller and a device may legitimately use different thresholds — and a decoder that re-derived the policy would corrupt data in exactly that legal case. It is a one-line implementation and a genuinely easy mistake to reason your way into.

13. Two Lanes on a x16

§12 encodes one group. A real device has one or more, and the count is a width property that JESD79-4 states explicitly in its write-CRC section.

For an x8 device the controller must send ones in transfer 8 and transfer 9 of the DBI_n lane when the DBI function is enabled. For an x16 device it must do the same on both the DBIL_n and DBIU_n lanes. So an x16 has two independent DBI groups — a lower byte and an upper byte — each with its own flag and its own decision.

That independence is the design point, and it is easy to get wrong in either direction:

Wrong approachWhat it produces
One flag for all 16 bitsA 16-bit group inverts together, so a beat that is bad in one byte and good in the other cannot be improved
Two flags computed from the 16-bit countBoth bytes follow the aggregate, so a balanced whole with two skewed halves is left alone
Two flags, each from its own byte's countCorrect — each byte is bounded independently

The reason the third is correct follows from §9's bound. The policy guarantees no group exceeds half its bits at the expensive level. Applying it per byte gives that guarantee per byte; applying it to a 16-bit aggregate guarantees only the aggregate, which permits one byte at eight of eight while the other sits at zero.

And per-byte is the electrically meaningful grouping for the same reason 20.2 §2 argued the byte lane is the unit of timing authority: the pins in a byte share a strobe and a routing path, so they are the pins that switch together and couple into one another (§3).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// dbi_group_wrapper -- one DBI codec per group, with per-group and
// channel-wide statistics.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHAT IT DOES NOT MODEL: everything dbi_codec excludes -- no voltage,
// no current, no margin, no eye, no crosstalk, no ISI, no loss.
//
// WHY IT EXISTS: JESD79-4 gives an x8 one DBI_n lane and an x16 two
// (DBIL_n and DBIU_n). Each group's decision MUST come from its own
// group's count, because §9's bound is per group. A single flag over
// all lanes, or per-group flags derived from an aggregate count, both
// lose the guarantee -- see the table above.
// ---------------------------------------------------------------------
module dbi_group_wrapper #(
  parameter int GROUPS  = 2,          // x8 -> 1, x16 -> 2
  parameter int GROUP_W = 8,
  parameter bit EXPENSIVE_LEVEL = 1'b0,
  parameter int THRESHOLD = GROUP_W/2,
  parameter int TOTAL_W = GROUPS * GROUP_W,
  parameter int CNT_W   = $clog2(GROUP_W + 1),
  // A COUNT over ALL lanes needs clog2(TOTAL_W+1), which is wider than
  // a per-group count. Reusing CNT_W here would silently truncate on
  // an x16 -- a count of 16 does not fit in four bits.
  parameter int TOT_W   = $clog2(TOTAL_W + 1),
  parameter int GRP_W   = (GROUPS <= 1) ? 1 : $clog2(GROUPS)
) (
  input  logic                 clk,
  input  logic                 rst_n,

  input  logic                 beat_valid,
  input  logic [TOTAL_W-1:0]   raw_data,

  // ── Per group: what goes on the wire, and its flag.
  output logic [TOTAL_W-1:0]   tx_data,
  output logic [GROUPS-1:0]    dbi_n,

  // ── READ side. MR5 A12 enables Read DBI independently of Write DBI
  //    (MR5 A11), so a device may invert on reads while the controller
  //    does not on writes. The decode path is therefore NOT optional
  //    just because the encode path is configured off.
  input  logic [TOTAL_W-1:0]   rx_data,
  input  logic [GROUPS-1:0]    rx_dbi_n,
  output logic [TOTAL_W-1:0]   rx_out,

  // ── Statistics. Per-group worst and channel-wide total.
  output logic [TOT_W-1:0]     total_expensive_in,
  output logic [TOT_W-1:0]     total_expensive_out,
  output logic [CNT_W-1:0]     worst_group_out,
  output logic [GROUPS-1:0]    group_at_fixed_point,
  output logic [15:0]          cnt_inversions,

  // A group exceeded the bound §9 guarantees. Should be impossible;
  // published so the guarantee is observable rather than assumed.
  output logic                 err_bound_violated,
  // Every group inverted on the same beat -- legal, and worth knowing,
  // because it means the whole beat was at the expensive level.
  output logic                 all_groups_inverted
);

  initial begin
    if (GROUPS < 1)
      $fatal(1, "dbi_group_wrapper: GROUPS must be at least 1");
    if (GROUP_W < 2)
      $fatal(1, "dbi_group_wrapper: GROUP_W must be at least 2");
    if (THRESHOLD * 2 < GROUP_W)
      $fatal(1, "dbi_group_wrapper: THRESHOLD below half of GROUP_W");
  end

  logic [CNT_W-1:0] cnt_in  [GROUPS];
  logic [CNT_W-1:0] cnt_out [GROUPS];
  logic             fixed   [GROUPS];

  // ── One codec per group. The generate loop is the whole argument:
  //    each instance sees ONLY its own group's bits, so its decision
  //    cannot be contaminated by another group's balance.
  for (genvar g = 0; g < GROUPS; g++) begin : g_codec
    logic [GROUP_W-1:0] slice_in, slice_out;
    logic               flag;

    assign slice_in = raw_data[g*GROUP_W +: GROUP_W];

    dbi_codec #(
      .GROUP_W         (GROUP_W),
      .EXPENSIVE_LEVEL (EXPENSIVE_LEVEL),
      .THRESHOLD       (THRESHOLD)
    ) u_codec (
      .enc_in             (slice_in),
      .enc_out            (slice_out),
      .enc_dbi_n          (flag),
      .enc_cnt_in         (cnt_in[g]),
      .enc_cnt_out        (cnt_out[g]),
      // Decode side unused in this direction; tied off explicitly
      // rather than left floating.
      .dec_in             ('0),
      .dec_dbi_n          (1'b1),
      .dec_out            (),
      .enc_at_fixed_point (fixed[g]),
      .err_no_improvement ()
    );

    assign tx_data[g*GROUP_W +: GROUP_W] = slice_out;
    assign dbi_n[g]                      = flag;
    assign group_at_fixed_point[g]       = fixed[g];

    // ── Decode, per group, and deliberately NOT through the codec's
    //    encode logic. §12 established that a decoder must OBEY the
    //    received flag rather than recompute the decision, because the
    //    two ends may legitimately use different thresholds. Writing
    //    it as a bare conditional inversion here makes that structural:
    //    there is no threshold in this path to disagree with.
    assign rx_out[g*GROUP_W +: GROUP_W] =
             rx_dbi_n[g] ? rx_data[g*GROUP_W +: GROUP_W]
                         : ~rx_data[g*GROUP_W +: GROUP_W];
  end

  // ── Aggregates. Summed at the wider TOT_W so an x16 cannot overflow.
  logic [TOT_W-1:0] sum_in, sum_out;
  logic [CNT_W-1:0] worst;
  logic             bound_bad;
  always_comb begin
    sum_in    = '0;
    sum_out   = '0;
    worst     = '0;
    bound_bad = 1'b0;
    for (int g = 0; g < GROUPS; g++) begin
      sum_in  = sum_in  + TOT_W'(cnt_in[g]);
      sum_out = sum_out + TOT_W'(cnt_out[g]);
      if (cnt_out[g] > worst) worst = cnt_out[g];
      if (cnt_out[g] > CNT_W'(THRESHOLD)) bound_bad = 1'b1;
    end
  end

  logic [15:0] inv_cnt;
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) inv_cnt <= '0;
    else if (beat_valid && (inv_cnt != 16'hFFFF))
      // Count inversions, not beats: a beat inverting both groups of
      // an x16 costs two flags' worth of decision, and the count is a
      // proxy for how hard the encoder is working.
      inv_cnt <= inv_cnt + 16'($countones(~dbi_n));
  end

  assign total_expensive_in  = sum_in;
  assign total_expensive_out = sum_out;
  assign worst_group_out     = worst;
  assign cnt_inversions      = inv_cnt;
  assign err_bound_violated  = beat_valid && bound_bad;
  assign all_groups_inverted = beat_valid && (dbi_n == '0);

endmodule

Two details in that block are worth calling out because both are the kind of thing that survives review and fails in the field.

TOT_W is a different width from CNT_W, and it has to be. A per-group count on an x8 reaches 8 and fits in four bits. A channel-wide count on an x16 reaches 16 and does not. Reusing the per-group width for the aggregate is exactly the count-versus-index error, and on an x16 it would report a full beat as zero.

The unused codec decode port is tied off explicitly. Leaving dec_in floating would synthesise, simulate, and produce an output nobody reads — until someone wires dec_out up later and inherits a latch. Tying it and leaving dec_out unconnected states the intent.

And the read path is separate from the codec entirely. MR5 A12 enables Read DBI independently of MR5 A11's Write DBI, so a device may invert on reads while the controller does not on writes — the two directions are configured separately and need not match. The wrapper's decode is therefore a bare conditional inversion driven by the received flag, with no threshold anywhere in it. That is §12's “obey, do not recompute” rule made structural: there is no policy in the decode path for the two ends to disagree about.

And two assertions belong with it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P10 -- §9's bound holds PER GROUP, on every beat. This is the
// property the wrapper exists to preserve, and the one that fails for
// both wrong approaches in the table above.
property p_bound_per_group;
  @(posedge clk) disable iff (!rst_n)
    beat_valid |-> (worst_group_out <= CNT_W'(THRESHOLD));
endproperty
assert property (p_bound_per_group);

// P11 -- the aggregate never worsens either. Weaker than P10 and worth
// having separately: it is the property a single-flag implementation
// could still satisfy while violating P10.
property p_aggregate_never_worse;
  @(posedge clk) disable iff (!rst_n)
    beat_valid |-> (total_expensive_out <= total_expensive_in);
endproperty
assert property (p_aggregate_never_worse);

// P12 -- the decode path contains no threshold and therefore cannot
// disagree with the transmitter. Stated as a round trip through the
// wrapper's own two paths, per group.
property p_wrapper_round_trip;
  @(posedge clk) disable iff (!rst_n)
    ((rx_data == tx_data) && (rx_dbi_n == dbi_n)) |-> (rx_out == raw_data);
endproperty
assert property (p_wrapper_round_trip);

// ── Cover: an x16 beat where the two groups DISAGREE. This is the
// case that distinguishes per-group decisions from an aggregate one,
// and a regression that never hits it has not tested the wrapper.
cover property (@(posedge clk) disable iff (!rst_n)
                  beat_valid && (GROUPS > 1) && (dbi_n != '0) && (dbi_n != '1));
cover property (@(posedge clk) disable iff (!rst_n) all_groups_inverted);
// A read beat whose groups' flags disagree -- the device inverted one
// byte and not the other, which only a per-group decode handles.
cover property (@(posedge clk) disable iff (!rst_n)
                  beat_valid && (GROUPS > 1) && (rx_dbi_n != '0) && (rx_dbi_n != '1));

P10 and P11 together are the interesting pair. A single-flag-over-all-lanes implementation satisfies P11 and violates P10, which is precisely why both are worth writing: the weaker property passes for the wrong design, and only the per-group one catches it.

14. The Transition and Switching Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// transition_density_monitor -- counts the digital events the three
// mechanisms of §1 are functions of.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHAT IT DOES NOT MODEL:
//   - crosstalk, coupling coefficients, ISI, loss, amplitude, margin
//   - any relationship between a COUNT and a disturbance. §3: crosstalk
//     scales with simultaneous switching. This block counts the
//     switching. The scaling factor is a board property.
//
// WHY IT IS STILL WORTH BUILDING: §5 established that all three
// mechanisms are functions of the data pattern. The pattern is digital
// and fully observable, so its worst-case STRUCTURE is measurable in
// RTL even though its consequence is not.
// ---------------------------------------------------------------------
module transition_density_monitor #(
  parameter int LANES = 8,
  // Longest identical-bit run worth reporting. §5's first row: a long
  // run reinforces same-polarity reflections and arrives at full
  // amplitude, so it is one half of the worst moment.
  parameter int RUN_LIMIT = 6,
  parameter int CNT_W = $clog2(LANES + 1),
  parameter int RUN_W = (RUN_LIMIT <= 1) ? 1 : $clog2(RUN_LIMIT + 2)
) (
  input  logic             clk,
  input  logic             rst_n,

  input  logic             beat_valid,
  input  logic [LANES-1:0] beat_data,

  // ── Per-beat measures.
  output logic [CNT_W-1:0] switching_now,     // lanes that changed
  output logic [CNT_W-1:0] switching_worst,
  output logic             sso_alarm,         // all lanes switched

  // ── Run tracking, per lane, reduced to the worst.
  output logic [RUN_W-1:0] longest_run,
  output logic             run_over_limit,

  // ── §5's last row: a transition arriving on top of accumulated
  //    history. This is the composite the chapter names as the worst
  //    moment, and it is detectable in the digital domain even though
  //    its severity is not.
  output logic             worst_moment,
  output logic [15:0]      cnt_worst_moments,

  output logic             err_valid_without_change
);

  initial begin
    if (LANES < 1)
      $fatal(1, "transition_density_monitor: LANES must be >= 1");
    if (RUN_LIMIT < 1)
      $fatal(1, "transition_density_monitor: RUN_LIMIT must be >= 1");
  end

  logic [LANES-1:0] prev_data;
  logic             have_prev;

  // Declared with the state because the assigns below read them.
  logic [CNT_W-1:0] sw_worst;
  logic [RUN_W-1:0] run_len;
  logic [15:0]      wm_cnt;

  logic [LANES-1:0] changed;
  assign changed = have_prev ? (beat_data ^ prev_data) : '0;

  logic [CNT_W-1:0] sw_now;
  assign sw_now = beat_valid ? CNT_W'($countones(changed)) : '0;

  // Simultaneous switching of EVERY lane: §3's constructible worst
  // pattern, and the one a monitor should never silently pass.
  assign sso_alarm = beat_valid && have_prev && (sw_now == CNT_W'(LANES));

  // The composite of §5's last row: a run has accumulated AND this
  // beat brings a transition. Both halves are required -- a transition
  // with no history behind it is not the worst moment, and a long run
  // with no transition at its end has not yet been tested.
  assign worst_moment = beat_valid && have_prev
                     && (run_len >= RUN_W'(RUN_LIMIT))
                     && (sw_now != '0);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      prev_data <= '0;
      have_prev <= 1'b0;
      sw_worst  <= '0;
      run_len   <= '0;
      wm_cnt    <= '0;
    end else if (beat_valid) begin
      // Run length counts beats with NO change on any lane. Saturates
      // one above the limit so run_over_limit latches meaningfully
      // rather than wrapping back under it.
      if (have_prev && (changed == '0)) begin
        if (run_len <= RUN_W'(RUN_LIMIT)) run_len <= run_len + RUN_W'(1);
      end else begin
        run_len <= '0;
      end

      if (sw_now > sw_worst) sw_worst <= sw_now;

      if (worst_moment && (wm_cnt != 16'hFFFF)) wm_cnt <= wm_cnt + 16'd1;

      prev_data <= beat_data;
      have_prev <= 1'b1;
    end
  end

  assign switching_now     = sw_now;
  assign switching_worst   = sw_worst;
  assign longest_run       = run_len;
  assign run_over_limit    = (run_len >= RUN_W'(RUN_LIMIT));
  assign cnt_worst_moments = wm_cnt;

  // A valid beat identical to its predecessor is legal data, not an
  // error -- but it is worth publishing, because a stimulus that never
  // changes is §6's callout: a pattern carrying no information in time.
  assign err_valid_without_change = beat_valid && have_prev && (changed == '0);

endmodule

worst_moment is the output worth having, and it is the chapter's §5 argument made into a signal. It requires both an accumulated run and a transition at the end of it — because either alone is benign and the combination is what §5 identified as the hard case. A monitor that flagged long runs alone, or transitions alone, would report constantly and mean nothing.

15. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind unit note: P1-P5 constrain dbi_codec, which is purely
// combinational and has no clk port; they are written for a bind unit
// that supplies a sampling clock and they constrain a FUNCTION rather
// than a sequence. P6-P9 constrain transition_density_monitor and see
// its clk and rst_n directly.

// P1 -- the encoding polarity is JESD79-4's. DBI_n LOW means the
// output is the inverse of the input; HIGH means it is unchanged.
// If this holds, the block cannot be wired backwards.
property p_polarity_is_jedec;
  @(posedge clk)
    (enc_dbi_n == 1'b0) ? (enc_out == ~enc_in) : (enc_out == enc_in);
endproperty
assert property (p_polarity_is_jedec);

// P2 -- encode then decode is the identity. THE property: a codec that
// loses data is worse than no codec.
property p_round_trip_is_lossless;
  @(posedge clk)
    ((dec_in == enc_out) && (dec_dbi_n == enc_dbi_n)) |-> (dec_out == enc_in);
endproperty
assert property (p_round_trip_is_lossless);

// P3 -- the encoder never increases the expensive count. The two
// counts are computed independently from input and output, so this
// compares two measurements rather than checking an identity.
property p_never_worse;
  @(posedge clk)
    (enc_cnt_out <= enc_cnt_in);
endproperty
assert property (p_never_worse);

// P4 -- §9's bound. After encoding, the expensive count never exceeds
// the threshold. This is the guarantee the policy exists to provide.
property p_bound_holds;
  @(posedge clk)
    (enc_cnt_out <= CNT_W'(THRESHOLD));
endproperty
assert property (p_bound_holds);

// P5 -- the fixed point is left alone. An exactly-balanced group is
// not inverted, because inverting it would change nothing.
property p_fixed_point_untouched;
  @(posedge clk)
    enc_at_fixed_point |-> (enc_dbi_n == 1'b1);
endproperty
assert property (p_fixed_point_untouched);

// P6 -- a run and a switch are mutually exclusive on a given beat, so
// the run counter cannot both extend and reset.
property p_run_or_switch;
  @(posedge clk) disable iff (!rst_n)
    (beat_valid && (switching_now != '0)) |=> (longest_run == '0);
endproperty
assert property (p_run_or_switch);

// P7 -- the worst-switching high-water mark never falls.
property p_switching_worst_monotonic;
  @(posedge clk) disable iff (!rst_n)
    (switching_worst >= $past(switching_worst, 1));
endproperty
assert property (p_switching_worst_monotonic);

// P8 -- §5's composite requires BOTH halves. A worst moment without an
// accumulated run, or without a transition, is not one.
property p_worst_moment_needs_both;
  @(posedge clk) disable iff (!rst_n)
    worst_moment |-> (run_over_limit && (switching_now != '0));
endproperty
assert property (p_worst_moment_needs_both);

// P9 -- every lane switching is always reported. §3's constructible
// worst pattern must never pass unnoticed.
property p_sso_always_reported;
  @(posedge clk) disable iff (!rst_n)
    (beat_valid && (switching_now == CNT_W'(LANES))) |-> sso_alarm;
endproperty
assert property (p_sso_always_reported);

// ── Cover.
cover property (@(posedge clk) enc_dbi_n == 1'b0);            // inverted
cover property (@(posedge clk) enc_at_fixed_point);            // §9's 4/4
cover property (@(posedge clk) enc_cnt_in == CNT_W'(GROUP_W)); // all expensive
cover property (@(posedge clk) disable iff (!rst_n) sso_alarm);
cover property (@(posedge clk) disable iff (!rst_n) worst_moment);
// A long run that ends WITHOUT a transition -- benign, and the
// discriminator that proves P8 is not vacuous.
cover property (@(posedge clk) disable iff (!rst_n)
                  run_over_limit && (switching_now == '0));
// The §6 trap: a stimulus that never changes at all.
cover property (@(posedge clk) disable iff (!rst_n)
                  err_valid_without_change ##[1:8] err_valid_without_change);

P2 and P4 are the pair to keep. P2 is correctness — a codec that loses data is worse than no codec — and P4 is the only thing in this chapter that resembles a guarantee about the channel: after encoding, no beat presents more than half its bits at the expensive level.

The sixth cover exists to keep P8 honest. A long run that ends without a transition is benign, and covering it proves the property is discriminating between the two halves of §5's composite rather than firing on either.

16. Corner Cases

CaseBehaviourWhy
All bits at the expensive levelInverted; output count is zeroThe best case for the policy
Exactly balanced groupNot inverted; enc_at_fixed_point§9 — inverting changes nothing
THRESHOLD below half GROUP_W$fatal at elaborationWould invert already-good beats, increasing the count
THRESHOLD = GROUP_WNever inverts; DBI is inertLegal as a baseline for comparison
GROUP_W oddNo fixed point existsenc_at_fixed_point never asserts; the bound is strict
EXPENSIVE_LEVEL = 1Counts and inverts on ones insteadWhich level is costly depends on the termination scheme
Decoder policy differs from encoder'sStill correctThe decoder obeys the flag; §12 explains why it must not recompute
dec_dbi_n corrupted in transitData silently invertedNo detection here — §17's first row; this is what CRC is for
Monitor: first beathave_prev low, no switching countedA first beat has no predecessor to differ from
Monitor: identical consecutive beatsRun extends; err_valid_without_change§6's callout — a stimulus with no information in time
Monitor: run at the limit then a switchworst_moment fires onceBoth halves of §5's composite present
RUN_LIMIT longer than any real burstworst_moment never firesPolicy; the run length is still published

Row eight is the one that deserves attention, and it is the bridge to the next chapter. DBI_n is a wire like any other, and if it is corrupted, a whole group of bits is inverted with no local indication. DBI has no self-checking property — it cannot tell a flipped flag from data that genuinely wanted inverting. That failure is detectable only by something that covers the whole frame, which is 22.4's write CRC.

17. DV — Exhaustive on the Codec, Structural on the Monitor

The codec is small enough to verify exhaustively, and when that is possible it should be done rather than sampled.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Exhaustive codec check. For GROUP_W = 8 this is 256 cases, which is
// nothing -- so there is no excuse for random sampling here.
// The reference is computed a DIFFERENT way from the DUT: by building
// the expected output from the count rather than by inverting.
// SIMULATION-ONLY.
function automatic bit check_all(int unsigned gw, int unsigned thr,
                                bit expensive);
  for (int unsigned v = 0; v < (1 << gw); v++) begin
    int unsigned n = 0;
    for (int unsigned b = 0; b < gw; b++)
      if (((v >> b) & 1) == expensive) n++;
    begin
      bit inv          = (n > thr);
      int unsigned exp = inv ? (~v & ((1 << gw) - 1)) : v;
      int unsigned n2  = 0;
      for (int unsigned b = 0; b < gw; b++)
        if (((exp >> b) & 1) == expensive) n2++;
      // three independent claims per input value
      if (n2 > thr)  return 1'b0;   // the bound
      if (n2 > n)    return 1'b0;   // never worse
      if ((gw % 2 == 0) && (n == gw/2) && inv) return 1'b0;  // fixed point
    end
  end
  return 1'b1;
endfunction
CheckWhat it establishes
All 2**GROUP_W inputs: bound, never-worse, fixed pointP3, P4 and P5 exhaustively, not by sampling
All inputs: encode then decode returns the inputP2 exhaustively
Decode with a deliberately different threshold in the referenceThe decoder obeys the flag rather than recomputing — §12
Flip dec_dbi_n; expect the output to be the inverse of the input§16's row eight, confirmed as undetectable locally
EXPENSIVE_LEVEL = 1: repeat the full sweepThe parameter is real, not decorative
THRESHOLD = GROUP_W: expect enc_dbi_n always highThe inert baseline
Monitor: all-lanes-switch pattern; expect sso_alarmP9, driven
Monitor: run of RUN_LIMIT then a switch; expect one worst_moment§5's composite, counted
Monitor: run of RUN_LIMIT with no switch; expect no worst_momentThe discriminator — P8 is not vacuous
Replay §11's eight beats; expect zeros_out of 0,1,4,0,4,2,3,4The published table, reproduced from the RTL

The third check is the one that catches a subtle and tempting bug:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  DECODER RECOMPUTES THE POLICY
    GROUP_W = 8, EXPENSIVE_LEVEL = 0.
    Transmitter threshold 4 (the natural policy).
    Receiver built with threshold 5 -- also legal, since JEDEC
    specifies the encoding and NOT the decision (§8).

    transmit 0x07 : five zeros, 5 > 4  -> INVERT
                    on the wire 0xF8, DBI_n = 0

    decoder as specified in §12 (obeys the flag)
        DBI_n = 0 -> invert 0xF8 -> 0x07    CORRECT

    decoder that recomputes with ITS threshold of 5
        received 0xF8 has three zeros, 3 > 5 is false
        -> concludes "not inverted", returns 0xF8    WRONG

    diagnosis : the recomputing decoder is self-consistent and wrong.
                It asks "would I have inverted this?" when the only
                answerable question is "did the transmitter invert it?"
    why it passes review : with matched thresholds it is correct on
                every input, so an exhaustive test of a matched pair
                finds nothing. It fails ONLY across a legal policy
                mismatch -- which is exactly the case a single-vendor
                testbench never constructs.
    caught by : this check only, and only because the reference was
                deliberately built with a different threshold. P2 does
                not fire, because P2 compares the codec against
                itself.

That is the most instructive failure in the chapter. An exhaustive test of a matched encoder and decoder pair passes completely, and the bug appears only when the two ends legitimately disagree — which they are permitted to do, because the standard specifies the encoding and leaves the policy open. Verifying a codec against itself cannot find it.

18. Debugging

SymptomLikely causeHow to confirm
A whole byte group inverted, occasionallyDBI_n corrupted in transit — §16No local detection; needs frame-level CRC (22.4)
Data masking stopped workingDBI enabled; DM shares the pin — §8MR5 A10/A11/A12 — one pin, one function
Errors correlate with specific byte valuesPattern-dependent mechanism — §5Sweep patterns; note which beats fail
Errors worst after a long run of identical bits§5's last row — accumulated history plus an edgeworst_moment count; try a pattern with shorter runs
Errors worst when many lanes switch togetherCrosstalk — §3sso_alarm; a victim-static, neighbours-switching pattern reproduces it
Margin poor and termination is correctLoss-dominated, not reflection-dominated — §2Termination will not help; equalisation may (22.4)
Margin poor, degrades sharply with rateLoss compounding — §4Two mechanisms worsen together: content and interval
Training passes, traffic failsPattern differs between the two — §6Compare the training pattern's transition count with real traffic
Training region wide with one MPR pattern, narrow with anotherExpected — §6Not a bug; the patterns exercise different mechanisms
DBI enabled and margin unchangedThe channel was not level-limitedDBI bounds a bit count, not a margin — §15's callout
DBI enabled and margin worseTransition count rose — §9's calloutDBI is level-balancing, not transition-minimising
err_no_improvement assertsTHRESHOLD below half; guard bypassedThe elaboration guard exists for this

Rows ten and eleven are the pair worth internalising, because they are the honest answer to “should I turn DBI on?” DBI helps a channel that is limited by how many pins sit at the expensive level. It does nothing for a channel limited by anything else, and it can make a transition-limited channel worse — because §9's callout established that reducing levels and reducing transitions are different objectives.

Row six is the diagnostic that saves the most time. If margin is poor and termination is demonstrably correct, the ISI is probably loss-dominated rather than reflection-dominated (§2), and no amount of termination tuning will help.

19. Misconceptions

“Signal integrity is one problem.” §1. Three mechanisms, and two add energy while the third removes it — so they cannot be lumped into a single noise term.

“ISI and reflections are different things.” §2. Reflections are one source of ISI, and 22.2 §3 explained the mechanism. Loss is the other source, and it needs a different remedy.

“Crosstalk can be equalised away.” §3. Crosstalk arrives roughly simultaneously rather than delayed, so a post-cursor equaliser has nothing to subtract.

“Loss just makes the signal smaller.” §4. It is frequency-selective, so it makes amplitude pattern-dependent and it also produces ISI by slowing transitions.

“The three mechanisms are independent, so worst cases rarely coincide.” §5. They are correlated through the data pattern, which is why 20.5 §3 said the contributors cannot be added up.

“A schmoo plot is an eye diagram.” §7. Two integer code axes with no scale, no error-rate dimension, and a boundary that is a single threshold crossing. JEDEC says of its own receiver mask that it is not the valid data-eye.

“DBI opens the eye.” §8 and §10's diagram. It changes the pattern; the three mechanisms then respond to a different input. It touches none of them directly.

“DBI is free.” §8. It takes the DM pin. Enabling DBI means giving up data masking, and no configuration resolves that — there is one pin.

“JEDEC specifies when to invert.” §8's callout. It specifies what DBI_n means. The decision policy is the transmitter's, which is why §17's failure is possible.

“The decoder should check the transmitter's decision.” §12 and §17. It must obey the flag. Recomputing corrupts data whenever the two ends use different, legal policies.

“DBI reduces transitions.” §9's callout. It reduced them incidentally on §9's eight beats and it optimises something else. A transition-limited channel needs a different policy.

“A DBI bound is a margin guarantee.” §15's callout. P4 bounds a bit count. The coupling that turns a count into a disturbance is a board property.

20. Interview Reasoning

Name the three mechanisms and say which one is different. ISI, crosstalk and loss. Loss is different in kind: the first two add unwanted energy, loss removes wanted energy — so the receiver sees a changed ratio rather than a bigger disturbance.

Where does ISI come from? Two places. Reflections returning after a round trip, which lands energy from bit N on bit N+k, and loss slowing a transition so the previous bit's level has not finished settling. They need different remedies.

Why can't an equaliser fix crosstalk? Because crosstalk is not delayed. An equaliser subtracts a predicted contribution from earlier bits on the same line; crosstalk arrives with its aggressor, from a different line.

Why are the three mechanisms correlated? They are all functions of the data pattern. Run length drives reflection build-up, simultaneous switching drives crosstalk, and frequency content drives loss — all from one shared input.

What is the worst moment on a channel? Not the worst pattern for any single mechanism. It is a transition arriving on top of accumulated history: the run built the reflection and delivered full amplitude, then the edge fights both the residue and its own attenuation.

Why is an all-zeros training pattern useless? It has no transitions, so every sampling position inside the gate window passes and the sweep measures nothing. It is also the easiest pattern for the channel, for the same underlying reason.

What does DBI actually do? It conditionally inverts a group of bits and signals that it did so, to bound how many pins sit at the expensive level. It does not change the channel; it changes what the channel carries.

What does DBI cost? The DM pin. They are multiplexed, so enabling DBI means giving up byte-granular write masking. It costs no extra pins and about an eighth of the write-CRC frame's input space.

Who decides when to invert? The transmitter — the controller for write DBI, the device for read DBI. JEDEC specifies only what DBI_n means, so the two ends may legitimately use different thresholds.

Then what must the decoder do? Obey the flag. A decoder that recomputes the policy is self-consistent and wrong whenever the ends disagree, and an exhaustive test of a matched pair will not find it.

You enable DBI and margin gets worse. Is that possible? Yes. DBI minimises bits at the expensive level, not transitions. On a channel limited by transition density rather than by level, the re-encoded pattern can be the worse of the two.

21. Exercises

  1. Recompute §9's table for THRESHOLD = 5 instead of 4. Which beats change decision, what happens to the worst-single-beat bound, and why is 4 the better policy despite both being legal?

  2. GROUP_W odd has no fixed point. Work through GROUP_W = 7 with THRESHOLD = 3 and state the bound the policy then guarantees. Why is the odd case strictly better behaved, and why is it never used?

  3. Write the transition-minimising alternative to §12's policy — one that considers the previous beat — and find an eight-beat sequence on which it and the level-balancing policy produce different DBI_n streams. Which would you ship, and what would you need to know to decide?

  4. §17's failure needs mismatched thresholds. Construct the smallest input for which a threshold-4 encoder and a threshold-5 recomputing decoder disagree, and prove no matched-pair test can find it.

  5. DBI_n corruption inverts a whole group with no local detection. Using only this chapter's blocks, propose a detection scheme, then explain why it cannot work and what property a scheme would need. Compare with what 22.4 provides.

  6. worst_moment requires both a run and a transition. Construct the pattern that maximises its count over sixteen beats for RUN_LIMIT = 4, and explain why maximising the count is not the same as maximising the severity.

  7. §15's callout says the counters bound a risk factor rather than a risk. Specify exactly what additional information would convert switching_worst into a bound on a disturbance, and say which discipline owns each item.

  8. §6 argues a training result is only as good as its pattern. Design the minimal set of MPR patterns that would exercise all three mechanisms of §1, using only the four verified defaults and the two read formats of 21.2 §5. Which mechanism remains under-exercised, and why?

22. Where This Goes

Three mechanisms, one shared input, and one digital mitigation that changes the input rather than the channel.

The most useful thing in this chapter is §5's coupling argument, because it explains a result that has been sitting unexplained since Module 20: the published data-valid window exceeds the subtraction of the individual limits because the mechanisms are correlated through the data pattern, their worst cases involve different patterns, and the composite is therefore measured rather than computed.

The second most useful is the honest ceiling. §15's callout is the widest gap in the module so far: P4 bounds a bit count, the coupling that turns a count into a disturbance is a board property, and a channel with good counts and large coupling is marginal while a channel with poor counts and small coupling is fine. The counters bound a risk factor. They are not a gate.

Two things remain. §2 established that ISI has a loss-driven component that termination cannot help, and §4 established that loss compounds with rate for two reasons at once. That points at a mitigation this module has not yet covered — one that predicts a contribution and subtracts it, rather than absorbing energy or re-encoding data. Chapter 22.2 §3's table already showed its shape and its limit: arrivals within four unit intervals are reachable, and arrivals beyond are not.

And §16's row eight left a failure with no detection: a corrupted DBI_n inverts a whole group silently, and nothing local can tell. That needs a check over the whole frame.

Chapter 22.4 closes the module with both — equalisation at DDR5 rates, and the write CRC whose polynomial, frame format and detection guarantees are specified precisely enough to build and to verify.

Continue learning

Standards & specifications

Governing standard
JEDEC JESD79 (DDR SDRAM)(opens JEDEC Solid State Technology Association in a new tab)

Defines the DDR SDRAM device itself — signals, command encoding, mode registers, timing parameters and the initialisation sequence — one document per generation. Memory-controller microarchitecture, address-mapping policy, PHY training algorithms and board-level design are not specified by it.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the DDR curriculum.