DDR · Module 18
Row-Bank-Column Mapping
Two lossless address maps over the same device disagree about every resource a workload touches. The field order decides what changes on the next cache line, and that decides row locality and bank distribution before the controller sees anything.
Chapter 8.6 built the complete seven-field mapper, proved it lossless, and inverted it from observed commands. It ended by noting that two maps can both be perfectly lossless and still disagree about three fields out of seven — and then deferred the obvious next question.
This module answers it. The question is not how to slice an address. It is which slicing, and why.
A mapping policy does not change a single DDR timing rule. It changes which resources a workload presents to those rules.
That sentence is the whole of Module 18, and this chapter establishes the mechanism behind it: the field order decides what changes on the next access, and what changes on the next access decides everything downstream.
1. One Device, Two Maps
Fix a device and an address width, and keep them fixed for the whole module.
EDUCATIONAL CONFIGURATION — NOT A JEDEC-MANDATED ADDRESS MAP
Chosen small so every worked number can be checked by hand.
byte offset 6 bits 64-byte access granule
column 4 bits 16 columns
bank 2 bits 4 banks per group
bank group 2 bits 4 bank groups -> 16 banks
row 8 bits 256 rows
------------------------------------------------
physical address 22 bits 4 MiB of deviceNow two orders over exactly those fields. Both use every bit once; both are lossless; both address the same 4 MiB.
POLICY R — "row-bank-column", locality-first
21 14 13 12 11 10 9 6 5 0
+-------------------+-------+-------+--------+----------+
| row | bg | bank | column | offset |
+-------------------+-------+-------+--------+----------+
POLICY B — bank-interleaved, distribution-first
21 14 13 10 9 8 7 6 5 0
+-------------------+--------+-----+-----+----------+
| row | column | bg |bank | offset |
+-------------------+--------+-----+-----+----------+Stated algebraically, because a picture alone has never caught a bug:
POLICY R POLICY B
offset = PA[5:0] offset = PA[5:0]
column = PA[9:6] bank = PA[7:6]
bank = PA[11:10] bg = PA[9:8]
bg = PA[13:12] column = PA[13:10]
row = PA[21:14] row = PA[21:14]The row field is in the same place in both. Only the middle changes — and the middle is where the module's entire argument lives.
2. The Low Bits, and the Fields That Are Not Here
Two things about §1's configuration need stating before any conclusion rests on it, because both are routinely glossed over and both change the arithmetic.
The six offset bits are not a DDR field. They are the bits the access granularity consumes before the map sees anything. Four different quantities get conflated here, and they are genuinely different:
| Quantity | What it is | In this configuration |
|---|---|---|
| Byte address | what the requester supplies | the full 22 bits |
| Access granule | the smallest unit the controller maps | 64 B — PA[5:0] is within it |
| DDR burst | beats the device transfers per column command | Module 12's subject |
| DDR column | the operand the device receives | PA[9:6] or PA[13:10], by policy |
A column value is not a cache-line address and a burst length is not a cache-line size. They are related through the map and the device's transfer width, and Chapter 12.1 owns the relationship. What matters here is narrower: the offset bits are consumed below the map, so no field in §1 is influenced by them, and a stride smaller than 64 B cannot change any decoded field at all.
And there is no rank or channel field. Chapter 8.6's mapper carries seven fields; this module's configuration uses five, and the omission is deliberate rather than an oversight.
Rank (8.5) and channel (5.1) sit above the device in the hierarchy, and including them would add a second axis of distribution on top of the bank axis this module is isolating. Both policies in §1 place their fields identically outside the middle, so the middle is the only variable — the same control §7's row-boundary analysis relies on.
What changes when they are present is worth one sentence each, because the reasoning transfers unchanged. A channel field low in the address interleaves consecutive granules across independent interfaces, which is the strongest form of distribution available, since channels do not share a command or data bus the way banks do (16.1 §2). A rank field low in the address distributes across devices that do share the bus, and therefore buys less than it appears to — and costs rank-to-rank turnaround (5.4). Neither is universal: Chapter 18.4 shows that channel interleaving in particular is a configurable platform decision rather than an architectural constant.
One worked decode, in full binary, so the field boundaries are unambiguous. Take PA = 0x0A4C0:
0x0A4C0 = 0000001010010011000000 (22 bits, bit 21 on the left)
bit 21 20 19 18 17 16 15 14 | 13 12 | 11 10 | 9 8 7 6 | 5..0
value 0 0 0 0 0 0 1 0 | 1 0 | 0 1 | 0 0 1 1 | 000000
POLICY R |--------- row ---------|-- bg -|- bank-|-- column --|offset
row = 0x02 = 2 bg = 2 bank=1 col = 3 0
bit 21 20 19 18 17 16 15 14 | 13 12 11 10 | 9 8 | 7 6 | 5..0
value 0 0 0 0 0 0 1 0 | 1 0 0 1 | 0 0 | 1 1 | 000000
POLICY B |--------- row ---------|--- column ---|- bg -|-bank-|offset
row = 0x02 = 2 col = 0x9 = 9 bg = 0 bank=3 0Same address, same device, both lossless — and they disagree about where it lives. Policy R places it in bank 1 of group 2, which is flat bank 9, at column 3. Policy B places it in bank 3 of group 0, which is flat bank 3, at column 9. Reconstructing either set of five fields returns 0x0A4C0 exactly, which is Chapter 8.6's round trip and is not in dispute. Which bank the access lands in is.
3. The Only Question That Matters
Do not read a field diagram by naming the fields. Read it by asking one question:
When the address advances by one access granule, which field changes?
A field at bit position p changes once every 2^p bytes. So the field sitting immediately above the offset is the field that changes first, and it changes on every single consecutive access.
| Policy | Field just above the offset | Changes every | Consequence for a sequential walk |
|---|---|---|---|
| R | column, at bit 6 | 64 B | stays in one bank, one row; walks the columns |
| B | bank, at bit 6 | 64 B | changes bank on every access |
4. Eight Consecutive Cache Lines
The rule above, made concrete. Eight accesses from 0x00000, one access granule apart.
| PA | PA[13:6] | R: bank | R: row | R: col | B: bank | B: row | B: col |
|---|---|---|---|---|---|---|---|
0x00000 | 00000000 | 0 | 0 | 0 | 0 | 0 | 0 |
0x00040 | 00000001 | 0 | 0 | 1 | 1 | 0 | 0 |
0x00080 | 00000010 | 0 | 0 | 2 | 2 | 0 | 0 |
0x000C0 | 00000011 | 0 | 0 | 3 | 3 | 0 | 0 |
0x00100 | 00000100 | 0 | 0 | 4 | 4 | 0 | 0 |
0x00140 | 00000101 | 0 | 0 | 5 | 5 | 0 | 0 |
0x00180 | 00000110 | 0 | 0 | 6 | 6 | 0 | 0 |
0x001C0 | 00000111 | 0 | 0 | 7 | 7 | 0 | 0 |
The bank column is the whole story. Policy R holds it at 0 and advances col. Policy B advances it on every row of the table while col stays at 0.
Extend to sixteen accesses and the counts are exact:
16 consecutive access granules from 0x00000
Policy R distinct banks : 1 of 16 distinct rows : 1
Policy B distinct banks : 16 of 16 distinct rows : 15. What That Becomes at the Device
Apply Chapter 9.3's taxonomy — HIT, CLOSED, CONFLICT — with every bank starting closed. Nothing new is defined here; the classification is read off the decoded fields and the bank's currently open row.
POLICY R — eight sequential granules
0x00000 bank 0 row 0 col 0 prev: none CLOSED -> ACT then RD
0x00040 bank 0 row 0 col 1 prev: row 0 HIT -> RD
0x00080 bank 0 row 0 col 2 prev: row 0 HIT -> RD
0x000C0 bank 0 row 0 col 3 prev: row 0 HIT -> RD
0x00100 bank 0 row 0 col 4 prev: row 0 HIT -> RD
0x00140 bank 0 row 0 col 5 prev: row 0 HIT -> RD
0x00180 bank 0 row 0 col 6 prev: row 0 HIT -> RD
0x001C0 bank 0 row 0 col 7 prev: row 0 HIT -> RD
POLICY B — the same eight granules
0x00000 bank 0 row 0 col 0 prev: none CLOSED -> ACT then RD
0x00040 bank 1 row 0 col 0 prev: none CLOSED -> ACT then RD
0x00080 bank 2 row 0 col 0 prev: none CLOSED -> ACT then RD
0x000C0 bank 3 row 0 col 0 prev: none CLOSED -> ACT then RD
0x00100 bank 4 row 0 col 0 prev: none CLOSED -> ACT then RD
0x00140 bank 5 row 0 col 0 prev: none CLOSED -> ACT then RD
0x00180 bank 6 row 0 col 0 prev: none CLOSED -> ACT then RD
0x001C0 bank 7 row 0 col 0 prev: none CLOSED -> ACT then RD6. The Bit-Influence Matrix
The compact form of everything above: which decoded field owns each physical-address bit.
| PA bit | Changes every | Policy R | Policy B |
|---|---|---|---|
| 0–5 | 1–32 B | offset | offset |
| 6 | 64 B | column | bank |
| 7 | 128 B | column | bank |
| 8 | 256 B | column | bank group |
| 9 | 512 B | column | bank group |
| 10 | 1 KiB | bank | column |
| 11 | 2 KiB | bank | column |
| 12 | 4 KiB | bank group | column |
| 13 | 8 KiB | bank group | column |
| 14–21 | 16 KiB+ | row | row |
Read a row of this table and you can answer a design question without simulating anything.
“Which address bit changes the bank?” Bit 10 under R; bit 6 under B. “What stride moves to a new bank?” 1 KiB under R; 64 B under B. “What stride leaves the bank unchanged?” Any multiple of 16 KiB under R; any multiple of 1 KiB under B — and that second answer is Chapter 18.3's entire subject.
The matrix is also the debugging tool. When traffic concentrates unexpectedly, the question is always which bits of my address stream are actually varying, compared against which bits this map uses for bank selection. §13 works that procedure.
7. Row Boundaries
One case deserves separate treatment because it is where a sequential stream stops being free.
Under Policy R, the row field is PA[21:14], so a row covers 2^14 bytes — 16 KiB — of contiguous physical address. Walk to the end of it:
0x03FC0 row 0 bg 3 bank 3 col 15 last granule of row 0's span
0x04000 row 1 bg 0 bank 0 col 0 first granule of row 1's spanThree fields change at once, and the important one is subtle: the bank returns to 0. Bank 0's open row is still row 0, and the request wants row 1 in bank 0 — a CONFLICT by Chapter 9.5's definition, requiring PRE, then ACT, then the column command.
So under Policy R a purely sequential walk is not uniformly cheap. It is 256 cheap accesses followed by one expensive one, repeating. The cost is amortised and small; the shape is worth knowing, because it is the first thing that a naive "sequential is all hits" model gets wrong.
Under Policy B the same boundary at 0x04000 lands in bank 0 as well, and with col also returning to 0 — but bank 0's open row was row 0 from 16 accesses earlier, so it is equally a conflict. The boundary behaves the same way in both policies, because the row field is in the same place in both. That is a useful control: it isolates the middle-field difference as the only variable in this comparison.
8. Where the Decode Already Lives
Module 18 writes no new address mapper, and saying so explicitly prevents the most likely structural mistake in this module.
| Responsibility | Owner | Block | Module 18 does |
|---|---|---|---|
| Full seven-field PA decode | 8.6 | sys_addr_mapper | consume |
| Losslessness / inversion | 8.6 | addr_roundtrip_check | consume |
| Bank-low vs bank-high, one field | 8.3 | bank_field_policy | consume |
| Row / column / BG / rank fields | 8.1–8.5 | field blocks | consume |
| Hit / miss / conflict classification | 9.3 | row_request_classifier | consume |
| Bank-state model | 5.2 | ddr_bank_state_table | consume |
What is missing from that list — and what this chapter's block supplies — is the thing none of them does: turn a policy into the answer to “which field owns this bit, and at what stride does it move?” That is §6's matrix, computed rather than tabulated by hand.
9. The Bit-Influence Block
// ─────────────────────────────────────────────────────────────────────
// mapping_bit_influence
//
// CLASSIFICATION
// Synthesizable educational RTL. Purely combinational. One
// responsibility: for a declared field order, report which decoded
// field each physical-address bit belongs to, and the byte stride at
// which each field first changes.
//
// It is a POLICY DESCRIPTOR, not a mapper. It decodes nothing.
//
// WHAT IT DOES NOT MODEL
// - No address decode. Chapter 8.6's sys_addr_mapper does that, and
// this module deliberately does not duplicate it.
// - No reconstruction or losslessness proof. Chapter 8.6 owns both.
// - No bank state, no classification. Chapters 5.2 and 9.3 own them.
// - No hashing. Bits map to exactly one field here; Chapter 18.3's
// bank_xor_hash is where one PA bit starts influencing two fields.
// - No timing, no scheduling, no performance claim.
// ─────────────────────────────────────────────────────────────────────
module mapping_bit_influence #(
parameter int PA_W = 22,
parameter int OFFSET_W = 6,
parameter int COLUMN_W = 4,
parameter int BANK_W = 2,
parameter int BG_W = 2,
parameter int ROW_W = 8,
// 0 = POLICY R : row | bg | bank | column | offset
// 1 = POLICY B : row | column | bg | bank | offset
parameter int POLICY = 0
) (
// ── One bit per PA position, reporting the owning field.
// 0 = offset, 1 = column, 2 = bank, 3 = bank group, 4 = row.
output logic [PA_W-1:0][2:0] bit_field,
// ── The byte stride at which each field first changes: 2^lsb.
// Published as the LSB position rather than the stride itself so
// the value stays narrow; the stride is 2^value by construction.
output logic [5:0] column_lsb,
output logic [5:0] bank_lsb,
output logic [5:0] bg_lsb,
output logic [5:0] row_lsb,
// ── The field immediately above the offset — §3's question answered
// structurally. This is the field a sequential walk sweeps first.
output logic [2:0] first_field_above_offset
);
// ── Every field must be at least one bit wide: a zero-width field
// has no bit positions to report and would make the part-selects
// below illegal. Resolved at elaboration rather than at runtime.
if (OFFSET_W < 1) $fatal(1, "mapping_bit_influence: OFFSET_W must be >= 1");
if (COLUMN_W < 1) $fatal(1, "mapping_bit_influence: COLUMN_W must be >= 1");
if (BANK_W < 1) $fatal(1, "mapping_bit_influence: BANK_W must be >= 1");
if (BG_W < 1) $fatal(1, "mapping_bit_influence: BG_W must be >= 1");
if (ROW_W < 1) $fatal(1, "mapping_bit_influence: ROW_W must be >= 1");
// The fields must exactly tile the address. A gap would leave a PA
// bit owned by nothing; an overlap would make the map lossy, which
// Chapter 8.6 §2 showed is the one failure a round trip detects.
if (OFFSET_W + COLUMN_W + BANK_W + BG_W + ROW_W != PA_W)
$fatal(1, "mapping_bit_influence: field widths must sum to PA_W");
if (POLICY != 0 && POLICY != 1)
$fatal(1, "mapping_bit_influence: POLICY must be 0 or 1");
localparam logic [2:0] F_OFFSET = 3'd0;
localparam logic [2:0] F_COLUMN = 3'd1;
localparam logic [2:0] F_BANK = 3'd2;
localparam logic [2:0] F_BG = 3'd3;
localparam logic [2:0] F_ROW = 3'd4;
// ── Field LSBs follow directly from the order. Both policies place
// offset at 0 and row at the top; they differ only in the middle,
// which is §1's observation expressed as arithmetic.
localparam int R_COL_LSB = OFFSET_W;
localparam int R_BANK_LSB = R_COL_LSB + COLUMN_W;
localparam int R_BG_LSB = R_BANK_LSB + BANK_W;
localparam int B_BANK_LSB = OFFSET_W;
localparam int B_BG_LSB = B_BANK_LSB + BANK_W;
localparam int B_COL_LSB = B_BG_LSB + BG_W;
localparam int ROW_LSB = OFFSET_W + COLUMN_W + BANK_W + BG_W;
assign column_lsb = 6'((POLICY == 0) ? R_COL_LSB : B_COL_LSB);
assign bank_lsb = 6'((POLICY == 0) ? R_BANK_LSB : B_BANK_LSB);
assign bg_lsb = 6'((POLICY == 0) ? R_BG_LSB : B_BG_LSB);
assign row_lsb = 6'(ROW_LSB);
// ── §3's rule as hardware: whichever field has the smallest LSB
// above the offset is the one a sequential walk sweeps first.
assign first_field_above_offset = (POLICY == 0) ? F_COLUMN : F_BANK;
// ── Walk every PA position once and name its owner. A loop rather
// than five part-selects, so that adding a field later cannot
// leave a position silently unassigned.
always_comb begin
for (int b = 0; b < PA_W; b++) begin
if (b < OFFSET_W) bit_field[b] = F_OFFSET;
else if (b >= ROW_LSB) bit_field[b] = F_ROW;
else if (POLICY == 0) begin
if (b < R_BANK_LSB) bit_field[b] = F_COLUMN;
else if (b < R_BG_LSB) bit_field[b] = F_BANK;
else bit_field[b] = F_BG;
end else begin
if (b < B_BG_LSB) bit_field[b] = F_BANK;
else if (b < B_COL_LSB) bit_field[b] = F_BG;
else bit_field[b] = F_COLUMN;
end
end
end
endmoduleSimulating it. Elaborate once per policy and print bit_field from bit 0 upward; the output is §6's matrix, column by column. With the chapter's defaults, POLICY = 0 reports bank_lsb = 10 and POLICY = 1 reports bank_lsb = 6 — the two numbers that produced every difference in §4 and §5.
Synthesis. Everything here is elaboration-time constant. The generated hardware is a set of constant drivers, and a synthesis tool will fold the whole module away. That is appropriate: it is a descriptor meant to be read by a checker or a testbench, and the point of writing it in RTL rather than in a comment is that a checker can hold the mapper to it — §10's P2 does exactly that.
10. What the Assertions Prove
These properties are combinational and this block has no clock, so they belong in a testbench or a bind unit sampled on the surrounding environment's clock — the same arrangement Chapter 16.2 used for its combinational mask block.
// ── P1. Every PA bit is owned by exactly one field, and the fields
// tile the address with no gap and no overlap. The elaboration
// guard checks the WIDTHS sum; this checks the ASSIGNMENT actually
// realises that sum, which is a different claim.
property p_fields_tile_address;
@(posedge clk) disable iff (!rst_n)
($countones({ (bit_field_count_offset == OFFSET_W),
(bit_field_count_column == COLUMN_W),
(bit_field_count_bank == BANK_W),
(bit_field_count_bg == BG_W),
(bit_field_count_row == ROW_W) }) == 5);
endproperty
a_fields_tile_address: assert property (p_fields_tile_address);
// ── P2. THE property that makes this block worth writing. The
// descriptor must agree with the real mapper: the bit the
// descriptor calls the bank LSB must be the bit that, when
// toggled, changes sys_addr_mapper's bank output and nothing else.
// This is what turns a comment into a checkable contract.
property p_bank_lsb_moves_bank;
@(posedge clk) disable iff (!rst_n)
(probe_valid && probe_addr_b == (probe_addr_a ^ (1 << bank_lsb)))
|-> (mapper_bank_b != mapper_bank_a)
&& (mapper_row_b == mapper_row_a);
endproperty
a_bank_lsb_moves_bank: assert property (p_bank_lsb_moves_bank);
// ── P3. A bit the descriptor assigns to the row field must not move
// the bank. The mirror of P2, and the one that catches a field
// boundary that is off by one -- the most common mapper bug, and
// the hardest to see by inspection.
property p_row_bit_leaves_bank;
@(posedge clk) disable iff (!rst_n)
(probe_valid && probe_addr_b == (probe_addr_a ^ (1 << row_lsb)))
|-> (mapper_bank_b == mapper_bank_a)
&& (mapper_row_b != mapper_row_a);
endproperty
a_row_bit_leaves_bank: assert property (p_row_bit_leaves_bank);
// ── P4. The descriptor is a pure function of parameters, so it must
// never change during a run. Catches a testbench that reparameterised
// one instance and compared it against another.
property p_descriptor_stable;
@(posedge clk) disable iff (!rst_n)
$stable(bank_lsb) && $stable(row_lsb)
&& $stable(column_lsb) && $stable(bg_lsb);
endproperty
a_descriptor_stable: assert property (p_descriptor_stable);
// ── Covers. P2 and P3 are single-bit-toggle properties; if the
// stimulus never toggles those exact bits they pass having checked
// nothing at all.
c_bank_lsb_toggled: cover property
(@(posedge clk) disable iff (!rst_n)
probe_valid && probe_addr_b == (probe_addr_a ^ (1 << bank_lsb)));
c_row_lsb_toggled: cover property
(@(posedge clk) disable iff (!rst_n)
probe_valid && probe_addr_b == (probe_addr_a ^ (1 << row_lsb)));What they prove. That the declared field order and the mapper's actual behaviour agree at the boundaries that matter, verified by toggling one address bit and observing exactly one field move.
What they do not prove. Nothing here says the map is lossless — that is Chapter 8.6 §2's round trip, and a map can pass P1 through P4 while aliasing two addresses if a field is read twice. Nothing says the policy is a good one; P2 is equally satisfied by both policies in §1, which is the point. And nothing says the workload will exercise the bits the matrix describes — that is 18.3's subject, and it is where the difference between a correct map and a useful one appears.
Vacuity is the live risk here, which is why both covers exist. P2 and P3 fire only on an address pair differing in exactly one specific bit. A random-address testbench will essentially never generate such a pair, so without directed single-bit-toggle stimulus these properties pass while checking nothing. §12's walking-bit test is what makes them real.
11. Corner Cases
Address mappers fail at boundaries far more often than in the middle, and most of the failures are parameterisation rather than logic.
| Situation | Correct behaviour | Failure if mishandled |
|---|---|---|
| a field declared zero-width | elaboration failure | logic [-1:0] — an illegal declaration, or a silently absent field |
widths do not sum to PA_W | elaboration failure | a gap leaves a PA bit owned by nothing; an overlap makes the map lossy |
POLICY outside its legal set | elaboration failure | the always_comb default silently produces one policy while the caller believes the other |
| every field one bit wide | legal; bit_field still tiles the address | loops that assume a field spans more than one bit position |
OFFSET_W equal to PA_W | elaboration failure by the sum check | no mapped field exists and every access decodes identically |
| address above the device | 8.6's addr_above_device flags it | high bits silently truncated, wrapping onto a valid location |
| field boundary off by one | the walking-bit test names the bit | one field a bit too wide and its neighbour a bit too narrow — and it still round-trips |
| offset narrower than the access granule | — | the granule's constant bits enter a mapped field; half that field's range becomes unreachable (§2) |
The last two rows are the ones worth memorising, because both survive a losslessness check. A round trip verifies that the decomposition is reversible; it does not verify that the fields sit where you intended. Those are independent properties needing independent tests — the round trip for the first, §12's walking bit for the second.
12. DV — The Walking-Bit Test
The independent model must not re-slice the address the way the RTL does. Chapter 8.6 §8 established the general form of that rule; here the concrete instruction is: if the DUT uses part-selects, the checker uses masks and shifts, expressed from the algebra in §1 rather than copied from the RTL.
The highest-value directed test in all of address mapping is the walking bit:
for b in 0 .. PA_W-1:
A = 0
B = 1 << b
decode A and B
record which decoded fields differOne pass produces the entire bit-influence matrix empirically, from the DUT, with no reference to how the DUT was written. Compare it against §6's table and every field-boundary error appears as a single misplaced row.
WALKING-BIT MISMATCH
policy under test : R
PA bit toggled : 10
expected field (§6) : bank
observed field change : column
expected bank delta : yes observed : no
expected column delta : no observed : yes
first differing field : column
diagnosis : the column field is one bit too wide, or the bank
field's LSB is one too high. Both present identically
at this bit and are distinguished by bit 9: if bit 9
also moves the column, the column is too wide.
blast radius : every bank-distribution result in 18.2 computed
against this mapper is measuring the wrong field.The last two lines are what makes the report worth building. A single-bit report that names two candidate root causes and the discriminating experiment saves the engineer the step they would otherwise have to invent.
Beyond walking bits, the directed set worth running: all-zeros; all-ones within the legal width; each field's boundary at −1, boundary, +1; two addresses differing only in a bank-select source bit; two differing only in a row bit; the last granule of a row's span and the first of the next; and a full round trip through 8.6's addr_roundtrip_check to confirm losslessness has not been broken by the reordering.
13. Debugging
Symptom: a sequential workload concentrates in one bank.
This is expected under Policy R and pathological under Policy B, so the first move is to establish which map is actually in force — not which one the documentation claims.
what varies in the address stream?
└─ capture the stream; XOR consecutive addresses; note which bits move
└─ compare against the bank field's bit range (§6)
├─ bank bits are varying → the mapping is not the problem;
│ look at the scheduler (17.4) or the bank state (5.2)
└─ bank bits never vary → the stream's varying bits and the
map's bank bits do not overlap
├─ intended (Policy R, sequential) → this is by design
└─ unintended → 18.3's stride aliasingSymptom: the decode is correct but the bank distribution is wrong. Both can be true at once, and conflating them wastes days. Losslessness and field placement are independent properties: 8.6's round trip will happily pass on a map whose bank bits sit somewhere useless. Run the walking-bit test — it checks placement, which the round trip does not.
Symptom: two mappers disagree and both round-trip. Then both are lossless and they are simply different maps, exactly as Chapter 8.6 §5 showed. The question is not which is correct but which one the other component — the firmware, the model, the monitor — is assuming. That is 18.4's problem, and it is a documentation problem before it is a hardware one.
Symptom: everything works until the address crosses a power of two. Almost always an off-by-one field boundary. The discriminator is §12's walking bit at exactly the suspected boundary, and the tell is a field that responds to one bit too many.
14. Misconceptions
“JEDEC defines the physical-address map.” It defines what the device does with a row, bank and column it is given. The transformation from a system address is controller and platform policy — 18.4 quotes a vendor saying so. Clue: a design that expects one map to be portable across platforms.
“Row bits are always the highest bits.” They are highest in both policies here, and that is a choice this chapter made to isolate one variable. It is common, not universal. Clue: a bit diagram drawn from memory rather than from the configuration.
“Bank bits are always low-order.” They are at bit 6 under B and bit 10 under R. Clue: an assumed 1 KiB bank stride that has never been checked against the map in force.
“Contiguous addresses stay in one row.” Only for as long as the fields below the row field last — 16 KiB here — and §7 shows the boundary is a conflict, not a free transition. Clue: a model that predicts a 100% hit rate for sequential traffic.
“Sequential traffic always produces row hits.” Under Policy B, eight sequential granules produced zero hits and eight activations. Clue: a hit-rate expectation that does not name the mapping it assumes.
“More row hits is always better.” §5 — Policy R's seven hits are serialised in one bank. Chapter 16.3 built an entire chapter on hit rate and bank parallelism being different quantities. Clue: a mapping chosen on hit rate alone.
“Spreading across banks is always better.” The mirror error, and 18.3 shows a stride where the spreading map collapses to a single bank. Clue: a policy justified by "more parallelism" with no reference to the workload.
“Different banks are fully independent.” They share the command bus, the data bus, the activation budget, and within a bank group part of the column path (16.4). Clue: a bandwidth estimate that scales linearly with bank count.
“A cache-line address is a DDR column address.” Here one access granule is 64 B and the column field is four bits selecting among 16 such granules within a row's span. They are related by the map, not equal. Clue: a column value used directly as an address offset.
“If it round-trips, the mapping is right.” A round trip proves losslessness. It says nothing about placement, and nothing about whether it matches the map some other component assumes. Clue: a mapper verified only by reconstruction.
“Changing the mapping changes the timing parameters.” tRCD, tRP and tCCD are device properties. The mapping changes how often each one is encountered. Clue: a proposal to "fix tRCD" by remapping.
15. Interview Reasoning
“Why does address mapping belong to the controller rather than the DRAM?” The device is told a row, bank and column; it has no notion of a system address. The transformation is where system knowledge lives — page size, interleaving, channel count — none of which the DRAM can see.
“Two maps both round-trip. Can they behave differently?” Yes, completely — §1's two policies produce opposite resource behaviour on identical addresses. The follow-up that separates understanding from recall: what would you measure to tell them apart? Bank distribution over a known stream, or a walking-bit test.
“Which address bit changes the bank, and how would you find out?” Read it off the field order if you have it; toggle single bits and observe the decode if you do not. The second answer is the stronger one, because it works on a system whose map you have not been given.
“What changes on the next cache line?” The field immediately above the offset. Answering with that rule rather than with a specific field shows the reasoning transfers to any map.
“Your sequential benchmark gets a 95% row-hit rate and disappointing bandwidth. What do you suspect?” A map that concentrates the stream in one bank — high hit rate, no parallelism, everything serialised behind one bank's column timing. Then name the measurement that distinguishes it: distinct banks touched per window.
“What does mapping change, and what does it not?” It changes which resources a workload presents to the timing rules. It changes no timing rule, no command semantics, and no device behaviour.
16. Exercises
1. Under Policy R, give the smallest positive stride that changes the bank group on every access. Verify against §6.
2. Under Policy B, decode 0x0A4C0 into offset, bank, bank group, column and row. Then reconstruct the address from those five fields and confirm you recover 0x0A4C0.
3. Policy R's row span is 16 KiB. What is Policy B's row span, and why is the answer the same? Which property of the two policies makes it so?
4. Walk 32 consecutive granules from 0x03F00 under Policy R. How many CONFLICT classifications occur, and at which addresses?
5. A colleague proposes moving the row field to PA[13:6] and the column to PA[21:14]. The map is still lossless. Describe what a sequential walk now does, and name the field that a 64-byte stride now moves.
6. Write the walking-bit expected-result table for Policy B, bits 6 through 14. Which two adjacent bits would be indistinguishable if the bank field were one bit wider than declared?
7. P2 and P3 both pass on a mapper whose bank and bank-group fields are swapped. Write the additional property that catches it.
8. Explain why mapping_bit_influence synthesises to constants, and why that is an argument for writing it in RTL rather than against it.
17. Where This Goes
The mechanism is established: the field order decides what changes on the next access, and that decides which resources a stream touches.
Chapter 18.2 takes one fixed address stream, runs it through both policies, and measures what each produces — row relations and bank distribution together, because 16.3 proved that either one alone misleads. 18.3 asks what happens when the workload's stride and the map's bank bits interact badly, and introduces hashing as the standard answer. 18.4 asks the question this chapter has been quietly assuming away: on a real system, how do you know which map is in force?
Continue learning
Related tutorials
- Related topic
Channels
A channel has its own command bus, data bus and scheduling, so two channels contend for nothing. That independence means the address alone decides which channel a request uses, making the mapping — not the hardware — the thing that determines whether the parallelism is real.
- Related topic
Physical Mapping
Five chapters of fields assembled into one map, and then the two questions none of them could ask alone: is the decomposition lossless, and can a monitor invert it from what it actually observed?
- Related topic
Mapping for Performance
The same mapping policy scores 93.8 percent row hits on one address stream and zero on another. Mapping drives hit rate completely — which is exactly why hit rate alone cannot be used to choose a mapping.
- Related topic
Row-Buffer Locality
Chapter 9.6 counts hits, misses and conflicts. This owns what produces them: a stride, a mapping, and a temporal structure that identical totals cannot distinguish.
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.
