DDR · Module 19
PHY Responsibilities
The controller decides which DDR operation should happen. The PHY makes it real at a pin boundary whose timing the controller cannot meet — and the line between them is not the same in any two implementations.
Eighteen modules have built one side of a memory system: what DRAM is, what its commands mean, what its timing permits, and how a controller turns transaction intent into a legal command stream. Module 17 ended with a controller that decides correctly. Module 18 ended with the mapping that decides what it has to work with.
None of it has yet reached a wire.
This module begins the layer that does, and it opens by fixing a boundary that gets blurred more than almost any other in the field:
The controller decides which DDR operation should happen. The PHY is responsible for making that decision real at a pin boundary whose timing the controller cannot meet and should not try to.
That sentence needs a qualification immediately, and the qualification is not a footnote — it is one of the chapter's main results. The exact line between them is implementation-dependent. Two correct DDR designs can place the same function on opposite sides of it.
1. Why a PHY Exists at All
The honest way in is to ask what happens if you try to do without one.
A memory controller is ordinary synchronous digital logic. It is written in RTL, synthesised into standard cells, placed and routed by the same flow as the rest of the SoC, and verified by simulating its clock. Everything Module 17 built is that kind of logic.
Now look at what the DRAM interface demands. Data moves on both edges of a strobe that the DRAM itself generates during a read (Chapter 6.10). That strobe arrives with a phase relationship set by board trace length, package delay and temperature — none of which the SoC knows at synthesis time. The received data must be sampled inside a window that may be a small fraction of a bit period wide. And the same wires reverse direction between reads and writes (Chapter 10.5).
2. Three Layers, Named
The bottom band carries a point that is easy to skim past and shapes everything else. The DRAM sees only its pins. It has no idea whether a well-formed command came from a sophisticated scheduler or a state machine that got lucky, and no idea whether the data arriving on a write was aligned by a carefully trained delay or by accident. Every guarantee the upper layers provide has to survive as far as the pins to be worth anything.
That is why the PHY's job is not decoration on top of the controller's. The controller's correctness is only as good as the PHY's realisation of it, and §8's failure taxonomy is organised around exactly that dependency.
3. The Ownership Table
The central artefact of this chapter. Read the right-hand columns as carefully as the left.
| Function | Controller | PHY | DRAM | Implementation-dependent? | Detail owned by |
|---|---|---|---|---|---|
| Accept upstream transaction | yes | no | no | no | 17.5 |
| Address mapping | yes | no | no | yes — some platforms map above the controller | Module 18 |
| Bank-state tracking | yes | no | — | no | 5.2 |
| Command scheduling and arbitration | yes | no | no | no | 17.1, 17.4 |
| Timing-legality enforcement | yes | no | no | yes — some timing is enforced jointly | Modules 13–14 |
| Refresh policy | yes | no | no | yes — some devices self-manage part of it | 17.3 |
| Command / address launch at the pins | no | yes | no | no | 19.3 |
| Write-data serialisation | no | yes | no | yes — ratio varies | 19.2 |
| Read-data deserialisation | no | yes | no | yes — ratio varies | 19.2 |
| Write strobe generation | no | yes | no | no | 19.4 |
| Read strobe generation | no | no | yes | no | 6.10 |
| Strobe gating on receive | no | yes | no | no | 19.4 |
| Adjustable delay elements | no | yes | no | no | 19.5 |
| Discovering trained values | varies | varies | no | strongly — firmware, controller or PHY-internal | Module 21 |
| Read-data capture | no | yes | no | no | Module 20 |
| Data-bus turnaround management | yes (spacing) | yes (direction) | — | yes | 19.3, Module 22 |
| ODT control | yes (decides) | yes (applies) | yes (terminates) | yes | Module 22 |
| Output-driver calibration (ZQ) | yes (invokes) | — | yes (performs) | yes by generation | 7.7 |
| Electrical I/O | no | yes | yes | no | Module 22 |
| Transaction completion | yes | no | no | no | 17.5 §8 |
4. The PHY's Internal Paths
Zooming into the third band. Four paths, and they are not variations on one another.
The command/address path is unidirectional and the data path is not. That single asymmetry produces most of the PHY's complexity: a one-way path needs launching, while a two-way path needs launching, direction management, gating and capture.
The box marked CAPTURE stays closed in this module. Everything else on the drawing is opened by a chapter of Module 19; that one is Module 20's. The discipline is deliberate — §8's taxonomy depends on being able to say “the fault is at the capture layer” as a distinct claim.
5. What Cannot Be Written in Portable RTL
§1 argued this from a failing example. Here it is as a list, because it is the sharpest way to see what the PHY is.
| Structure | Why portable RTL cannot express it | What actually provides it |
|---|---|---|
| Sub-cycle adjustable delay | RTL has no notion of a fraction of a bit period | technology-specific delay cell, configured post-manufacture |
| Data-rate serialiser at the pin | cannot be closed against a data-rate path in the fabric | hardened I/O serialiser |
| Strobe-domain capture flop | clocked by a signal the design does not generate | dedicated capture circuitry in the I/O cell |
| Clock multiplication and phase generation | a frequency relationship, not a logic function | PLL / DLL resource |
| Termination and drive strength | electrical parameters, not logic values | I/O cell with calibrated legs (Module 22) |
| Sensing the correct sampling point | requires measuring a physical margin | calibration hardware plus an algorithm (Module 21) |
6. The Handover
The controller and the PHY meet at a synchronous digital contract. What crosses it, in architectural terms:
controller ----> PHY command, address, rank/chip select
write data, write mask
direction / turnaround intent
PHY ----> controller read data, with framing
interface status and readinessAn industry-standard interface exists for this boundary. The DDR PHY Interface (DFI) specification defines signals, timing and programmable parameters for transferring control information and data between a memory controller and a PHY. It was developed as a multi-company industry standard rather than a JEDEC device specification — the two are different kinds of document, and only the latter constrains the DRAM.
The DFI-relevant fact worth carrying into Chapter 19.2 is its frequency-ratio concept: DFI defines the relationship between the controller and PHY reference clocks and supports 1:1, 1:2 and 1:4 controller-to-PHY ratios, so that a controller may run at half or quarter the PHY's rate. That is the standardised expression of the width-versus-rate trade 19.2 is built on — and it is the reason this module never invents a ratio of its own.
What this chapter does not do is reproduce DFI's signal set. Signal names, widths and semantics are version-specific and belong to the specification; a tutorial that paraphrases them from memory produces a plausible-looking interface that matches nothing. The block below therefore models a generic command handover with explicitly-named signals of its own, and says so.
7. Calibration Is Not One Thing
A terminology trap worth clearing before Chapter 19.5, because two different things share the word.
ZQ calibration is a DRAM-side activity: the device calibrates its own output-driver and termination characteristics against a reference resistor. Chapter 7.7 owns it, including the important detail that its result is not directly observable to the controller, and that DDR5 changed how it is invoked.
PHY calibration is a PHY-side activity: the PHY establishes its own delay settings, gate positions and sampling points so that it can transmit and receive correctly against this particular board and device. Chapter 19.5 owns its lifecycle; Module 21 owns the algorithms.
They are different mechanisms, on different sides of the interface, producing different state, invoked differently. A system can have a perfectly calibrated DRAM output driver and a completely mis-trained PHY receiver, and the symptoms will not resemble each other. §8's taxonomy separates them into distinct rows for exactly that reason.
8. A Failure Taxonomy Across Six Modules
The practical payoff of the ownership table: when a DDR interface misbehaves, the first question is which layer, and the answer determines which module's knowledge applies.
| Layer | Typical symptom shape | Distinguishing evidence | Owned by |
|---|---|---|---|
| A — Controller logic | illegal command, wrong address, starvation | protocol checker flags a violation; the command stream is wrong before it leaves | Module 17 |
| B — Controller/PHY interface | data present but wrong order, lost handshake, framing off | controller-side word and PHY-side beat sequence disagree | this module, 19.2 |
| C — PHY digital datapath | lane swap, beat reversal, ownership error | fault is deterministic and rate-independent | this module, 19.2–19.4 |
| D — Capture / timing | corruption that worsens with rate or temperature | lower data rate works; specific lanes marginal | Module 20 |
| E — Training / calibration | works after warm reset, fails cold; one lane only | failure correlates with bring-up, not traffic | 19.5 lifecycle, Module 21 algorithms |
| F — Signal integrity | pattern-dependent errors, worse on long traces | depends on data pattern and board position | Module 22 |
The rate test is the single most useful discriminator, and it is worth knowing precisely what it does and does not prove. If the interface works at a lower data rate and fails at full rate, layers A, B and C are largely exonerated — digital ordering bugs do not care about frequency. It points at D, E or F. It does not say which of the three, and treating “fails only at speed” as proof of a signal-integrity problem skips two layers that are more likely and cheaper to check.
9. The Command Handover Block
One responsibility: the digital contract by which the controller hands a scheduled command to the PHY, and the PHY accepts ownership of launching it.
// ─────────────────────────────────────────────────────────────────────
// phy_command_stage
//
// CLASSIFICATION
// Synthesizable educational RTL. Sequential. One responsibility: the
// controller-to-PHY command handover -- acceptance, ownership, and
// the gating of that acceptance on interface readiness.
//
// It is a GENERIC contract. It is NOT an implementation of DFI or of
// any other standard interface; signal names here are this module's
// own and are not a paraphrase of any specification.
//
// WHAT IT DOES NOT MODEL
// - No command legality. Chapter 13.1 owns the legality layers and
// Chapter 17.1 owns the commit that produced this command. A PHY
// cannot repair an illegal command and this block does not try.
// - No scheduling, queueing or arbitration (Module 17).
// - No physical launch. cmd_launch is an abstract handoff toward the
// command/address path; pin timing, slew, drive and the actual
// CA signalling are outside this model entirely.
// - No data path. Chapter 19.2 owns serialisation.
// - No training, no delay elements, no calibration algorithm. The
// readiness input is produced by Chapter 19.5's lifecycle; how the
// settings behind it were found is Module 21's subject.
// - No clock-domain crossing. Everything here is in ONE domain; §11
// explains why a real PHY's domain structure is not modelled.
// ─────────────────────────────────────────────────────────────────────
module phy_command_stage #(
parameter int CMD_W = 4,
parameter int ADDR_W = 24,
parameter int CS_W = 2
) (
input logic clk,
input logic rst_n,
// ── From the controller. Chapter 17.1's commit produced this; here
// it is offered across the interface and may be refused.
input logic ctrl_cmd_valid,
input logic [CMD_W-1:0] ctrl_cmd,
input logic [ADDR_W-1:0] ctrl_addr,
input logic [CS_W-1:0] ctrl_cs,
output logic ctrl_cmd_ready,
// ── From Chapter 19.5's lifecycle. NOT the same as !rst_n: reset
// release is the first of several steps before traffic is legal,
// and conflating them is §12's misconception.
input logic phy_operational,
// ── Toward the command/address path. Abstract: this is a handoff
// to logic that will launch it, not a pin.
output logic cmd_launch,
output logic [CMD_W-1:0] launch_cmd,
output logic [ADDR_W-1:0] launch_addr,
output logic [CS_W-1:0] launch_cs,
input logic launch_taken,
// ── Observability.
output logic busy,
output logic err_accept_while_not_operational
);
// ── Zero-width operands have no meaning at this interface.
if (CMD_W < 1) $fatal(1, "phy_command_stage: CMD_W must be >= 1");
if (ADDR_W < 1) $fatal(1, "phy_command_stage: ADDR_W must be >= 1");
if (CS_W < 1) $fatal(1, "phy_command_stage: CS_W must be >= 1");
logic held;
logic [CMD_W-1:0] h_cmd;
logic [ADDR_W-1:0] h_addr;
logic [CS_W-1:0] h_cs;
// ── THE CONTRACT, stated once.
// Producer : the controller. Consumer : this stage.
// Transfer : ctrl_cmd_valid && ctrl_cmd_ready, on the rising edge.
// Readiness depends on downstream capacity and on the interface
// being operational -- it never depends on ctrl_cmd_valid, so no
// combinational loop can form through this block.
assign ctrl_cmd_ready = phy_operational && (!held || launch_taken);
assign busy = held;
// ── A command accepted while the interface is not operational is a
// controller error, surfaced rather than silently launched into a
// PHY that cannot carry it.
assign err_accept_while_not_operational =
ctrl_cmd_valid && ctrl_cmd_ready && !phy_operational;
// ── The launch offer. Combinational from held state: the stage
// presents what it owns and waits to be taken.
assign cmd_launch = held;
assign launch_cmd = h_cmd;
assign launch_addr = h_addr;
assign launch_cs = h_cs;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
held <= 1'b0;
h_cmd <= '0;
h_addr <= '0;
h_cs <= '0;
end else begin
// ── Release first, then capture. Written in this order so that a
// same-cycle take-and-accept is a clean handover rather than a
// dropped command: the slot frees and is immediately refilled.
if (held && launch_taken) held <= 1'b0;
if (ctrl_cmd_valid && ctrl_cmd_ready) begin
held <= 1'b1;
h_cmd <= ctrl_cmd;
h_addr <= ctrl_addr;
h_cs <= ctrl_cs;
end
end
end
endmoduleSimulating it. Hold phy_operational low and drive ctrl_cmd_valid: ctrl_cmd_ready stays low and nothing is accepted — the interface refuses traffic before it is usable. Raise phy_operational, and the first command is accepted and appears on launch_* the following cycle. Hold launch_taken low and offer a second command: ctrl_cmd_ready stays low, the held command is unchanged, and the controller must wait. §10's trace walks this.
Synthesis. One holding register of CMD_W + ADDR_W + CS_W bits plus a valid flag — for the defaults, thirty-one bits. Nothing here is a timing concern, and nothing here is technology-specific, which is precisely why it is the part a digital designer writes and the structures in §5 are not.
10. The Handover, Cycle by Cycle
EDUCATIONAL TIMING — cycle numbers show ordering only. No pin timing, no CA bus timing, and no relationship to any device parameter is implied.
cyc operational ctrl_valid ctrl_ready accepted held launch taken
─── ─────────── ────────── ────────── ──────── ──── ────── ─────
0 0 1 0 no 0 0 0
1 0 1 0 no 0 0 0
2 1 1 1 YES 0 0 0
3 1 1 0 no 1 1 0
4 1 1 0 no 1 1 0
5 1 1 1 YES 1 1 1
6 1 0 1 no 1 1 1
7 1 0 1 no 0 0 0Three cycles carry the lesson.
Cycles 0–1. The controller is offering a command and the PHY is refusing it. Reset was released before cycle 0 — rst_n is high throughout — but phy_operational is low, so nothing is accepted. Reset release and readiness are different events, separated here by two cycles and in a real system by a great deal more (Chapter 19.5).
Cycles 3–4. A command is held, offered on launch_*, and not yet taken. ctrl_cmd_ready is low, so the controller's next command waits. The held operands do not change — and must not, because the downstream path is reading them.
Cycle 5. launch_taken and a new acceptance occur in the same cycle. The slot is released and refilled on one edge. This is the case the always_ff ordering in §9 exists to make correct, and §11's P4 asserts it: a simultaneous take-and-accept must neither drop the incoming command nor extend the outgoing one.
11. What the Assertions Prove
phy_command_stage has its own clk and rst_n, so these may sit inside the module or in a bind unit.
// ── P1. No acceptance before the interface is operational. The
// property that separates reset release from readiness, which §12
// lists as the module's most consequential misconception.
property p_no_accept_before_operational;
@(posedge clk) disable iff (!rst_n)
(ctrl_cmd_valid && ctrl_cmd_ready) |-> phy_operational;
endproperty
a_no_accept_before_operational: assert property (p_no_accept_before_operational);
// ── P2. A held command is immutable until it is taken. If the
// operands change underneath a downstream path that is reading
// them, the launched command is not the accepted one.
property p_held_command_stable;
@(posedge clk) disable iff (!rst_n)
(held && !launch_taken)
|=> (held && $stable(launch_cmd) && $stable(launch_addr)
&& $stable(launch_cs));
endproperty
a_held_command_stable: assert property (p_held_command_stable);
// ── P3. Ownership is not silently overwritten: a new command may be
// accepted only into a free slot, or into one being freed this
// cycle. Catches the classic "accept whenever valid" bug, which
// loses the held command without any error indication.
property p_no_overwrite;
@(posedge clk) disable iff (!rst_n)
(ctrl_cmd_valid && ctrl_cmd_ready) |-> (!held || launch_taken);
endproperty
a_no_overwrite: assert property (p_no_overwrite);
// ── P4. The simultaneous case of §10's cycle 5. A take and an accept
// in one cycle must leave the stage held -- with the NEW command.
// Written on the following cycle because that is when the effect of
// both events is observable.
property p_simultaneous_take_and_accept;
@(posedge clk) disable iff (!rst_n)
(held && launch_taken && ctrl_cmd_valid && ctrl_cmd_ready)
|=> held;
endproperty
a_simultaneous_take_and_accept: assert property (p_simultaneous_take_and_accept);
// ── P5. Readiness never depends on the offer. A ready that waits for
// valid is the combinational loop Chapter 17.5 §2 forbids, and it
// is easy to introduce while "optimising" the expression above.
property p_ready_independent_of_valid;
@(posedge clk) disable iff (!rst_n)
ctrl_cmd_ready == (phy_operational && (!held || launch_taken));
endproperty
a_ready_independent_of_valid: assert property (p_ready_independent_of_valid);
// ── Covers. Each names a case the properties are otherwise checking
// in an empty space.
c_refused_before_ready: cover property (@(posedge clk) disable iff (!rst_n)
ctrl_cmd_valid && !phy_operational);
c_stalled_launch: cover property (@(posedge clk) disable iff (!rst_n)
held && !launch_taken);
c_take_and_accept: cover property (@(posedge clk) disable iff (!rst_n)
held && launch_taken && ctrl_cmd_valid && ctrl_cmd_ready);What they prove. That the handover is a genuine ownership transfer: nothing is accepted before the interface can carry it, nothing held is mutated, nothing is overwritten, and the simultaneous case behaves.
What they do not prove — and the gap is the whole point of this module. Nothing here establishes that the command is legal; that is 13.1's and 17.1's contract, and a PHY cannot repair an illegal command. Nothing establishes that the command reaches the DRAM correctly — that depends on launch alignment (19.3), on the physical CA path, and on electrical margin (Module 22), none of which is in this model. And nothing establishes anything at all about timing at the pins: every cycle number in §10 is an ordering label.
Vacuity note. P4's antecedent requires four conditions simultaneously and a random testbench will produce it rarely; c_take_and_accept exists so that a passing P4 can be distinguished from an unexercised one. The same applies to P1 and c_refused_before_ready — if the stimulus never offers a command before readiness, P1 has checked nothing.
12. Corner Cases
| Situation | Correct behaviour | Failure if mishandled |
|---|---|---|
ctrl_cmd_valid while phy_operational low | refused; nothing accepted | a command launched into an unusable interface |
| reset released, readiness not yet asserted | still refused | the module's central misconception, in hardware |
held and launch_taken low | operands frozen; ready low | downstream reads a mutating command |
| take and accept in one cycle | slot refilled; held stays high | the incoming command is dropped, or the outgoing one repeats |
launch_taken asserted while held low | ignored | a phantom launch acknowledged |
phy_operational drops while a command is held | held command survives; no new acceptance | a launch abandoned with no record |
CS_W = 1 | legal — single chip select | zero-width vector |
| back-to-back accepts with immediate takes | one command per cycle sustained | a needless bubble every other cycle |
| reset while held | slot cleared | a stale command launched after reset release |
The row about phy_operational dropping mid-flight deserves a note. This block keeps the held command and simply stops accepting new ones, which is the conservative choice. A real PHY that loses readiness mid-operation has a more serious problem than one command's fate, and what should happen is a system-level decision rather than a detail this block should invent.
13. DV — Checking a Contract, Not a Datapath
There is no data transformation here, so the independent model is not a datapath model. It is a queue of ownership events.
Maintain, independently of the RTL's held flag: a single-entry reference slot, filled on each observed ctrl_cmd_valid && ctrl_cmd_ready and emptied on each observed cmd_launch && launch_taken. Then assert three things the RTL never gets to state: the slot is never filled when already full, never emptied when already empty, and the operands launched are byte-identical to the operands accepted.
COMMAND HANDOVER MISMATCH
cycle : 41
event : accept
reference slot state : FULL (filled at cycle 38, not yet taken)
launch_taken at 41 : 0
RTL ctrl_cmd_ready : 1 <- should be 0
diagnosis : the stage asserted ready while holding an untaken
command. The command accepted at 38 will be
overwritten and lost.
caught by : P3 (no overwrite)
NOT caught by : any data comparison -- the launched command is a
perfectly well-formed command. It is simply not the
one the controller believes it issued.The last two lines are the reason the checker tracks ownership rather than values. A lost command is not corrupt data; it is a command the DRAM never receives while the controller's model says it did — and the symptom appears later, as a bank whose state the controller has wrong.
Directed cases worth running: offer during reset; offer before readiness and confirm refusal; the take-and-accept collision of §10's cycle 5; a long stall with the controller holding valid; readiness dropping while held; and back-to-back sustained acceptance, which is the only case that exercises the ready expression's second term.
14. Debugging
Symptom: the interface works after a warm reset and fails after a cold boot. Layer E. The discriminator is what differs between the two paths: a warm reset typically leaves trained settings intact while a cold boot must establish them. If traffic is being accepted before calibration has completed, P1 is the property that would have caught it and phy_operational is the signal to inspect. Chapter 19.5 owns the lifecycle.
Symptom: commands appear to be lost — the controller's bank model disagrees with the device. Layer B. Compare the count of accepted handovers against the count of taken launches; §13's reference slot does this directly. An accept-while-held bug produces exactly this and produces no corrupt data anywhere.
Symptom: every command is late by one cycle, consistently. Not a bug in this block. Consistent, deterministic offset is the signature of launch alignment, which is Chapter 19.3's subject, and it is worth checking before suspecting anything physical.
Symptom: failures that correlate with data rate. Layers D, E or F, and §8 says the rate test cannot distinguish them. The next discriminator is which lanes — a single failing lane points at per-lane training or a board issue; all lanes failing together points at clocking or a common timing reference.
Symptom: one byte lane fails and the rest pass. Not a controller problem and almost never a scheduling problem, because the controller does not know lanes exist. Per-lane state lives in the PHY (19.4, 19.5) and per-lane physical differences live on the board (Module 22).
15. Misconceptions
“The controller and the PHY are the same block.” They are different design problems with different tooling — one is portable RTL, the other is technology-specific (§5). Clue: a block diagram with no interface between them, and an RTL model that claims to cover both.
“The PHY decides which DDR command to issue.” It launches what it is given. Module 17 decides. Consequence: a legality bug gets investigated in the wrong layer. Clue: a scheduling question raised against the PHY team.
“The PHY is just a serialiser.” Serialisation is one of four paths in §4, and the receive side needs gating, capture and trained delays that transmission does not. Clue: a PHY model with no receive-side state.
“The PHY is entirely analogue” / “the PHY is entirely RTL.” Both wrong, and in opposite directions. §5's table is the boundary: real PHYs are a mixture, which is why they are technology-specific. Clue: either a PHY treated as a black box no digital engineer can touch, or one assumed portable across processes.
“Ordinary fabric flops can implement a DDR interface.” §1's callout. Clue: always_ff @(posedge dqs).
“PHY ready means reset is deasserted.” §10's cycles 0–1 and P1. Consequence: traffic issued into an uncalibrated interface, which fails on cold boot and passes on warm. Clue: a design where phy_operational is wired to rst_n.
“The controller can compensate for a PHY timing error.” It can space commands differently; it cannot move a sampling point. Those are different mechanisms in different layers. Clue: a proposal to fix a capture problem by relaxing a controller timing parameter.
“The PHY can repair an illegal command.” It launches what it is given. Clue: an expectation that the PHY will enforce a timing rule the controller violated.
“Correct RTL guarantees a working DDR interface.” Everything in §5 is outside the RTL. A gearbox that reorders beats correctly says nothing about whether they were sampled correctly. Clue: sign-off based on simulation alone.
“Every DDR PHY has the same architecture.” §3's fourth column lists six functions whose ownership genuinely varies. Clue: an architecture diagram presented as the DDR PHY.
16. Interview Reasoning
“Why does a DDR system need a PHY at all?” Because a specific set of functions cannot be expressed in portable synchronous RTL — §5's table. The strong answer names two or three of them rather than saying “because it's fast”.
“What should the controller own and what should the PHY own?” Give the clear cases, then volunteer the implementation-dependent ones. Naming training ownership as varying is the discriminating detail.
“Why is the boundary implementation-dependent?” Because the functions are defined by what must happen, not by where it happens, and vendors partition them differently — particularly training and ODT control.
“What is the difference between ZQ calibration and PHY calibration?” §7. Different sides of the interface, different state, different invocation. Confusing them sends a debug effort to the wrong die.
“Your DDR interface works at a lower rate and fails at full rate. What does that tell you?” That layers A, B and C are largely exonerated, because digital ordering bugs are rate-independent. It does not identify which of capture, training or signal integrity is at fault — and saying so is the better answer.
“Why must PHY readiness be separate from reset release?” Because clocks must stabilise and calibration must complete before the interface can carry traffic. Conflating them produces a design that passes warm-reset testing and fails cold boot.
“What can you verify about a PHY in simulation, and what can you not?” Ordering, ownership, handshakes and framing — yes. Sampling position, margin and electrical behaviour — no. Being explicit about the second half is what separates a useful answer from a confident one.
17. Exercises
1. Classify each as controller, PHY, DRAM or implementation-dependent: refresh scheduling; strobe generation on a write; strobe generation on a read; deciding when ODT is applied; discovering a read delay value; transaction completion.
2. §3 marks six rows implementation-dependent. Pick the training row and describe two real partitions that would both be correct, and one consequence of each for the controller's interface.
3. Draw the write path from a controller-accepted transaction to the DQ pins, naming the chapter that owns each stage.
4. Do the same for the read path, and mark the one box this module does not open.
5. Construct a stimulus where phy_command_stage accepts a command and the controller's model and the DRAM's state permanently disagree. Which property catches it, and which does not?
6. ctrl_cmd_ready is phy_operational && (!held || launch_taken). Rewrite it to also require ctrl_cmd_valid and name the failure that introduces.
7. A colleague wires phy_operational to rst_n. Give the test that passes and the test that fails, and explain why that ordering is the common one.
8. Explain why an assertion proving beat order is correct says nothing about whether the eye was sampled correctly. Name the module that owns the second claim.
18. Where This Goes
The boundary is drawn and the paths are named. Four of the boxes in §4's diagram are opened by the rest of this module.
Chapter 19.2 opens the width-versus-rate conversion on both the write and read paths — why it exists, what ordering it must preserve, and why a correct gearbox is a genuine digital component with genuine bugs. 19.3 opens launch alignment: the relationship the PHY must hold between a command and the data that belongs to it. 19.4 opens strobe generation and the receive gate — the PHY's answer to a strobe that is not always driven. 19.5 opens the lifecycle that produced phy_operational, and the settings every other chapter has been consuming as opaque values.
The box marked CAPTURE stays closed until Module 20. That is not an omission — it is the next module, and this one exists to make its boundary legible.
Continue learning
Related tutorials
- Related topic
Ranks
A rank is the set of devices that together supply the controller's data width. Because ranks share one physical data bus, only one may drive it at a time — which makes a rank a question of resource ownership, and means adding ranks adds capacity without adding bandwidth.
- Related topic
DQ — The Data Bus
Saying DQ is bidirectional says almost nothing. What matters is who drives these wires right now, how ownership changes, and what guarantees both sides never drive at once — which is a state machine, not a property.
- Related topic
Data Return
During a read the DRAM drives the bus and supplies the timing reference. Controller RTL never sees that — it receives already-captured beats from a boundary it must not cross.
- Related topic
Burst Writes
A read's controller receives beats as they arrive. A write's controller must produce every beat on consecutive transfer opportunities, without interruption, because the device is sampling on a schedule and will not wait.
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.
