DDR · Module 30
Controller-Scheduling Question
Whiteboard a scheduler. The failure is collapsing request, candidate, grant and committed command into one thing — and the interviewer's next three additions expose it. Includes a fairness fix that issues illegal commands.
“Design a DDR memory-controller scheduler on the whiteboard.” It is the broadest question in the set and the one most often failed with a correct-sounding answer.
The failure is not a missing feature. Nobody is marked down for omitting a prefetcher. The failure is collapsing four distinct things into one — and the interviewer does not have to point it out, because their next three additions will do it for them.
The law this chapter is built on:
A request is not a candidate, a candidate is not a grant, and a grant is not a committed command. Answer which requests are legal before which one should issue, and never in the same box.
1. What the Question Is Really Grading
Four things, and only the fourth is about DDR.
| What is graded | How it shows |
|---|---|
| Whether you separate legality from policy | the first box you draw |
| Whether you hold four stages apart | how you answer "why didn't it issue?" |
| Whether your structure absorbs a new requirement | the three perturbations in §6–§8 |
| Whether you know the DDR-specific constraints | which rules you name, and their scope |
The third row is the whole interview. An interviewer with fifteen minutes cannot check a scheduler's correctness. What they can do is add one requirement and watch whether you change a layer or start again.
So the answer is graded on structure, and structure is decided in the first two minutes — which is why §2 is about build order rather than about DDR.
2. Build Order — What to Draw First
Draw the pipeline, in this order, and say the order out loud as you draw it.
1 REQUEST INGEST what arrives, and what an entry retains
2 EXPANSION one request -> one to three commands
3 LEGALITY per candidate command: is it legal THIS cycle?
4 POLICY among the legal ones, which is preferable?
5 ARBITRATION one winner, one bus slot
6 COMMIT the single instant state may change
7 TIMING STATE UPDATE fed ONLY by the commitStages 3 and 4 being separate boxes is the answer to the question. Everything else is detail that can be filled in under questioning.
CURRICULUM-DERIVED from 17.4 §3, which owns the rule and states the consequence exactly: a broken legality filter issues illegal commands with a brilliant policy; a broken policy issues legal commands badly. The first is a protocol violation and the second a performance problem, and keeping them in separate blocks is what keeps their bugs separable.
Stage 7 is the loop that most whiteboards omit. Timing state is updated by the commit, never by the grant and never by the request — 17.1 owns the commit point as the single instant architectural state may change. A design that updates timing state on the grant has a timing model that diverges from the bus the first time a grant does not commit.
The three wrong first moves, in descending order of how often they happen:
| Wrong first move | What it signals | What it costs later |
|---|---|---|
| Drawing the arbiter first | policy is being treated as the problem | legality ends up inside it — 17.4 §3's inverse error, which is worse |
| Drawing the queue first and stopping | the question was heard as design a queue | expansion is never reached, so one request stays one command |
| Listing heuristics — FR-FCFS, open-page, write batching | recall is substituting for structure | no place to put them; 23.4 and 23.5 own them and they are stage 4 |
The third is the most common and the most damaging, because it sounds knowledgeable. Naming a heuristic before drawing the box it lives in gives the interviewer nowhere to put it, and every follow-up then lands on a structure that does not exist.
3. The Four-Way Separation
One command's life has four stages, and each collapse produces a specific, recognisable wrong answer.
| Stage | It means | Collapsing it into the next produces |
|---|---|---|
| Request | a requester asked for bytes | one request is one command — no precharge, no activate, no expansion |
| Candidate | a specific command that is legal this cycle | the arbiter checks timing — 17.4 §3's worse error |
| Grant | policy and arbitration chose it | chosen means issued — no accounting for the cycle a grant is dropped |
| Committed command | it is on the bus; state may now change | timing state updates on selection — a model that drifts from the bus |
Each row is a different chapter's subject and the four-way checklist is this chapter's. 17.2 owns row one, 17.4 rows two and three, 17.1 row four.
The single question that tests all four at once: a read has been waiting 200 cycles — why has it not issued? A design with four stages answers precisely:
it is not yet a CANDIDATE -> a timing rule blocks it; WHICH rule
it is a candidate, never GRANTED -> policy prefers others; starvation
granted but not COMMITTED -> the bus slot went elsewhere
committed, and this is the NEXT command of the same request
-> expansion; it was never one commandA design with one box answers "it's busy". CURRICULUM-DERIVED from 17.4 §3: an empty result from a combined legality-and-policy block is just a loop that found nothing, while an empty mask arriving at a pure policy block is a reportable fact with an attributable cause. That is the argument for the separation, and it is a debuggability argument rather than an aesthetic one — which is why it is worth making unprompted.
4. The Life of One Command
Four stages, and the three edges people forget.
Three edges are worth more than the four states.
CANDIDATE back to QUEUED — legality is not latched. Another command committing to the same resource can withdraw candidacy on the next cycle. A whiteboard that computes a legal set once and then arbitrates over it has a stale set, which is 30.2's stale-state hazard in scheduler form.
The self-loop on CANDIDATE — legal every cycle and never chosen. That loop is starvation, and 17.4 §8 owns demonstrating it concretely rather than asserting it. §8 is the interviewer asking you to bound it.
COMMITTED back to QUEUED — one request is many commands. A conflicting read commits a precharge, then re-enters the queue for an activate, then again for the column command. A candidate whose diagram has no such edge has answered a question about one command, not about a scheduler.
5. Working the Question
Q1 (establishing). What does the scheduler choose among?
Commands, not requests — and stating that distinction is the answer. Each queued request has expanded into the specific next command it needs, which depends on the target bank's row state: a hit needs a column command, a miss an activate, a conflict a precharge first. CURRICULUM-DERIVED from 17.1 §3.
So the input to legality is a set of candidate commands, one or more per request, and the output is at most one commit per cycle — because there is one command bus.
Q2 (applying). Five requests are queued. Which one issues this cycle?
The correct answer is two phases and refusing to merge them.
Phase one, legality — a filter that knows nothing about preference:
for each candidate command, ask ONLY:
does the target resource's timing state permit it THIS cycle?
the rules, with their SCOPE -- and scope is the part that is
graded (30.3 §3, 14.6):
same bank tRCD, tRP, tRAS, tRC
same bank group the longer column spacing
any bank the shorter column spacing; tRRD; tFAW
the whole rank direction turnaround; refresh occupancy
output: a MASK. no ordering, no preference, no scores.Phase two, policy — a chooser that never re-checks legality:
among the legal mask ONLY, prefer in layers (17.4 §4):
layer 1 urgency an aged entry must become reachable (§8)
layer 2 locality row hits over misses (23.3, 23.4)
layer 3 direction batch to avoid turnaround (§7)
layer 4 rotation a pointer, advanced on COMMIT (17.4 §7)
each layer NARROWS the set. if a layer would empty it,
it passes the set through unchanged.The narrowing rule is the mechanism worth stating. A layer that empties the set has expressed a preference that cannot be satisfied, and the correct behaviour is to ignore that preference rather than to issue nothing. CURRICULUM-DERIVED from 17.4 §4's layered mask: layers, not numbers.
And the sentence that shows you understand why layers rather than scores: a weighted score collapses the two phases back together, because a large enough weight can outvote legality. Layers cannot, because legality is not a layer — it is the set the layers operate inside.
6. Perturbation One — The Interviewer Adds Refresh
“Now handle refresh.”
The tempting answer is a high-priority request class, and it is wrong for a structural reason — not because the priority is too low.
CURRICULUM-DERIVED from 17.3 §2, which owns the argument in full: an arbiter's job is to choose among legal candidates, so if refresh is a candidate, then by construction there exist cycles where something else is chosen instead. Raising its priority makes those cycles rarer, and rarer is not a property you can bound.
And the constraint is the wrong shape for an arbiter. An all-bank refresh requires all banks idle before issue, so the controller cannot merely prefer refresh — it must stop opening rows and wait for open ones to close. That is a phase change in what the scheduler is permitted to do, lasting many cycles, and no priority number expresses it.
So what changes in the §2 pipeline:
| Stage | Change |
|---|---|
| 3 Legality | during refresh occupancy, normal commands are illegal — they leave the mask entirely |
| 4 Policy | a new input, preparing_for_refresh: stop opening rows, prefer precharges |
| 5 Arbitration | unchanged |
| 6 Commit | the refresh commit is what starts the occupancy window |
Two boxes changed and nothing was restructured — which is the answer the perturbation is testing for. And the distinction to say out loud: during the drain the read is legal and merely not preferred, which is policy; during occupancy it is not a candidate at all, which is legality. A refresh does not outrank a read; for part of the window it removes the read from the legal set — 17.3 §2.
7. Perturbation Two — The Interviewer Adds Writes
“Now there are writes too.”
The trap is answering add a write queue. A second queue is trivial. What writes actually introduce is a shared resource with a direction, and direction is structurally unlike every other dimension.
CURRICULUM-DERIVED from 17.4 §10, which owns direction as a dimension with its own, independent starvation problem.
Why it is different from the other layers: every other preference costs a little efficiency when you get it wrong. Direction costs a turnaround penalty on the shared data bus every time you change it, so the efficient policy is to batch, and batching is exactly what starves the other direction.
DERIVED from a stated model, ILLUSTRATIVE turnaround = 8 cycles,
ILLUSTRATIVE burst occupancy = 4 cycles per command.
alternating R W R W R W 6 commands, 5 turnarounds
data cycles 6 * 4 = 24
turnaround 5 * 8 = 40
-> 24 / 64 = 37.5% of the bus carried data
batched R R R W W W 6 commands, 1 turnaround
data cycles 6 * 4 = 24
turnaround 1 * 8 = 8
-> 24 / 32 = 75% of the bus carried data
so batching DOUBLES useful bus occupancy here -- and a read
arriving at the start of a write batch waits for the whole batch.That is the trade stated as a number rather than as an opinion, and stating it as a number is the difference between a senior answer and a plausible one. Chapter 12.4 owns the measure this is — slot utilisation — and why it must stay distinct from the other three.
What changes in the pipeline: direction enters as layer 3 of policy with a bounded batch length, and turnaround enters legality as a rank-scope rule. Two stages again, and the bound is the part that must be stated — an unbounded batch is §8's problem with a different name.
8. Perturbation Three — “Prove It Cannot Starve”
This is the one that separates levels, because a layered design starves by construction and saying so is the correct first move.
CURRICULUM-DERIVED from 17.4 §8, which demonstrates starvation concretely: a locality preference plus a stream of row hits to one bank means another bank's request is legal every cycle and chosen on none. §4's self-loop is that entry.
So the honest answer begins: as described, it starves, and here is the mechanism — then the fix.
CURRICULUM-DERIVED from 17.4 §9: the fix must make the starving entry reachable, which means a layer above the preference, not a bigger number inside it. Age each entry; above a threshold, the aged entry becomes the layer-1 set, and the preference layers then narrow within it and find nothing to narrow.
Three things an interviewer will probe, and all three have real answers:
The threshold must exceed the longest legitimate wait, or the mechanism fires constantly and the preference layer is disabled. CURRICULUM-DERIVED from 17.4 §9: that bound includes tRAS plus tRP plus tRCD in the conflict case, plus the refresh drain and occupancy, plus contention from every other entry.
The age counter must be wide enough to hold the threshold. A threshold of 64 in a 6-bit counter makes the comparison unreachable — a fairness mechanism present in the source and absent in the silicon, and the COUNT-versus-INDEX hazard in its most consequential form.
And the bypass must select only among legal candidates. This is where the fix most often breaks the thing it was added to, and §9 is that code.
The qualification that makes the answer senior: there is no single correct bypass mechanism. CURRICULUM-DERIVED from 17.4 §9, which lists age thresholds, bypass counting, urgency promotion and rotating preference as all legitimate with different bounds. What is general is that a preference layer with no bounded override starves and the override must sit above the preference to be reachable. Claiming one mechanism is the answer is the failure; naming the general rule and one mechanism is the answer.
9. RTL Review — Candidate Generation and the Bounded Bypass
The intended contract:
legal_maskis an input. This block must not compute or re-check legality — 17.4 §3.grantmust be a subset oflegal_mask: an entry that is not legal can never be granted, whatever its age.- At most one
grantbit is set, because there is one command bus. - Policy may only narrow the legal set; a layer that would empty it passes it through.
- An entry legal on every cycle must be granted within
AGE_THRESHOLDcycles of becoming legal.
// ---------------------------------------------------------------------
// sched_policy_pick -- INTENTIONALLY DEFECTIVE, for review (§9).
//
// CLASSIFICATION: synthesisable, ILLUSTRATIVE, and CONTAINS A BUG.
//
// WHAT IT IS MEANT TO DO: the five-clause contract above -- choose one
// grant from a legality mask supplied by a separate block, preferring
// row hits, with a bounded age-based override so no legal entry can
// starve.
//
// WHY IT EXISTS HERE: §8 establishes that a layered policy starves by
// construction and that the override must sit ABOVE the preference to
// be reachable. This block is what happens when "above the preference"
// is implemented as "above everything".
//
// HOW TO RUN IT: hold one entry's age above AGE_THRESHOLD while its
// legal_mask bit is LOW.
// EXPECTED RESULT under clause 2: it is NOT granted.
// EXPECTED TRACE: grant must remain a subset of legal_mask on every
// cycle, including the cycle the age threshold is crossed.
//
// SYNTHESIS: one age counter per entry plus two priority encoders.
// LIMITATIONS: models one dimension of preference (row hit) and one
// override (age). Direction batching (§7) and rotation are out of
// scope and are NOT what the bug is.
// ---------------------------------------------------------------------
module sched_policy_pick #(
parameter int NUM_ENTRIES = 8,
parameter int AGE_THRESHOLD = 64, // ILLUSTRATIVE
// COUNT, not INDEX: the counter must REPRESENT AGE_THRESHOLD, so it
// needs $clog2(AGE_THRESHOLD+1) bits. Too narrow and the comparison
// is unreachable -- §8's "present in the source, absent in the
// silicon". This is the hazard the guard below exists to prevent.
parameter int AGE_W = $clog2(AGE_THRESHOLD + 1)
)(
input logic clk,
input logic rst_n,
input logic [NUM_ENTRIES-1:0] valid,
input logic [NUM_ENTRIES-1:0] legal_mask, // clause 1: an INPUT
input logic [NUM_ENTRIES-1:0] row_hit, // preference, layer 2
input logic commit, // a grant reached the bus
input logic [NUM_ENTRIES-1:0] freed,
output logic [NUM_ENTRIES-1:0] grant,
output logic urgent_active
);
initial begin
if (AGE_THRESHOLD >= (1 << AGE_W))
$fatal(1, "sched_policy_pick: AGE_W too narrow for AGE_THRESHOLD");
end
logic [AGE_W-1:0] age [NUM_ENTRIES];
always_ff @(posedge clk) begin
if (!rst_n) begin
for (int i = 0; i < NUM_ENTRIES; i++) age[i] <= '0;
end else begin
for (int i = 0; i < NUM_ENTRIES; i++) begin
if (freed[i] || (commit && grant[i])) age[i] <= '0;
else if (valid[i] && age[i] != {AGE_W{1'b1}}) age[i] <= age[i] + 1'b1;
end
end
end
// Lowest set bit of a mask -- one bus, one grant (clause 3).
function automatic logic [NUM_ENTRIES-1:0] lowest(input logic [NUM_ENTRIES-1:0] m);
return m & (~m + 1'b1);
endfunction
logic [NUM_ENTRIES-1:0] cand, pref, aged;
// Layer 0 -- the legal set. Consumed, never recomputed (clause 1).
assign cand = valid & legal_mask;
// Layer 2 -- prefer row hits, but only NARROW (clause 4).
assign pref = (|(cand & row_hit)) ? (cand & row_hit) : cand;
// Layer 1 -- the bounded age override of §8.
always_comb begin
aged = '0;
for (int i = 0; i < NUM_ENTRIES; i++)
if (valid[i] && age[i] >= AGE_THRESHOLD[AGE_W-1:0])
aged[i] = 1'b1; // <-- THE DEFECT
end
assign urgent_active = |aged;
assign grant = (|aged) ? lowest(aged) : lowest(pref);
endmoduleBefore reading on: which clause, what trace, and why does it pass a one-hot check and a fairness check?
10. The Defect — The Fix Broke the Contract It Sat Above
The violated clause is 2, and the defect is an omitted term: aged is built from valid alone, where the contract requires valid & legal_mask.
The override was placed above the preference — correctly, per §8 — and above legality as well, which is not the same thing.
ILLUSTRATIVE AGE_THRESHOLD = 64, NUM_ENTRIES = 8.
entry 3 targets a bank whose row was just activated; tRCD has
not elapsed, so legal_mask[3] = 0. entry 3 has been valid and
waiting, so its age advances regardless.
cyc legal_mask valid age[3] aged grant legal?
─── ────────── ───── ────── ──── ───── ──────
700 0b11110111 0b1..1 62 0b0.. entry 0 yes
701 0b11110111 0b1..1 63 0b0.. entry 0 yes
702 0b11110111 0b1..1 64 1<<3 entry 3 NO <--
^ aged ignores legal_mask
grant = 0b00001000 while legal_mask[3] = 0.
the controller commits a column command before tRCD has
elapsed -- a protocol violation, produced by the fairness fix.This is precisely 17.4 §3's inverse error, which is worse: a design that lets policy decide legality issues an illegal command with a perfectly sensible-looking grant.
Now the question an interviewer is actually asking: why does this pass review?
Because every property anyone thinks to write about an arbiter is about its shape. grant is one-hot. The fairness bound holds — better than required, since it fires even earlier than it should. urgent_active pulses exactly when documented. Nothing about the block's shape is wrong. The wrong thing is its content: which set it chose from. §11 is that distinction.
And note the second-order effect, which is the part worth saying unprompted: because aged ignores legality, an entry blocked for a long time accumulates age while it cannot possibly be served, so it crosses the threshold at the worst possible moment — the cycle it is most likely still illegal. The defect does not merely permit illegality occasionally; it is biased toward producing it.
The corrected layer, and the one-line rule to state alongside it:
// CORRECTED. The override sits above the PREFERENCE, not above
// LEGALITY. Clause 2: grant is a subset of legal_mask whatever an
// entry's age. 17.4 §3 -- a broken policy issues legal commands
// badly; a policy that decides legality issues illegal commands.
always_comb begin
aged = '0;
for (int i = 0; i < NUM_ENTRIES; i++)
if (cand[i] && age[i] >= AGE_THRESHOLD[AGE_W-1:0])
aged[i] = 1'b1; // cand = valid & legal_mask
endAnd the consequence to acknowledge rather than hide: with the fix, clause 5's bound now applies only to entries that are legal, which is the only bound that was ever achievable. An entry that is never legal cannot be rescued by an arbiter, and if that happens the fault is upstream — a timing rule that never releases, which is 17.4 §9's threshold argument read backwards. Saying which bound you can actually prove is stronger than claiming the one you cannot.
11. SVA Review — Shape Versus Content
Three properties offered as proof that the arbiter is correct:
// All three pass on the defective block in §9.
property p_grant_onehot0;
@(posedge clk) disable iff (!rst_n) $onehot0(grant);
endproperty
assert property (p_grant_onehot0) else $error("multiple grants");
property p_grant_is_valid;
@(posedge clk) disable iff (!rst_n) (grant != '0) |-> ((grant & valid) == grant);
endproperty
assert property (p_grant_is_valid) else $error("granted an invalid entry");
property p_urgent_reported;
@(posedge clk) disable iff (!rst_n) urgent_active |-> (grant != '0);
endproperty
assert property (p_urgent_reported) else $error("urgent with no grant");Q. All three pass. What do they prove, and what does the prose claim?
They prove the grant's shape: how many bits, that the entry exists, that urgency produces a grant. None of them names legal_mask, so none can detect that the grant left the legal set. A property that never mentions the set cannot constrain membership in it.
This is the review question worth carrying out of the chapter:
Before accepting a property as evidence, name the signal whose corruption it would catch. If the contract's key signal does not appear in the property, the property is about something else.
What actually covers clause 2:
// Clause 2. The subset property -- and legal_mask APPEARS in it,
// which is the whole difference from the three above.
property p_grant_subset_of_legal;
@(posedge clk) disable iff (!rst_n)
(grant & ~legal_mask) == '0;
endproperty
assert property (p_grant_subset_of_legal)
else $error("grant left the legal set");
// Clause 1. The block must not be able to widen its own input. An
// invariant rather than an implication: there is no cycle on which
// a non-legal grant is acceptable, so there is no antecedent.
property p_cand_is_subset;
@(posedge clk) disable iff (!rst_n)
(cand & ~legal_mask) == '0;
endproperty
assert property (p_cand_is_subset)
else $error("candidate set is wider than the legal set");
// The cover WITHOUT WHICH p_grant_subset_of_legal proves little:
// the defect only fires when an entry is AGED AND ILLEGAL. An
// environment whose entries always become legal quickly never
// reaches it, and the assertion passes without exercising the bug.
cover property (@(posedge clk) disable iff (!rst_n)
urgent_active && ((aged & ~legal_mask) != '0));
// And the antecedent of clause 5's fairness property, published
// separately -- a zero here means no entry ever aged out, which is
// a different fact from the fairness bound holding.
cover property (@(posedge clk) disable iff (!rst_n) urgent_active);p_grant_subset_of_legal is one line and it is the property this block needed. It is also the one nobody writes, because the mental model that produced the bug also produces the property list — 30.4 §8 established that pattern and this is its second instance, which is why 30.9 treats it as a method rather than an anecdote.
Follow-up an interviewer should ask: is the cover reachable in a correct design? In the corrected block, no — aged is a subset of cand by construction, so aged & ~legal_mask is identically zero and the cover is dead. That is the right answer and the right behaviour: the cover's job is to prove the defective case was reachable during bring-up, and its going dead after the fix is the evidence the fix worked. A cover that must stay live and one that must go dead are different instruments, and naming which you are writing is the senior move.
12. What Would You Measure?
Q. The controller's bandwidth is below model. What does the scheduler need to report?
| Instrument | What it distinguishes | Owner |
|---|---|---|
| Cycles with a non-empty legal mask and no commit | policy failing to choose, versus nothing being legal | §3 |
| Cycles with an empty legal mask, broken down by binding rule | which timing rule is the limit — 30.3 §10 | 13.3 |
urgent_active rate | whether the fairness override is firing constantly, disabling the preference layer | 17.4 §9 |
| Grants that did not commit | the grant-versus-commit collapse, and its cost | 17.1 §7 |
| Age histogram, not maximum age | a tail a maximum hides, and whether the threshold is set sanely | 17.2 |
| Turnaround cycles as a share of bus cycles | whether direction batching is working — §7 | 12.4 |
| The cover on aged and illegal | whether §9's bug class is even reachable here | §11 |
Rows one and two together are the whole diagnosis, and they are only separable because legality and policy are separate boxes. A combined block reports "nothing issued" and nothing more — §3's debuggability argument, arriving as a measurement plan.
Row three is the one people omit and it inverts a conclusion. A high urgent_active rate means the fairness mechanism is firing so often that the locality preference is effectively off, so the controller is fair and slow rather than unfair and fast — and the fix is the threshold, not the preference.
13. Common Wrong Answers
“The arbiter checks the timing rules.” §2, §3. 17.4 §3's inverse error, which is worse — it issues illegal commands with a sensible-looking grant. Legality is decided before the arbiter sees anything.
“Give each request a priority score and pick the highest.” §5. A large enough weight outvotes legality. Legality is not a layer; it is the set the layers work inside.
“I'd use FR-FCFS with open-page policy.” §2. Both are real and both are stage 4. Naming a heuristic before drawing the box it lives in leaves every follow-up with nowhere to land — 23.4 and 23.5 own them.
“One request becomes one command.” §4. A conflicting read is a precharge, an activate and a column command, re-entering the queue between each — 17.1 §3.
“Compute the legal set, then arbitrate over it.” §4. Legality is not latched; a commit elsewhere can withdraw candidacy next cycle. A set computed once is a stale set.
“Refresh is just a high-priority request.” §6. 17.3 §2: if it is a candidate, cycles exist where something else wins, and rarer is not a bound. It is a phase change, not a priority.
“Writes just need their own queue.” §7. The queue is trivial; the shared bus with a direction is not, and batching to avoid turnaround creates a second, independent starvation problem.
“Round-robin prevents starvation.” §8. Round-robin over a set a preference layer has already narrowed is fair within that set and starves everything outside it. The override must sit above the preference.
“My scheduler cannot starve.” §8. A layered design starves by construction, and saying so before being told is the stronger opening. Then bound it.
“Raise the priority until it stops starving.” §8. A bigger number inside a preference layer does not make an entry reachable from above it.
“The grant is one-hot and always valid, so the arbiter is proven.” §11. Those are shape properties. Neither mentions legal_mask, so neither can see the grant leave the legal set.
“The fairness assertion passes, so entries are served.” §11. Check its cover first — if no entry ever aged out, the bound held vacuously.
“Timing state updates when a command is selected.” §2, §3. On the commit. A model updated on the grant diverges from the bus the first time a grant does not commit.
14. Self-Check
-
Write the seven pipeline stages in order from memory, then state which single adjacency is the answer to the interview question and why.
-
For each of the four stages in §3, give the one-sentence wrong answer that collapsing it into the next produces.
-
A read has waited 200 cycles. Give the four distinct, mutually exclusive explanations a four-stage design can offer, and name the counter that identifies each.
-
Explain why refresh cannot be modelled as a high-priority request, then list which of the seven stages change and which do not.
-
Using §7's illustrative model, recompute the two slot-utilisation figures for a turnaround of 4 cycles instead of 8. State whether batching is still worth its starvation cost, and name the counter you would consult.
-
Find the defect in §9 without reading §10. Then explain why it passes a one-hot check, a validity check and a fairness check.
-
State the one-line property that catches it, and the general review rule §11 derives from the fact that nobody writes it.
-
Say why §11's
aged & ~legal_maskcover is supposed to go dead after the fix, and name a cover in this chapter that must stay live instead.
15. Where This Goes
A scheduler question is a structure question. Draw legality and policy as separate boxes before naming a single heuristic; hold request, candidate, grant and committed command apart, because each collapse has its own wrong answer; update timing state on the commit alone; and expect three perturbations — refresh, writes, and a starvation bound — each of which a correctly layered design absorbs by changing two stages.
Three results carry forward. A fairness fix placed above legality rather than above the preference issues illegal commands, and it is biased toward doing so. A property that does not name the contract's key signal is about something else — the second instance of 30.4 §8's pattern. And some covers must stay live while others must go dead, which is an instrument choice rather than an oversight.
Chapter 30.6 crosses the boundary this chapter stopped at. Everything so far assumed the interface works; training is the question of how it came to work at all, and it has its own central confusion. Training does not create margin — it finds where the margin already is, and a pass bitmap is not a physical eye. The question is usually walk me through write levelling, and the answer has to survive being asked what happens when it passes and the interface still fails.
Continue learning
Related tutorials
- Related topic
The Command Scheduler
A DDR command scheduler does not schedule requests. It re-derives each outstanding request's next required command every cycle, and advances architectural state only at the commit point where a command is genuinely issued.
- Related topic
Arbitration
Arbitration runs after legality has already decided. It narrows subsets rather than comparing priority numbers, advances its pointer only on commit, and needs a bounded bypass because row-hit-first genuinely starves.
- Related topic
Row-Buffer Question
Three outcomes, not two — and the cost of an access is a property of the stream rather than of the access. Includes the starvation case the optimisation itself produces, and a property that proves something adjacent to its claim.
- Related topic
tRCD Question
Earliest legal time is a maximum over every applicable rule, so one parameter yields a lower bound. The same timing sentence has three readings differing by one cycle, and a conservative checker violates nothing while costing bandwidth.
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.
