Skip to content

UVM RAL · Chapter 8 · Memories

Large Memories & Debugging

A large memory cannot be tested exhaustively, because writing and reading every location of a multi-megabyte RAM costs far more simulation time than any regression can afford. So memory verification is a deliberate sampling strategy: choose a small set of accesses that exercises what actually breaks, and stay honest about what the sample did not cover. The failure family that matters most is address-decode bugs, which live in specific address bits, so a sweep that only touches low addresses never toggles the high bits and cannot catch them. The strategy that finds them is an address-bit walk that toggles each address bit, plus boundaries, a few random spot-checks, and backdoor seeding followed by frontdoor spot-reads. The discipline is coverage honesty: a sample verifies only what it touched, so memory verified must be backed by which bits and regions were exercised.

Foundation12 min readUVM RALlarge memoryaddress decodeaddress-bit walkcoverage

Chapter 8 · Section 8.5 · Memories

1. Why Should I Learn This?

Real memories are too large to test exhaustively, so the quality of your memory verification is entirely determined by how you sample — and a sample that misses the high address bits misses the whole class of address-decode bugs that memories are most prone to. Knowing to walk the address bits, hit the boundaries, seed by backdoor, and — above all — report what you actually covered is what separates memory verification that finds decode bugs from a sweep that passes while an address bit is broken.

Learning a large-memory strategy — targeting decode logic with an address-bit walk and staying honest about coverage — is the capstone of the memories chapter, drawing together placement (8.2), bursts (8.3), and backdoor (8.4) into a practical, honest test plan.

2. Industry Story — 'memory verified' with a broken address bit

A team verifies a large frame buffer with a sweep: write a pattern to the first few thousand locations, read it back, all pass. 'Frame buffer verified' goes in the report. The sweep only ever touched low addresses — the high-order address bits were always 0 throughout the test.

In the RTL, two high address bits were swapped in the decode — a real bug that makes two large regions of the buffer alias each other. But the sweep never set those high bits (it stayed in the low addresses where both broken and correct decode behave identically), so it never exercised the swap and never saw the aliasing. The bug shipped, and in the field two regions of the frame buffer corrupted each other, producing display artifacts that traced back — after a long hunt — to an address-decode error that verification had signed off on. The gap was not a missing check; it was a sample that never toggled the bits where the bug lived, combined with a 'verified' claim the sample did not support. The post-mortem lesson: a large memory can only be sampled, and address-decode bugs live in specific address bits — so a low-address-only sweep never toggles the high bits and cannot catch a decode bug there; verifying a memory means walking the address bits and boundaries, and claiming 'verified' only for the address space the sample actually exercised.

3. Concept — sample what breaks, and report what you covered

Because exhaustive testing is infeasible, a large-memory strategy targets the failure modes and is honest about coverage:

  • Address-decode bugs live in address bits. Stuck bits, swapped bits, ignored upper bits — all in the decode. So the highest-value stimulus is an address-bit walk: access addresses that toggle each address bit individually (0x1, 0x2, 0x4, …, up to the top bit), so every bit of the decode is exercised. A low-address-only sweep leaves the high bits at 0 and cannot catch a bug in them.
  • Boundaries matter. First location, last location, and each memory's span end (8.2) — where off-by-one and overrun bugs (8.3) live. Include power-of-2 addresses and their neighbours.
  • Seed by backdoor, spot-check by frontdoor. Frontdoor seeding a huge memory is infeasible; poke seeds it in sim time (8.4), then frontdoor reads (and backdoor-vs-frontdoor compares) spot-check the access path at the sampled addresses.
  • Report coverage honestly. A sample verifies what it touched. Record which address bits and regions were exercised, and claim 'verified' only for those — never generalize a low-address pass to the whole memory.

Here is the strategy as a flow, ending in the honesty step that the story skipped:

Large-memory strategy: backdoor seed, walk address bits, boundaries, spot-check compare, report coverage honestlySeed by backdoor (poke) — frontdoor seeding a huge memory does not scale (8.4)Seed by backdoor(poke) —frontdoorseeding a huge…WALK the address bits — toggle each bit (0x1, 0x2, ... top) to exercise the whole DECODEWALK the addressbits — toggle eachbit (0x1, 0x2, ...top) to exercise th…Hit BOUNDARIES — first, last, each span end (8.2/8.3) + power-of-2 neighboursHit BOUNDARIES —first, last, eachspan end (8.2/8.3) +power-of-2…Random spot-checksacross the addressspaceCompare frontdoor vsbackdoor/scoreboardat each sampledaddressREPORT what was covered (which bits/regions) — claim 'verified' ONLY for thatREPORT what wascovered (whichbits/regions) —claim 'verified…
Figure 1 — a large-memory test strategy (you cannot test every location). Seed the memory by backdoor (poke, 8.4) since frontdoor seeding does not scale. Then sample where bugs live: WALK the address bits (toggle each bit — 0x1, 0x2, ... top bit — to exercise the whole decode), hit BOUNDARIES (first, last, span ends — 8.2/8.3), and add random spot-checks. Compare frontdoor against backdoor/scoreboard at each sampled address. Finally REPORT what was actually covered — which address bits and regions — and claim 'verified' only for those, never generalizing a low-address pass to the whole memory.

4. Mental Model — you cannot test every location, so test every address bit

5. Working Example — an address-bit walk with honest coverage

Seed by backdoor, walk the address bits and boundaries, and report the coverage you actually achieved:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Seed the whole memory by backdoor (frontdoor seeding a huge memory is infeasible, 8.4):
uvm_status_e s;  uvm_reg_data_t bd, fd;
foreach (seed[i]) buffer.poke(s, i, seed[i]);
 
// ADDRESS-BIT WALK: toggle EACH address bit so the whole decode is exercised — this is what
// catches stuck/swapped/ignored address bits that a low-address sweep never touches.
for (int b = 0; b < $clog2(buffer.get_size()); b++) begin
  int unsigned idx = (1 << b);                 // 0x1, 0x2, 0x4, ... up to the top address bit
  buffer.write(s, idx, 32'hA5A5_0000 | b);     // frontdoor write to a single-bit-set address
  buffer.read (s, idx, fd);
  buffer.peek (s, idx, bd);                     // independent view (8.4)
  if (fd !== bd)
    `uvm_error("MEMDBG", $sformatf("decode/data mismatch toggling addr bit %0d (idx %0h)", b, idx))
end
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// BOUNDARIES: first, last, and the span end (8.2/8.3) — where off-by-one/overrun bugs live.
int unsigned edges[] = '{0, buffer.get_size()-1};
foreach (edges[k]) begin
  buffer.write(s, edges[k], 32'hDEAD_0000 | edges[k][7:0]);
  buffer.read (s, edges[k], fd);  buffer.peek(s, edges[k], bd);
  if (fd !== bd) `uvm_error("MEMDBG", $sformatf("boundary mismatch at %0h", edges[k]))
end
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// COVERAGE HONESTY: report what was actually exercised. 'Verified' is scoped to THIS, not the whole array.
`uvm_info("MEMDBG",
  $sformatf("coverage: address bits 0..%0d walked; boundaries {0, size-1} checked; N random spots. "
          , $clog2(buffer.get_size())-1), UVM_LOW)
// Do NOT log 'memory fully verified' — a sample verifies what it touched. Say which bits/regions.

The address-bit walk exercises the decode with a handful of accesses — where a low-address sweep would leave the high bits untouched — and the coverage note scopes the claim to what was actually tested. The next section is the bug that follows from skipping both.

6. Debugging Session — a low-address sweep that missed a decode bug

1

A low-address-only sweep never toggles the high address bits, so it cannot catch an address-decode bug there — and 'memory verified' over-claims coverage the sample never achieved

WALK THE ADDRESS BITS
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 'Verify' a large frame buffer by sweeping only the first few thousand LOW addresses:
for (int i = 0; i < 4096; i++) begin           // i stays small -> HIGH address bits are always 0
  buffer.write(s, i, pattern[i]);
  buffer.read (s, i, rd);
  if (rd !== pattern[i]) `uvm_error("MEM", "mismatch")
end
`uvm_info("MEM", "frame buffer verified", UVM_LOW)   // BUG: over-claims — high address space never touched
// Two HIGH address bits are swapped in the RTL decode -> aliases two regions -> never exercised here.
Symptom

The sweep passes cleanly and 'frame buffer verified' is reported — but a real address-decode bug (two high address bits swapped, aliasing two large regions) is not caught, because the sweep only touched addresses 0..4095, where the high bits are always 0 and the swap has no visible effect (both regions the swap would confuse are up in the high address space the test never reaches). In the field, the two aliased regions corrupt each other, producing artifacts that trace back — after a long hunt — to an address-decode error that verification signed off on. There is no failure in simulation; the tell is only that the 'verified' claim rests on a sample that never toggled the high address bits where the bug lives.

Root Cause

Two connected causes. The stimulus cause: address-decode bugs live in specific address bits, and this sweep only exercised low addresses, so the high address bits stayed 0 for the entire test and were never toggled. A swapped pair of high bits only mis-decodes when those bits differ across accesses that should reach distinct regions — which requires setting them — so a sample that never sets them cannot expose the swap; the broken and correct decode are indistinguishable in the low-address region the test lived in. The reporting cause: 'frame buffer verified' generalized a low-address pass to the whole memory — a coverage over-claim, because a sample verifies only the address space it touched, and this one never touched the high addresses. Neither a missing check nor a broken model is at fault; the sample simply did not exercise the bits where the bug was, and the claim did not admit it. Because the low addresses genuinely worked, nothing in simulation flagged the gap.

Fix

Replace (or augment) the low-address sweep with an address-bit walk that toggles each address bit — accessing 0x1, 0x2, 0x4, …, up through the top address bit — so the high bits are exercised and a swapped/stuck/ignored high bit is caught; add boundary and random spot-checks, and compare frontdoor against backdoor (8.4). Then scope the claim: report which address bits and regions were exercised and reserve 'verified' for those, rather than generalizing. The rule the bug teaches: a large memory can only be sampled, and address-decode bugs live in address bits — so you must walk the address bits (not just sweep low addresses), and you must claim 'verified' only for the address space the sample actually exercised. A pass over the low addresses says nothing about the high ones; the two most common large-memory failures are not walking the address bits and over-claiming coverage from a partial sweep, and this bug is both at once.

7. Common Mistakes

  • Sweeping only low addresses. The high address bits stay 0 and decode bugs there are never exercised — walk each address bit instead.
  • Claiming 'memory verified' from a partial sample. A sample verifies what it touched; scope the claim to the bits/regions actually exercised.
  • Trying to test every location. Infeasible for large memories and misdirected — bugs are in the decode (address bits), not the data. Sample the failure modes.
  • Skipping boundaries. First, last, and span ends (8.2/8.3) are where off-by-one and overrun bugs live — always include them.
  • Frontdoor-seeding a huge memory. Infeasible in sim time — seed by backdoor poke (8.4), then frontdoor spot-check.

8. Industry Best Practices

  • Walk the address bits. Toggle each address bit (0x1, 0x2, …, top) so the whole decode is exercised — the single most valuable large-memory stimulus.
  • Sample failure modes, not locations. Address-bit walk + boundaries + a few random spots covers the bugs far better than a huge low-address sweep.
  • Seed by backdoor, spot-check by frontdoor. poke to set up (8.4); frontdoor reads and backdoor-vs-frontdoor compares to check the access path.
  • Report coverage honestly. Record which address bits and regions were exercised; reserve 'verified' for those — never generalize a low-address pass.
  • Include memory span boundaries. Tie the boundary checks to placement and burst limits (8.2/8.3) so overrun and off-by-one bugs surface.

9. Interview / Review Questions

10. Key Takeaways

  • A large memory cannot be tested exhaustively — memory verification is a deliberate sampling strategy aimed at the failure modes, plus honesty about what the sample covered.
  • The dominant failure family is address-decode bugs, which live in specific address bits — so the highest-value stimulus is an address-bit walk (toggle each bit: 0x1, 0x2, …, top), not a low-address sweep.
  • Add boundaries (first, last, span ends — 8.2/8.3) and random spot-checks; seed by backdoor poke (frontdoor seeding a huge memory does not scale) and spot-check by frontdoor with backdoor-vs-frontdoor compares (8.4).
  • A low-address-only sweep never toggles the high bits, so it cannot catch a decode bug there — and reporting 'memory verified' from it over-claims coverage the sample never achieved.
  • Scope 'verified' to what you exercised — which address bits and regions — and never generalize a low-address pass to the whole memory; keeping the untested (most bug-prone) address space visible as untested is what gets it covered.