Skip to content
VLSI Mentor

DDR · Module 19

DQS Handling

On a write the PHY generates the strobe. On a read it must decide when to listen, because the strobe is not driven between bursts — so the gate is an estimate of a window the PHY cannot see.

Chapter 19.3 aligned a command with the data that belongs to it and left two things unbuilt. On a write, the data the gearbox launches has to be accompanied by a strobe, because the device captures against that strobe and not against a clock. On a read, 19.3 §3 showed that the receive side needs a window rather than an instant.

This chapter builds both, and they are much less symmetric than they sound.

On a write the PHY generates the strobe, so it knows exactly where it is. On a read the strobe is generated by the device, arrives after a journey the PHY cannot measure, and is not driven at all between bursts — so the PHY must decide when to listen using an estimate.

That asymmetry is the chapter. Everything difficult about DQS handling is on the receive side, and it is difficult for one reason: the gate is a guess about a signal the PHY cannot observe until it has already committed to listening.

1. The Write Side Is the Easy One

Take the easy direction first, because it makes the hard one legible by contrast.

On a write, Chapter 6.10 established that the controller side drives the strobe. The PHY generates it, alongside the data 19.2's gearbox is launching, in a defined relationship to that data.

Everything about this is knowable at the PHY:

  • When it starts — the PHY decided, via 19.3's launch offset.
  • How long it lasts — the burst length, which the controller programmed (12.1).
  • Where it sits relative to the data — a relationship the PHY constructs, because it is generating both signals from the same place.
  • When it stops — the PHY decided that too.

There is no estimate anywhere in that list. The transmit strobe is a signal the PHY authors from scratch, and the only hard part is the electrical one of getting it onto a wire with the right characteristics, which is Module 22's.

2. The Read Side Has a Hole in It

Now the hard direction, and the structural fact that creates the whole difficulty.

Between bursts, the read strobe is not driven by anybody. The device drives it during a read burst and releases it afterwards; the PHY drives it during a write burst and releases it afterwards. In the intervals between, the line is floating — owned by nobody, carrying whatever the electrical environment leaves on it.

Chapter 6.10 established that ownership handoff. Its consequence for a receiver is severe:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  a naive receiver, always listening

    line floating   ->  noise, coupling, reflections, slow drift
                        an edge-sensitive receiver sees EDGES here
                        and captures whatever DQ happens to carry

    burst arrives   ->  real edges, real data

    line floating   ->  more spurious edges, more garbage captured

An always-listening receiver does not merely capture extra data — it captures extra data that looks exactly like real data. There is no flag on a spurious beat. It enters the assembler (19.2 §6), takes a position in a word, and shifts every subsequent beat.

So the PHY must listen only during the burst, and that requires a control signal that opens the receive path at the start and closes it at the end.

That control is the gate. Its existence is not an optimisation; without it the receive path has no defined behaviour at all.

3. The Gate Is an Estimate

Here is the part that makes gating genuinely hard, and it is worth stating as sharply as possible.

The gate must open before the strobe arrives — because if it opens after the first edge, that edge is gone. But the PHY cannot detect the strobe's arrival in order to open the gate, because detecting it is what the gate enables. The information needed to open the gate correctly is on the other side of the gate.

So the gate is opened on a prediction:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  RD command committed at the PHY
        |
        |  CL cycles            <- programmed, from the mode register (14.9)
        |  + flight time out    <- physical, not knowable by calculation
        |  + device response    <- device-internal
        |  + flight time back   <- physical, not knowable by calculation
        v
  strobe actually arrives at the PHY's pins

Only the first term is computable. The rest is a round trip through a board the designer has never measured — and the sum is precisely Chapter 19.3 §2's two-term decomposition, appearing again on the receive side. A programmed part anyone can look up, and a measured part somebody must find.

4. Believed Window Versus Actual Window

The cleanest way to hold this chapter's idea is as two windows that ought to coincide.

The actual strobe windowThe believed window
What it iswhen the strobe is genuinely driven on the wirewhen the PHY's gate is open
Who determines itthe device, plus the board's delaysthe PHY, from CL and a trained offset
Modelled by6.10's dqs_ownership_window§6's phy_dqs_gate_ctrl
Knowable at design time?noyes — it is what the PHY was told
Failure if they disagree§3's two symptoms

Chapter 6.10's block and this chapter's block model the same interval from opposite sides. That is not duplication; it is the whole point. One describes what happens, the other describes what the PHY believes happens, and a verification environment that instantiates both and compares them is checking exactly the thing that matters. §10 builds that comparison.

It also explains why 6.10's block carries preamble and postamble parameters and this one does not. The preamble exists so that the actual window begins before the data does, giving a receiver something to align to. How wide it is, and what it is for, is 6.10's subject. What this chapter needs from it is only that the actual window is wider than the data, which is the margin the gate is aiming at.

5. The Gate in Time

EDUCATIONAL TIMING — cycle positions show ordering and window structure only. No edge placement, no sub-cycle relationship, and no device parameter is implied. The real gate position is a trained, largely sub-cycle value (§3).

A read burst: the actual strobe window, the PHY's gate, and what a late gate costs

10 cycles
Ten cycles of a read. A read command is committed at cycle zero. Nothing happens on the strobe line for several cycles while the command travels to the device and the device responds. From cycle four to cycle eight the strobe is genuinely active on the wire, with the burst's four data beats occupying cycles five through eight. The PHY's gate, when correctly positioned, opens at cycle four just before the strobe becomes active and closes after cycle eight, so every beat reaches the capture path and no floating-line activity does. A second gate trace shows the same gate opened three cycles late at cycle seven: the first two beats arrive while the receive path is still closed and are lost, so only two of the four beats are captured and every assembled word is displaced. The line is marked as floating and owned by nobody outside the active window.line floating — nobody drivesline floating — nobody drivesstrobe genuinely activestrobe genuinely activeRD committed at the PHYRD committed at the PHYgate must already be opengate must already be openlate gate: B0 and B1 lostlate gate: B0 and B1 lostCKcmdRD------------------dqs_drivendq_beats----------B0B1B2B3--gate_okcaptured_okgate_latecaptured_latet0t1t2t3t4t5t6t7t8t9

Read the two gate traces against each other. gate_ok is open from cycle 4, before the first beat at cycle 5 — that lead is what the actual window's extra width buys, and it is the margin the gate is spending. gate_late opens at cycle 7 — three cycles later than it should — and the consequence is immediate and permanent: captured_late shows two beats where four were sent, because B0 and B1 arrived at cycles 5 and 6 while the path was still closed.

The assembler downstream has no way to know. It receives two beats and a burst framing signal, places them, and produces a word that is two beats short. Chapter 19.2 §6's captured_first framing limits the damage to one burst rather than shifting all subsequent ones — but the burst itself is simply wrong, and nothing in the digital path can detect it.

6. Generating and Gating

The two DQS responsibilities inside the PHY, drawn as separate transmit and receive paths. On the transmit side, the launch trigger from chapter nineteen point three starts both the write data gearbox and a strobe generator, which drives the strobe line toward the device for the duration of the burst; everything on this path is authored by the PHY and nothing is estimated. On the receive side, a read command plus the programmed read latency plus an opaque trained gate offset produce a gate window; the incoming strobe line from the device passes through that gate, and only what the gate admits reaches the abstract capture block owned by module twenty. Beneath, a comparison block marks that chapter six point ten's ownership window models the strobe window as it actually occurs, while this chapter's gate models what the PHY believes, and that the difference between them is the failure.Launch trigger19.3 — decided hereStrobe generatorPHY authors itDQS outnothing estimatedRD + CL14.9 — programmedTrained offset19.5 holds · M21 findsGate controlthis chapterDQS infloating between burstsThe gateadmits only the burstCAPTUREModule 20 opens thisActual window6.10 — what occursBelieved windowthe gate — §412

The drawing's asymmetry is §1 against §2. The transmit path has no input from a trained setting on the drawing because the PHY authored every part of it; the receive path has one, and everything difficult follows from that.

7. The Gate Controller

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────
// phy_dqs_gate_ctrl
//
// CLASSIFICATION
//   Synthesizable educational RTL. Sequential. One responsibility:
//   produce the receive gate window for a read burst -- open it before
//   the strobe is expected, hold it for the burst, close it after.
//
//   It models the PHY's BELIEF about when the strobe will be present,
//   expressed in whole cycles. The actual strobe window is Chapter
//   6.10's dqs_ownership_window; §4 is about the difference.
//
// WHAT IT DOES NOT MODEL
//   - NO CAPTURE. What happens to DQ once the gate is open -- edge
//     sampling, where the sampling point sits, setup and hold -- is
//     Module 20 entirely. This block produces an enable and stops.
//   - NO SUB-CYCLE POSITION. A real gate edge is placed to a fraction
//     of a bit period by a delay element (Chapter 19.1 §5). This
//     counts whole cycles and §8 states plainly that the quantity
//     which decides success is therefore outside the model.
//   - NO GATE TRAINING. gate_offset is an opaque INPUT. How a good
//     value is searched for is Chapter 21.3's subject.
//   - No preamble or postamble semantics. Chapter 6.10 owns the
//     structure of the actual window; this block only needs it to be
//     wider than the data, which is the margin it spends.
//   - No electrical behaviour of an undriven line (Module 22).
//   - No write-side strobe generation: that path has no estimate in it
//     (§1) and is a timed enable off Chapter 19.3's launch trigger.
// ─────────────────────────────────────────────────────────────────────
module phy_dqs_gate_ctrl #(
  parameter int MAX_LATENCY = 48,
  parameter int MAX_BEATS   = 16,
  // Cycles the gate opens ahead of the expected first edge, and holds
  // past the expected last one. Margin, spent deliberately: too little
  // and a small error loses beats, too much and more floating line is
  // admitted (§9).
  parameter int LEAD_CYCLES  = 1,
  parameter int TRAIL_CYCLES = 1,
  parameter int LAT_W = (MAX_LATENCY <= 1) ? 1 : $clog2(MAX_LATENCY + 1),
  parameter int BEAT_W = (MAX_BEATS  <= 1) ? 1 : $clog2(MAX_BEATS + 1)
) (
  input  logic               clk,
  input  logic               rst_n,

  // ── From Chapter 17.1's commit, seen at the PHY.
  input  logic               rd_cmd_committed,
  input  logic [BEAT_W-1:0]  burst_beats,

  // ── Chapter 19.3 §2's two terms again, on the receive side.
  input  logic [LAT_W-1:0]   cl_cycles,      // programmed (14.9)
  input  logic [LAT_W-1:0]   gate_offset,    // measured, opaque here
  input  logic               gate_trained,

  // ── The gate. Everything the capture path is allowed to see.
  output logic               gate_open,

  // ── Observability. Published separately so a checker can compare
  //    this believed window against Chapter 6.10's actual one (§10).
  output logic               waiting,
  output logic [LAT_W-1:0]   countdown,
  output logic               err_gate_untrained,
  output logic               err_overlapping_read,
  output logic               err_zero_beats
);

  if (MAX_LATENCY  < 1) $fatal(1, "phy_dqs_gate_ctrl: MAX_LATENCY must be >= 1");
  if (MAX_BEATS    < 1) $fatal(1, "phy_dqs_gate_ctrl: MAX_BEATS must be >= 1");
  if (LEAD_CYCLES  < 0) $fatal(1, "phy_dqs_gate_ctrl: LEAD_CYCLES must be >= 0");
  if (TRAIL_CYCLES < 0) $fatal(1, "phy_dqs_gate_ctrl: TRAIL_CYCLES must be >= 0");

  // ── When the first edge is expected, less the lead -- and less one
  //    more for the state register's own latency. The FSM reaches
  //    G_OPEN one cycle after its countdown expires, so loading
  //    (arrive - LEAD) would open the gate at (arrive - LEAD + 1) and
  //    silently consume the whole lead. With LEAD_CYCLES = 1 that
  //    yields ZERO real lead; with LEAD_CYCLES = 0 it opens a cycle
  //    LATE and loses the first beat, which is §3's failure caused by
  //    the block meant to prevent it.
  //    Saturated at zero: a lead larger than the latency would mean
  //    opening before the command was issued, which is a parameter
  //    error rather than a runtime condition.
  localparam int LEAD_TOTAL = LEAD_CYCLES + 1;
  logic [LAT_W:0] arrive;
  logic [LAT_W:0] open_at;
  assign arrive  = {1'b0, cl_cycles} + {1'b0, gate_offset};
  assign open_at = (arrive > (LAT_W+1)'(LEAD_TOTAL))
                     ? arrive - (LAT_W+1)'(LEAD_TOTAL)
                     : '0;

  // ── How long the gate stays open: the burst, plus trailing margin.
  logic [LAT_W:0] hold_cycles;
  assign hold_cycles = {1'b0, LAT_W'(burst_beats)}
                     + (LAT_W+1)'(LEAD_TOTAL)
                     + (LAT_W+1)'(TRAIL_CYCLES);

  typedef enum logic [1:0] {
    G_IDLE = 2'd0,   // closed; line floating, admitting nothing
    G_WAIT = 2'd1,   // command seen, counting toward the open point
    G_OPEN = 2'd2    // admitting the strobe to the capture path
  } gate_state_e;

  gate_state_e      state;
  logic [LAT_W-1:0] cnt;

  assign gate_open = (state == G_OPEN);
  assign waiting   = (state == G_WAIT);
  assign countdown = cnt;

  // ── Opening a gate against an unestablished offset puts the window
  //    in an arbitrary place. Reported, not prevented: this block does
  //    not police bring-up order, Chapter 19.5's lifecycle does.
  assign err_gate_untrained   = rd_cmd_committed && !gate_trained;
  // A second read whose window would overlap the first. A single-window
  // controller cannot represent two; §9 states the limitation.
  assign err_overlapping_read = rd_cmd_committed && (state != G_IDLE);
  assign err_zero_beats       = rd_cmd_committed && (burst_beats == '0);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state <= G_IDLE;
      cnt   <= '0;
    end else begin
      unique case (state)

        G_IDLE:
          if (rd_cmd_committed && burst_beats != '0) begin
            if (open_at == '0) begin
              // Degenerate but legal: the window starts immediately.
              state <= G_OPEN;
              cnt   <= LAT_W'(hold_cycles);
            end else begin
              state <= G_WAIT;
              cnt   <= LAT_W'(open_at);
            end
          end

        G_WAIT:
          if (cnt <= LAT_W'(1)) begin
            state <= G_OPEN;
            cnt   <= LAT_W'(hold_cycles);
          end else begin
            cnt <= cnt - LAT_W'(1);
          end

        G_OPEN:
          if (cnt <= LAT_W'(1)) begin
            // Closing is as important as opening: a gate left open
            // admits the floating line again (§2), and the capture
            // path has no way to tell that from more burst.
            state <= G_IDLE;
            cnt   <= '0;
          end else begin
            cnt <= cnt - LAT_W'(1);
          end

        default: state <= G_IDLE;
      endcase
    end
  end

endmodule

On the write side there is no equivalent block in this chapter, and the omission is deliberate rather than an oversight. Transmit strobe generation is a timed enable hung off 19.3's launch trigger — it starts when the PHY says, runs for the burst, and stops. There is no estimate, no window to predict, and no failure mode that is not already covered by 19.3's alignment or Module 22's electrical behaviour. Writing a block for it would add lines without adding a lesson.

8. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── P1. The gate is closed unless a read put it there. The property
//    that forbids the always-listening receiver of §2 -- which is the
//    default behaviour of a design with no gate at all.
property p_gate_only_after_read;
  @(posedge clk) disable iff (!rst_n)
    $rose(gate_open) |-> $past(state == G_WAIT || rd_cmd_committed, 1);
endproperty
a_gate_only_after_read: assert property (p_gate_only_after_read);

// ── P2. The gate always closes. A gate that opens and never closes
//    admits the floating line indefinitely, and the capture path
//    cannot distinguish that from a very long burst.
property p_gate_closes;
  @(posedge clk) disable iff (!rst_n)
    gate_open |-> ##[1:$] !gate_open;
endproperty
a_gate_closes: assert property (p_gate_closes);

// ── P3. The open window is exactly the burst plus both margins.
//    Catches a hold computation that forgets the lead it already
//    spent -- which shortens the window at the far end and loses the
//    LAST beats rather than the first, a symptom easily mistaken for
//    a postamble problem.
property p_window_length;
  @(posedge clk) disable iff (!rst_n)
    $rose(gate_open)
      |-> (gate_open [*1:$]) ##0 (countdown == LAT_W'(1));
endproperty
a_window_length: assert property (p_window_length);

// ── P4. One window per read. A second command while a window is live
//    is reported rather than silently merged into the first.
property p_overlap_reported;
  @(posedge clk) disable iff (!rst_n)
    (rd_cmd_committed && (waiting || gate_open)) |-> err_overlapping_read;
endproperty
a_overlap_reported: assert property (p_overlap_reported);

// ── P5. An untrained gate is reported. Not blocked -- Chapter 19.5
//    owns bring-up order -- but never silent, because the resulting
//    window lands arbitrarily and the symptom looks like corruption.
property p_untrained_reported;
  @(posedge clk) disable iff (!rst_n)
    (rd_cmd_committed && !gate_trained) |-> err_gate_untrained;
endproperty
a_untrained_reported: assert property (p_untrained_reported);

// ── Covers. The error paths are the ones a directed happy-path test
//    never reaches, and P4 and P5 check nothing without them.
c_untrained_read:  cover property (@(posedge clk) disable iff (!rst_n)
                     rd_cmd_committed && !gate_trained);
c_overlapping:     cover property (@(posedge clk) disable iff (!rst_n)
                     rd_cmd_committed && gate_open);
c_immediate_open:  cover property (@(posedge clk) disable iff (!rst_n)
                     rd_cmd_committed && open_at == '0);

What they prove. That the gate is a genuine window with a cause: it opens only in response to a read, it always closes, its length matches the burst and margins, and the two bring-up-order violations are reported.

What they do not prove — and on this block the gap is total. Not one property is sensitive to whether the window is in the right place. P1 through P5 pass identically for a gate_offset of 3, of 30, or of a value that puts the window entirely outside the burst. The block counts faithfully to whatever number it is given, and correctness of the count is independent of correctness of the value.

That is not a defect in the properties. It is the boundary of the layer: §3 established that the right value is not computable, only measurable, and a property cannot check a number against a measurement that has not been taken. The gate position is verified on hardware, with real signals, which is Module 21's subject — and §10's cross-check is the closest a simulation can come.

Vacuity. P2's ##[1:$] is satisfied by any eventual close and proves nothing about when; P3 carries the length claim and is the one that can actually fail. P1's antecedent requires a rising edge on gate_open, so a test with no reads passes it trivially — which is what c_immediate_open and the other covers exist to rule out.

9. Corner Cases

SituationCorrect behaviourFailure if mishandled
gate_trained low at a readwindow opens; error reportedan arbitrarily placed window, presenting as corruption
burst_beats = 0no window; error reporteda zero-length or never-closing gate
LEAD_CYCLES exceeds total latencyopen_at saturates to zero; opens immediatelyan attempt to open before the command — unrepresentable
second read while waitingreported; not mergedtwo bursts sharing one window, half of each lost
second read while openreportedthe same, with the first burst truncated
MAX_BEATS = 1BEAT_W guarded; one-beat burst workszero-width beat count
LEAD_CYCLES = 0legal; window opens exactly at the expected edgeno margin — any error at all loses the first beat
TRAIL_CYCLES = 0legal; closes at the last expected beatno margin at the far end; late bursts lose their tail
reset mid-windowgate closes immediatelya gate stuck open, admitting the floating line
gate_offset changes while waitingthe loaded count is unaffecteda window that moves mid-flight during recalibration

The two zero-margin rows are worth reading together. LEAD_CYCLES and TRAIL_CYCLES are margin deliberately spent: each cycle of lead protects against the window being late at the cost of admitting one more cycle of floating line. That is a genuine trade and it has no universally right answer — it depends on how much the trained value can drift and how badly the floating line behaves, both of which are board properties. Module 21 chooses the centre; these parameters choose how much error the centre can absorb.

10. DV — Comparing Belief Against Reality

This is the one block in Module 19 whose verification has a genuinely good answer in simulation, and it comes from §4's observation.

Instantiate both windows and compare them. Drive Chapter 6.10's dqs_ownership_window from the device's behaviour to produce the actual strobe window, and this chapter's phy_dqs_gate_ctrl from the PHY's command stream to produce the believed window. Then check the relationship that has to hold:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  for correct reception, on every burst:

     actual_window  MUST BE ENTIRELY INSIDE  believed_window

  not merely overlapping, and not merely coincident:
     gate opens strictly before the strobe becomes active
     gate closes strictly after it goes inactive

That is a stronger and more useful check than either block alone, and it is a containment property rather than an equality one — which is the right shape, because the margins of §9 are supposed to make the believed window larger.

The reference model should compute the actual window's bounds from the stimulus independently, not from 6.10's block, so that a shared misunderstanding cannot hide.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  GATE CONTAINMENT VIOLATION
    read index          : 7
    command at cycle    : 2,140
    cl_cycles           : 17
    gate_offset         : 6
    LEAD / TRAIL        : 1 / 1

    believed window     : cycles 2,162 .. 2,168   (gate_open)
    actual  window      : cycles 2,160 .. 2,167   (strobe driven)

    containment         : FAILED at the opening edge
    actual starts       : 2 cycles BEFORE the gate opens
    beats lost          : 2 of 4  (the first two)

    diagnosis : the believed window is late. The programmed term is
                correct by construction, so the discrepancy is in the
                measured term -- gate_offset is 2 cycles too large,
                or the real round trip is 2 cycles shorter than the
                trained value assumes.
    symptom shape : DETERMINISTIC displacement. Every burst loses the
                same first two beats, so read words are consistently
                short rather than randomly corrupt (§3).
    NOT indicated : anything about sub-cycle position. Containment at
                cycle granularity is necessary and nowhere near
                sufficient; a window can contain the burst by whole
                cycles and still sample in the wrong place entirely.

The last block is the one that keeps the report honest. Cycle-level containment is a real check that catches real bugs, and it is still only the coarse half of the problem.

Directed cases worth running: MAX_BEATS = 1; LEAD_CYCLES = 0, where containment must fail the moment the actual window is even slightly early; a gate_offset swept across its range, which should show containment passing over a band of values rather than one — and the width of that band is exactly the margin Module 21 is trying to centre in; back-to-back reads that overlap; and a reset landing inside an open window.

11. Synthesis and What Is Missing

Cost. Two state bits, one LAT_W-wide counter, and two adders. Instantiated per byte lane, because 19.3 §5 established that the physical term differs per lane and therefore so does the gate position.

What a production gate has that this does not:

  • Sub-cycle edge placement, which is the entire difficulty and is a delay element rather than logic.
  • Per-lane independence beyond parameterisation — real gates are trained per lane and can differ.
  • Pipelining. One window at a time is a real limitation; Chapter 14.6's column spacing permits reads closer together than the latency, so a production PHY tracks several windows. err_overlapping_read marks the boundary rather than hiding it.
  • Drift tracking. Temperature and voltage move the correct position during operation, which is why 19.5 has a periodic path and not only a bring-up one.
  • A defined response to a strobe that never arrives — a read that gets no data at all needs a timeout somewhere, and where that lives is a system decision.

12. Debugging

Symptom: read words are consistently short or displaced by the same amount every time. A late gate (§3). Deterministic displacement is the signature. Check containment at the opening edge before suspecting anything in capture, because Module 20's failures corrupt rather than displace.

Symptom: read words contain garbage at the start, varying from burst to burst. An early gate. The varying content is the tell — floating-line behaviour is not repeatable, so a deterministic-looking error is not this.

Symptom: reads work at low rate and fail at full rate. Margin, not logic. The gate's cycle placement is rate-independent; what changes with rate is how much of a bit period a given sub-cycle error represents. This is Module 21's centring problem or Module 22's margin problem, and no RTL inspection will resolve it.

Symptom: one lane loses beats and the others are fine. Per-lane gate position (§11). Not the gearbox, which slices uniformly (19.2 §14), and not the controller, which does not know lanes exist.

Symptom: the first read after bring-up fails and later ones succeed. Check err_gate_untrained. A read issued before the gate offset is established opens a window in an arbitrary place; once training completes, subsequent reads work — which makes the failure look transient when it is actually an ordering problem (19.5).

Symptom: back-to-back reads lose data, isolated reads are fine. err_overlapping_read. A single-window controller meeting closely-spaced column reads cannot represent both windows, and the loss lands on whichever burst the merged window misses.

13. Misconceptions

“DQS is just another clock.” It is not free-running, it is not always driven, and its owner changes with the direction of the data (6.10). Those three properties are exactly why a gate is needed. Clue: a receive path with no gate at all.

“DQS replaces CK.” CK still carries the command/address path (19.1 §4). The strobe times data only. Clue: a PHY block diagram with one timing reference.

“Read and write use the same strobe path in reverse.” §1 against §2 — one is authored with no estimate, the other is predicted. Clue: a receive design derived from the transmit design by flipping a direction bit.

“The gate can be opened when the strobe is detected.” Detecting it is what the gate enables (§3). Clue: a design with an edge detector upstream of the gate.

“A wider gate is always safer.” More lead admits more floating line before the burst. Margin is spent in both directions and §9's trade has no universal answer. Clue: margin parameters set to their maximum "to be safe".

“Lost beats mean a capture problem.” Lost beats at the start, deterministically, mean a gate problem. Capture failures corrupt. Clue: a debug effort that starts at Module 20 without checking containment.

“If the gate logic is correct the gate is correct.” §8 — every property passes on a window in the wrong place. Clue: gate sign-off from simulation alone.

“Gate position can be calculated from the datasheet.” Only the programmed term can. The round trip through the board cannot (19.3 §2). Clue: a design with a gate constant and no training step.

14. Interview Reasoning

“Why does a DDR PHY need a DQS gate?” Because the strobe is not driven between bursts, so an always-listening receiver captures noise that is indistinguishable from data. The strong answer names indistinguishable — that is why it matters.

“Why can't the PHY just detect the strobe and open then?” Because detection requires the receive path to be enabled, which is what the gate controls. The information is on the far side of the decision.

“Reads lose the first two beats of every burst, identically.” A late gate. Then the discriminator: deterministic displacement points at the gate, varying corruption points at an early gate or at capture.

“What is the difference between the gate window and the strobe window?” One is what the PHY believes, the other is what occurs. Containment of the second inside the first is the correctness condition (§10).

“What can simulation prove about a gate?” That it opens only on a read, always closes, and has the right length — and containment, if the actual window is modelled. Not that it is in the right place at sub-cycle precision, which is measured on hardware.

“Why is the gate trained per lane?” Because lanes have different board traces, so the round trip differs per lane (19.3 §5).

15. Exercises

1. With cl_cycles = 17, gate_offset = 6, LEAD_CYCLES = 2 and a read committed at cycle 500, give arrive, open_at and hold_cycles, then the first and last cycle on which gate_open is high for a four-beat burst with TRAIL_CYCLES = 1.

2. §5's waveform shows a gate two cycles late losing two beats. Redraw the captured row for a gate two cycles early. How many beats are captured, and how many of them are real?

3. LEAD_CYCLES = 0. Give the smallest gate-position error that loses a beat, and say why the same error is harmless with LEAD_CYCLES = 2.

4. Construct a stimulus that sets err_overlapping_read. What is the minimum read spacing that avoids it, in terms of the window length?

5. Write the containment property of §10 as SVA. Which signal must come from outside phy_dqs_gate_ctrl, and why can the property not live inside that module?

6. All five properties in §8 pass with gate_offset set to zero. Describe the hardware behaviour and explain why no RTL property can object.

7. A colleague proposes holding the gate open permanently to avoid losing beats. Describe what the assembler receives, and why the resulting words are worse than short ones.

8. Explain why a single-lane beat loss exonerates both the controller and 19.2's gearbox, naming what each does uniformly across lanes.

16. Where This Goes

Three chapters have now consumed the same two things without ever producing them. 19.3 needed a trained_offset and a trained_valid. This chapter needed a gate_offset and a gate_trained. And every block since 19.1 has been gated by a phy_operational that nothing has yet asserted.

Those signals are not incidental plumbing. They are the reason a DDR interface has a bring-up sequence at all, and the reason a PHY is not usable the instant reset is released.

Chapter 19.5 supplies them: the lifecycle that takes a PHY from reset to operational, what has to happen in what order, which parts run once and which run continuously, and what it means for a trained value to become valid — or to stop being valid while traffic is running.

The algorithms that find those values remain Module 21's, and the box marked CAPTURE is still Module 20's.

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.