DDR · Module 4
DDR4
DDR4 is the generation where prefetch depth stops changing. With granularity already at a cache line, the rate had to come from overlapping independent accesses instead — which is what bank groups are, and why peak bandwidth became conditional on the access pattern.
Three chapters, three prefetch doublings: 2n, 4n, 8n. DDR4 keeps 8n.
That is the most informative fact in this module. A pattern that held for three generations breaks here, and it breaks for a reason Chapter 4.4 §9 already established in arithmetic: granularity has reached the size of the thing requesters actually want, so another doubling raises the peak bandwidth number without raising the delivered one.
So DDR4 had to find rate somewhere other than in the size of an access. Its answer is the last place available: overlap. If a single access cannot be made bigger and the array cannot be made faster, then the remaining option is to arrange for independent accesses inside the device to proceed closer together — and that is what bank groups are.
This chapter also collects a cluster of changes that look unrelated and are not. DDR4's signalling, its command encoding, its data and command integrity protection, and its trained reference voltage are all consequences of the same pressure: at these rates, every remaining source of error and every remaining watt has to be accounted for individually.
1. The Wall, Restated
The arithmetic from 4.4 §9, because everything in this chapter follows from it:
minimum granularity = prefetch_depth × interface_width
DDR3 8n × 64 bits = 64 bytes -- one typical cache line
"DDR4" 16n × 64 bits = 128 bytes -- TWICE a cache lineA 16n prefetch on a 64-bit channel would transfer a wanted line and an unwanted one on every access. The peak bandwidth doubles; the useful bandwidth does not move. Chapter 4.4 §13 worked that out in numbers, and the result was identical delivered bandwidth for twice the array work.
Burst chop does not rescue it. Chopping a 16n prefetch back to 8 beats means the array read 16 words to deliver 8 — the array waste that 4.4 §5 established chop never addresses.
So the three levers this module has used are all exhausted.
| Lever | Ended at | Why |
|---|---|---|
| Transfers per clock cycle | DDR1 | A clock cycle has two edges |
| Prefetch depth | DDR4 | Granularity reaches the requester's access size |
| Interface width | never available | Pins, traces, drivers, switching energy |
And the clock was never a free lever either, bounded below by what the interface can settle and above by nothing useful — the whole point of prefetch was to let the interface clock exceed the array's access rate, and that ratio is the prefetch depth, which is now fixed.
Which leaves one thing. Every lever so far made one access bigger or faster. The untried option is to stop treating accesses one at a time.
2. Bank Groups — Rate From Overlap
Chapter 3.6 established that a DRAM device is partitioned into banks, each with its own row state, so accesses to different banks are more independent than accesses to the same bank. DDR4 adds a level to that hierarchy: banks are organised into groups.
The reason is resource sharing. Serving a column access requires internal machinery — the path from the sense amplifiers through the prefetch register to the interface. That machinery is not replicated per bank, because replicating it per bank would cost area on a die whose economics Chapter 1.6 showed are dominated by area. It is shared by a set of banks.
So two accesses that contend for the same shared machinery must be separated in time. Two accesses that use different copies of it need not be — or at least, need much less separation. Grouping the banks makes that distinction explicit and exposes it to the controller:
Accesses to banks within the same group must be separated by a longer minimum interval, because they share the internal column path. Accesses to banks in different groups may be separated by a shorter minimum interval, because they use independent copies of it.
The address space gains another field. Chapter 3.6 §5 decomposed an address into bank, row and column; DDR4 inserts a bank-group field. Which address bits carry it decides whether sequential addresses interleave across groups or pile into one — and that mapping choice, made in the controller, can be the difference between reaching the rated rate and missing it substantially. Modules 8 and 16 own the mapping; the point here is that a new field in the address decomposition is the price of the new parallelism.
3. The Other Changes, and Why They Are Not a List
Four more DDR4 changes are usually presented as a feature list. They are better read as four answers to the same question: at 1.2 V with the transfer interval halved again, what is left that can still go wrong?
Signalling — pull-up-only termination. DDR4 replaces DDR3's signalling standard with POD12: a pseudo-open-drain scheme at 1.2 V in which termination is to the supply rather than balanced, so current flows through the termination only when the line is driven low. That makes the driver's impedance deliberately asymmetric between the two output states. The reason is energy. With termination on one side only, a line held at the high level dissipates no termination current — and since 4.2 §4's argument means the interface is now switching far more often per second than any earlier generation, eliminating the static term on one state is a real saving. The cost is that the two states are no longer electrically symmetric, which matters to margin analysis and is part of why 4.6 needs a more capable receiver.
Command encoding — a dedicated activate signal. DDR4 adds ACT_n. When it is asserted, the address inputs carry a row address for an activate; when it is not, those same inputs behave as the conventional control signals. The reason is that the row address outgrew the pins available. Larger arrays need more row-address bits, and pins are the resource Chapter 3.2 §4 priced as the most expensive in the system. Rather than add address pins, DDR4 reuses the control pins as address bits during an activate — and needs one new signal to say which interpretation applies. This is a clean example of the module's recurring economics: encoding is cheaper than pins, and a generation will add mechanism to avoid adding width. Module 7 owns the command set.
Integrity on the data path — write CRC. DDR4 can append a check code to the end of a write burst, computed over the transferred data, so the device can detect a write that arrived corrupted. The reason is that at these rates, occasional bit errors on the interface stop being negligible — and a corrupted write is far worse than a corrupted read, because it silently poisons stored state that will be read back as authoritative later. Detection converts a silent corruption into a reported error.
Integrity on the command path — command and address parity. The device can check a parity bit over the command and address inputs and report a violation. The reason is asymmetry of consequence. A corrupted data bit affects one datum. A corrupted command bit can activate the wrong row, precharge a bank in use, or be misread as a different command entirely — which corrupts state the requester never touched and produces failures with no relationship to the access that caused them. Protecting the command path matters more per bit than protecting the data path, and DDR4 is where that stopped being optional.
And one more measured quantity. Following DDR3's pattern, DDR4 makes the receiver's reference voltage — the threshold against which an incoming level is judged — a trained value rather than a fixed one, adjusted per system to sit where the signal's actual high and low levels place the best decision point. This is the same idea as ZQ calibration applied to the vertical axis instead of the horizontal one: a quantity that cannot be designed accurately enough is made adjustable and then measured in situ. Chapter 4.4 §1's callout predicted exactly this, and the trend continues in 4.6.
Two further mechanisms, named for completeness and forward-linked. Data bus inversion lets the device or controller transmit an inverted copy of a data byte, with a flag, when inverting reduces the number of lines held in the current-drawing state — a direct consequence of POD12's asymmetry, and an energy mechanism rather than an integrity one. And per-device addressability allows a configuration operation to target one device on a shared bus rather than all of them, which is what makes per-device training results applicable in the first place. Modules 7 and 19 to 21 own both.
4. The Generation, With Layers Labelled
| Property | DDR3 | DDR4 | Layer changed |
|---|---|---|---|
| Prefetch depth | 8n | 8n — unchanged | — |
| Bank organisation | banks | banks within bank groups | Device architecture |
| Sustained rate depends on | the device | the access pattern | Consequence |
| Address decomposition | bank, row, column | + bank group | Device architecture |
| Signalling | balanced termination | POD12, pull-up-only, asymmetric | External interface |
| Activate encoding | control signals | dedicated ACT_n, address on control pins | External interface |
| Write integrity | — | write CRC | Interface / device |
| Command integrity | — | command and address parity | Interface / device |
| Receiver threshold | fixed | trained reference voltage | Interface / PHY |
| Supply voltage | 1.5 V | 1.2 V | Interface / device |
| Minimum granularity | 64 bytes | 64 bytes — unchanged | — |
| Array organisation | unchanged | unchanged | none |
Two rows read "unchanged", and they are the same row twice. The prefetch depth did not change because the granularity could not change. That is the whole of DDR4's architecture, and every other row is either the new parallelism or a consequence of running the same-sized access at a higher rate on a lower voltage.
And the last row still reads "unchanged" too, five generations in.
5. RTL — Access Spacing That Depends on the Group
Problem
A stream of column accesses, each targeting a bank group, must be issued subject to two different minimum separations: a longer one between accesses to the same group, and a shorter one between accesses to any two consecutive accesses. The block must gate issue accordingly, distinguish which constraint stalled a request, and measure the sustained rate achieved.
Distinguishing the two stall causes is the part worth building. A controller that knows only "the device was busy" cannot tell a scheduling problem from a bandwidth ceiling — and those have opposite fixes.
Classification
SYNTHESIZABLE RTL, with abstracted timing parameters. The gating logic, the per-group timers and the counters are real controller logic.
The gap values are abstractions and must not be read as specifications. SAME_GAP and DIFF_GAP are stand-ins for the real minimum-separation parameters, whose names, definitions, units and per-speed-grade values belong to Modules 13 and 14. This block teaches the structure of the constraint — that it is group-dependent — not any device's numbers, and using its parameters as timing values would be a straightforward error.
What it does not model. No array, no row state, no activate or precharge, no refresh, no banks within a group, no data. It gates when a column access may issue, given only the group it targets. Whether the row was open — Chapter 3.6 §5's classification — is upstream and entirely absent.
Interface
req_valid with req_group presents an access. req_ready grants it. issue_valid and issue_group report an issue. stall_same and stall_diff report which constraint blocked a request. issued_acc and stall_acc are saturating telemetry.
How to simulate it
vlog bank_group_spacer.sv tb_bank_group_spacer.sv then vsim -c tb_bank_group_spacer -do "run -all"; VCS vcs -sverilog bank_group_spacer.sv tb_bank_group_spacer.sv && ./simv; Xcelium xrun -sv bank_group_spacer.sv tb_bank_group_spacer.sv.
// ─────────────────────────────────────────────────────────────────────────
// BANK GROUP SPACER.
// Classification: SYNTHESIZABLE RTL, WITH ABSTRACTED TIMING PARAMETERS.
//
// Gates column accesses subject to TWO minimum separations:
// SAME_GAP -- between accesses to the SAME bank group (they share the
// internal column path)
// DIFF_GAP -- between ANY two consecutive accesses (the interface itself
// cannot do better)
//
// SAME_GAP AND DIFF_GAP ARE ABSTRACTIONS. They are NOT any device's timing
// parameters, they are not in any datasheet's units, and they must not be
// used as specification values. Modules 13 and 14 own the real parameters,
// their definitions and their per-speed-grade values. What this block
// teaches is that the constraint is GROUP-DEPENDENT -- which is DDR4's
// architectural claim -- and what that dependence does to sustained rate.
//
// WHAT THIS DOES NOT MODEL: the array, row state, activate, precharge,
// refresh, individual banks inside a group, or any data. It answers one
// question: may a column access to THIS group issue THIS cycle.
// ─────────────────────────────────────────────────────────────────────────
module bank_group_spacer #(
parameter int GROUPS = 4,
// Abstract minimum separation, in cycles, between same-group accesses.
parameter int SAME_GAP = 4,
// Abstract minimum separation, in cycles, between any two accesses.
parameter int DIFF_GAP = 2,
parameter int ACC_W = 16,
parameter int GRP_W = (GROUPS <= 1) ? 1 : $clog2(GROUPS),
parameter int TMR_W = $clog2(SAME_GAP + 1)
) (
input logic clk,
input logic rst_n,
input logic req_valid,
input logic [GRP_W-1:0] req_group,
output logic req_ready,
output logic issue_valid,
output logic [GRP_W-1:0] issue_group,
// WHICH constraint blocked the request. Distinguishing them is the point:
// stall_same means the access pattern is concentrated and rescheduling
// could help; stall_diff means the interface is saturated and no
// rescheduling can help. Opposite conclusions from one "busy" signal.
output logic stall_same,
output logic stall_diff,
output logic group_invalid,
output logic [ACC_W-1:0] issued_acc,
output logic [ACC_W-1:0] stall_acc
);
// ── COMPILE-TIME legality. DIFF_GAP > SAME_GAP would invert the
// architecture -- same-group accesses cheaper than cross-group ones --
// and is rejected rather than silently simulated.
if (GROUPS < 1) begin : g_grp_min
initial $fatal(1, "bank_group_spacer: GROUPS must be >= 1");
end
if (DIFF_GAP < 1) begin : g_diff_min
initial $fatal(1, "bank_group_spacer: DIFF_GAP must be >= 1");
end
if (SAME_GAP < DIFF_GAP) begin : g_gap_order
initial $fatal(1, "bank_group_spacer: SAME_GAP must be >= DIFF_GAP");
end
// ── Per-group countdown: cycles remaining until this group may be
// accessed again. One timer per group, all decrementing independently,
// which is the structural expression of "the groups are independent".
logic [TMR_W-1:0] grp_timer [GROUPS];
// ── Global countdown: cycles remaining until ANY access may issue.
logic [TMR_W-1:0] any_timer;
logic grp_free, any_free, grp_ok;
// ── Range check on req_group.
//
// WHY THIS IS A GENERATE AND NOT A ONE-LINE COMPARISON. The obvious
// form, `req_group < GRP_W'(GROUPS)`, is WRONG whenever GROUPS is a
// power of two: GRP_W is $clog2(GROUPS), so GRP_W'(GROUPS) TRUNCATES
// to zero and the comparison is never true. At the default GROUPS = 4,
// GRP_W is 2 and 2'(4) is 0, so grp_ok would be permanently false and
// the block would issue nothing at all.
//
// When GROUPS fills its index width every value is in range, so no
// check is needed; otherwise the comparison is done one bit wider than
// the index so the bound is representable.
if (GROUPS >= (1 << GRP_W)) begin : g_all_groups_valid
assign grp_ok = 1'b1;
end else begin : g_group_range_check
assign grp_ok = ({1'b0, req_group} < (GRP_W+1)'(GROUPS));
end
assign grp_free = grp_ok && (grp_timer[req_group] == '0);
assign any_free = (any_timer == '0);
assign req_ready = req_valid && grp_ok && grp_free && any_free;
assign issue_valid = req_ready;
assign issue_group = req_group;
// Stall attribution, in priority order: a same-group block is reported in
// preference to a cross-group one, because it is the actionable cause.
assign stall_same = req_valid && grp_ok && !grp_free;
assign stall_diff = req_valid && grp_ok && grp_free && !any_free;
assign group_invalid = req_valid && !grp_ok;
// ── Saturating telemetry. Computed one bit wide to expose the carry,
// then clamped -- a counter that wraps reports a flattering rate, and
// an instrument that lies optimistically is worse than one obviously
// pinned.
logic [ACC_W:0] i_sum, s_sum;
always_comb begin
i_sum = {1'b0, issued_acc} + (ACC_W+1)'(1);
s_sum = {1'b0, stall_acc} + (ACC_W+1)'(1);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int g = 0; g < GROUPS; g++) grp_timer[g] <= '0;
any_timer <= '0;
issued_acc <= '0;
stall_acc <= '0;
end else begin
// Every timer decrements independently. Guarded so a timer at zero
// stays at zero rather than wrapping to its maximum -- which would
// make a free group look blocked for SAME_GAP cycles.
for (int g = 0; g < GROUPS; g++) begin
if (grp_timer[g] != '0) grp_timer[g] <= grp_timer[g] - 1'b1;
end
if (any_timer != '0) any_timer <= any_timer - 1'b1;
if (req_ready) begin
// Both constraints are armed by every issue. The targeted group is
// blocked for the long interval; everything is blocked for the
// short one. Assigned AFTER the decrements above, so these win.
grp_timer[req_group] <= TMR_W'(SAME_GAP - 1);
any_timer <= TMR_W'(DIFF_GAP - 1);
issued_acc <= i_sum[ACC_W] ? {ACC_W{1'b1}} : i_sum[ACC_W-1:0];
end else if (stall_same || stall_diff) begin
stall_acc <= s_sum[ACC_W] ? {ACC_W{1'b1}} : s_sum[ACC_W-1:0];
end
end
end
endmoduleCombinational logic
Three conditions and their attribution. grp_free and any_free are the two constraints; req_ready is their conjunction; stall_same and stall_diff split the negation by cause, which is the block's most useful output. Note the priority: a same-group block is reported even if the global timer is also running, because it is the cause a scheduler can act on.
Sequential logic
GROUPS independent countdowns plus one global countdown, all decrementing unconditionally-but-guarded. The guard matters: an unguarded - 1 at zero wraps to the maximum and makes a free group appear blocked for a full SAME_GAP — a bug that reduces throughput by a large factor while breaking nothing functionally.
The assignment order is deliberate. The decrements come first and the issue-time loads come second, so a load wins over a decrement in the same cycle.
Cycle trace
GROUPS = 4, SAME_GAP = 4, DIFF_GAP = 2, with req_valid held high throughout.
Pattern A — every request to group 0:
| Cycle | req_group | req_ready | stall_same | grp_timer[0] |
|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 0 |
| 1 | 0 | 0 | 1 | 3 |
| 2 | 0 | 0 | 1 | 2 |
| 3 | 0 | 0 | 1 | 1 |
| 4 | 0 | 1 | 0 | 0 |
One issue every 4 cycles. Throughput is set by SAME_GAP.
Pattern B — requests rotating through all four groups:
| Cycle | req_group | req_ready | stall_diff |
|---|---|---|---|
| 0 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 2 | 0 | 1 |
| 4 | 2 | 1 | 0 |
One issue every 2 cycles — twice pattern A's throughput, from identical hardware. The only difference is which groups the requests target. That factor is DDR4's entire architectural contribution, and here it is the ratio SAME_GAP ÷ DIFF_GAP.
And note what the stall signals say. Pattern A stalls on stall_same, meaning the pattern is the problem and rescheduling could help. Pattern B stalls on stall_diff, meaning the interface is saturated and rescheduling cannot help. Same throughput shortfall, opposite conclusions — which is why one undifferentiated "busy" signal is not enough.
Simulation
A directed test should run pattern A and pattern B and compare sustained issue rates against SAME_GAP and DIFF_GAP; confirm the stall attribution matches the cause in each; sweep SAME_GAP/DIFF_GAP ratios and confirm throughput tracks them; check that a group's timer expiring frees only that group; drive an out-of-range req_group and confirm group_invalid with no issue; and drive long enough to confirm the counters pin rather than wrap.
Synthesis
GROUPS small counters, one more, a multiplexer indexed by req_group, and two saturating adders. The indexed read grp_timer[req_group] is the only structure worth watching — it is a multiplexer that grows with GROUPS, and at large group counts it is on the critical path of the ready signal.
Corner cases
GROUPS == 1 makes every access a same-group access, and the design degenerates to a single-rate gate — which is the pre-DDR4 behaviour, and reproducing it at that parameter is a useful self-check. SAME_GAP == DIFF_GAP removes the architectural benefit entirely and should produce identical throughput for both patterns; a test that does not verify this cannot distinguish a working design from one that ignores req_group. DIFF_GAP == 1 allows back-to-back cross-group issues. An out-of-range req_group is reported rather than masked, because masking would silently retarget an access.
Verification
What DV must prove: no two same-group issues closer than SAME_GAP; no two issues at all closer than DIFF_GAP; stall attribution correct in both directions, since a mis-attributed stall misdirects every subsequent optimisation; a group's timer not affecting other groups; sustained rates matching the parameters for both patterns; invalid groups reported and never issued; counters saturating.
Debugging
If both patterns give the same throughput, the group index is being ignored — check that grp_timer is indexed by req_group and not by a constant. If throughput is far below both bounds, check the decrement guard: an unguarded decrement wraps a zero timer to its maximum. If cross-group issues are closer than DIFF_GAP, check that any_timer is armed on every issue rather than only on same-group ones. If stall_same and stall_diff both assert, check the grp_free term in stall_diff — the attribution is meant to be mutually exclusive, and P3 in §6 exists to catch that.
Limitations
No bank-level state, so it cannot model a row conflict within a group — the constraint that Chapter 3.6 showed often dominates. No activate, precharge or refresh, all of which consume the same resources in a real device. No read/write turnaround. No reordering: it gates the request it is given rather than choosing among several, and a real scheduler's value is largely in that choice — Module 17's subject. And the gap values are abstract, as the header states at length.
6. Four Assertions Worth Writing
// VERIFICATION-ONLY, inside bank_group_spacer.
// P1 -- THE SAFETY PROPERTY. No two accesses to the same group closer than
// SAME_GAP. Violating it would issue an access the device cannot serve,
// which is a correctness failure and not a performance one.
property p_same_group_spacing;
@(posedge clk) disable iff (!rst_n)
(issue_valid, int unsigned g = issue_group)
|=> !(issue_valid && (issue_group == GRP_W'(g))) [*(SAME_GAP-1)];
endproperty
assert property (p_same_group_spacing);
// P2 -- no two issues at all closer than DIFF_GAP.
property p_any_spacing;
@(posedge clk) disable iff (!rst_n)
issue_valid |=> !issue_valid [*(DIFF_GAP-1)];
endproperty
assert property (p_any_spacing);
// P3 -- stall attribution is MUTUALLY EXCLUSIVE and complete. This is the
// property that protects the diagnosis rather than the design: two stall
// causes asserting together, or a stalled request reporting neither, sends
// every downstream optimisation in a direction the data did not support.
property p_stall_exclusive;
@(posedge clk) disable iff (!rst_n)
!(stall_same && stall_diff);
endproperty
assert property (p_stall_exclusive);
property p_stall_complete;
@(posedge clk) disable iff (!rst_n)
(req_valid && !req_ready && !group_invalid)
|-> (stall_same || stall_diff);
endproperty
assert property (p_stall_complete);
// P4 -- THE ARCHITECTURAL PROPERTY. A request to a group whose timer is
// clear, on a cycle when nothing else blocks, MUST issue. Without this, a
// design that simply never issues satisfies P1 and P2 perfectly -- the
// classic vacuous pass on a pair of safety properties that only forbid.
property p_issues_when_free;
@(posedge clk) disable iff (!rst_n)
(req_valid && grp_ok && (grp_timer[req_group] == '0) && (any_timer == '0))
|-> issue_valid;
endproperty
assert property (p_issues_when_free);
// P5 -- an invalid group issues nothing.
property p_invalid_group_blocked;
@(posedge clk) disable iff (!rst_n)
group_invalid |-> !issue_valid;
endproperty
assert property (p_invalid_group_blocked);P4 is the one to internalise, and its lesson generalises beyond this block. P1 and P2 are safety properties: they forbid things. A design that issues nothing at all satisfies both perfectly. A list of purely forbidding properties can be satisfied completely by a dead design, and that is the most common way a large assertion suite gives false confidence. P4 is a liveness-flavoured property — it requires something to happen — and it is what makes the pair meaningful.
When you review an assertion list, count how many properties forbid and how many require. If none require, the suite cannot distinguish your design from a wire tied low.
P3 protects the instrument, as 4.4's P4 did. Mis-attributed stalls do not break the device; they break the reasoning about the device, and that failure propagates into every optimisation decision made afterward.
What none of them prove. Nothing about POD12, the asymmetric driver, reference-voltage training, CRC or parity coverage, or any electrical property — §3's entire subject matter is outside simulation's reach, exactly as 4.4 §7 established. And nothing about whether the real device's timing parameters are what this block's abstractions suggest, which is Modules 13 and 14' job and not an assertion's.
7. Interleaving in Cycles
bank_group_spacer — cross-group interleaving doubles the issue rate
10 cyclesstall_same is flat at zero for all ten cycles. That is the figure's real content. Every stall is stall_diff — the interface limit, not the group limit — which means the access pattern is doing everything it can and no amount of rescheduling would help.
Run the same ten cycles against one group and the picture inverts: issues on cycles 0, 4 and 8 only, with every stall reported as stall_same. Three issues instead of five, from identical hardware, distinguished only by which groups were targeted — and the stall signal correctly identifies the pattern as the culprit.
That inversion is the diagnostic value of splitting the stall cause. A controller reporting only "blocked" cannot tell those two situations apart, yet one calls for a different address mapping and the other calls for accepting the ceiling.
What the figure does not claim. The gap values are abstract, so the cycle counts are illustrative of the structure and not of any device's timing. And nothing here says whether the rows were open — that constraint is upstream and can dominate everything shown.
8. The New Costs, and the Next Bottleneck
Cost 1 — peak rate is now conditional on the access pattern. A DDR4 device's rated bandwidth assumes interleaving across groups. A workload that does not interleave cannot reach it, with nothing malfunctioning — and the device's specification does not become wrong, it becomes conditional. Every bandwidth figure from DDR4 onward carries an implicit "given a suitable access pattern".
Cost 2 — the controller absorbed another responsibility. Address-bit mapping now determines whether sequential access interleaves or concentrates, and getting it wrong costs a large fraction of the device's rate. Module 17 exists in the size it does because of accumulated costs of this kind, one per generation.
Cost 3 — more state, more mechanism, more to configure and to get wrong. Parity and CRC must be enabled and their error reports handled; the reference voltage must be trained; inversion must be configured. Each is a mechanism that is invisible when working and produces a marginal or silently-degraded system when misconfigured — the fault class 4.4 §9 named, now several times larger.
And the next bottleneck is a return of the granularity problem from the other direction.
Bank groups raised the column-access rate. But the interface still transfers 8 beats on a 64-bit channel, and to raise the transfer rate further, the beats have to come faster or there have to be more of them. Push the burst to 16 beats and the arithmetic of §1 returns unchanged:
16 beats × 64 bits = 128 bytes -- twice a cache line. Still fatal.So the burst length is stuck for exactly the same reason the prefetch depth was. Unless — and this is DDR5's insight — the width of the thing the burst crosses is reduced at the same time.
16 beats × 32 bits = 64 bytes -- a cache line. Unchanged.Halve the channel width and double the burst length, and the granularity comes out identical. That is not a compromise; it is an exact preservation, and it is why DDR5 splits its channel into two independent narrower ones. Chapter 4.6 works that arithmetic properly — and shows that the split buys something beyond the arithmetic, because two independent channels are two independent request streams.
9. Common Misconceptions
"DDR4 has a 16n prefetch."
Wrong mental model: every generation deepens the prefetch.
Engineering action: deriving the interface clock as transfer rate over 16 rather than over 8; assuming granularity of 128 bytes; expecting the internal access rate to be half what it is.
Observable failure / bad conclusion: a factor-of-two error in every derived internal rate, and — more damagingly — a complete inability to explain why bank groups exist, since on this model DDR4 got its rate the same way its predecessors did.
Correct model: DDR4 keeps 8n, because granularity had reached a cache line and deepening would transfer unwanted data. Its additional rate comes from bank groups permitting overlapped accesses to independent column paths.
Prevention: compute prefetch_depth × interface_width and ask whether the answer exceeds a plausible access size. When it does, the depth has stopped growing.
"Bank groups are just more banks." Wrong mental model: grouping is a naming convention over a larger bank count. Engineering action: treating all bank pairs as equally independent; mapping address bits without regard to the group field; expecting uniform timing between any two accesses. Observable failure / bad conclusion: sustained bandwidth far below the rated figure with no functional error, on a controller whose address mapping concentrates sequential access into one group. This is one of the most common real DDR4 performance defects. Correct model: a group is a set of banks sharing an internal column path. Accesses within a group need a longer minimum separation than accesses across groups, so the achievable rate depends on the pattern, not only on the device. Banks add independent row state; groups add independent column-path resource. Prevention: ask what is shared. Banks share nothing that matters for row state; a group's banks share the machinery that serves a column access.
"Command parity and write CRC are equivalent protections." Wrong mental model: both are error detection, so both matter equally. Engineering action: enabling one and not the other on the assumption that the coverage overlaps; treating a parity error and a CRC error as the same severity. Observable failure / bad conclusion: a system that detects corrupted write data but silently accepts a corrupted command — which can activate the wrong row or precharge a bank in use, corrupting state belonging to an access that was never issued, with failures that have no traceable relationship to any request. Correct model: the consequences are asymmetric per bit. A corrupted data bit damages one datum. A corrupted command bit can damage arbitrary state and produce failures elsewhere in the system. The command path is worth more protection per bit than the data path. Prevention: ask what the blast radius of one corrupted bit is. On the data path it is one datum; on the command path it is unbounded.
"POD12 is just a lower-voltage version of the previous signalling." Wrong mental model: the signalling change is only the voltage. Engineering action: assuming symmetric drive and symmetric margin between the high and low states; analysing eye margin as though the two levels behave identically; not connecting data bus inversion to anything. Observable failure / bad conclusion: margin analysis that is systematically wrong on one of the two levels, and an inability to explain why data bus inversion exists at all — it makes no sense against symmetric signalling. Correct model: POD12 terminates to the supply, so current flows through the termination only in one state. The driver's impedance is deliberately asymmetric, and the two states are not electrically equivalent. Data bus inversion is a direct consequence: inverting a byte can reduce how many lines sit in the current-drawing state. Prevention: ask whether the two logic levels cost the same. Under pull-up-only termination they do not, and that asymmetry propagates into energy, margin and the reason inversion exists.
10. Debugging — Sustained Bandwidth Is Far Below Rated With No Errors
Symptom. A DDR4 system is functionally perfect — no errors, no corruption, no instability — and sustained bandwidth on a streaming workload sits well below the device's rated figure. The interface appears busy.
No errors plus low throughput eliminates the entire integrity and margin layer, which is a large and welcome reduction. What remains is structural.
Mechanism 1 — the address mapping concentrates access into one bank group. Inspect: which address bits the controller assigns to the bank-group field, and how the workload's access stride interacts with them. Expected evidence: a stride that leaves the group field constant across consecutive accesses. Discriminator: same-group versus cross-group stall counts — exactly §5's stall_same and stall_diff. Predominantly same-group stalls mean the pattern is concentrated and remapping will help. This is the first thing to check because it is the most common DDR4-specific defect and it is a configuration change, not a hardware one.
Mechanism 2 — row conflicts, not group conflicts. Inspect: the row hit and conflict classification from Chapter 3.6 §5, per bank. Expected evidence: a high conflict rate — accesses to a bank whose open row is the wrong one. Discriminator: is the device stalling on the column path or on activate and precharge? A group-mapping problem stalls column accesses; a row-conflict problem stalls before them. They present identically as "low bandwidth" and they are different layers with different fixes, and the classification of 3.6 is what separates them.
Mechanism 3 — access granularity waste. Inspect: useful bytes over transferred bytes, exactly 4.4 §6's counters. Expected evidence: the interface busy at near 100% while delivered useful bandwidth is a fraction of it. Discriminator: is the interface idle or busy? A busy interface delivering little means the bytes are being transferred and discarded — a granularity problem, not a scheduling one, and no amount of interleaving fixes it.
Mechanism 4 — read/write turnaround. Inspect: the read-to-write mix and whether the stream alternates direction frequently. Expected evidence: throughput strongly dependent on the read/write ratio, with mixed streams far worse than either pure one. Discriminator: separate the workload into pure-read and pure-write runs. If each is fast and the mix is slow, the cost is direction changes — the bidirectional-strobe turnaround 4.2 §3 flagged — and the fix is batching by direction rather than remapping addresses.
Mechanism 5 — refresh. Inspect: the fraction of time the device is unavailable for refresh, and whether it rises with temperature. Expected evidence: a throughput ceiling that moves with temperature and correlates with nothing in the access pattern. Discriminator: temperature dependence with no pattern dependence. Chapter 2.3 established that retention falls as temperature rises, so the refresh burden grows — and that is a floor on unavailability no scheduling can recover.
Discrimination, cheapest first. Read the same-group versus cross-group stall counts, which is a counter read and resolves mechanism 1 outright. Then read useful-over-transferred bytes, which splits granularity waste from scheduling entirely. Then split the workload by direction, which isolates turnaround. Then look at row hit and conflict rates. Then check temperature dependence.
The reasoning lesson. Every one of these five mechanisms produces "the interface is busy and the bandwidth is low", and they live in five different layers — address mapping, row state, granularity, bus direction, and refresh. Guessing among them is hopeless, and instrumenting them is cheap. This is why §5's block reports which constraint stalled it rather than merely that it stalled, and why 4.4 §6 built efficiency counters at all: from DDR4 onward, a memory controller without per-cause telemetry is not debuggable in any reasonable time. The generation whose peak rate became conditional on the access pattern is the generation that made measuring the pattern mandatory.
11. Interview Reasoning
"Why didn't DDR4 deepen the prefetch?" Because minimum granularity is prefetch depth times interface width, and at DDR3's 8n on a 64-bit channel that was already 64 bytes — one typical cache line. A 16n prefetch would make it 128 bytes, so every access would transfer a line that was wanted and a line that was not. The peak bandwidth number would double and the delivered bandwidth would not move, while the array did twice the work. Burst chop cannot rescue it either, because chopping back to 8 beats still means the array read 16 words to deliver 8. The lever was exhausted, so DDR4 found rate in overlap instead.
"What is a bank group and why does it raise the transfer rate?" A set of banks that share the internal path serving a column access — from the sense amplifiers through the prefetch register to the interface. That machinery is not replicated per bank, because die area is the dominant cost. So two accesses to the same group contend for it and must be separated by a longer minimum interval, while two accesses to different groups use independent copies and can be separated by a shorter one. A stream that alternates between groups therefore sustains a higher column-access rate than one confined to a single group — without any change to prefetch depth, burst length or width. The rate comes from interleaving, which makes it a property of the access pattern rather than of the device.
"Where does DDR4's rated bandwidth assume something about the software?" In the interleaving. The rated figure is reachable only by an access stream that rotates across bank groups; a stream concentrated in one group is limited by the longer same-group separation and can fall to a fraction of it, with nothing malfunctioning. Which address bits the controller maps to the bank-group field determines whether a given stride interleaves or concentrates, so a mapping choice in the controller can cost a large share of the device's rate. From DDR4 onward every peak bandwidth figure carries an implicit "given a suitable access pattern".
"Why protect the command path as well as the data path?" Because the blast radius of one corrupted bit is completely different. A corrupted data bit damages one datum, and the requester that asked for it sees the error. A corrupted command bit can activate the wrong row, precharge a bank that is in use, or be interpreted as a different command entirely — corrupting state belonging to an access that was never issued, and producing failures with no traceable relationship to any request. The command path is worth more protection per bit than the data path, and that asymmetry is why DDR4 added command and address parity alongside write CRC rather than treating data integrity as sufficient.
"A DDR4 system shows low sustained bandwidth and no errors. How do you narrow it down?" No errors removes the whole integrity and margin layer, so the cause is structural, and there are about five candidates that all present identically. First read the same-group versus cross-group stall counts: predominantly same-group stalls mean the address mapping is concentrating access and remapping will help, which is the most common DDR4-specific defect and a configuration fix. Then read useful bytes over transferred bytes, because a busy interface delivering little useful data is a granularity problem no scheduling can fix. Then split the workload into pure reads and pure writes — if each is fast and the mix is slow, the cost is bus turnaround, and the fix is batching by direction. Then look at row hit and conflict rates, which is a different layer stalling before the column path. And check whether the ceiling moves with temperature, which points at refresh burden rather than anything in the pattern. The general point is that all five look the same from outside and only per-cause telemetry separates them.
12. Engineering Check
An educational DDR4-style channel: 64 bits wide, 8n prefetch, 4 bank groups. Abstract minimum separations of 4 cycles same-group and 2 cycles cross-group. Educational figures, not any real part's timing.
1. A stream confined to one bank group issues a column access every how many cycles, and at what fraction of the cross-group rate? Every 4 cycles, which is 50% of the one-per-2-cycles cross-group rate.
2. The same stream rotating across all four groups? Every 2 cycles — twice the throughput from identical hardware. The only variable was which group each access targeted.
3. If the cross-group separation were also 4 cycles, what would bank groups be worth? Nothing. Both patterns would issue every 4 cycles. The benefit is entirely the ratio between the two separations, which is why §5's corner-case note says a test that does not check SAME_GAP == DIFF_GAP cannot tell a working design from one ignoring the group index.
4. Granularity, and the useful fraction for a 64-byte cache-line workload? 8n × 64 bits = 64 bytes, so a cache-line workload is 100% efficient. This is what DDR4 protected by not deepening the prefetch — and it is why a generation that added no granularity is a generation that added real bandwidth.
5. The burst is lengthened to 16 beats on the same 64-bit channel. Granularity and the useful fraction for the same workload? 16 × 64 bits = 128 bytes, so useful is 64 ÷ 128 = 50%. The transfer rate doubled and the delivered useful bandwidth is unchanged. The same wall as question 5 of 4.4, reached from the burst-length side instead of the prefetch side — which shows it is a property of the granularity arithmetic, not of the mechanism that produced it.
6. Now 16 beats on a 32-bit channel. Granularity and useful fraction? 16 × 32 bits = 64 bytes — 100% efficient, identical to question 4. Halving the width exactly cancelled doubling the burst. So a 16-beat burst is available provided the channel is narrowed at the same time, and the peak rate rises with the beat count while the granularity does not move at all. That is DDR5 in one line of arithmetic, and the next chapter's job is to show why the halved channel is split into two independent channels rather than simply made narrower.
13. Summary
DDR4 keeps an 8n prefetch, and that break in a three-generation pattern is the most informative fact about it. Granularity is prefetch_depth × interface_width, which at 8n on a 64-bit channel is already one typical cache line; another doubling would transfer a wanted line and an unwanted one on every access, raising peak bandwidth without raising delivered bandwidth — and burst chop cannot rescue it, because the array would still read the full width.
With all three previous levers exhausted — transfers-per-cycle at DDR1, prefetch depth here, width never available — DDR4 finds rate in the one untried place: overlap.
Bank groups organise banks by the internal column path they share. Accesses within a group contend for that machinery and need a longer minimum separation; accesses across groups use independent copies and need a shorter one. A stream that interleaves across groups therefore sustains a higher rate than one that does not, with no change to depth, burst length or width — which means the achievable rate became a property of the access pattern rather than of the device.
The supporting changes are one answer repeated. At 1.2 V with the interval halved again, everything that can still go wrong must be accounted for individually: POD12 pull-up-only signalling with a deliberately asymmetric driver, for energy; a dedicated ACT_n so control pins can carry row-address bits, because encoding is cheaper than pins; write CRC and command/address parity, because a corrupted command bit has an unbounded blast radius while a corrupted data bit damages one datum; and a trained reference voltage, which is DDR3's calibration pattern applied to the vertical axis.
Three costs carry forward. Peak rate is now conditional on the access pattern, so every bandwidth figure from here on carries an implicit qualifier. The controller absorbed address-mapping responsibility with a large throughput penalty for getting it wrong. And there is more configurable mechanism whose misconfiguration degrades silently — which is why per-cause telemetry stopped being optional: five different layers produce the identical symptom of a busy interface delivering low bandwidth.
And the next bottleneck is the granularity arithmetic returning from the burst-length side. Sixteen beats on a 64-bit channel is 128 bytes — fatal for the same reason 16n prefetch was. But 16 beats × 32 bits = 64 bytes is exactly unchanged. Halve the width while doubling the burst and the granularity is preserved precisely.
14. What Comes Next
Chapter 4.6 takes that arithmetic and builds a generation on it. DDR5 transfers 16 beats per burst, and preserves granularity by splitting each channel into two independent narrower sub-channels, each with its own command path and its own bank hierarchy.
And the split buys something the arithmetic alone does not show. Two independent sub-channels are two independent request streams, which doubles the number of accesses that can be in flight — the memory-level parallelism Chapter 1.8 §7 argued matters more than peak bandwidth, arriving here as a structural property of the interface.
DDR5 also moves error correction inside the device, changes how refresh is targeted, adds protection against a disturbance mechanism Chapter 2.2 described, gives the receiver the ability to compensate for the channel's own distortion, and relocates power regulation onto the module.
Return to DDR3 for the prefetch wall and for calibration, DDR2 for prefetch as a frequency ratio, or Memory Matrices and Hierarchy for the bank hierarchy bank groups extend. The full path is on the DDR tutorials index.
Continue learning
Related tutorials
- Related topic
Bank Groups
Not all bank pairs are equally independent. A bank group is the scope at which the internal column data path is shared, and the three-way classification of a request against its predecessor is the interface every later timing module consumes.
- Related topic
tCCD — CAS-to-CAS Delay
The first parameter that is not bank-local. Column spacing is constrained by resources every bank shares, and DDR4 splits it by bank group — so a per-bank state machine cannot enforce it.
- Related topic
DDR4 / DDR5 Bank-Group Concepts
The same-group penalty is not one number. It reaches twice for column commands, tracks that for activates on x8 parts, and on one verified x16 configuration disappears entirely.
- Related topic
DDR5 Bank-Group Enhancements
Chapter 16.3 recorded this as a question it could not close from primary material. The die datasheet closes it — and shows a x16 part has half the banks of a x8 and twice the page.
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.
