Skip to content
VLSI Mentor

DDR · Module 25

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.

Chapter 16.3 §4 is titled “DDR4 to DDR5 — What Carries and What I Could Not Close.” It reported DDR5's bank organisation as corroborated but not retrieved in primary form, recorded that “Micron's asset and distributor servers refused every automated retrieval I attempted,” and noted this was “the seventh DDR5 question this curriculum has been unable to close from accessible primary material.”

This chapter closes it. The organisation below is read from a named DDR5 die datasheet's own addressing table, which also supplies something 16.3 never had: the page size.

And the page size produces a result worth the retrieval on its own. A x16 DDR5 device has half the banks of a x8 and twice the page. It is not a wider x8 — it is a different organisation, and §6 works through what that costs.

1. The Retrieval That Closed

DEVICE-VERIFIED, from the Micron 16Gb DDR5 SDRAM Die Revision D addendum covering part numbers MT60B4G4, MT60B2G8 and MT60B1G16 — document 16gb_ddr5_sdram_dierevD.pdf, Rev. F, obtained from a distributor mirror. The document states it is JESD79-5 compliant.

Its feature list gives the organisation in two lines:

  • 32 internal banks (x4, x8): 8 groups of 4 banks each
  • 16 internal banks (x16): 4 groups of 4 banks each

And its addressing table gives the rest. DEVICE-VERIFIED, Table 1 of that document:

4Gb x42Gb x81Gb x16
Groups / banks per group / banks8 / 4 / 328 / 4 / 324 / 4 / 16
Bank group addressBG0BG2BG0BG2BG0BG1
Bank address within groupBA0BA1BA0BA1BA0BA1
Row addressR0R15R0R15R0R15
Column addressC0C10C0C9C0C9
Page size1 KB1 KB2 KB

The counts match what 16.3 §4 reported from search and what 5.3 §2 wrote independently. Three routes, same numbers — and now one of them is the document itself.

2. Reading the Addressing Fields

The table's field widths are not decoration — every other number in the chapter derives from them, so it is worth confirming they are consistent.

DERIVED, recomputed:

WidthBG bitsBA bitsTotal bank bitsBanks addressed
x43 (BG0BG2)252⁵ = 32
x832532
x162 (BG0BG1)242⁴ = 16

The address field width and the stated bank count agree exactly at every width. That is a small check and it is the kind worth doing: a datasheet's feature list and its addressing table are written by different processes, and when they agree the organisation is confirmed twice.

Now the page size, which is the figure 16.3 did not have. DERIVED from the column field and the device width:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   page size  =  2^(column address bits)  x  device width

   x4  :  2^11 x  4 bits  =  8192 bits  =  1 KB     published 1 KB
   x8  :  2^10 x  8 bits  =  8192 bits  =  1 KB     published 1 KB
   x16 :  2^10 x 16 bits  = 16384 bits  =  2 KB     published 2 KB

All three reproduce the published page size exactly. Note where the x4 device differs: it has eleven column bits where the others have ten, which is exactly what keeps its page at 1 KB despite being the narrowest part.

3. The Whole Die, Checked Against Itself

One more consistency check, and this one closes the organisation completely. If the row count, the page size and the bank count are all right, they must multiply to the die's density.

DERIVED, recomputed for all three widths:

WidthRows× Page= Per bank× Banks= Total
x465,5361 KB512 Mb3216 Gb
x865,5361 KB512 Mb3216 Gb
x1665,5362 KB1 Gb1616 Gb

Every width arrives at exactly 16 Gb. Three independent field widths, a stated bank count and a stated page size, and the product is the density on the label in all three cases.

Two things that follow immediately, and the second is this chapter's central observation.

The row count is identical at every width. R0R15 — 65,536 rows per bank, regardless of organisation. So the width does not change how many rows a bank has; it changes how wide each row is and how many banks there are.

And the x16 device trades banks for page. It has half the banks and twice the page of the x8, and the product is preserved. That is not a smaller version of the same architecture — it is the same silicon partitioned differently, and §6 is about which workloads prefer which partitioning.

4. What Doubling the Groups Buys — the Structural Half

Chapter 5.3 §1 established the mechanism: a bank group is the scope at which the column data path is shared, so two accesses to the same group contend for a resource that two accesses to different groups do not. Chapter 16.3 §3 measured that the resulting penalty varies with class, width and grade, and found one configuration where it vanishes entirely.

What can be concluded without any DDR5 timing value is the change in how often the expensive relationship arises.

DERIVED. Pick a bank, then pick a different bank at random. The chance the second shares a group with the first is (banks per group − 1) / (total banks − 1):

OrganisationSame-group chance
DDR4 x8 — 4 groups × 4 banks3/15 = 20.000%
DDR5 x8 — 8 groups × 4 banks3/31 = 9.677%
DDR5 x16 — 4 groups × 4 banks3/15 = 20.000%

At x8, DDR5 makes the cheap relationship roughly twice as likely — a factor of 2.067 fewer same-group pairs, recomputed. Chapter 16.3 §4 stated this result in passing (“roughly 3 times in 15” against “3 in 31”) and this chapter confirms it against the verified counts.

And at x16, DDR5's group statistics are identical to DDR4's x8. Same four groups, same four banks per group, same 20%. A controller that tuned its mapping around DDR5's improved group statistics gets nothing from an x16 part.

5. Concurrency Is Not the Only Thing That Doubled

Thirty-two banks against sixteen is the headline, and taken alone it overstates the change. The banks are not just more numerous — they are smaller.

DERIVED, from §3's table:

DDR4 x8, 16 GbDDR5 x8, 16 Gb
Banks1632
Per-bank capacity1 Gb512 Mb
Page size1 KB1 KB
Rows per bank131,07265,536

DDR4's row count here is DERIVED from 16.3 §4's verified 16-bank organisation at the same density and page size, not read from a DDR5 document.

So the same silicon is cut into twice as many pieces, each half the size. Two consequences, pointing in opposite directions.

More banks means more independent row state, which is Module 17's subject: more banks can hold open rows simultaneously, so a scattered access stream finds more of its requests landing in an already-open row.

And fewer rows per bank means a given bank covers less of the address space. Each DDR5 bank spans half the addresses a DDR4 bank of the same density does, so under a flat mapping a sequential walk leaves a bank twice as often.

But the magnitudes make that far less significant than it sounds, and it is worth putting numbers on rather than leaving as a ratio. DERIVED from §3's figures:

Boundaryx8 spanx16 span
Page1 KiB2 KiB
Bank64 MiB128 MiB
Bank group256 MiB512 MiB

All six recomputed, and each width's four groups or eight multiply back to 2 GiB — the 16 Gb of §3.

Now apply them. A 1 MiB sequential read on the x8 organisation, under a flat mapping:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   page crossings        1,024
   bank crossings            0
   bank-group crossings      0

A sequential stream of any realistic size never leaves its bank at all, because the bank spans 64 MiB. So “DDR5 crosses bank boundaries twice as often” is arithmetically true and practically empty — it is twice as often as almost never.

The real conclusion is the opposite of what the ratio suggests. Bank and bank-group relationships are scattered-access concerns, not sequential ones. §4's same-group probability and §5's bank count matter when consecutive requests come from unrelated places; for a contiguous walk, the only boundary being crossed is the page, a thousand times per megabyte, and the bank organisation is irrelevant to it.

So which effect dominates depends on the access pattern, and for anything contiguous the answer is neither. §11's monitor measures the group relationship on a real stream rather than assuming one.

6. Why x16 Is a Different Architecture

§3 established that the x16 part has half the banks and twice the page. It is worth spelling out who wants which, because the choice between an x8-based and an x16-based module is usually made for pin-count reasons and has consequences that are not about pins at all.

x8 (8 groups, 32 banks, 1 KB page)x16 (4 groups, 16 banks, 2 KB page)
Concurrencyhigher — 32 independent row stateslower — 16
Same-group chance9.677%20.000%
Row-hit opportunity per open rowsmaller pagelarger page — 2 KB of contiguous data
Devices per 32-bit sub-channel42

The last row is 25.2's subject and is included because it is the reason anyone chooses x16: fewer devices per sub-channel means fewer loads, fewer packages and a smaller module.

The architectural consequence is that x16 favours locality and x8 favours concurrency. A stream with strong spatial locality gets more out of a 2 KB page — Chapter 23.3 quantifies at length what a high row-hit rate is worth. A stream of scattered small requests from many requesters gets more out of 32 banks.

And a controller cannot compensate for the wrong choice. An address mapping can spread accesses across whatever banks exist, but it cannot manufacture banks that are not there. Chapter 18.1 owns making the most of a given organisation; the organisation itself is a purchasing decision.

7. The Two Organisations Hold Exactly the Same Amount Open

§6 said x16 favours locality and x8 favours concurrency, which is true and vague. Multiplying two verified numbers makes it precise, and the product is not what it looks like it should be.

How much of the address space can a device hold open at once? That is banks times page size, and both are device-verified. DERIVED, recomputed:

OrganisationBanks× Page= Openable at once
DDR4 x8, 16 Gb161 KB16,384 B
DDR5 x8, 16 Gb321 KB32,768 B
DDR5 x16, 16 Gb162 KB32,768 B

The two DDR5 organisations are identical on this measure — exactly 32,768 bytes, both of them. And both double DDR4's at the same density.

So the x8-versus-x16 choice is not about how much data can be open. It is about how that same fixed amount is divided:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   x8  :  32 pieces of 1 KB
   x16 :  16 pieces of 2 KB
                                both total 32,768 B

That reframes §6's table into something a purchaser can actually evaluate. Take a workload with K concurrent localities, each with a working set of W bytes. An organisation serves it without conflict when it has enough banks for K and a page large enough for W. DERIVED, with ILLUSTRATIVE K and W:

K streamsW eachx8 (32 × 1 KB)x16 (16 × 2 KB)Verdict
8512 Bfitsfitseither
81536 Bno — page too smallfitsx16 only
24512 Bfitsno — too few banksx8 only
161024 Bfitsfitseither
241536 Bnononeither
40256 Bno — too few banksnoneither

The two boundaries are clean. x8 wins once K exceeds 16, because x16 does not have the banks. x16 wins once W exceeds 1 KB, because x8 does not have the page. Between those, either works.

8. The Three Quantities, at This Level

DDR5's changes in this chapter improve one thing and cost another every time, and the pattern is worth naming before the RTL.

ChangeConcurrencyGranularityIntegrity
8 groups instead of 4 (x4/x8)better — cheaper pairs more oftenunchangedunchanged
32 banks instead of 16better — more open rowsworse — bank crossings twice as oftenunchanged
x16's 2 KB pageworse — 16 banksbetter for locality, worse for small accessunchanged

Nothing in this chapter touches integrity — that column is 25.4's entirely, and leaving it visibly empty is more honest than implying the organisation helps.

And the middle column is the one usually left out. “More banks” is reported as a pure win, and §5 established it is not: the banks are smaller, so the boundaries are closer together. A chapter that reported only the first column would be selling.

9. The Organisation as a Structure

How a DDR5 die's organisation derives from its addressing fields, in four rows. The top row holds the four given quantities: the die density of sixteen gigabits, the device width of four, eight or sixteen bits, the column address field width, and the row address field width of sixteen bits. The second row holds the four derived organisational quantities: the number of bank groups which is eight at x4 and x8 but four at x16, the four banks per group which is constant at every width, the page size which follows from column bits times device width, and the per-bank capacity which follows from rows times page size. The third row holds the three consequences: the total bank count, the resulting concurrency, and the access granularity. The bottom row is the trade between concurrency and granularity that every row above feeds. Edges run from each given quantity into the derived quantity it determines, from those into the consequences, and from the consequences into the trade.Density 16 Gbthe label on the partWidth x4/x8/x16the purchasing decision —§6Column bitsC0-C10 or C0-C9Row bitsR0-R15, same at everywidthBank groups8 at x4/x8, 4 at x16 — §14 banks per groupconstant at every widthPage size2^colbits x width — §2Per-bank capacityrows x page — §3Total banks32 or 16; checked to 16GbConcurrencyindependent row state —§5Granularitybank crossings, page span— §5The tradenever one without theother — §812

Two things the structure makes visible that the tables do not.

Only one given quantity drives both branches. Width feeds the group count and the page size, which is why it is the parameter that decides the architecture rather than merely its size. Density feeds only the total check; the row field is constant. So the entire x8-versus-x16 difference in §6 traces to one arrow in this diagram forking.

And Page size feeds both a consequence and a capacity. It determines granularity directly and per-bank capacity through the row count — which is why a larger page both helps locality and reduces the bank count at fixed density. The two effects §6 describes are not independent choices a designer makes; they are one structural consequence seen from two sides.

10. The Organisation Invariant

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// ddr5_bank_organisation -- holds a DDR5 die's organisation and proves
// at elaboration that the fields, the page size, the bank count and
// the density all agree.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHY ELABORATION AND NOT RUNTIME: 4.6 §5 established the principle --
// an architectural rule belongs where a violation cannot build. A
// runtime check would let a mis-parameterised design elaborate and
// then mis-address silently, which is the failure this prevents.
//
// WHAT IT DOES NOT MODEL:
//   - the array, any timing parameter, or the shared column data path
//     (5.3 owns that mechanism; 14.6 and 14.7 own its timing)
//   - any access. It describes a device, it does not serve requests.
//   - DDR5 timing values, which 16.3 §4 could not retrieve and this
//     chapter did not either. NOTHING here depends on one.
//
// THE FIGURES IT DEFAULTS TO ARE DEVICE-VERIFIED for a named 16Gb
// DDR5 die at x8 (§1). Other widths are legal parameterisations and
// the checks below are what keep them honest.
// ---------------------------------------------------------------------
module ddr5_bank_organisation #(
  parameter int DEVICE_WIDTH = 8,        // 4, 8 or 16
  parameter int BANK_GROUPS  = 8,        // 8 at x4/x8, 4 at x16
  parameter int BANKS_PER_BG = 4,        // constant at every width
  parameter int ROW_BITS     = 16,       // R0-R15
  parameter int COL_BITS     = 10,       // C0-C9 at x8/x16, C0-C10 at x4
  parameter int DENSITY_GB   = 16,       // gigabits on the label

  // ── Derived, not supplied. A caller that could override these
  //    could break the agreement the module exists to enforce.
  parameter int BANKS        = BANK_GROUPS * BANKS_PER_BG,
  parameter int PAGE_BITS    = (2 ** COL_BITS) * DEVICE_WIDTH,
  // Field widths. BG indexes into BANK_GROUPS, so clog2(BANK_GROUPS).
  parameter int BG_BITS      = (BANK_GROUPS  <= 1) ? 1 : $clog2(BANK_GROUPS),
  parameter int BA_BITS      = (BANKS_PER_BG <= 1) ? 1 : $clog2(BANKS_PER_BG),
  parameter int BANK_BITS    = BG_BITS + BA_BITS,
  // A COUNT of banks up to BANKS needs clog2(BANKS+1), not clog2(BANKS).
  parameter int BANKCNT_W    = $clog2(BANKS + 1)
) (
  input  logic                 clk,
  input  logic                 rst_n,

  // ── Query interface. A bank address in, its decomposition out.
  input  logic                 q_valid,
  input  logic [BANK_BITS-1:0] q_bank,

  output logic [BG_BITS-1:0]   q_group,
  output logic [BA_BITS-1:0]   q_bank_in_group,
  output logic                 q_in_range,

  // ── The organisation, published so a consumer need not recompute it.
  output logic [BANKCNT_W-1:0] n_banks,
  output logic [BG_BITS:0]     n_groups,
  output logic [31:0]          page_bytes,
  output logic [31:0]          rows_per_bank,
  output logic [63:0]          per_bank_bits,

  // §5: how much of the address space one bank spans. The quantity
  // that makes "more banks" cost something.
  output logic [63:0]          bank_span_bits,

  output logic                 err_bank_out_of_range,
  output logic                 any_error
);

  // ── The organisation invariant, as elaboration checks. Each one is
  //    a check §2 or §3 performed by hand on the datasheet's table.
  initial begin
    if (!(DEVICE_WIDTH inside {4, 8, 16}))
      $fatal(1, "ddr5_bank_organisation: DEVICE_WIDTH must be 4, 8 or 16 (got %0d)",
             DEVICE_WIDTH);
    if (BANKS_PER_BG != 4)
      // DEVICE-VERIFIED constant at every width (§1). A different
      // value is not a DDR5 organisation.
      $fatal(1, "ddr5_bank_organisation: BANKS_PER_BG must be 4 (got %0d)",
             BANKS_PER_BG);
    if (!(BANK_GROUPS inside {4, 8}))
      $fatal(1, "ddr5_bank_organisation: BANK_GROUPS must be 4 or 8 (got %0d)",
             BANK_GROUPS);
    // §1: the x16 organisation has FOUR groups and the narrower
    // widths have eight. A x16 part with eight groups is not a
    // device this addendum describes.
    if ((DEVICE_WIDTH == 16) && (BANK_GROUPS != 4))
      $fatal(1, "ddr5_bank_organisation: x16 has 4 bank groups, not %0d",
             BANK_GROUPS);
    if ((DEVICE_WIDTH != 16) && (BANK_GROUPS != 8))
      $fatal(1, "ddr5_bank_organisation: x4/x8 have 8 bank groups, not %0d",
             BANK_GROUPS);
    // Powers of two, with the >= 2 lower bound the shift arithmetic
    // needs. A power-of-two test ALONE accepts 0 and 1, both of which
    // would make a clog2 zero and the field width wrong.
    if (BANK_GROUPS < 2 || (BANK_GROUPS & (BANK_GROUPS - 1)) != 0)
      $fatal(1, "ddr5_bank_organisation: BANK_GROUPS must be a power of two >= 2");
    if (BANKS < 2 || (BANKS & (BANKS - 1)) != 0)
      $fatal(1, "ddr5_bank_organisation: BANKS must be a power of two >= 2");
    if (BANK_BITS != $clog2(BANKS))
      // The field widths must address exactly the stated bank count --
      // §2's check that the feature list and the addressing table agree.
      $fatal(1, "ddr5_bank_organisation: BG+BA bits (%0d) do not address %0d banks",
             BANK_BITS, BANKS);
    if (ROW_BITS < 1 || COL_BITS < 1)
      $fatal(1, "ddr5_bank_organisation: field widths must be positive");
    // §3's whole-die check: rows x page x banks must equal the density.
    if (((2 ** ROW_BITS) * PAGE_BITS * BANKS) != (DENSITY_GB * (1024 ** 3)))
      $fatal(1,
        "ddr5_bank_organisation: rows(%0d) x page(%0d bits) x banks(%0d) != %0d Gb",
        2 ** ROW_BITS, PAGE_BITS, BANKS, DENSITY_GB);
    if ((PAGE_BITS % 8) != 0)
      $fatal(1, "ddr5_bank_organisation: page must be a whole number of bytes");
  end

  logic e_range;

  // Declared above the continuous assigns that read it.
  logic in_rng;
  assign in_rng = q_valid && ({{(32-BANK_BITS){1'b0}}, q_bank} < 32'(BANKS));

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) e_range <= 1'b0;
    else        e_range <= q_valid && !in_rng;
  end

  // Field split follows the datasheet's own decomposition: the group
  // field is the high part and the bank-within-group the low part.
  // 8.4 owns extracting these from a full address; this is the
  // device-side decomposition of an already-formed bank number.
  assign q_group         = q_bank[BANK_BITS-1 -: BG_BITS];
  assign q_bank_in_group = q_bank[BA_BITS-1 : 0];
  assign q_in_range      = in_rng;

  assign n_banks       = BANKCNT_W'(BANKS);
  assign n_groups      = (BG_BITS+1)'(BANK_GROUPS);
  assign page_bytes    = 32'(PAGE_BITS / 8);
  assign rows_per_bank = 32'(2 ** ROW_BITS);
  assign per_bank_bits = 64'(2 ** ROW_BITS) * 64'(PAGE_BITS);
  // §5: one bank spans this much of the address space, so a
  // sequential walk crosses a bank boundary every this-many bits.
  assign bank_span_bits = 64'(2 ** ROW_BITS) * 64'(PAGE_BITS);

  assign err_bank_out_of_range = e_range;
  assign any_error = e_range;
endmodule

Every elaboration check here is a check §2 or §3 did by hand, and that is the point of the block. The datasheet's feature list, its addressing table and its density label are three independent statements about one device; the module refuses to build unless they agree.

The width-specific group check is the sharpest one. §1 verified that x16 has four groups and the narrower widths have eight. A parameterisation claiming a x16 part with eight groups describes no device in the addendum — and without that check it would elaborate happily and produce a four-bit bank field for a part that needs five.

11. The Same-Group Opportunity Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// same_group_opportunity_monitor -- measures the observed same-group
// fraction of a request stream against the structural baseline §4
// derives from the organisation alone.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHY IT COMPARES AGAINST A BASELINE: §4 computes that a random pair
// of distinct banks shares a group 3/31 of the time at x8 and 3/15 at
// x16. That is what a RANDOM stream would produce. A real stream that
// does better has a mapping working for it; one that does worse has a
// mapping working against it -- and neither is visible from the
// same-group count alone.
//
// WHAT IT DOES NOT MODEL:
//   - the shared column path (5.3), or any timing (14.6, 14.7, 16.3)
//   - ANY PENALTY. §4's callout: DDR5's same-group cost is not
//     verified anywhere in this chapter, so this block reports
//     FREQUENCY and never converts it to cycles.
//
// NO DIVISION: the observed fraction and the baseline are compared by
// CROSS-MULTIPLICATION, and both are published as numerator and
// denominator pairs so the caller states its own denominator.
// ---------------------------------------------------------------------
module same_group_opportunity_monitor #(
  parameter int BANK_GROUPS  = 8,
  parameter int BANKS_PER_BG = 4,
  parameter int BANKS        = BANK_GROUPS * BANKS_PER_BG,
  parameter int BG_BITS      = (BANK_GROUPS <= 1) ? 1 : $clog2(BANK_GROUPS),
  parameter int BANK_BITS    = $clog2(BANKS),
  parameter int CNT_W        = 32
) (
  input  logic                 clk,
  input  logic                 rst_n,

  input  logic                 req_valid,
  input  logic [BANK_BITS-1:0] req_bank,

  // ── Observed, as a pair. Consecutive DISTINCT banks only: a repeat
  //    of the same bank is neither a same-group nor a different-group
  //    pair, and counting it as either would bias the fraction.
  output logic [CNT_W-1:0]     n_pairs,
  output logic [CNT_W-1:0]     n_same_group,
  output logic [CNT_W-1:0]     n_diff_group,
  output logic [CNT_W-1:0]     n_same_bank,

  // ── The structural baseline of §4: (BANKS_PER_BG-1)/(BANKS-1).
  output logic [CNT_W-1:0]     base_num,
  output logic [CNT_W-1:0]     base_den,

  // Verdict by cross-multiplication, never by dividing. A mapping is
  // "better than random" when observed/pairs < base_num/base_den.
  output logic                 better_than_random,
  output logic                 worse_than_random,
  output logic                 at_baseline,

  output logic                 err_bank_out_of_range,
  output logic                 any_saturated
);

  initial begin
    if (BANKS_PER_BG < 2)
      $fatal(1, "same_group_opportunity_monitor: BANKS_PER_BG must be >= 2");
    if (BANKS <= BANKS_PER_BG)
      // With one group every pair is same-group and the baseline is 1,
      // which makes the comparison meaningless rather than extreme.
      $fatal(1, "same_group_opportunity_monitor: need more than one bank group");
    if (BANK_BITS != $clog2(BANKS))
      $fatal(1, "same_group_opportunity_monitor: BANK_BITS must address BANKS");
  end

  logic [CNT_W-1:0]     c_pair, c_same, c_diff, c_reps;
  logic                 have_prev, sat, e_rng;
  logic [BANK_BITS-1:0] prev_bank;

  // Declared above the continuous assigns that read them.
  logic [BG_BITS-1:0] this_grp, prev_grp;
  assign this_grp = req_bank[BANK_BITS-1 -: BG_BITS];
  assign prev_grp = prev_bank[BANK_BITS-1 -: BG_BITS];

  logic in_rng;
  assign in_rng = ({{(32-BANK_BITS){1'b0}}, req_bank} < 32'(BANKS));

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_pair <= '0; c_same <= '0; c_diff <= '0; c_reps <= '0;
      have_prev <= 1'b0; prev_bank <= '0; sat <= 1'b0; e_rng <= 1'b0;
    end else begin
      e_rng <= 1'b0;
      if (req_valid) begin
        if (!in_rng) begin
          // Out of range: reported, and NOT folded into the counts. A
          // bad bank number classified as either kind would corrupt
          // the fraction the block exists to report.
          e_rng <= 1'b1;
        end else begin
          if (have_prev) begin
            if (req_bank == prev_bank) begin
              // Same bank is neither pair kind -- counted separately.
              if (c_reps != {CNT_W{1'b1}}) c_reps <= c_reps + CNT_W'(1);
            end else begin
              if (c_pair == {CNT_W{1'b1}}) sat <= 1'b1;
              else begin
                c_pair <= c_pair + CNT_W'(1);
                if (this_grp == prev_grp) c_same <= c_same + CNT_W'(1);
                else                      c_diff <= c_diff + CNT_W'(1);
              end
            end
          end
          prev_bank <= req_bank;
          have_prev <= 1'b1;
        end
      end
    end
  end

  assign n_pairs      = c_pair;
  assign n_same_group = c_same;
  assign n_diff_group = c_diff;
  assign n_same_bank  = c_reps;
  assign base_num     = CNT_W'(BANKS_PER_BG - 1);
  assign base_den     = CNT_W'(BANKS - 1);

  // observed(c_same/c_pair) vs baseline(base_num/base_den), compared
  // as c_same*base_den vs base_num*c_pair. No divide.
  assign better_than_random =
    (c_pair != '0) && ((c_same * CNT_W'(BANKS - 1)) <  (CNT_W'(BANKS_PER_BG - 1) * c_pair));
  assign worse_than_random =
    (c_pair != '0) && ((c_same * CNT_W'(BANKS - 1)) >  (CNT_W'(BANKS_PER_BG - 1) * c_pair));
  assign at_baseline =
    (c_pair != '0) && ((c_same * CNT_W'(BANKS - 1)) == (CNT_W'(BANKS_PER_BG - 1) * c_pair));

  assign err_bank_out_of_range = e_rng;
  assign any_saturated = sat;
endmodule

A repeated bank is counted separately and excluded from the pairs. §4's baseline is the chance that a different bank shares a group, so a stream that hits the same bank repeatedly would drag the observed fraction toward zero and look like an excellent mapping. Separating the three outcomes keeps the comparison to the question the baseline answers.

And the comparison is a cross-multiplication. Dividing two counters to get a percentage would introduce a divide and a rounding decision, and the verdict only needs an ordering. The two published pairs let a caller compute whatever ratio it wants with its own denominator.

12. The Page-Granularity Accountant

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// page_granularity_accountant -- counts page and bank crossings for a
// stream, which is what §5 says "more banks" costs.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHAT IT ADDS: §5 argues that DDR5's smaller banks put boundaries
// twice as close together at x8, so a sequential walk crosses them
// twice as often. That is a claim about a STREAM, not about a device,
// so it needs measuring rather than deriving.
//
// WHAT IT DOES NOT MODEL: energy, bandwidth, any timing parameter, or
// the row buffer's contents. 23.3 owns row-buffer locality and what a
// hit is worth; this counts boundary crossings and nothing more.
// ---------------------------------------------------------------------
module page_granularity_accountant #(
  parameter int ADDR_W    = 36,
  parameter int PAGE_BYTES = 1024,           // DEVICE-VERIFIED at x8 (§1)
  parameter int BANKS      = 32,
  parameter int ROWS       = 65536,
  parameter int PAGE_SH    = $clog2(PAGE_BYTES),
  parameter int CNT_W      = 32
) (
  input  logic              clk,
  input  logic              rst_n,

  input  logic              acc_valid,
  input  logic [ADDR_W-1:0] acc_addr,
  input  logic [15:0]       acc_bytes,

  // ── Crossings.
  output logic [CNT_W-1:0]  n_accesses,
  output logic [CNT_W-1:0]  n_page_crossings,
  output logic [CNT_W-1:0]  n_within_page,
  // An access that itself spans a page boundary -- distinct from two
  // consecutive accesses landing in different pages.
  output logic [CNT_W-1:0]  n_self_spanning,

  // ── Span facts, published so a caller need not recompute them.
  output logic [31:0]       page_span_bytes,
  output logic [63:0]       bank_span_bytes,

  output logic              err_zero_length,
  output logic              err_access_exceeds_page,
  output logic              any_saturated
);

  initial begin
    if (PAGE_BYTES < 2 || (PAGE_BYTES & (PAGE_BYTES - 1)) != 0)
      // The lower bound matters: a power-of-two test alone accepts 0
      // and 1, either of which makes PAGE_SH zero and every shift a
      // no-op.
      $fatal(1, "page_granularity_accountant: PAGE_BYTES must be a power of two >= 2");
    if (BANKS < 1 || ROWS < 1)
      $fatal(1, "page_granularity_accountant: BANKS and ROWS must be positive");
    if (PAGE_SH >= ADDR_W)
      $fatal(1, "page_granularity_accountant: page field exceeds ADDR_W");
  end

  logic [CNT_W-1:0]   c_acc, c_cross, c_within, c_span;
  logic               have_prev, sat, e_zero, e_big;
  logic [ADDR_W-1:0]  prev_page;

  // Declared above the continuous assigns that read them.
  logic [ADDR_W-1:0] this_page, end_page;
  assign this_page = acc_addr >> PAGE_SH;
  // The page the LAST byte of this access falls in. Equal to
  // this_page unless the access itself straddles a boundary.
  assign end_page  = (acc_bytes == '0) ? this_page
                   : ((acc_addr + ADDR_W'(acc_bytes) - ADDR_W'(1)) >> PAGE_SH);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_acc <= '0; c_cross <= '0; c_within <= '0; c_span <= '0;
      have_prev <= 1'b0; prev_page <= '0; sat <= 1'b0;
      e_zero <= 1'b0; e_big <= 1'b0;
    end else begin
      e_zero <= 1'b0; e_big <= 1'b0;
      if (acc_valid) begin
        if (acc_bytes == '0) begin
          e_zero <= 1'b1;
        end else begin
          if (c_acc == {CNT_W{1'b1}}) sat <= 1'b1;
          else begin
            c_acc <= c_acc + CNT_W'(1);
            if (have_prev) begin
              if (this_page != prev_page) c_cross  <= c_cross  + CNT_W'(1);
              else                        c_within <= c_within + CNT_W'(1);
            end
            if (end_page != this_page) c_span <= c_span + CNT_W'(1);
          end
          if ({{(32-16){1'b0}}, acc_bytes} > 32'(PAGE_BYTES))
            // Larger than a page: legal to request and it guarantees
            // a crossing, so it is reported rather than silently
            // inflating the crossing count.
            e_big <= 1'b1;
          prev_page <= this_page;
          have_prev <= 1'b1;
        end
      end
    end
  end

  assign n_accesses       = c_acc;
  assign n_page_crossings = c_cross;
  assign n_within_page    = c_within;
  assign n_self_spanning  = c_span;
  assign page_span_bytes  = 32'(PAGE_BYTES);
  // §5: one bank spans rows x page bytes of the address space.
  assign bank_span_bytes  = 64'(ROWS) * 64'(PAGE_BYTES);
  assign err_zero_length          = e_zero;
  assign err_access_exceeds_page  = e_big;
  assign any_saturated = sat;
endmodule

n_self_spanning is separate from n_page_crossings and the distinction is real. Two consecutive accesses in different pages is a stream property that an address mapping can improve. A single access that straddles a page boundary is an alignment property that no mapping fixes — it needs two row activations no matter how the address space is arranged.

13. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind unit note: P1-P5 reference ddr5_bank_organisation's internals,
// P6-P10 same_group_opportunity_monitor's, P11-P14
// page_granularity_accountant's. Each is written as though bound into
// its own module.

// P1 -- the decomposition is exact: recombining the group and the
// bank-within-group reproduces the queried bank.
property p_decomposition_is_lossless;
  @(posedge clk) disable iff (!rst_n)
    q_in_range |-> ({q_group, q_bank_in_group} == q_bank);
endproperty
assert property (p_decomposition_is_lossless);

// P2 -- the published bank count always equals the parameterised
// organisation. Guards the COUNT-vs-INDEX trap: a too-narrow
// BANKCNT_W would silently truncate 32 to 0.
property p_bank_count_matches_organisation;
  @(posedge clk) disable iff (!rst_n)
    (n_banks == BANKCNT_W'(BANK_GROUPS * BANKS_PER_BG));
endproperty
assert property (p_bank_count_matches_organisation);

// P3 -- §3's whole-die identity, as a runtime invariant as well as an
// elaboration check: rows x page x banks is the density.
property p_die_identity_holds;
  @(posedge clk) disable iff (!rst_n)
    ((per_bank_bits * 64'(BANKS)) == (64'(DENSITY_GB) * 64'(1024*1024*1024)));
endproperty
assert property (p_die_identity_holds);

// P4 -- FORBIDDEN. A bank number outside the organisation is never
// reported in range, and is always flagged.
property p_out_of_range_never_accepted;
  @(posedge clk) disable iff (!rst_n)
    (q_valid && ({{(32-BANK_BITS){1'b0}}, q_bank} >= 32'(BANKS)))
      |-> (!q_in_range ##1 err_bank_out_of_range);
endproperty
assert property (p_out_of_range_never_accepted);

// P5 -- §2's page derivation, as an invariant: page bytes is
// 2^colbits x width / 8.
property p_page_size_derivation;
  @(posedge clk) disable iff (!rst_n)
    (page_bytes == 32'(((2 ** COL_BITS) * DEVICE_WIDTH) / 8));
endproperty
assert property (p_page_size_derivation);

// P6 -- the three pair outcomes partition every classified request
// after the first. Nothing is double-counted or lost.
property p_pair_outcomes_partition;
  @(posedge clk) disable iff (!rst_n)
    ((n_same_group + n_diff_group) == n_pairs);
endproperty
assert property (p_pair_outcomes_partition);

// P7 -- FORBIDDEN. A repeated bank never counts as a pair. §11: it is
// neither same-group nor different-group, and counting it as either
// would bias the fraction toward "better than random".
property p_repeat_is_never_a_pair;
  @(posedge clk) disable iff (!rst_n)
    (req_valid && (req_bank == $past(req_bank, 1)) && $past(req_valid, 1))
      |=> (n_pairs == $past(n_pairs, 1));
endproperty
assert property (p_repeat_is_never_a_pair);

// P8 -- FORBIDDEN. An out-of-range bank is never folded into any
// pair count. A bad bank number classified either way corrupts the
// measurement the block exists to produce.
property p_bad_bank_never_counted;
  @(posedge clk) disable iff (!rst_n)
    err_bank_out_of_range |-> ((n_pairs      == $past(n_pairs, 1)) &&
                               (n_same_group == $past(n_same_group, 1)));
endproperty
assert property (p_bad_bank_never_counted);

// P9 -- the baseline is the structural fraction of §4 and does not
// depend on the stream at all.
property p_baseline_is_structural;
  @(posedge clk) disable iff (!rst_n)
    ((base_num == CNT_W'(BANKS_PER_BG - 1)) && (base_den == CNT_W'(BANKS - 1)));
endproperty
assert property (p_baseline_is_structural);

// P10 -- the three verdicts are mutually exclusive, and none holds
// before any pair has been seen. A verdict on zero samples would be
// an opinion rather than a measurement.
property p_verdict_exclusive_and_grounded;
  @(posedge clk) disable iff (!rst_n)
    ((n_pairs == '0) ? (!better_than_random && !worse_than_random && !at_baseline)
                     : $onehot({better_than_random, worse_than_random, at_baseline}));
endproperty
assert property (p_verdict_exclusive_and_grounded);

// P11 -- crossings and within-page accesses partition every access
// after the first.
property p_crossings_partition;
  @(posedge clk) disable iff (!rst_n)
    ((n_page_crossings + n_within_page) <= n_accesses);
endproperty
assert property (p_crossings_partition);

// P12 -- an access larger than a page is always flagged, and always
// self-spanning. It cannot fit, so it must straddle.
property p_oversize_access_spans;
  @(posedge clk) disable iff (!rst_n)
    (acc_valid && (acc_bytes != '0) &&
     ({{(32-16){1'b0}}, acc_bytes} > 32'(PAGE_BYTES)))
      |=> err_access_exceeds_page;
endproperty
assert property (p_oversize_access_spans);

// P13 -- FORBIDDEN. A zero-length access is never counted. It is not
// silently treated as one byte, which would invent a crossing.
property p_zero_length_not_counted;
  @(posedge clk) disable iff (!rst_n)
    (acc_valid && (acc_bytes == '0))
      |=> ((n_accesses == $past(n_accesses, 1)) && err_zero_length);
endproperty
assert property (p_zero_length_not_counted);

// P14 -- §5's span arithmetic: a bank covers rows x page bytes.
property p_bank_span_is_rows_times_page;
  @(posedge clk) disable iff (!rst_n)
    (bank_span_bytes == (64'(ROWS) * 64'(PAGE_BYTES)));
endproperty
assert property (p_bank_span_is_rows_times_page);

// ── Cover.
// Every bank group reached, and every bank within a group.
cover property (@(posedge clk) disable iff (!rst_n)
                  q_in_range && (q_group == '0));
cover property (@(posedge clk) disable iff (!rst_n)
                  q_in_range && (q_group == BG_BITS'(BANK_GROUPS - 1)));
cover property (@(posedge clk) disable iff (!rst_n)
                  q_in_range && (q_bank_in_group == BA_BITS'(BANKS_PER_BG - 1)));
// Each forbidden condition ATTEMPTED. A property forbidding something
// is vacuous until the testbench tries it.
cover property (@(posedge clk) disable iff (!rst_n) err_bank_out_of_range);
cover property (@(posedge clk) disable iff (!rst_n) err_zero_length);
cover property (@(posedge clk) disable iff (!rst_n) err_access_exceeds_page);
// A repeated bank actually driven -- P7 is vacuous without it.
cover property (@(posedge clk) disable iff (!rst_n)
                  req_valid && $past(req_valid,1) && (req_bank == $past(req_bank,1)));
// All three mapping verdicts reached. "At baseline" is the one a
// suite is likeliest to miss.
cover property (@(posedge clk) disable iff (!rst_n) better_than_random);
cover property (@(posedge clk) disable iff (!rst_n) worse_than_random);
cover property (@(posedge clk) disable iff (!rst_n) at_baseline);
// Both crossing kinds, and the distinction of §12.
cover property (@(posedge clk) disable iff (!rst_n)
                  acc_valid && (n_page_crossings != $past(n_page_crossings,1)));
cover property (@(posedge clk) disable iff (!rst_n) n_self_spanning != '0);
// Both verified organisations exercised: 32 banks and 16 banks.
cover property (@(posedge clk) disable iff (!rst_n) n_banks == BANKCNT_W'(32));
cover property (@(posedge clk) disable iff (!rst_n) n_banks == BANKCNT_W'(16));

14. Corner Cases

CaseBehaviourWhy
x16 parameterised with 8 bank groups$fatal at elaboration§1 — x16 has four groups; the bank field would be a bit short
x8 parameterised with 4 bank groups$fatal at elaboration§1 — the narrower widths have eight
BANKS_PER_BG other than 4$fatal at elaborationDEVICE-VERIFIED constant at every width
BANK_GROUPS of 0 or 1$fatal at elaborationA power-of-two test alone accepts both
Fields that do not address the stated bank count$fatal at elaboration§2's check that the two datasheet statements agree
Rows × page × banks ≠ density$fatal at elaboration§3's whole-die identity
Bank number ≥ BANKSReported; never in range, never countedP4, P8
Same bank twice in a rowCounted as a repeat, not a pairP7 — counting it would bias toward “better than random”
No pairs seen yetNo verdict assertedP10 — a verdict on zero samples is an opinion
Observed exactly at the structural fractionat_baselineThe stream is behaving randomly with respect to groups
Zero-length accessReported; not countedP13 — treating it as one byte would invent a crossing
Access larger than a pageReported; counted; always self-spanningP12 — it cannot fit, so it must straddle
Access straddling a page boundaryn_self_spanning, separately from crossings§12 — alignment, which no mapping fixes
PAGE_BYTES of 0 or 1$fatal at elaborationPAGE_SH would be zero and every shift a no-op

Row eight is the one worth dwelling on. A stream that hammers one bank produces zero same-group pairs, and a monitor that counted repeats as different-group pairs would report it as an ideal mapping. It is the opposite — a stream with no bank-level parallelism at all.

15. DV — Two Representations of One Organisation

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Independent organisation reference. Builds the bank list explicitly
// as a table of (group, bank-in-group) pairs rather than by field
// arithmetic, so agreement with the DUT is evidence rather than a
// restatement of the same shift. SIMULATION-ONLY.
class organisation_ref;
  int grp_of[$];          // grp_of[bank] = its group
  int idx_of[$];          // idx_of[bank] = its index within the group
  int groups, per_group;

  function void build(int n_groups, int banks_per_group);
    groups = n_groups; per_group = banks_per_group;
    grp_of.delete(); idx_of.delete();
    for (int g = 0; g < n_groups; g++)
      for (int b = 0; b < banks_per_group; b++) begin
        grp_of.push_back(g);
        idx_of.push_back(b);
      end
  endfunction

  function int n_banks(); return grp_of.size(); endfunction

  // The structural baseline of §4, as a pair -- never as a real.
  function void baseline(output int num, output int den);
    num = per_group - 1;
    den = grp_of.size() - 1;
  endfunction

  // Page size from the fields, computed independently of the DUT.
  function int page_bytes(int col_bits, int width);
    return ((1 << col_bits) * width) / 8;
  endfunction

  // §3's whole-die identity.
  function bit die_consistent(int row_bits, int col_bits, int width,
                              int density_gb);
    longint total;
    total = longint'(1 << row_bits) * longint'((1 << col_bits) * width)
            * longint'(grp_of.size());
    return total == longint'(density_gb) * 1024 * 1024 * 1024;
  endfunction
endclass
CheckWhat it establishes
Every bank's (group, index) matches the reference tableP1 against a non-arithmetic representation
Sweep all 32 banks at x8 and all 16 at x16Both verified organisations, and the two bank-count covers
Reference page_bytes against the DUT at x4, x8 and x16§2's derivation reproduces all three published page sizes
Reference die_consistent at all three widths§3's identity, independently computed
Parameterise x16 with 8 groups; expect no elaboration§14's first row
Parameterise BANK_GROUPS as 0 and 1The guard that a power-of-two test alone would pass
Drive a uniformly random bank stream over many pairsObserved fraction converges on the reference baseline
Drive a stream that alternates between two groupsbetter_than_random, strongly
Drive a stream confined to one groupworse_than_random
Drive a stream repeating one bankZero pairs, no verdict, n_same_bank rising
Drive out-of-range banks between valid onesP8 — counts unchanged across the bad request
Sequential walk of 1 MB at 1 KB and at 2 KB pageCrossing count halves; §5's claim, measured
Unaligned 64 B accesses across a page boundaryn_self_spanning rises while crossings do not

The tenth check is the one that produces a report worth publishing, because it is the failure mode §14's row eight describes and it looks like success:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  THE STREAM THAT LOOKS LIKE A PERFECT MAPPING
    A request stream confined to a single bank, 100,000 accesses.

    n_pairs            0
    n_same_group       0
    n_diff_group       0
    n_same_bank   99,999

    verdict:      none asserted  (P10 holds: no pairs, no opinion)

    assertions:   P6  pair outcomes partition     PASS (0 + 0 == 0)
                  P7  repeat is never a pair      PASS
                  P9  baseline is structural      PASS
                  all 14 properties               PASS

    what a NAIVE monitor would have reported:
                  same-group fraction  0 / 99,999  =  0.000%
                  baseline             3 / 31       =  9.677%
                  verdict:  BETTER THAN RANDOM, by a wide margin

    diagnosis : the naive reading is exactly backwards. A stream with
      zero same-group pairs because it never leaves one bank has NO
      bank-level parallelism at all -- it is the worst possible
      stream, reported as the best.

    what makes it dangerous : the same-group count is the number a
      mapping study naturally optimises, and it is minimised by the
      degenerate stream. The metric and the goal diverge precisely
      where the design is worst.

    the fix : the three outcomes must PARTITION and the repeat count
      must be visible (§11). P10's refusal to render a verdict on
      zero pairs is what stops the degenerate case from being scored
      at all, and n_same_bank is what names it.

16. Debugging

SymptomLikely causeHow to confirm
Design will not elaborate at x16Group count left at 8 — §14The $fatal message names the expected value
Bank field one bit shortx16 organisation with an x8 field widthP2; BANK_BITS against $clog2(BANKS)
Addresses alias onto the wrong bankField widths do not match the bank count§2's check; the elaboration guard catches it first
Page size disagrees with the datasheetColumn bits taken from the wrong widthP5; x4 has eleven column bits, not ten
Whole-die check failsRow, page and bank figures from different parts§3's identity; all three must come from one table
Same-group fraction suspiciously lowThe stream may be hitting one bank — §14 row eightn_same_bank against n_pairs
No verdict ever assertedFewer than one distinct-bank pair seenP10 — this is correct behaviour, not a fault
Mapping tuned for DDR5 gains nothingThe part is x16 — its group statistics equal DDR4 x8§4's table; check the width, not the generation
Concurrency lower than 32 banks suggestsMapping not spreading across groupsworse_than_random; 18.1 owns the fix
More activations than DDR4 at the same workloadSmaller banks, closer boundaries — §5n_page_crossings; bank_span_bytes halved
Crossings high but mapping looks correctAccesses straddling boundaries, not stream driftn_self_spanning — alignment, not mapping

Row eight is the entry this chapter exists to add. “We moved to DDR5 and the bank-group mapping stopped helping” is explained by the device width, not by the generation — and an engineer who does not know that x16 has DDR4's group count will look for the fault in the controller.

17. Misconceptions

“DDR5 doubled the bank groups.” §1. At x4 and x8, yes — eight against four. At x16 it is four, the same as DDR4's x8.

“DDR5 has 32 banks.” §1. At x4 and x8. An x16 part has 16.

“x16 is a wider x8.” §3. It has half the banks and twice the page. Same silicon, different partition.

“DDR5 pages are smaller.” §3's callout. Nothing retrieved shows a page below the 1 KB that DDR4 parts of this density also carry, and the x16 part's page is 2 KB.

“More banks is a pure win.” §5. The banks are also smaller — half the rows each — so a sequential stream crosses bank boundaries twice as often.

“DDR5's bank groups are twice as good.” §4's callout. The expensive relationship arises about half as often at x8. Whether it costs the same as DDR4's is not established anywhere in this chapter.

“The same-group penalty carried over from DDR4.” §4. 16.3 §4 left this open and no DDR5 timing value was retrieved here either. An inherited claim needs a reason.

“A low same-group fraction means a good mapping.” §15. A stream confined to one bank has a fraction of zero and no parallelism whatsoever.

“The row count grew with capacity.” §3. R0R15 at every width — 65,536 rows per bank, identical. What changes is the row's width and the bank count.

“This chapter verified DDR5's timings.” §1's callout. It verified the organisation. The timing questions 16.3 §4 opened are still open.

18. Interview Reasoning

How many banks does a DDR5 device have? It depends on the width. At x4 and x8, eight bank groups of four banks each — 32 banks. At x16, four groups of four — 16 banks.

Derive a DDR5 device's page size from its addressing fields. Page size is 2 to the power of the column-address bits, times the device width. At x8 with ten column bits: 1024 × 8 bits = 8192 bits = 1 KB. At x16 with ten column bits: 1024 × 16 = 2 KB.

Check a die's organisation for self-consistency. Rows times page size times bank count must equal the density. At x8: 65,536 × 1 KB × 32 = 16 Gb. If it does not multiply out, one of the four figures is from a different part.

Why is x16 not simply a wider x8? Half the banks and twice the page. It favours locality where x8 favours concurrency, and the choice is usually made for device count per sub-channel rather than for either.

What does doubling the bank groups actually change? How often two distinct banks share a group — 3 in 15 at four groups, 3 in 31 at eight. The cheap relationship becomes about twice as likely; the cost of the expensive one is a separate question.

Does that mean DDR5 is twice as good here? No. The frequency halves; the penalty's size is a different claim, and it needs its own citation rather than inheritance from DDR4.

What does having more banks cost? The banks are smaller at fixed density — half the rows each — so each bank spans half as much address space and a sequential stream crosses bank boundaries twice as often.

Your same-group fraction is zero. Is your mapping perfect? Possibly the opposite. Check whether the stream is repeating one bank: zero distinct-bank pairs means no bank-level parallelism, which minimises the metric and maximises the problem.

A bank-group-aware mapping that helped on one DDR5 part does nothing on another. Why? Check the device width. An x16 part has four groups — DDR4's count — so the improved group statistics the mapping was tuned for do not exist on it.

19. Exercises

  1. Derive the page size, per-bank capacity and bank count for a hypothetical 32 Gb DDR5 x8 die, stating which field you must change from §1's table and why the others cannot absorb the difference.

  2. §4 gives the same-group chance for a randomly chosen different bank. Derive the chance for two independently chosen banks, including the same-bank case, and explain why §11's monitor uses the first definition.

  3. Using §5's figures, compute how many more activations a 16 MB sequential read issues on DDR5 x8 than on DDR4 x8 at the same density, stating every assumption you must add.

  4. Remove the width-specific group check from §10's block and parameterise an x16 part with eight groups. Which property fires, and which does not? Explain why P3 still passes.

  5. §4's callout says the frequency halves while the penalty is unverified. Construct the condition on the DDR5 penalty under which DDR5 x8 is worse than DDR4 x8 for same-group traffic, and say whether the retrieved material can rule it out.

  6. §11's monitor excludes repeated banks. Construct the stream that maximises better_than_random while having the worst possible bank-level parallelism, and propose the additional output that would expose it.

  7. For an x16 part, compute the same-group chance and compare it against DDR4 x8. Then state what an x16 DDR5 part does offer over a DDR4 x8 part of the same density, using only figures verified in this chapter.

  8. §12 separates stream crossings from self-spanning accesses. Derive the fraction of 64-byte accesses that self-span at 1 KB and at 2 KB pages under uniformly random alignment, and say which page size is better on that measure alone.

20. Where This Goes

DDR5's bank organisation is now verified rather than corroborated. Eight bank groups of four banks at x4 and x8 for 32 banks; four groups of four at x16 for 16; 65,536 rows per bank at every width; and a page of 1 KB, 1 KB and 2 KB respectively — with all three widths multiplying out to exactly 16 Gb.

Four results carry forward. The retrieval closed, which upgrades 16.3 §4's corroboration to a primary citation without changing its numbers. The x16 organisation is not a scaled x8 — half the banks, twice the page, and DDR4's group statistics. More banks costs granularity, because at fixed density the banks are smaller and the boundaries closer. And the frequency of the expensive bank relationship halves at x8 while its cost stays unverified, which is a weaker and more useful claim than the one usually made.

Two things left open, and both were open before this chapter. DDR5's nCCD, nRRD and nFAW values did not retrieve, and the die addendum consulted supersedes parts of a core datasheet that did not retrieve either. Nor did the _S / _L naming question close. Chapter 16.3 §4 counted seven DDR5 questions this curriculum could not close from accessible material; this chapter closed one of them and leaves the timing ones where they were.

What every section here has assumed is that a command simply arrives at the device. Chapter 7.1 §4 is explicit that it does not: its command encoder refuses to elaborate for DDR5, with the comment that “DDR5's CA encoding is not modelled” and Module 25 owns it. Chapter 6.4 §7 adds that in DDR5 the arrangement changed again, “with a differently encoded command/address interface in which RAS# does not appear.”

Chapter 25.2 takes up that debt: a fourteen-bit command and address bus, commands that occupy one or two clock cycles, CS_n that is part of the command code rather than a qualifier beside it, and two sub-channels each carrying its own copy of all of it. Chapter 4.6 §2 argued why that duplication is necessary; the next chapter is what is actually duplicated.

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.