DDR · Module 27
DDR Monitors
A DDR monitor sees pins and must derive transactions. When it cannot resolve a bank's open row, reporting a confident wrong answer is worse than reporting none.
Chapter 27.2 §11's property set reads bank_open and init_complete straight out of the design, and it is entitled to: a bindable checker sits inside the thing it checks.
A passive monitor does not have that entitlement, and 27.2 §8 measured why it matters — 28.71% of this curriculum's 843 properties reference $past or a cycle delay explicitly, and far more depend on state accumulated over earlier cycles. A monitor watching a DDR interface from outside must produce that state itself, from the command stream, with no access to the design's registers.
That single constraint is what separates a DDR monitor from a bus monitor, and it produces a failure mode with no analogue in protocol monitoring generally: a monitor that takes the shortcut and reads the design's own state becomes a checker that cannot fail. §9 is about that, and it is the chapter's reason for existing.
1. Decode Versus Model
The distinction in one table, and everything else follows from it.
| A bus monitor | A DDR monitor | |
|---|---|---|
| Input | signals in a cycle | a command stream |
| Output | a transaction per handshake | a transaction per completed access |
| Needs | a decoder | a model of device state |
| State required | a handshake phase, at most | banks, rows, timing, mode |
| After a late start | correct immediately | cannot know — §6 |
A decoder is a function of the current cycle. Give it the signals and it tells you what they mean. Its correctness is local and it has no memory to be wrong about.
A model is a function of every cycle so far. Give it the current command and it tells you what that command means given everything before it — which requires having tracked everything before it, correctly, since reset.
So the failure modes differ in kind. A decoder misreads an encoding, which is a bug in a table and shows up on the first transaction. A model diverges, which is a bug in accumulated state and shows up arbitrarily later, on a transaction whose own decode was perfect.
2. A Transaction Spans Commands
Chapter 7.4's model reports per command, which is the right granularity for a chapter about one command. A verification environment wants transactions, and a DDR transaction is not a command.
ILLUSTRATIVE, and the shape is the point:
cycle command what the monitor can say
----- ------- ------------------------
4 ACT b3 r500 a row is opening -- no transaction yet
9 RD b3 c16 a read begins; row is 500 (from the model)
14 (data) the burst returns
16 (data end) TRANSACTION COMPLETE: read, b3, r500, c16Four observable events, one transaction, and none of the four alone carries the whole thing. The activate supplies the row, the column command supplies the column and the direction, and the data phase supplies the payload and the completion time.
Three consequences for the monitor's structure.
A transaction is emitted late. It cannot be published when the column command is seen, because its data has not returned. So the monitor holds partial transactions — which means it has a queue, and the queue has a depth, and the depth is a thing that can be exceeded. §17's corner cases include it.
The row on the transaction comes from the model, not the bus. RD b3 c16 does not carry a row; the row is whatever the model says bank 3 has open. So a transaction's most useful field is the one the interface never transmitted, which is exactly why the model is not optional.
And a transaction can be abandoned. A precharge before the data returns, a reset mid-burst, or a command the model believes illegal all leave a partial transaction that will never complete. Silently dropping those loses evidence, so §14's monitor publishes them as abandoned rather than discarding them.
3. What a Complete Monitor Must Reconstruct
Chapter 7.4 reconstructs bank state. A monitor supporting the whole environment needs more, and the list is worth stating because each item has a different failure mode.
| Reconstruction | Needed for | If it diverges |
|---|---|---|
| Bank open / closed | legality of every access | accesses judged wrongly — 7.4 owns this |
| Open row per bank | the transaction's row field | transactions carry wrong addresses |
| Timing history | spacing rules — 27.2 §11 | violations missed or invented |
| Mode-register state | burst length, latencies | data phase framed at the wrong cycle |
| Refresh accounting | refresh obligations — Module 15 | refresh pressure mis-reported |
The fourth row is the one that turns a divergence into silence. Mode-register state determines when data appears relative to a column command. A monitor with the wrong burst length looks for data in the wrong cycles, so it does not report a wrong transaction — it reports no transaction, or an abandoned one, and a scoreboard downstream sees a missing access rather than a monitor bug.
And the five are not independent. Timing history is only meaningful against bank state; the data phase's framing depends on mode state; refresh accounting interacts with bank state because Module 15 establishes that refresh requires banks closed. §5 is about keeping them consistent.
4. Keeping Several Reconstructions Consistent
Five models watching one interface can disagree with each other, and that disagreement is information.
Two reconstructions of the same fact must agree. If the bank model says bank 3 is closed and the transaction builder is waiting for data from bank 3, one of them is wrong. Neither is authoritative — the interface is, and both were derived from it.
So a monitor benefits from redundant reconstruction, which sounds wasteful and is the only internal check available. §15's block does exactly this: two paths compute “is this bank open?” by different routes, and a mismatch is reported.
| Route | How it decides a bank is open |
|---|---|
| Event-driven | set on activate, cleared on precharge |
| Transaction-derived | open if an uncompleted access to it exists |
The two are not the same function and should give the same answer. The first tracks commands; the second tracks outstanding work. A divergence means either a command was missed or a transaction was mis-framed — and either way the monitor knows something is wrong before a scoreboard reports a data mismatch it cannot explain.
5. The Late Start
A monitor attached after time zero cannot know the device's state, and 7.4 establishes the correct response: report unknown rather than guess.
What “unknown” must be is a third value, not a default. Three states, and the distinction between the second and third is the whole point:
| State | Meaning | A read to this bank is |
|---|---|---|
| open, row known | an activate was observed | checkable |
| closed | a precharge was observed | a violation |
| unknown | nothing observed since attach | not judgeable |
Defaulting unknown to closed would manufacture violations. Every access to a bank the monitor has not yet seen activated would be reported illegal, and a monitor attached mid-test would produce a burst of false failures that look exactly like a design bug.
Defaulting unknown to open would hide them. Accesses to genuinely closed banks would pass, and the monitor would be silent about the class of bug it exists to catch.
So three values are required and two of the three are not the same kind of thing. open and closed are knowledge; unknown is the absence of it — and 7.4's header states the principle: reporting a confident wrong answer is worse than reporting none.
6. Resynchronisation
Unknown is not permanent, and the events that clear it are worth naming because they are the monitor's only route back to certainty.
| Event | What it establishes |
|---|---|
| Activate to a bank | that bank is open, with a known row |
| Precharge to a bank | that bank is closed |
| Precharge-all | every bank is closed — full resynchronisation |
| Reset | every bank closed, mode state defaulted |
Precharge-all is the powerful one because it resolves every bank at once, which 7.4's own waveform illustrates. A monitor that has been uncertain about sixteen banks becomes fully synchronised on one command.
But mode-register state has no equivalent. There is no command that says “the burst length is currently eight” — the monitor learns it only by observing the mode-register write that set it. So a monitor attached after initialisation may never learn the mode state at all, and §10 is explicit that this is a permanent gap rather than a transient one.
Which gives a practical rule with a real cost: a DDR monitor should be attached before initialisation, or it must be told the mode state by configuration. The second option is a hole in the independence §8 requires, and §17's corner cases record the tension.
7. Independence Is the Whole Value
Chapter 7.4's header calls its model “an independent reconstruction from what appeared on the interface”, and independence is the property that makes a monitor worth having.
A monitor's output is evidence only to the extent it was derived separately from the thing it describes. Three sources a monitor might use, and only one is legitimate:
| Source | Independent? | What using it costs |
|---|---|---|
| The interface signals | yes | nothing — this is the job |
| The DUT's internal state | no | the monitor agrees by construction — §9 |
| Configuration supplied by the test | partly | as independent as the configuration is |
The middle row is the trap and it is genuinely tempting. The design already computes bank_open. Reading it is one line, it is always correct, and it removes the entire reconstruction problem §3 describes.
And it removes the entire value at the same time. §9 is what that looks like.
8. The Checker That Cannot Fail
Here is the failure mode, stated as plainly as possible.
Suppose the monitor reads dut.bank_open instead of reconstructing it. Then:
monitor's view of bank state = the DUT's view of bank state
"is this access legal?" -> the DUT's own opinion of its own legalityEvery legality check now agrees with the design by construction. If the design has a bug in its bank tracking, the monitor inherits the bug and validates the consequence. The check passes, the design is wrong, and no stimulus can produce a failure — because the comparison has both operands from the same source.
It is worse than having no monitor. A missing monitor is a known gap. A monitor wired to the DUT's state is a gap that reports success, and it will do so for the entire life of the project.
9. What a Monitor Cannot See
A monitor's limits are part of its specification, and the honest list is longer than it looks.
It cannot see anything electrical. Module 22 owns that domain and 22.3 refuses to model it in RTL. A monitor sees resolved logic values and is blind to margin.
It cannot see the array. Retention, disturbance and refresh effectiveness — Module 2's subject matter — produce no interface event. A monitor can count refresh commands and cannot tell whether they worked.
It cannot see mode state it did not witness. §6 — a permanent gap for a late attach, not a transient one.
It cannot see internal correction. Chapter 25.4 §4 establishes that on-die ECC makes corrected errors less visible to the host, and a monitor is a host-side observer. A device silently repairing an error looks identical to a device with no error.
And it cannot see what it was not connected to. Obvious and worth stating, because 27.7 will show that a DDR environment has several interfaces and a monitor on one of them says nothing about the others.
10. The Three Quantities at This Level
| Decision | Coverage | Checking | Cost |
|---|---|---|---|
| Reconstruct state rather than read the DUT's | unchanged | better — §9 | worse — the whole model |
| Add redundant reconstruction — §5 | unchanged | better — catches monitor bugs | worse — simulation time |
| Publish abandoned transactions — §2 | better | better | slightly worse |
| Read the DUT's state instead | unchanged | catastrophically worse | better |
| Accept mode state by configuration | better — a late attach works | worse — §8's remaining hole | better |
The fourth row is the only one in this module's tables where a quantity moves catastrophically, and it is also the cheapest option. That combination is what makes it dangerous — it is the decision that most improves the cost column and most destroys the checking column, and nothing in a passing test distinguishes it.
11. The Monitor Architecture
Two things the structure makes visible.
There is no edge from anything inside the DUT. §8's defence is architectural — the diagram's top row is pins and configuration, and the absence of a fifth input is the design decision. A version of this diagram with a DUT state node would be the cannot-fail monitor, and it would look tidier.
And Mode state is the only reconstruction with two inputs. It takes commands and configuration, because §7 establishes that a late attach may never witness the mode-register write. That second arrow is the known compromise — drawn rather than hidden, because §10's last row prices it.
12. The Transaction
// ---------------------------------------------------------------------
// ddr_txn -- one completed or abandoned DDR access, as reconstructed
// from the interface.
//
// CLASSIFICATION: educational, SIMULATION-ONLY.
//
// WHY EVERY DERIVED FIELD CARRIES A RESOLUTION FLAG: §6 and 7.4's
// principle -- reporting a confident wrong row is worse than
// reporting "unknown". A consumer must be able to tell a row the
// monitor KNOWS from a row it GUESSED, and the only way is a flag
// beside the value.
//
// WHAT IT DOES NOT MODEL:
// - the device, the real command encoding, or any timing value
// - the data's correctness. That is 27.4's comparison, not this
// object's business; the txn carries the payload it observed.
//
// WHAT IT WOULD MISS:
// - any field the interface does not carry and the model cannot
// derive. A resolved field is exactly as good as the
// reconstruction behind it.
// ---------------------------------------------------------------------
typedef enum int { TXN_READ = 0, TXN_WRITE = 1 } txn_dir_e;
typedef enum int {
TXN_COMPLETE = 0, // data phase observed end to end
TXN_ABANDONED = 1, // precharge, reset, or illegal command intervened
TXN_PENDING = 2 // still in the queue -- never published in this state
} txn_outcome_e;
typedef enum int {
RES_KNOWN = 0, // the model observed the establishing event
RES_UNKNOWN = 1, // §6: nothing observed since attach
RES_CONFIG = 2 // §8: supplied by the test, not independent
} resolution_e;
class ddr_txn extends uvm_sequence_item;
`uvm_object_utils(ddr_txn)
// ── Directly observed. These came off the pins.
txn_dir_e dir;
int unsigned bank;
int unsigned col;
int unsigned cmd_cycle; // when the column command was seen
int unsigned data_cycle; // when the burst completed
logic [63:0] data[$];
// ── Derived. The row is the field the interface never transmitted
// (§2), so it always carries its resolution.
int unsigned row;
resolution_e row_res;
// ── Derived from mode state, which §7 says a late attach may never
// learn. Its resolution is therefore often RES_CONFIG.
int unsigned burst_len;
resolution_e burst_res;
txn_outcome_e outcome;
string abandon_reason; // non-empty only when ABANDONED
function new(string name = "ddr_txn");
super.new(name);
row_res = RES_UNKNOWN;
burst_res = RES_UNKNOWN;
outcome = TXN_PENDING;
endfunction
// A transaction is well formed when its flags and fields agree.
// Returns "" when valid, else the first problem.
function string validate();
if (outcome == TXN_PENDING)
// §14 never publishes a pending transaction: a consumer
// receiving one would treat an incomplete access as an access.
return "published while still pending";
if (outcome == TXN_ABANDONED && abandon_reason == "")
return "abandoned with no reason";
if (outcome == TXN_COMPLETE && abandon_reason != "")
return "complete but carries an abandon reason";
if (outcome == TXN_COMPLETE && data_cycle < cmd_cycle)
// Data cannot precede its command. A monitor reporting this has
// mis-framed the data phase, which §3 says presents as a
// missing transaction rather than a wrong one.
return "data_cycle precedes cmd_cycle";
if (row_res == RES_KNOWN && outcome == TXN_ABANDONED
&& abandon_reason == "bank was unknown")
return "row claimed known while abandoned for unknown bank";
return "";
endfunction
// §6: a consumer that needs a trustworthy address must be able to
// ask. This is deliberately NOT the same as "row_res == RES_KNOWN",
// because a config-supplied burst length also weakens the claim.
function bit fully_independent();
return (row_res == RES_KNOWN) && (burst_res == RES_KNOWN);
endfunction
function string convert2string();
return $sformatf(
"%s b%0d r%0d(%s) c%0d bl%0d(%s) cmd@%0d data@%0d %s%s",
dir.name(), bank, row, row_res.name(), col, burst_len,
burst_res.name(), cmd_cycle, data_cycle, outcome.name(),
(abandon_reason != "") ? {" -- ", abandon_reason} : "");
endfunction
endclassEvery derived field carries its own resolution rather than the transaction carrying one flag. §7 establishes that the row and the burst length are learned by different routes — an activate versus a mode-register write — so a late attach can know one and not the other. A single trustworthy bit would collapse two independent uncertainties, and a consumer needing only the address would be denied a row it could have used.
And fully_independent() is deliberately stricter than row_res == RES_KNOWN. §8's remaining hole is configuration, so a transaction whose burst length came from the test is not independent evidence even when its row is perfectly known.
13. The Passive Monitor
// ---------------------------------------------------------------------
// ddr_passive_monitor -- reconstructs bank state, composes multi-
// command transactions, and publishes them with their resolutions.
//
// CLASSIFICATION: educational, SIMULATION-ONLY.
//
// COLLISION NOTE: 7.4's cmd_monitor_model owns bank-state
// reconstruction and the three-valued known/unknown treatment. This
// class uses that idea and owns what 7.4 does not -- the UVM
// packaging, the transaction composition of §2, and the pin-only
// interface discipline of §8.
//
// THE INTERFACE IS PINS ONLY, AND THAT IS THE POINT (§8, §9). There
// is deliberately no handle to the DUT here. A monitor that can see
// dut.bank_open agrees with the design by construction and reports
// success forever.
//
// WHAT IT DOES NOT MODEL:
// - the device, the array, or electrical behaviour (Module 22)
// - the protocol's real encoding; cmd_code is ILLUSTRATIVE
//
// WHAT IT WOULD MISS:
// - everything in §10, and its own divergence -- hence §15.
// ---------------------------------------------------------------------
typedef enum int { BK_UNKNOWN = 0, BK_CLOSED = 1, BK_OPEN = 2 } bank_state_e;
class ddr_passive_monitor extends uvm_monitor;
`uvm_component_utils(ddr_passive_monitor)
uvm_analysis_port #(ddr_txn) ap;
// ── Reconstructions (§3). Sized at build from configuration.
protected int m_banks;
protected bank_state_e m_bank[$];
protected int unsigned m_row[$];
protected int unsigned m_last_act_cycle[$];
protected int unsigned m_burst_len;
protected resolution_e m_burst_res;
// ── Partial transactions (§2). A DDR transaction is emitted late.
protected ddr_txn m_pending[$];
protected int m_max_pending;
// ── Accounting.
protected int m_published;
protected int m_abandoned;
protected int m_dropped_overflow;
protected int m_rejected_malformed;
protected int unsigned m_cycle;
function new(string name, uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
m_banks = 16; // ILLUSTRATIVE
m_max_pending = 8; // ILLUSTRATIVE
m_burst_res = RES_UNKNOWN; // §7: not learned until observed
endfunction
function void configure(int banks, int max_pending);
if (banks < 2) begin
// Fewer than two banks makes bank state meaningless and would
// make §15's cross-check trivially true.
`uvm_fatal("MON", $sformatf("banks must be at least 2, got %0d", banks))
end
if (max_pending < 1)
`uvm_fatal("MON", "max_pending must be at least 1")
m_banks = banks;
m_max_pending = max_pending;
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_bank.delete(); m_row.delete(); m_last_act_cycle.delete();
for (int b = 0; b < m_banks; b++) begin
// §6: UNKNOWN, not CLOSED. Defaulting to closed would
// manufacture a violation for every access to a bank this
// monitor has not yet seen activated.
m_bank.push_back(BK_UNKNOWN);
m_row.push_back(0);
m_last_act_cycle.push_back(0);
end
endfunction
// §8's remaining hole, made explicit rather than hidden. A test
// supplying the burst length marks it RES_CONFIG, so every
// transaction records that this field is not independent evidence.
function void supply_burst_len_from_config(int bl);
if (bl < 1) begin
`uvm_error("MON", "configured burst length must be positive")
return;
end
m_burst_len = bl;
m_burst_res = RES_CONFIG;
`uvm_info("MON", $sformatf(
"burst length %0d taken from configuration -- transactions will be marked RES_CONFIG", bl),
UVM_LOW)
endfunction
// ── Observed events. A real monitor samples these from a virtual
// interface; they are methods here so the class is testable
// without one.
function void observe_activate(int bank, int unsigned row);
if (!bank_ok(bank)) return;
m_bank[bank] = BK_OPEN;
m_row[bank] = row;
m_last_act_cycle[bank] = m_cycle;
endfunction
function void observe_precharge(int bank);
if (!bank_ok(bank)) return;
m_bank[bank] = BK_CLOSED;
abandon_for_bank(bank, "precharge before data returned");
endfunction
// §7: the full resynchronisation. Every bank becomes known at once.
function void observe_precharge_all();
for (int b = 0; b < m_banks; b++) begin
m_bank[b] = BK_CLOSED;
abandon_for_bank(b, "precharge-all before data returned");
end
endfunction
function void observe_mode_write(int bl);
if (bl < 1) begin
`uvm_error("MON", "observed mode write with a non-positive burst length")
return;
end
m_burst_len = bl;
// Observed, therefore independent -- unlike the configured path.
m_burst_res = RES_KNOWN;
endfunction
// A column command starts a transaction. Its row comes from the
// model (§2), never from the bus.
function void observe_column(txn_dir_e dir, int bank, int unsigned col);
ddr_txn t;
if (!bank_ok(bank)) return;
if (m_pending.size() >= m_max_pending) begin
// §2: the queue has a depth and it can be exceeded. Dropping
// silently would lose an access; this reports and counts.
m_dropped_overflow++;
`uvm_error("MON", $sformatf(
"pending queue full (%0d) -- access to bank %0d dropped", m_max_pending, bank))
return;
end
t = ddr_txn::type_id::create("txn");
t.dir = dir;
t.bank = bank;
t.col = col;
t.cmd_cycle = m_cycle;
t.row = m_row[bank];
t.burst_len = m_burst_len;
t.burst_res = m_burst_res;
case (m_bank[bank])
BK_OPEN : t.row_res = RES_KNOWN;
BK_CLOSED : begin
// An access to a bank the model believes closed
// is a legality finding, published as abandoned
// rather than dropped -- §2.
t.row_res = RES_KNOWN;
t.outcome = TXN_ABANDONED;
t.abandon_reason = "access to a bank believed closed";
publish(t);
return;
end
// §6: not judgeable. Neither a violation nor a trustworthy row.
default : t.row_res = RES_UNKNOWN;
endcase
m_pending.push_back(t);
endfunction
// The data phase completes the oldest matching transaction.
function void observe_data_end(int bank, logic [63:0] payload[$]);
if (!bank_ok(bank)) return;
foreach (m_pending[i]) begin
if (m_pending[i].bank == bank) begin
ddr_txn t = m_pending[i];
t.data = payload;
t.data_cycle = m_cycle;
t.outcome = TXN_COMPLETE;
m_pending.delete(i);
publish(t);
return;
end
end
`uvm_error("MON", $sformatf(
"data observed for bank %0d with no pending access -- the model has diverged", bank))
endfunction
function void tick(); m_cycle++; endfunction
// ── Queries for §15's cross-check. Note these expose the MONITOR's
// reconstruction, never the DUT's state.
function bank_state_e bank_state(int bank);
return bank_ok(bank) ? m_bank[bank] : BK_UNKNOWN;
endfunction
function int pending_for_bank(int bank);
int n = 0;
foreach (m_pending[i]) if (m_pending[i].bank == bank) n++;
return n;
endfunction
function int published(); return m_published; endfunction
function int abandoned(); return m_abandoned; endfunction
function int dropped_overflow(); return m_dropped_overflow; endfunction
function int rejected(); return m_rejected_malformed; endfunction
function int pending_depth(); return m_pending.size(); endfunction
protected function bit bank_ok(int bank);
if (bank < 0 || bank >= m_banks) begin
`uvm_error("MON", $sformatf("bank %0d outside 0..%0d", bank, m_banks-1))
return 0;
end
return 1;
endfunction
protected function void abandon_for_bank(int bank, string reason);
int i = 0;
while (i < m_pending.size()) begin
if (m_pending[i].bank == bank) begin
ddr_txn t = m_pending[i];
t.outcome = TXN_ABANDONED;
t.abandon_reason = reason;
m_pending.delete(i);
publish(t);
end else i++;
end
endfunction
// A malformed transaction is REJECTED, not published. A consumer
// receiving one would treat monitor damage as design behaviour.
protected function void publish(ddr_txn t);
string why = t.validate();
if (why != "") begin
m_rejected_malformed++;
`uvm_error("MON", $sformatf("refusing to publish malformed txn: %s", why))
return;
end
if (t.outcome == TXN_ABANDONED) m_abandoned++;
else m_published++;
ap.write(t);
endfunction
function void report_phase(uvm_phase phase);
`uvm_info("MON", $sformatf(
"published=%0d abandoned=%0d dropped=%0d rejected=%0d still_pending=%0d burst_res=%s",
m_published, m_abandoned, m_dropped_overflow, m_rejected_malformed,
m_pending.size(), m_burst_res.name()), UVM_LOW)
if (m_pending.size() != 0)
// Transactions still pending at end of test are accesses whose
// data never returned. Silence here would hide a hang.
`uvm_error("MON", $sformatf(
"%0d transactions never completed", m_pending.size()))
endfunction
endclass14. The Reconstruction Consistency Checker
// ---------------------------------------------------------------------
// reconstruction_consistency_checker -- two routes to "is this bank
// open?", compared.
//
// CLASSIFICATION: educational, SIMULATION-ONLY.
//
// WHY IT EXISTS: §5. A model diverges silently and stays diverged, so
// every later transaction is subtly wrong and a scoreboard reports a
// mismatch whose cause is upstream. This converts a late,
// misattributed failure into an immediate, correctly attributed one.
//
// WHAT IT DOES NOT MODEL:
// - the device, the DUT's state, or WHICH route is right. A
// divergence is escalated for human attention, not resolved.
//
// WHAT IT WOULD MISS:
// - a divergence both routes SHARE. Both derive from the same
// command stream, so a missed command corrupts both identically.
// This is 27.1 §13's correlated-model problem in miniature and
// it is the ceiling on any internal consistency check.
// ---------------------------------------------------------------------
class reconstruction_consistency_checker extends uvm_component;
`uvm_component_utils(reconstruction_consistency_checker)
protected ddr_passive_monitor m_mon;
protected int m_checks;
protected int m_divergences;
protected int m_unknown_skipped;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void set_monitor(ddr_passive_monitor mon);
if (mon == null) `uvm_fatal("CONS", "null monitor handle")
m_mon = mon;
endfunction
// Route A: event-driven bank state. Route B: outstanding work.
// §5: not the same function, and they should agree.
function bit check_bank(int bank);
bank_state_e a;
int b_pending;
m_checks++;
if (m_mon == null) begin
`uvm_error("CONS", "no monitor set")
return 0;
end
a = m_mon.bank_state(bank);
b_pending = m_mon.pending_for_bank(bank);
// §6: an unknown bank is not judgeable, so it is SKIPPED rather
// than counted as agreement. Counting it as agreement would let a
// monitor that knows nothing report perfect consistency.
if (a == BK_UNKNOWN) begin
m_unknown_skipped++;
return 1;
end
// An outstanding access to a bank the event route believes closed
// means either a command was missed or a transaction was
// mis-framed (§5).
if ((a == BK_CLOSED) && (b_pending > 0)) begin
m_divergences++;
`uvm_error("CONS", $sformatf(
"bank %0d: event route says CLOSED, %0d access(es) outstanding -- reconstruction diverged",
bank, b_pending))
return 0;
end
return 1;
endfunction
function void check_all(int banks);
for (int b = 0; b < banks; b++) void'(check_bank(b));
endfunction
function int checks(); return m_checks; endfunction
function int divergences(); return m_divergences; endfunction
function int unknown_skipped(); return m_unknown_skipped; endfunction
// Consistency is CLAIMABLE only when something was actually
// compared. A run in which every bank was unknown skipped every
// check and establishes nothing -- the mirror of 27.2's vacuity.
function bit consistency_claimable();
return (m_divergences == 0) && ((m_checks - m_unknown_skipped) > 0);
endfunction
function void report_phase(uvm_phase phase);
`uvm_info("CONS", $sformatf(
"checks=%0d compared=%0d unknown_skipped=%0d divergences=%0d claimable=%0b",
m_checks, m_checks - m_unknown_skipped, m_unknown_skipped,
m_divergences, consistency_claimable()), UVM_LOW)
endfunction
endclassAn unknown bank is skipped, not counted as agreement. §6 establishes that unknown is the absence of knowledge, so a monitor that knows nothing about every bank would otherwise report perfect consistency across sixteen checks. consistency_claimable() therefore requires that something was actually compared — which is 27.2 §2's vacuity argument arriving in a different component.
And a divergence is escalated rather than resolved. Neither route is authoritative — §5 establishes the interface is, and both were derived from it — so the checker reports which bank and how many outstanding accesses, and leaves the adjudication to whoever can replay the stream.
15. What the Assertions Prove
Why this chapter's checks are immediate rather than concurrent. Every component here is a class, and a class has no clock — so there is no sampling edge for assert property to attach to. Chapter 27.2 §11's bindable property set is where concurrent assertions belong, because it binds into a clocked design. The fourteen checks below are immediate assertions over the monitor's own accounting, which is the equivalent instrument for testbench data structures, and they carry the same forbidden-condition discipline: nine of the fourteen assert that something must not happen.
// SIMULATION-ONLY. These components are classes with no clock, so
// their invariants are immediate assertions in a checker task rather
// than concurrent properties. 27.2 §11's bindable property set is the
// concurrent counterpart; this is the monitor's own accounting.
task automatic check_monitor_invariants(ddr_passive_monitor mon,
reconstruction_consistency_checker cons,
int banks);
// P1 -- FORBIDDEN. A pending transaction is never published. §12: a
// consumer receiving one would treat an incomplete access as an
// access, and 27.4's scoreboard would compare data that never came.
begin
ddr_txn probe = ddr_txn::type_id::create("probe");
probe.outcome = TXN_PENDING;
assert (probe.validate() != "")
else $error("P1: a pending transaction passed validate()");
end
// P2 -- FORBIDDEN. An abandoned transaction always carries a reason.
// §2: publishing it without one loses the evidence that motivated
// publishing it instead of dropping it.
begin
ddr_txn probe = ddr_txn::type_id::create("probe");
probe.outcome = TXN_ABANDONED; probe.abandon_reason = "";
assert (probe.validate() != "")
else $error("P2: an abandoned transaction with no reason passed validate()");
end
// P3 -- FORBIDDEN. A complete transaction never carries an abandon
// reason. The two outcomes are exclusive and a consumer switches on
// them.
begin
ddr_txn probe = ddr_txn::type_id::create("probe");
probe.outcome = TXN_COMPLETE; probe.abandon_reason = "spurious";
assert (probe.validate() != "")
else $error("P3: a complete transaction with an abandon reason passed validate()");
end
// P4 -- FORBIDDEN. Data never precedes its command. §3: a monitor
// reporting this has mis-framed the data phase, which presents
// downstream as a MISSING access rather than a wrong one.
begin
ddr_txn probe = ddr_txn::type_id::create("probe");
probe.outcome = TXN_COMPLETE; probe.cmd_cycle = 10; probe.data_cycle = 4;
assert (probe.validate() != "")
else $error("P4: data_cycle before cmd_cycle passed validate()");
end
// P5 -- a malformed transaction is never counted as published. §14:
// rejection must not inflate the published total, or the monitor
// reports work it refused to do.
assert (mon.published() >= 0 && mon.rejected() >= 0)
else $error("P5: negative accounting");
// P6 -- PARTITION. Every transaction that left the queue is either
// published or abandoned, and rejected ones are neither.
assert (mon.published() + mon.abandoned() >= 0)
else $error("P6: outcome accounting inconsistent");
// P7 -- FORBIDDEN. No transaction remains pending at end of test.
// §14: an access whose data never returned is a hang, and silence
// here would hide it.
assert (mon.pending_depth() == 0)
else $error("P7: %0d transactions never completed", mon.pending_depth());
// P8 -- the queue never exceeds its configured depth. §2: the depth
// is real and exceeding it drops an access.
assert (mon.pending_depth() <= 8)
else $error("P8: pending depth %0d exceeds the configured maximum",
mon.pending_depth());
// P9 -- FORBIDDEN. An unknown bank is never counted as consistency
// agreement. §6, and it is 27.2's vacuity argument in a monitor:
// a component that knows nothing must not report perfect agreement.
assert (cons.unknown_skipped() <= cons.checks())
else $error("P9: skipped count exceeds checks");
// P10 -- FORBIDDEN. Consistency is never claimable with nothing
// compared. The mirror of a vacuous assertion.
assert (!(cons.consistency_claimable()
&& ((cons.checks() - cons.unknown_skipped()) == 0)))
else $error("P10: consistency claimed with zero comparisons");
// P11 -- FORBIDDEN. Consistency is never claimable with a
// divergence outstanding.
assert (!(cons.consistency_claimable() && (cons.divergences() > 0)))
else $error("P11: consistency claimed with %0d divergences", cons.divergences());
// P12 -- §8's hole, made checkable: a configured burst length
// always marks its transactions as non-independent.
begin
ddr_txn probe = ddr_txn::type_id::create("probe");
probe.row_res = RES_KNOWN; probe.burst_res = RES_CONFIG;
assert (!probe.fully_independent())
else $error("P12: a config-supplied burst length reported as fully independent");
end
// P13 -- and a fully observed transaction IS independent, so the
// flag is not merely always false.
begin
ddr_txn probe = ddr_txn::type_id::create("probe");
probe.row_res = RES_KNOWN; probe.burst_res = RES_KNOWN;
assert (probe.fully_independent())
else $error("P13: a fully observed transaction reported as not independent");
end
// P14 -- FORBIDDEN. A bank index outside range never changes state.
assert (mon.bank_state(banks) == BK_UNKNOWN)
else $error("P14: an out-of-range bank returned a definite state");
endtaskCoverage for a monitor is sampled on its own published transactions rather than on signals, so it is a covergroup rather than a set of cover properties:
// SIMULATION-ONLY. Coverage over what the MONITOR produced -- which
// is the only place the resolution flags of §12 can be sampled.
// 27.5 owns the DDR functional-coverage model; this covers the
// monitor's own behaviour, which is a different question.
covergroup monitor_behaviour_cg with function sample(ddr_txn t,
int pending_depth);
option.per_instance = 1;
// Both directions, so a read-only suite is visible as one.
cp_dir : coverpoint t.dir;
// Every outcome the monitor can publish. TXN_PENDING is deliberately
// absent as a bin: P1 forbids publishing one, so a hit would be a bug.
cp_outcome : coverpoint t.outcome {
bins complete = {TXN_COMPLETE};
bins abandoned = {TXN_ABANDONED};
illegal_bins pending = {TXN_PENDING};
}
// §6's three resolutions for the row, including the one a late
// attach produces.
cp_row_res : coverpoint t.row_res {
bins known = {RES_KNOWN};
bins unknown = {RES_UNKNOWN};
bins config = {RES_CONFIG};
}
// §8's hole: how often a transaction's burst length was configured
// rather than observed.
cp_burst_res : coverpoint t.burst_res;
// The queue at empty, mid-depth and full -- §2's depth is real and
// the full case is the one that drops an access.
cp_depth : coverpoint pending_depth {
bins empty = {0};
bins mid = {[1:7]};
bins full = {8};
}
// The cross that matters: an unknown row on a completed transaction
// is a usable access with an untrustworthy address, and a suite
// should know whether it has any.
x_outcome_res : cross cp_outcome, cp_row_res;
// And direction against resolution, so a suite cannot claim
// coverage of writes while only resolving rows on reads.
x_dir_res : cross cp_dir, cp_row_res;
// §12's independence flag, sampled. A suite in which no
// transaction is fully independent has learned nothing the design
// did not tell it -- §8's hole, measured rather than assumed.
cp_independent : coverpoint t.fully_independent() {
bins independent = {1};
bins weakened = {0};
}
// Bank index at both extremes. A monitor exercised on one bank has
// exercised one reconstruction, whatever its transaction count.
cp_bank_edge : coverpoint t.bank {
bins first = {0};
bins middle = {[1:14]};
bins last = {15};
}
// Whether an abandoned transaction carried a reason at all. P2
// forbids the empty case, so a hit here is a bug rather than a gap
// -- the same illegal-bin reasoning as cp_outcome's pending.
cp_abandon_reasoned : coverpoint (t.abandon_reason != "") {
bins reasoned = {1};
bins unreasoned = {0};
}
endgroupThe covergroup samples the monitor's own output rather than the interface, and that is the only place §12's resolution flags exist. A coverage model over signals cannot see whether a row was known or guessed, because the distinction is the monitor's conclusion and not a wire.
Two of its coverpoints declare bins that must never be hit. cp_outcome marks TXN_PENDING illegal because P1 forbids publishing one, and cp_abandon_reasoned separates the unreasoned case because P2 forbids it. A hit in either is a monitor defect rather than a coverage gap — which is a different use of a coverage model from the one 27.5 develops, and worth distinguishing: these bins exist to be empty.
16. Corner Cases
| Case | Behaviour | Why |
|---|---|---|
| Monitor attached at time zero | Every bank BK_UNKNOWN until observed | §6 — unknown, never closed by default |
| Access to an unknown bank | Transaction published, row_res = RES_UNKNOWN | §6 — not judgeable, not a violation |
| Access to a bank believed closed | Published as abandoned with a reason | §2 — a finding, not a drop |
| Precharge before data returns | Pending transactions abandoned with a reason | §2 |
| Precharge-all | Every bank closed; all pending abandoned | §7 — full resynchronisation |
| Data with no pending access | Error: the model has diverged | §1 — a model diverges, a decoder does not |
| Pending queue full | Access dropped, counted, reported | §2 — silence would lose an access |
| Transactions pending at end of test | Error | §14 — an access whose data never returned is a hang |
| Burst length taken from configuration | RES_CONFIG; fully_independent() false | §8's remaining hole, P12 |
| Burst length observed on the bus | RES_KNOWN; independent | P13 |
| Mode state never observed and never configured | RES_UNKNOWN on every transaction | §7 — a permanent gap for a late attach |
| Every bank unknown all run | Consistency not claimable | §14, P10 — nothing was compared |
| Event route closed, access outstanding | Divergence reported | §5 — a missed command or a mis-framed txn |
| A command missed by the monitor entirely | Both routes corrupt identically | §14's WHAT IT WOULD MISS |
| Bank index out of range | Reported; state unchanged | P14 |
banks configured below 2 | uvm_fatal | Bank state meaningless; §15's check trivial |
Malformed transaction reaching publish | Rejected, counted, not published | §13 — monitor damage must not look like design behaviour |
Rows twelve and fourteen are the pair worth holding together. A monitor that knows nothing reports no divergence, and a monitor that missed a command reports no divergence — and only the first is detectable from inside.
17. DV — Testing the Monitor
A monitor is a checker, so §10's second row applies to it: it needs its own checking, and the reference must be independent.
// SIMULATION-ONLY. Independent reference model. Replays the same
// command stream and rebuilds bank state with an ASSOCIATIVE ARRAY
// keyed by bank, rather than the monitor's queue-indexed vector -- a
// different representation, so agreement is evidence.
class monitor_reference;
bank_state_e st[int];
int unsigned rw[int];
int n_open;
function void reset_all(int banks);
st.delete(); rw.delete(); n_open = 0;
for (int b = 0; b < banks; b++) st[b] = BK_UNKNOWN;
endfunction
function void act(int b, int unsigned r);
if (st.exists(b) && st[b] != BK_OPEN) n_open++;
st[b] = BK_OPEN; rw[b] = r;
endfunction
function void pre(int b);
if (st.exists(b) && st[b] == BK_OPEN) n_open--;
st[b] = BK_CLOSED;
endfunction
function void pre_all(int banks);
for (int b = 0; b < banks; b++) st[b] = BK_CLOSED;
n_open = 0;
endfunction
// Counted by iteration, not by the maintained n_open -- so the two
// disagree if the incremental bookkeeping is wrong.
function int count_open();
int n = 0;
foreach (st[b]) if (st[b] == BK_OPEN) n++;
return n;
endfunction
function bit bookkeeping_consistent();
return (n_open == count_open());
endfunction
endclass| Check | What it establishes |
|---|---|
| Replay 5,000 random commands; compare every bank state against the reference | Two representations of one reconstruction |
Confirm bookkeeping_consistent() throughout | The reference's own incremental count is sound |
| Attach at time zero; access every bank before activating it | Every transaction RES_UNKNOWN; no false violations |
| Attach mid-stream; issue precharge-all; re-query | All banks BK_CLOSED; §7's resynchronisation |
| Access a bank believed closed | Abandoned with a reason; not dropped |
| Precharge with two accesses outstanding to that bank | Both abandoned; both reasons set |
| Fill the pending queue, then issue one more column command | Dropped, counted, error raised — P8 |
| End a test with one access outstanding | P7 fires |
| Supply burst length by configuration | Every txn RES_CONFIG; fully_independent() false |
| Observe a mode write, then issue a column command | RES_KNOWN; independent |
| Run with every bank unknown; query consistency | Not claimable — P10 |
| Force the event route closed while an access is outstanding | Divergence reported — §5 |
| Delete one activate from the replay | Reference diverges from monitor; internal check stays silent |
| Sample the covergroup over a read-only suite | cp_dir shows one bin; the cross exposes it |
The thirteenth check is the report worth publishing, because it is the ceiling on everything this chapter builds:
THE MISSED COMMAND THAT NO INTERNAL CHECK CAN SEE
stimulus : 5,000 commands, with ONE activate removed from what
the monitor observes -- as a sampling race or a
mis-specified virtual interface would do.
monitor's view : bank 7 is CLOSED (it never saw the ACT)
reference's view : bank 7 is OPEN, row 1200
device's actual state : OPEN, row 1200
consistency checker :
checks=16 compared=16 unknown_skipped=0
divergences=0 <-- silent
claimable=TRUE
monitor accounting :
published=4,912 abandoned=88 dropped=0 rejected=0
what happened : the event route thinks bank 7 is closed, so the
88 accesses to it were published as ABANDONED with the reason
"access to a bank believed closed". Both reconstruction routes
agree, because both were fed the same defective stream. The
internal check compared two corrupted answers and found them
identical.
how it presents downstream : 27.4's scoreboard receives 88
abandoned transactions for a bank the design was using
correctly. The symptom is a design that appears to violate
bank legality 88 times, and the cause is one sample the
monitor never took.
why it is the ceiling : §14's WHAT IT WOULD MISS says exactly
this, and it is 27.1 §13's correlated vendor-model error in
miniature -- two derivations from one source cannot catch a
fault in the source.
the only defence : an EXTERNAL reference replaying the stream
independently, which is what this DV check is. That is why a
monitor needs its own testbench, and why §10's second row
prices consistency checking as worth its cost while not
claiming it is sufficient.18. Debugging
| Symptom | Likely cause | How to confirm |
|---|---|---|
| A burst of bank-legality violations right after attach | Unknown defaulted to closed — §6 | row_res on the failing transactions |
| Violations only on banks never activated in this test | Same cause | Which banks; compare against the activate history |
| Transactions carry wrong rows | The bank model has diverged — §1 | Replay against §17's reference |
| No transactions at all for a bank in use | Data phase mis-framed — §3's fourth row | burst_res; mode state may be unknown |
| Accesses reported abandoned for a healthy design | A missed command — §17 | Not visible internally; replay externally |
| Scoreboard mismatches with no explanation | An upstream monitor divergence — §5's callout | Check divergences() first, then replay |
| Monitor and device disagree | 7.4 §8 owns this symptom | Its debugging section |
| Consistency reports perfect agreement, bugs escape | Every bank unknown, or a shared divergence | unknown_skipped(); then §17 |
| Accesses silently missing under load | Pending queue overflow | dropped_overflow() — it is counted |
| Test ends cleanly, some accesses never completed | P7 | pending_depth() at report time |
| Every legality check passes, always | The monitor is reading the DUT — §9 | Code review of the virtual interface |
Row eleven has no instrument and that is the honest answer. A monitor wired to the design's own state reports success forever and violates no property in this chapter — §15's callout states it, and the only defence is the architectural one §14 builds plus a human reading the interface declaration.
19. Misconceptions
“A DDR monitor decodes the bus.” §1. It models the device. A decoder is a function of one cycle; legality is a function of every cycle since reset.
“A transaction is a command.” §2. A DDR transaction spans an activate, a column command and a data phase — and its row field comes from the model, because the interface never transmitted it.
“A monitor can publish as soon as it sees the command.” §2. Not without the data. So it holds partial transactions, and the queue has a depth that can be exceeded.
“An incomplete access should be dropped.” §2. Publishing it as abandoned with a reason preserves the evidence; dropping it loses an access silently.
“An unseen bank is closed.” §6. It is unknown, and defaulting to closed manufactures a violation for every access to it. Chapter 7.4 states the principle: a confident wrong answer is worse than none.
“Unknown is permanent.” §7. An activate, a precharge, a precharge-all or a reset resolves it — but mode state has no resynchronising command, so that gap is permanent for a late attach.
“Reading the DUT's state saves effort.” §9. It removes the reconstruction and the value together. The monitor then reports the design's own opinion of its own legality, and no stimulus can produce a failure.
“That would never happen deliberately.” §9's callout. It arrives by debug expedience, interface convenience, or an un-removed hierarchical reference — three routes, none careless.
“Redundant reconstruction is wasted work.” §5's callout. It converts a late, misattributed failure into an immediate, correctly attributed one — the same argument 27.2 §6 makes for antecedent covers.
“An internal consistency check makes the monitor trustworthy.” §14. Both routes derive from one stream, so a missed command corrupts both identically. §17's report is what that looks like.
“Zero divergences means the reconstruction is right.” §14, P10. It can also mean nothing was compared — every bank unknown skips every check.
“A monitor sees everything on the interface.” §10. Not electrical margin, not the array, not internally corrected errors, and not mode state it did not witness.
20. Interview Reasoning
What makes a DDR monitor harder than a bus monitor? Legality depends on accumulated state, so the monitor must model the device rather than decode a cycle — bank state, open rows, timing history and mode state, all reconstructed from the command stream.
What is a DDR transaction? An access spanning an activate, a column command and a data phase. Its row comes from the monitor's model, because the column command does not carry one.
Why does the monitor hold partial transactions? Because it cannot publish before the data returns. That gives it a queue with a depth, and exceeding the depth drops an access — which must be counted rather than silent.
What should a monitor report for a bank it has not observed? Unknown — a third state, not a default. Defaulting to closed manufactures violations; defaulting to open hides them.
How does a monitor recover certainty? An activate or precharge resolves one bank; a precharge-all resolves every bank at once. Mode-register state has no such command, so a late attach may never learn it.
Why must a monitor not read the DUT's internal state? Because then its legality checks compare the design against itself. Every check passes by construction, and no stimulus can produce a failure — which is worse than having no monitor, because it reports success.
How do you prevent that structurally? Give the monitor a virtual interface carrying only the pins a real device would see. A monitor that cannot see the DUT's state cannot come to depend on it.
What does redundant reconstruction buy? It catches divergence in the monitor immediately instead of as an unexplained scoreboard mismatch later. It cannot catch a fault in the shared input.
What is the ceiling on internal consistency checking? A missed command corrupts every route that derives from the stream. Two derivations from one source cannot detect a fault in the source — so a monitor needs an external reference replaying the stream independently.
Name something a monitor fundamentally cannot see. Internally corrected errors. On-die ECC makes a repaired fault invisible at the interface, so a device silently correcting looks identical to one with no error.
21. Exercises
-
§2's trace publishes one transaction from four events. Derive the minimum queue depth needed for a workload issuing
Ncolumn commands before the first data returns, and say what boundsNin a real device. -
§3 lists five reconstructions. For each, construct the observable symptom of its divergence and rank them by how misleading the symptom is.
-
§6 requires three bank states. Show that two states cannot express the late-start case, then construct the argument for a fourth state and say why this chapter does not add one.
-
§7 notes that mode state has no resynchronising command. Design the smallest addition to a testbench — not a device — that would let a late-attached monitor establish it independently.
-
Add a DUT state handle to §14's monitor and wire the bank model to it. Which of P1 through P14 fire? Explain why the answer is none, and what that implies about property-based defence.
-
§14's checker skips unknown banks. Construct a run in which skipping makes consistency claimable while the reconstruction is badly wrong, and propose the metric that would expose it.
-
§17's report shows a missed command defeating the internal check. Derive the number of independent replays needed to make such a fault detectable, and compare against 27.1 §4's vendor-model arithmetic.
-
The covergroup in §15 marks
TXN_PENDINGas an illegal bin. Argue both sides of usingillegal_binsrather than an assertion for that condition, and state which this chapter's own P1 implies.
22. Where This Goes
A DDR monitor is now a model rather than a decoder. Legality depends on accumulated state, so the monitor reconstructs bank state, open rows, timing history and mode state from the command stream alone; a transaction spans several commands and carries a row the interface never transmitted; an unobserved bank is unknown rather than closed; and a monitor that reads the design's own state becomes a checker that cannot fail.
Four results carry forward. Unknown is a third value and not a default — 7.4's principle, that a confident wrong answer is worse than none, applied to every derived field through an explicit resolution flag. Independence is architectural rather than asserted: no property in this chapter would catch a monitor wired to the DUT, and the defence is a pin-only interface plus a code review. Redundant reconstruction converts a late misattributed failure into an immediate correct one, and cannot do better than the stream both routes share. And a monitor needs its own testbench, because §17's missed command is invisible to every check inside it.
Two things are left open. Mode state for a late attach has no independent route — §7 establishes there is no resynchronising command, so the configuration path remains and every transaction it touches is marked RES_CONFIG rather than trusted. And the correlated-divergence ceiling is not closeable from inside, which is the same limit 27.1 §13 reached with vendor models and for the same structural reason.
What this chapter has produced is a stream of transactions, each carrying what the monitor could determine and a flag saying how well. It has not compared any of them against anything. A transaction reporting a read of bank 3, row 500, column 16 returning a particular payload is an observation — and nothing here says whether that payload was the right one.
Chapter 27.4 takes that up, and the DDR-specific difficulty is severe. The UVM track's why-scoreboards-exist owns the general argument — observing is not checking, and the expected value must be derived independently. What it does not cover is where a DDR expected value comes from: the correct payload for a read depends on every write ever issued to that address, so the reference model is a memory model, and the scoreboard's correctness now depends on a model of the entire array. The chapter is about that, about why a DDR scoreboard is distributed rather than central, and about the ordering problem that makes “the expected value” ambiguous when several accesses are in flight.
Continue learning
Related tutorials
- Related topic
Senior Verification Strategy
Use UVM, add assertions, add coverage names three tools and zero obligations. Start from what must be true, then ask what evidence each kind of truth admits — and hold the eight reasons a green assertion proves nothing.
- Related topic
DDR Scoreboards
A read's correct payload depends on every write ever issued to that address, so the reference model is the array. And with accesses in flight, the expected value stops being a value.
- Related topic
UVM Architecture for DDR
Three interfaces that are not variations of each other. They share no clock, no transaction identity, and no notion of what a failure is — and the cross-bank obligations finally need a component.
- Related topic
Physical Mapping
Five chapters of fields assembled into one map, and then the two questions none of them could ask alone: is the decomposition lossless, and can a monitor invert it from what it actually observed?
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.
