Skip to content
VLSI Mentor

DDR · Module 5

The DDR Device Structure

A DDR device is mostly selectors and shared resources wrapped around one kind of storage. Sorting every structure into those three categories is what makes banks, bank groups, ranks and channels arrive as consequences rather than as vocabulary to memorise.

Module 4 answered why DDR is shaped the way it is — eight bottlenecks and eight architectural responses. This module answers a different question, and it is a structural one:

What is a DDR memory system actually made of, and where does each unit of selection and parallelism live?

By the end of the module you should be able to take an arbitrary memory request and say which channel, which rank, which device participation, which bank group, which bank and which row it touches — and, more importantly, which resources it contends for and which other requests it can overlap with.

This chapter is the map. Its job is to establish a way of sorting the structures the rest of the module introduces, so that banks, bank groups, ranks, channels and modules arrive as consequences rather than as vocabulary to memorise.

1. Three Kinds of Thing

Open up any DDR device and almost everything inside it is one of three things. Sorting structures this way is the single most useful habit in memory architecture, because it turns "what is a bank group?" from a definition question into a question with a derivable answer.

Storage. Structures that hold bits. In a DRAM device there is exactly one kind: the 1T1C cell. Everything else is machinery.

Selectors. Structures that narrow a request down — from "somewhere in this device" to "these particular cells." A selector consumes an address field and produces a choice. Row decoders, column multiplexers and bank decoders are all selectors.

Shared resources. Structures that are used by many requests but replicated fewer times than the things that use them. A shared resource is the reason two requests that could logically proceed independently sometimes cannot. Chapter 3.5's sense amplifiers are shared by every row in their array; a device's data path is shared by every bank.

2. The Device, Sorted

A DDR device sorted into three categories. Storage is the DRAM cell array only. Selectors consume address fields and narrow the request: bank group decode, bank decode, row decode and column selection. Shared resources are used by many requests but replicated fewer times: the sense amplifiers of a bank, the column data path of a bank group, and the device's external interface.Selectorsconsume address fieldsStorage1T1C cells onlyShared resourcesreplicated fewer timesgroup · bank · row · colnarrow the requestthe only capacityeverything else is machinerysense amps · data path ·I/Owhere conflicts come fromreachvia12
Figure 1 — every structure in a DDR device is storage, a selector, or a shared resource.

The asymmetry in that figure is the point. One column holds all the capacity; the other two columns are the reason a memory system is complicated. A DRAM device is overwhelmingly selection and sharing machinery wrapped around a very simple storage element — which is exactly why Chapter 1.6 could argue that DRAM's cost advantage comes from the cell, and why every generation in Module 4 changed the machinery and left the cell alone.

Now the same device as a vertical stack, which is the form to memorise:

Seven levels of a DDR device from the external interface down to the cell. The external interface is shared by the whole device. Command decode determines the operation. Bank group selection chooses a group that owns a column data path. Bank selection chooses a bank that owns row state. Row selection activates one row into the sense amplifiers. The row buffer holds the sensed row and is shared by every column in it. Column selection chooses which bits of the held row reach the interface.External interfaceDQ · DQS · CA · CK — shared by the entire deviceDQ · DQS · CA · CK — shared by the entire deviceCommand decodeWhich operation · Module 7 owns the command setWhich operation · Module 7 owns the command setBank group selectChooses a group · a group owns a column data pathChooses a group · a group owns a column data pathBank selectChooses a bank · a bank owns row stateChooses a bank · a bank owns row stateRow selectActivates one row into the sense amplifiersActivates one row into the sense amplifiersRow bufferHolds the sensed row · shared by every column in itHolds the sensed row · shared by every column in itColumn selectChooses which bits of the held row reach the interfaceChooses which bits of the held row reach the interface
Figure 2 — the device stack, annotated with what each level selects and what it shares.

Read the variants, not just the labels. The blue bands are selectors — each consumes one address field. The amber bands are shared resources — each is a place two requests can collide. There are exactly two amber bands inside the device, and between them they explain most of DDR's performance behaviour:

The row buffer is shared by every column in its row. So two accesses to the same row are nearly free relative to each other, and two accesses to different rows of the same bank are expensive. That is Chapter 3.6's row-hit-versus-conflict result, restated as a sharing fact.

The external interface is shared by the whole device — and, as 5.4 and 5.5 will show, by every device on a rank and every rank on a channel. It is the most heavily shared resource in the entire memory system, which is why so much of the hierarchy above the device exists to manage access to it.

The cell is not in the figure, deliberately. It sits below column selection, and Module 2 owns it. This module never looks below the row buffer again.

3. Address Fields, Introduced Without Being Pinned Down

A selector consumes an address field. So the device's structure implies a set of fields:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   bank group   -> which group's column data path
   bank         -> which bank's row state
   row          -> which row to activate into the sense amplifiers
   column       -> which bits of the held row to move

and above the device, which 5.4 and 5.5 reach:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   channel      -> which independent interface path
   rank         -> which set of devices participates

4. The Numbers Are Powers of Two, and That Is Not an Accident

Real DDR organisations use power-of-two counts at every level. Verified organisations make this concrete:

GenerationDevice widthBank groupsBanks per groupTotal banks
DDR4x4, x84416
DDR4x16248
DDR5x4, x88432
DDR5x164416

Two observations, and the second is the useful one.

The x16 device has fewer banks in both generations. A wider device moves more bits per column access, so it needs fewer independent banks to keep its share of the interface busy — and banks cost die area. That is a structural trade, not an arbitrary difference, and it is the first hint that device width and bank count are coupled. 5.4 develops the coupling.

Every count is a power of two, and that has a consequence worth stating plainly: a selector field of width log2(N) has exactly N legal encodings when N is a power of two, so there are no illegal values to police. A non-power-of-two count would leave encodings that decode to nothing, and every level of the hierarchy would need a range check with defined behaviour for an out-of-range value.

Powers of two are therefore a simplification of the entire selection path, not merely a convention — and §5's RTL is built to show exactly what changes when the assumption is dropped.

5. RTL — The Device's Geometry as Elaboration-Time Arithmetic

Engineering problem

A device organisation — bank groups, banks per group, rows, columns, device width — determines the width of every selector field and the device's total capacity. Those are properties of the parameterisation, fixed before any request arrives, and they must be consistent: a hierarchy whose numbers do not agree is a broken design, not a runtime error.

Separately, if any count is not a power of two, the selector fields carry illegal encodings that something must police — and the block should make that difference visible rather than assumed.

Classification

COMPILE-TIME ARCHITECTURAL MODEL, with a small SYNTHESIZABLE range check.

The geometry derivation generates no hardware. It exists so that an inconsistent organisation cannot elaborate. The range check is genuine combinational logic, and its most interesting property is that it optimises away entirely for power-of-two organisations — which is the §4 insight expressed as hardware rather than as prose.

What it does not model. No cells, no sensing, no charge, no timing, no commands, no data. It does not decompose an address — Chapter 3.6's dram_array_frontend does that, and this block deliberately takes already-separated fields so the two do not overlap. It describes the shape the fields must fit, not how they were produced.

Interface

Already-decomposed fields in, per-field range violations out, plus a single geometry_ok. The capacity and selector widths are elaboration-time parameters, not ports.

State

None. The block is purely combinational, and that is the honest expression of the fact that a device's geometry does not change.

Combinational logic

Per-field comparisons against the configured counts, each generated only when the count is not a power of two.

Sequential logic

None. There is nothing to remember: geometry is fixed at elaboration and a field is either in range or not in the cycle it is presented.

Simulation

vlog ddr_device_geometry.sv tb_ddr_device_geometry.sv then vsim -c tb_ddr_device_geometry -do "run -all"; VCS vcs -sverilog ddr_device_geometry.sv tb_ddr_device_geometry.sv && ./simv; Xcelium xrun -sv ddr_device_geometry.sv tb_ddr_device_geometry.sv. Every later block in Module 5 simulates the same way.

Expected output: with power-of-two counts, every field value in the sweep reports in range and geometry_ok never falls. Re-elaborate with a non-power-of-two count and the out-of-range outputs begin firing for the top encodings.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// DDR DEVICE GEOMETRY.
// Classification: COMPILE-TIME ARCHITECTURAL MODEL
//                 + small SYNTHESIZABLE range check.
//
// A device's organisation fixes every selector field width and the total
// capacity. Those are properties of the PARAMETERISATION, so they are
// derived and checked at ELABORATION -- an inconsistent hierarchy should
// fail to build, not misbehave at runtime.
//
// The range-check half exists to make one architectural point concrete:
// when every count is a power of two, a selector field has exactly as many
// encodings as there are things to select, so THERE IS NOTHING TO POLICE
// and this logic optimises away to constants. Drop that assumption and
// every level of the hierarchy acquires a range check plus a policy for
// what an out-of-range value means.
//
// WHAT THIS DOES NOT MODEL: cells, sensing, charge, timing, commands, data.
// It also does NOT decompose an address -- Chapter 3.6's dram_array_frontend
// does that. This block takes already-separated fields and describes the
// SHAPE they must fit.
// ─────────────────────────────────────────────────────────────────────────
module ddr_device_geometry #(
  parameter int BANK_GROUPS     = 4,
  parameter int BANKS_PER_GROUP = 4,
  parameter int ROWS            = 65536,
  parameter int COLS            = 1024,
  // Bits the device presents externally per column access: x4, x8, x16.
  parameter int DQ_WIDTH        = 8,

  // ── DERIVED selector widths. Each guard keeps a count of 1 legal rather
  //    than producing a zero-width field, which would be an illegal
  //    declaration. A device with one bank group is unusual but must not be
  //    unrepresentable -- it is the degenerate case that proves the
  //    hierarchy is parameterised rather than hard-coded.
  parameter int BG_W        = (BANK_GROUPS     <= 1) ? 1 : $clog2(BANK_GROUPS),
  parameter int BA_W        = (BANKS_PER_GROUP <= 1) ? 1 : $clog2(BANKS_PER_GROUP),
  parameter int ROW_W       = (ROWS            <= 1) ? 1 : $clog2(ROWS),
  parameter int COL_W       = (COLS            <= 1) ? 1 : $clog2(COLS),
  parameter int TOTAL_BANKS = BANK_GROUPS * BANKS_PER_GROUP
) (
  // Already-decomposed fields. Presented together; no clock, because
  // geometry is not a temporal property.
  input  logic [BG_W-1:0]  bg,
  input  logic [BA_W-1:0]  ba,
  input  logic [ROW_W-1:0] row,
  input  logic [COL_W-1:0] col,

  output logic             bg_out_of_range,
  output logic             ba_out_of_range,
  output logic             row_out_of_range,
  output logic             col_out_of_range,
  output logic             geometry_ok
);

  // ── COMPILE-TIME CAPACITY DERIVATION.
  //    The unit chain is spelled out one named step at a time, for the
  //    reason Chapter 4.1 Section 10 gave: a memory-system budget hides its
  //    errors in unit conversions, so each conversion gets a name a reader
  //    can check independently.
  localparam longint CELLS_PER_BANK  = longint'(ROWS) * longint'(COLS)
                                     * longint'(DQ_WIDTH);
  localparam longint DEVICE_BITS     = CELLS_PER_BANK * longint'(TOTAL_BANKS);

  // SELF-CHECK OF THE ARITHMETIC. The default parameters describe a
  // 16-bank x8 organisation with 65536 rows and 1024 columns:
  //
  //   16 banks x 65536 rows x 1024 cols x 8 bits = 8 589 934 592 bits = 8 Gib
  //
  // That reproduces a real 8Gb x8 DDR4 device density, which is how you
  // know the formula is the right one rather than a plausible one. A
  // derivation that reproduces a published organisation is the only kind
  // worth trusting.

  // ── COMPILE-TIME LEGALITY.
  if (BANK_GROUPS < 1) begin : g_bg_min
    initial $fatal(1, "ddr_device_geometry: BANK_GROUPS must be >= 1");
  end
  if (BANKS_PER_GROUP < 1) begin : g_ba_min
    initial $fatal(1, "ddr_device_geometry: BANKS_PER_GROUP must be >= 1");
  end
  if (ROWS < 1 || COLS < 1) begin : g_arr_min
    initial $fatal(1, "ddr_device_geometry: ROWS and COLS must be >= 1");
  end
  // Device width is an externally visible property and the standard
  // organisations are x4, x8 and x16. Anything else is almost certainly a
  // configuration mistake, so it is rejected rather than simulated.
  if ((DQ_WIDTH != 4) && (DQ_WIDTH != 8) && (DQ_WIDTH != 16)) begin : g_dq
    initial $fatal(1, "ddr_device_geometry: DQ_WIDTH must be 4, 8 or 16");
  end
  if (DEVICE_BITS <= 0) begin : g_cap
    initial $fatal(1, "ddr_device_geometry: derived capacity overflowed or is empty");
  end

  // ── Range checks, generated ONLY where they can ever fire.
  //
  //    THE PATTERN HERE IS A HAZARD WORTH LEARNING. The obvious one-liner
  //
  //        assign bg_out_of_range = (bg >= BG_W'(BANK_GROUPS));
  //
  //    is WRONG whenever BANK_GROUPS is a power of two, because BG_W is
  //    $clog2(BANK_GROUPS) and the cast TRUNCATES the bound to zero -- so
  //    the comparison reads `bg >= 0`, which is ALWAYS TRUE, and every
  //    field would be reported out of range. Comparing a count against a
  //    field sized to hold exactly that many values is a recurring trap in
  //    parameterised hierarchy code.
  //
  //    Handled by asking the question at elaboration instead: if the count
  //    fills its field width there is nothing to check, and otherwise the
  //    comparison is done one bit wider so the bound is representable.
  if (BANK_GROUPS >= (1 << BG_W)) begin : g_bg_full
    assign bg_out_of_range = 1'b0;
  end else begin : g_bg_check
    assign bg_out_of_range = ({1'b0, bg} >= (BG_W+1)'(BANK_GROUPS));
  end

  if (BANKS_PER_GROUP >= (1 << BA_W)) begin : g_ba_full
    assign ba_out_of_range = 1'b0;
  end else begin : g_ba_check
    assign ba_out_of_range = ({1'b0, ba} >= (BA_W+1)'(BANKS_PER_GROUP));
  end

  if (ROWS >= (1 << ROW_W)) begin : g_row_full
    assign row_out_of_range = 1'b0;
  end else begin : g_row_check
    assign row_out_of_range = ({1'b0, row} >= (ROW_W+1)'(ROWS));
  end

  if (COLS >= (1 << COL_W)) begin : g_col_full
    assign col_out_of_range = 1'b0;
  end else begin : g_col_check
    assign col_out_of_range = ({1'b0, col} >= (COL_W+1)'(COLS));
  end

  assign geometry_ok = !(bg_out_of_range | ba_out_of_range
                       | row_out_of_range | col_out_of_range);

endmodule

Cycle trace

There are no cycles — the block is combinational — so the interesting trace is over parameterisations rather than over time:

BANK_GROUPSBG_WLegal encodingsIllegal encodingsbg_out_of_range logic
420–3noneconstant 0
830–7noneconstant 0
320–23a real comparator
630–56, 7a real comparator

The first two rows are why DRAM organisations are powers of two. The check is not merely cheap — it does not exist. The last two rows are what the hierarchy would cost otherwise: every level would need a comparator, and, far worse, a defined behaviour for an out-of-range value at every level.

Waveform expectation

Nothing temporal to show, which is why this chapter has no waveform. With power-of-two parameters a simulator would show all four out-of-range outputs flat at zero regardless of stimulus — and a flat trace is the correct and informative result here, because it is the visible form of the encodings-fit-exactly property.

Synthesis implication

With power-of-two counts: no gates at all. Every output is a constant, and geometry_ok ties high. With a non-power-of-two count: one magnitude comparator per offending field. The compile-time half synthesises to nothing by construction, which is the ideal cost for an architectural constraint.

Corner cases

BANK_GROUPS == 1 and BANKS_PER_GROUP == 1 both give a 1-bit field through the guard rather than $clog2(1) == 0, which would make the port declaration illegal — and BANK_GROUPS == 1 then has one legal encoding out of two, so its range check does materialise. That is a genuinely useful degenerate case: the single-group device is the one power-of-two-reasoning gets wrong, because 1 is a power of two but 1 << 1 == 2 > 1. DQ_WIDTH outside {4, 8, 16} does not elaborate. A capacity that overflows longint is rejected rather than silently wrapping.

Verification

What DV must prove: for power-of-two counts, no field value in an exhaustive sweep reports out of range; for non-power-of-two counts, exactly the encodings at or above the count report out of range and no others; geometry_ok is the exact NOR of the four flags; and at least one deliberately inconsistent parameterisation fails to elaborate — which is a test of the build, not of the simulation, and the one most often skipped. An elaboration check nobody has watched fire may not work.

SVA

§6.

Debugging

If every field reports out of range at power-of-two parameters, the truncating one-liner is back — check that the comparison bound is one bit wider than the field. If a non-power-of-two configuration reports nothing out of range, check that the generate condition is >= and not >; BANK_GROUPS > (1 << BG_W) is never true and would disable every check. If the derived capacity does not match a published device density, check the units before doubting the source: a factor of 8 is bits versus bytes, and a factor of 1024 is a binary versus decimal prefix.

Limitations

No address decomposition, by design. No notion of state — this block cannot say whether a bank is open, which is 5.2's subject. No sharing or contention model, which is 5.3's. Nothing above the device: ranks, channels and modules are 5.4 to 5.6. And the capacity arithmetic assumes a uniform organisation, which is the right assumption for a single device and the wrong one for a module with mixed configurations.

6. Three Assertions Worth Writing

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// VERIFICATION-ONLY, inside ddr_device_geometry.
// No clock: the block is combinational, so these are immediate assertions
// inside an always_comb rather than concurrent properties. Using
// @(posedge clk) here would be a category error -- there is no clock, and
// inventing one to host an assertion is how a combinational block acquires
// a spurious timing dependency.

always_comb begin
  // P1 -- geometry_ok is exactly the absence of every violation. A
  // composite status output that can disagree with its own inputs is worse
  // than no status output, because it is trusted.
  a_ok_is_exact: assert (geometry_ok == !(bg_out_of_range | ba_out_of_range
                                        | row_out_of_range | col_out_of_range));

  // P2 -- THE ARCHITECTURAL PROPERTY. When a count fills its field width,
  // no value of that field can be out of range. This is the powers-of-two
  // claim of Section 4 stated as a checkable property, and it is the one
  // that catches the truncating-comparison bug: that bug reports EVERY
  // value out of range, which violates this and nothing else here.
  if (BANK_GROUPS >= (1 << BG_W))
    a_bg_never_oor: assert (!bg_out_of_range);
  if (BANKS_PER_GROUP >= (1 << BA_W))
    a_ba_never_oor: assert (!ba_out_of_range);

  // P3 -- and the converse, which is what stops P2 from being satisfiable
  // by a design that simply never reports anything. When a count does NOT
  // fill its width, the encodings at or above it MUST be reported. Without
  // this, tying every output low passes P1 and P2 perfectly.
  if (BANK_GROUPS < (1 << BG_W))
    a_bg_oor_exact: assert (bg_out_of_range == ({1'b0, bg} >= (BG_W+1)'(BANK_GROUPS)));
end

P2 and P3 together are the lesson, and it is the same one Chapter 4.5 §6 drew: P2 only forbids, and a design that reports nothing satisfies it completely. P3 is the half that requires a report, and without it the pair is worthless.

Whenever you write a property of the form "X must never happen", ask what design would satisfy it trivially. If the answer is "one that does nothing", you need a companion property that requires something to happen. That is not a subtlety of this block — it is the most common structural weakness in real assertion suites.

What these prove. That the range logic matches the configured counts exactly, in both directions, and that the composite status is consistent with its parts.

What they do not prove. Nothing about capacity: DEVICE_BITS is an elaboration-time constant, so no assertion can check it — a wrong formula produces a wrong constant that every property agrees with. That is checked by deriving it independently and comparing against a published organisation, which §5's self-check comment does. And nothing about whether the fields presented are the right fields for the request — that is address mapping, and it is Module 18's problem, not an assertion's.

7. What the Controller Has to Remember

The device's structure implies a set of things a controller must track, and it is worth previewing the list because the rest of the module is largely an expansion of it.

LevelWhat the controller tracksIntroduced in
Bankwhether a row is open, and which one5.2
Bank groupwhich group was last used, for resource contention5.3
Rankwhich rank currently owns the shared data path5.4
Channelper-channel queues and independent scheduling state5.5
Moduleadded latency from any buffering layer5.6

Notice what is absent: the row buffer's contents and the cells themselves. The controller tracks which row is open, never what is in it. A memory controller has no idea what data it is moving — it tracks structure and state, and the data is opaque to it. That is worth internalising early, because it explains why a controller can be verified almost entirely with structural properties and why Chapter 4.3 §6's point stands: data correctness needs a scoreboard, never an assertion.

8. Common Misconceptions

"A DDR device is a memory array with an interface attached." Wrong mental model: the array is the device and the rest is packaging. Engineering action: reasoning about performance purely in terms of capacity and interface rate; treating the internal structure as an implementation detail; expecting uniform access cost across the address space. Observable failure / bad conclusion: an inability to explain why two accesses of identical size cost wildly different amounts, and performance models that predict a single latency for all accesses. Every structure between the interface and the cell exists to create or to limit parallelism, and a model that omits them cannot predict anything. Correct model: a DDR device is selection and sharing machinery wrapped around one storage element. The cell is the only thing that stores; everything else either narrows a request or is shared between requests. Prevention: for any structure, ask which of the three it is. If it neither holds bits nor consumes an address field, it is a shared resource — and shared resources are where the performance behaviour lives.

"Address bits have a fixed, standard DDR mapping." Wrong mental model: the standard defines which bits select which level. Engineering action: hard-coding a bit mapping into an analysis, a test, or a performance model; assuming a stride that interleaves well on one system does so on another. Observable failure / bad conclusion: a workload tuned to one platform's mapping performing badly on another with nothing changed, and bandwidth models that are confidently wrong. Mapping is also frequently not a contiguous slicing at all — controllers hash — so even reverse-engineering the field order may not yield a usable model. Correct model: the fields are structural; the mapping from physical address bits to fields is a controller and system design choice, is not fixed by the standard, and may involve hashing. Module 18 owns it. Prevention: keep the fields symbolic in any reasoning that must be portable, and treat any concrete mapping as a measured property of one system.

"More capacity means more parallelism." Wrong mental model: capacity and concurrency scale together. Engineering action: sizing a memory system by capacity and expecting bandwidth to follow; assuming a denser device is a faster one. Observable failure / bad conclusion: a high-capacity configuration delivering no more throughput than a small one — which is exactly what happens when capacity is added by making rows longer or arrays denser rather than by replicating banks, ranks or channels. Correct model: capacity comes from storage; parallelism comes from replicating what used to be shared. They are independent axes. A device with twice the rows and the same bank count has twice the capacity and identical concurrency. Prevention: count the replicated shared resources, not the bits. Ask how many independent row buffers, column paths and interfaces exist.

"Powers of two are just a convention." Wrong mental model: the counts could be anything and powers of two are habit. Engineering action: proposing a non-power-of-two bank or channel count without accounting for the consequences; writing selector logic that assumes exact fit and then reusing it where the assumption fails. Observable failure / bad conclusion: illegal encodings with no defined behaviour at every level of the hierarchy, and the specific truncating-comparison bug §5 describes — which reports every field out of range and is easy to write and hard to spot. Correct model: a power-of-two count makes a log2(N)-bit field have exactly N encodings, so there is nothing to police. It is a simplification of the entire selection path. Non-power-of-two counts are possible and cost a range check plus a defined policy at every level. Prevention: for any count, compute 1 << ceil(log2(N)) and compare with N. If they differ, illegal encodings exist and something must own them.

9. Debugging — Accesses of Identical Size Cost Wildly Different Amounts

Symptom. A workload issues uniformly sized accesses. Measured service time varies by a large factor between them, with no errors and no obvious pattern in the addresses.

Uniform request size with non-uniform cost means the variation is structural — it is coming from where in the hierarchy each request lands, not from what it asks for. The job is to find which level is responsible, and each level leaves a different signature.

Mechanism 1 — row buffer sharing: hits versus conflicts in the same bank. Inspect: per-bank open-row state at the moment each request is serviced, against the request's row field — Chapter 3.6 §5's classification. Expected evidence: the slow requests target a bank whose open row differs from theirs. Discriminator: does the slow request's row field match the bank's open row? This is first because it is the most common cause and because the state needed to decide it is a single table lookup. A conflict costs a close and an open before anything moves.

Mechanism 2 — bank group sharing: the column path. Inspect: the bank group of each request against its immediate predecessor's. Expected evidence: slow requests following a predecessor in the same group, fast ones following a predecessor in a different group. Discriminator: the group relationship between consecutive requests, not the absolute group. Cleanly distinct from mechanism 1 — this cost appears even on row hits, and it depends on history rather than on state. 5.3 is where this becomes precise.

Mechanism 3 — rank sharing: the data path changes owner. Inspect: the rank field of consecutive requests. Expected evidence: additional cost concentrated on requests whose rank differs from the previous one. Discriminator: cost correlated with a rank change, again not with a rank value. 5.4 explains why a change of owner on a shared bus is expensive when using it is not.

Mechanism 4 — channel sharing: queueing, not structure. Inspect: per-channel queue occupancy at request arrival. Expected evidence: slow requests arriving at a channel that already has a deep queue while another channel is idle. Discriminator: is the cost in the device or in the queue? If the request waited before reaching the device, no amount of device-level reasoning explains it — and this is the mechanism most often misattributed, because the symptom is indistinguishable from the others once you only look at total service time. Measure queueing separately from service.

Mechanism 5 — refresh: not structural at all. Inspect: whether slow requests coincide with refresh activity, and whether their frequency tracks temperature. Expected evidence: isolated slow requests with no relationship to any address field, at a rate that rises when hot. Discriminator: no address correlation whatsoever. Chapter 2.3 established that retention falls with temperature, so the refresh burden grows — and this mechanism is the one that produces outliers uncorrelated with everything else, which is precisely how to recognise it.

Discrimination, cheapest first. Separate queueing time from device service time — that single split removes mechanism 4 from consideration or proves it, and without it the other four cannot be distinguished at all. Then classify each request against the open-row state, which resolves mechanism 1. Then look at the relationship between consecutive requests — same group, same rank — which covers 2 and 3. Then check whether the residual outliers correlate with any address field; if they correlate with nothing, look at refresh.

The reasoning lesson. Mechanisms 1, 4 and 5 depend on a request's own properties; mechanisms 2 and 3 depend on its relationship to the previous request. That distinction is the most useful one in memory performance debugging, and it is invisible if you only record per-request latency. A trace that captures each request in isolation cannot diagnose a cost that comes from sequence — so capture the predecessor's fields alongside each request, or accept that half the causes are undiagnosable. This is why §7's table of controller state matters: the controller already has to remember all of it, so a trace that records it is nearly free.

10. Interview Reasoning

"What is actually inside a DDR device?" Three kinds of thing. One storage element — the 1T1C cell — which is the only thing that holds bits. A chain of selectors, each consuming one address field, that narrow a request from the whole device down to a set of cells: bank group, bank, row, column. And a set of shared resources, used by many requests but replicated fewer times than the things that use them — principally the row buffer, shared by every column in its row, and the external interface, shared by the whole device. Sorting structures that way is useful because storage explains capacity, selectors explain addressing, and shared resources explain performance.

"Why does the DDR hierarchy have so many levels?" Because each level exists to replicate something that was previously shared and was therefore limiting throughput. A bank replicates row state, so two requests to different banks each get their own open row. A bank group replicates the column data path. A rank replicates devices across the interface width. A channel replicates the whole interface. Parallelism is created by replication and destroyed by sharing, and nothing else — so the hierarchy is a historical record of which shared resources were worth the die area to duplicate. Each level's remaining cost is whatever it still shares.

"Why can't you infer DDR address mapping from a device datasheet?" Because the mapping from physical address bits to structural fields is a controller and system design choice, not a property of the DRAM. The device defines what fields it needs and how wide they are; it does not define which processor address bits supply them. Different controllers make different choices, the same controller may differ between configurations, and real controllers frequently hash rather than slicing contiguously. That matters because the choice determines whether a given access stride spreads across the hierarchy or concentrates in one part of it, which is a first-order bandwidth effect — so it is a performance decision made outside the device entirely.

"Why are DRAM bank and channel counts powers of two?" Because a selector field of width log2(N) has exactly N encodings when N is a power of two, so every encoding selects something real and there is nothing to police. Break that and every level of the hierarchy needs a range check plus a defined behaviour for an out-of-range value, and the selection path gets more complicated at every stage. It also avoids a specific and easily written bug: comparing a field against a count cast to the field's own width truncates the bound to zero when the count is a power of two, which makes the comparison always true and reports everything out of range.

"Why does a wider DRAM device tend to have fewer banks?" Because a wider device moves more bits per column access, so it needs less bank-level parallelism to keep its share of the interface busy — and banks cost die area, which is the dominant cost in DRAM. The verified organisations show it directly: a DDR4 x4 or x8 device has 16 banks in 4 bank groups, while a x16 has 8 banks in 2 groups; DDR5 doubles both to 32 and 16 respectively. So device width and bank count are coupled by a die-area trade rather than being independently chosen.

11. Engineering Check

Work through the following using the geometry of §5. These are educational organisations chosen for clean arithmetic; two of them reproduce real published device densities, and those are flagged.

1. A device has 16 banks, 65 536 rows, 1024 columns and is x8. What is its capacity? 16 × 65 536 × 1024 × 8 bits. Step by step: 16 × 65 536 = 1 048 576 banks·rows; × 1024 = 1 073 741 824; × 8 = 8 589 934 592 bits = 8 Gib. This reproduces a real 8 Gb x8 DDR4 organisation — and a derivation that lands on a published density is how you know the formula is right rather than merely plausible.

2. Same organisation but x16. Capacity? 16 × 65 536 × 1024 × 16 = 17 179 869 184 bits = 16 Gib — twice the capacity from twice the width, with the same number of banks and rows. But note what did not change: the bank count. The device holds twice as much and has exactly the same amount of bank-level parallelism.

3. What are the selector field widths for question 1's organisation, assuming 4 bank groups of 4 banks? BG_W = log2(4) = 2; BA_W = log2(4) = 2; ROW_W = log2(65 536) = 16; COL_W = log2(1024) = 10. Total 30 bits of device-level selection. Every one of those fields fits its count exactly, so none of them has an illegal encoding.

4. Now suppose the device had 3 bank groups instead of 4. What changes? BG_W is still 2, because ceil(log2(3)) = 2 — but now there are 4 encodings for 3 groups, so encoding 3 selects nothing. A range check appears, and something must define what happens when a request presents it. The field width did not change and the design got harder, which is the cleanest possible demonstration that powers of two are a structural simplification rather than a convention.

5. Two configurations have identical capacity: (a) 8 banks × 131 072 rows, (b) 32 banks × 32 768 rows, both x8 with 1024 columns. Verify, then say which offers more parallelism. (a) 8 × 131 072 × 1024 × 8 = 8 589 934 592; (b) 32 × 32 768 × 1024 × 8 = 8 589 934 592. Identical — 8 Gib each. But (b) has four times as many independent row buffers, so four times as many distinct rows can be open simultaneously. Same capacity, four times the bank-level concurrency — which is §8's third misconception in numbers, and the reason bank count appears on a datasheet at all.

6. Which of the two configurations in question 5 costs more die area, and why is that the actual trade? (b) does. Each bank needs its own row decoder and its own set of sense amplifiers, and Chapter 3.5 established that sense amplifiers are large — a full row's worth per bank. So going from 8 banks to 32 replicates that four times over. Since Chapter 1.6 showed DRAM economics are dominated by bits per unit area, bank count is bought with the one currency DRAM cannot spend freely — and that is why bank counts grow slowly across generations while row counts grow quickly. Capacity is cheap; parallelism is not.

12. Summary

A DDR device is mostly machinery. Every structure inside it is one of three things, and sorting them this way is the habit the rest of this module depends on.

Storage holds bits, and there is exactly one kind: the 1T1C cell. Selectors consume an address field and narrow the request — bank group, bank, row, column. Shared resources are used by many requests but replicated fewer times, and they are where every performance effect comes from. Inside the device there are two that matter: the row buffer, shared by every column of its row, and the external interface, shared by the entire device.

Storage explains capacity. Selectors explain addressing. Shared resources explain performance.

And each level of the hierarchy exists because something was worth replicating. A bank replicates row state; a bank group replicates the column path; a rank replicates devices across a width; a channel replicates the interface. Parallelism is created by replication and destroyed by sharing — so the hierarchy is a record of which shared resources justified their die area, and each level's residual cost is whatever it still shares. That is why this module is not a glossary.

The address fields are symbolic. Which physical bits carry which field is a controller and system choice, not defined by the standard, possibly hashed rather than sliced, and the subject of Module 18. It is also a first-order performance decision, because it determines whether an access pattern spreads across the hierarchy or concentrates in one corner.

The counts are powers of two, and that is structural rather than conventional: a log2(N)-bit field has exactly N encodings when N is a power of two, so no level needs a range check or a policy for illegal values. Verified organisations: DDR4 x4/x8 has 16 banks in 4 bank groups, x16 has 8 in 2; DDR5 doubles both to 32 in 8 and 16 in 4. The wider device has fewer banks in both generations, because width and bank-level parallelism are substitutes and banks cost area.

And capacity and parallelism are independent axes. Two organisations of identical capacity can differ four-fold in how many rows can be open at once. Capacity comes from cells; concurrency comes from replicated shared resources.

13. What Comes Next

Chapter 5.2 takes the first level the hierarchy replicated. Module 3 established that a bank is the scope of row state; 5.2 asks what having many of them actually buys, which resources stay bank-local and which remain shared above them, and — the part Module 3 never touched — what makes an operation on one bank legal or illegal given that bank's own state, and what guarantees it cannot disturb any other bank.

That isolation guarantee is the entire value of a bank, and it turns out to have exactly one deliberate exception.

Return to Memory Matrices and Hierarchy for the five-level array hierarchy this chapter extends, Sense Amplifiers for what a row buffer physically is, or Cost vs Density for why die area is the currency every level of this hierarchy is bought with. The full path is on the DDR tutorials index.

Continue learning

Standards & specifications

Governing standard
JEDEC JESD79 (DDR SDRAM)(opens JEDEC Solid State Technology Association in a new tab)

Defines the DDR SDRAM device itself — signals, command encoding, mode registers, timing parameters and the initialisation sequence — one document per generation. Memory-controller microarchitecture, address-mapping policy, PHY training algorithms and board-level design are not specified by it.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the DDR curriculum.