AMBA CHI · Module 17 · CHI Verification
Reference Models
The scoreboard's expected value comes from a reference model; this chapter builds one that catches bugs. A reference model is a separate, simpler implementation — an abstract memory model computing what a read should return, independent of the design. The defining requirement is independence: a comparison has power only when the sides can disagree, so the reference shares no logic or state with the design. If it shares the design's logic or state, a bug in both produces matching wrong answers and passes — a common-mode failure. The failure to avoid is a reference that samples the design's state, so a bug makes both actual and expected wrong the same way, and the compare passes. Representative model, not the specification.
Advanced16 min readAMBA CHIReference ModelGolden ModelIndependenceCommon-Mode
Module 17 · Chapter 17.4 · CHI Verification
Project thread — 17.3 matched responses to expected entries. 17.4 supplies the expected values; 17.5 formalizes rules as SVA.
1. Learning Outcomes
By the end of this chapter you should be able to:
- Explain that a reference model computes each transaction's expected result for the scoreboard.
- State that it is a separate, simpler implementation of the architectural behavior.
- Explain why the reference must be independent of the design.
- Define a common-mode failure — a bug shared by design and reference.
- Diagnose the false pass from a reference that samples the design's state.
- Implement a representative independent reference model in SystemVerilog, Verilog-2001, and VHDL.
2. Why Should I Learn This?
The scoreboard (Chapter 17.3) is only as good as the expected values it compares against — and those come from the reference model. A reference model is a golden, simpler implementation of what the architecture should do: an abstract memory-and-cache model that, for any read, computes the value the read ought to return. If the reference is right, the scoreboard catches every wrong value the design produces. If the reference is wrong in the same way the design is, the scoreboard catches nothing.
That is the crux: independence. A comparison has power only when the two sides can disagree about a bug. If the reference model shares the design's logic, state, or a common misreading of the spec, then a bug in both produces matching wrong answers — they agree, the compare passes, and the bug is invisible. This is a common-mode failure, and it is the most dangerous verification trap because it makes a whole comparison-based environment silently useless. This chapter is the reference model and the one property that gives it value — genuine independence from the design it checks.
3. Key Terms
4. Previous Chapter Connection
This chapter feeds the scoreboard of Chapter 17.3: the scoreboard matches a response to its expected entry (by transaction key), and the value in that entry is computed here, by the reference model. Together they form the data-checking half of the environment — 17.3 correlates, 17.4 predicts.
The reference model is a much simpler version of what Module 16 built. Where the RTL implemented coherence with directories, snoops, and trackers, the reference implements the same architectural effect — the value a read returns — with a plain memory model and last-writer tracking. That simplicity is deliberate: the reference is easy to get right because it is abstract, and its abstraction is a different implementation than the RTL, which is exactly what makes the comparison meaningful. This chapter is where the abstract "what should happen" (Modules 6–15) becomes an executable oracle — and where sharing it with the "what does happen" (Module 16) would defeat the whole purpose.
5. Core Concept — an independent oracle
A reference model is a separate, simpler implementation that computes each transaction's expected result — and it must be independent of the design, or a shared bug hides in a common-mode false pass.
- A golden oracle. The reference model computes what each transaction should produce — for a read, the value the address should return — from its own abstract memory/cache model.
- Simpler than the design. It models the architectural effect (last write to an address defines its value), not the RTL mechanics (directories, snoops). Simplicity makes it trustworthy.
- Independence is the whole point. The reference and the design must share no logic, state, or common assumption — so their errors do not correlate. A comparison only has power when the two can disagree.
- Common-mode defeats it. If the reference shares the design's logic/state (e.g. it samples the design's memory), a bug in both makes both answers wrong the same way — they match, the compare passes, and the bug is invisible.
The synthesis:
A reference model is a separate, simpler (golden) implementation that computes each transaction's expected result from its own architectural state. Its value rests entirely on independence from the design — because a comparison only catches a bug when the two sides can disagree. If the reference shares the design's logic or state (a common-mode failure), a shared bug produces matching wrong answers and the compare passes — the bug is invisible. Keep the reference independent.
6. Engineering Mental Model — two accountants, not one ledger
Think of checking a company's books by having the numbers recomputed independently.
- The right way: a second accountant (the reference model) recomputes the totals from the original receipts, using their own method, and you compare their total against the first accountant's (the design). If they disagree, there is an error to find.
- The comparison works because the two accountants worked independently — an error by one is unlikely to be matched by the other.
- The wrong way: the "second accountant" simply copies the first accountant's total and writes it down as their own (sampling the design's state). Now the two totals always agree — by construction.
- If the first accountant made an error, the copied "check" total contains the same error, they match, and the audit reports all correct. The check verified nothing — it only confirmed the number equals itself.
Two independent accountants can catch each other's errors; one accountant whose "check" copies the original catches nothing. A reference model that samples the design is the copying accountant — it always agrees, so it never catches a bug.
7. Engineering Diagram — independent reference vs the design
Both take the same stimulus but compute their results separately — the reference from its own state, never from the DUT's. The compare is meaningful because the two paths are independent. The DebugLab draws an arrow from the DUT's state into the reference, collapsing the independence.
8. Independent vs Shared Reference
The two structures contrasted.
| Property | Independent reference | Shared / sampling reference (anti-pattern) |
|---|---|---|
| Expected value source | own architectural state | the design's state |
| Can it disagree with the DUT? | yes | no — always agrees |
| Catches a design-only bug? | yes — mismatch | no — matching wrong answers |
| Failure mode | — | common-mode false pass |
The rule to carry: a reference model must derive the expected value without ever consulting the design. Its power comes entirely from being a second, independent computation — the moment it reads the design's state, it stops being a check and becomes a copy, and a copy always agrees. Independence should extend beyond state to implementation: a different author, a different abstraction, a fresh reading of the spec — so that even a conceptual bug (a misread spec rule) is unlikely to be shared. The more the reference and design differ in how they compute, the more bugs their disagreement can catch.
9. Why a Sampling Reference Catches Nothing
The common-mode failure, made explicit.
- The reference should predict independently. For a read, the reference should compute the expected value from its own last-writer model — "the last value written to this address."
- A sampling reference reads the design. Instead, it obtains the expected value by reading the design's cache or memory — so "expected" is defined as "whatever the design has."
- Actual and expected are the same source. The scoreboard then compares the design's response against the design's own state — two values from the same place. They agree by construction.
- A design bug corrupts both. If a design bug writes the wrong value, both the actual response and the sampled expected are that wrong value. They match, the compare passes, and the bug is invisible.
The point to carry:
Independence is the hidden assumption behind every comparison-based check, and its violation is uniquely dangerous because it fails silently and completely — not one test, but the entire comparison loses its power, while continuing to run green. A team looking at a passing scoreboard cannot tell, from the pass itself, whether the reference is independent; the pass looks identical whether the check is rigorous or vacuous. This is the common-mode trap in its purest form, and it recurs at every level: a reference that samples the design (state common-mode), a reference and design written by the same engineer from the same mental model (conceptual common-mode), or a reference and design that both call a shared library implementing the very behavior under test (code common-mode). The defense is to maximize the difference in how the two arrive at their answers — different state, different code, different author, different abstraction — because every dimension of difference is a dimension along which a bug can be caught. The most valuable reference model is the one least like the design, and the instinct to "reuse the RTL's logic to save effort" in the reference is exactly the instinct that destroys its value. A checker must compute its expectation from first principles, never from the thing it is checking.
10. Predicting a Read — independent vs sampling
A write of 0x11 to address X, then a read of X that the design bug returns as 0x99.
- Write X = 0x11. The stimulus writes 0x11 to address X. The independent reference updates its own model:
ref_mem[X] = 0x11. The design updates its RTL state (buggily, as it turns out). - Read X — design returns 0x99 (bug). The design's read returns 0x99 — wrong; it should be 0x11.
- Independent reference — expected 0x11. The reference computes the expected value from its own model:
ref_mem[X] = 0x11. The scoreboard compares 0x99 (actual) vs 0x11 (expected) → mismatch → bug caught. - Sampling reference — expected 0x99. The sampling reference reads the design's state for X, which the bug set to 0x99, and calls that the expected value. The scoreboard compares 0x99 vs 0x99 → match → pass.
- The bug is invisible. The sampling reference defined "expected" as "whatever the design has," so the design's wrong value equals its own expectation. The compare passes; the bug escapes.
The independent reference disagreed with the design and caught the bug; the sampling reference agreed by construction and hid it. The DebugLab is step 4.
11. Checker / Monitor View — an independent reference model
The reference maintains its own memory; a read's expected value comes from its own last-writer state, never the design. Representative.
// Representative independent reference model (educational).
// The reference maintains its OWN architectural state (a memory of last-written values).
// A read's expected value comes from ref_mem[addr] -- computed independently, NEVER by
// sampling the DUT. Sampling the DUT makes expected == actual by construction (common-mode).
module chi_ref_model #(parameter AW = 8, parameter DW = 32, parameter NMEM = 256) (
input logic clk, rst_n,
input logic wr_valid, // an observed write (from stimulus)
input logic [AW-1:0] wr_addr,
input logic [DW-1:0] wr_data,
input logic rd_valid, // a read whose expected value is requested
input logic [AW-1:0] rd_addr,
output logic [DW-1:0] expected_data // the value the read SHOULD return
);
// The model's OWN memory -- maintained separately from the DUT.
logic [DW-1:0] ref_mem [NMEM];
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i = 0; i < NMEM; i++) ref_mem[i] <= '0;
end else if (wr_valid) begin
ref_mem[wr_addr] <= wr_data; // last-writer defines the value
end
end
// Expected read value from the reference's OWN state -- independent of the DUT.
assign expected_data = ref_mem[rd_addr];
endmoduleThe same behavior in Verilog-2001:
// Representative independent reference model (Verilog-2001).
module chi_ref_model #(parameter AW = 8, parameter DW = 32, parameter NMEM = 256) (
input clk, rst_n, wr_valid, rd_valid,
input [AW-1:0] wr_addr, rd_addr,
input [DW-1:0] wr_data,
output [DW-1:0] expected_data
);
reg [DW-1:0] ref_mem [0:NMEM-1];
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0; i < NMEM; i = i + 1) ref_mem[i] <= {DW{1'b0}};
end else if (wr_valid) begin
ref_mem[wr_addr] <= wr_data;
end
end
assign expected_data = ref_mem[rd_addr]; // from the model's OWN memory
endmoduleAnd in VHDL:
-- Representative independent reference model (VHDL).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity chi_ref_model is
generic ( AW : integer := 8; DW : integer := 32; NMEM : integer := 256 );
port (
clk, rst_n : in std_logic;
wr_valid : in std_logic;
wr_addr : in std_logic_vector(AW-1 downto 0);
wr_data : in std_logic_vector(DW-1 downto 0);
rd_valid : in std_logic;
rd_addr : in std_logic_vector(AW-1 downto 0);
expected_data : out std_logic_vector(DW-1 downto 0)
);
end entity;
architecture rtl of chi_ref_model is
type mem_t is array (0 to NMEM-1) of std_logic_vector(DW-1 downto 0);
signal ref_mem : mem_t := (others => (others => '0'));
begin
process (clk, rst_n)
begin
if rst_n = '0' then
ref_mem <= (others => (others => '0'));
elsif rising_edge(clk) then
if wr_valid = '1' then
ref_mem(to_integer(unsigned(wr_addr))) <= wr_data; -- own last-writer state
end if;
end if;
end process;
expected_data <= ref_mem(to_integer(unsigned(rd_addr))); -- independent of the DUT
end architecture;All three maintain the reference's own ref_mem and derive expected_data from it — never from the DUT. The DebugLab replaces ref_mem[rd_addr] with a sample of the DUT's memory, so expected always equals actual.
12. Assertion View — the reference is independent of the DUT
The properties formalize independence: expected comes from the model's own state, and a design-only corruption shows as a mismatch.
// Bind to the environment (reference model + DUT + scoreboard).
// 1. The expected value equals the reference's own last-written value for the address.
property p_expected_from_ref_state;
@(posedge clk) disable iff (!rst_n)
rd_valid |-> (expected_data == ref_mem[rd_addr]);
endproperty
// 2. The expected value does NOT depend on the DUT's state (structural independence).
// expected_data's logic cone must exclude any DUT signal -- checked by inspection.
// 3. A design-only value corruption (actual != ref) is caught as a mismatch, not hidden.
property p_dut_corruption_caught;
@(posedge clk) disable iff (!rst_n)
(rd_valid && (dut_read_data != ref_mem[rd_addr])) |-> scoreboard_mismatch;
endpropertyThe system point, beyond the checks:
The decisive property here —
p_expected_from_ref_state, and especially the structural independence ofexpected_datafrom any DUT signal — is one that a simulation cannot prove, because a sampling reference and an independent reference produce identical waveforms on a correct design. The two are distinguishable only on a buggy design, where the independent reference disagrees and the sampling one does not — but if the environment has a sampling reference, you will never see that disagreement, because it is exactly the bug being hidden. This is the paradox of common-mode failures: the very defect they conceal is what you would need to reveal them. So the independence of a reference model cannot be established by running tests; it must be established by construction and review — confirming, in the code and the architecture, that the reference computes its expectation from its own state and never reads the design. This elevates a code-review discipline to a first-class verification activity: for every comparison in the environment, ask "where does the expected value come from, and is that source independent of the actual?" A green scoreboard answers neither question. Independence is designed and reviewed, not tested — the same lesson as vacuity (Chapter 17.5) and coverage gaps (Chapter 17.6): the health of a check is a property of the check, invisible in its passing output.
- What it proves: expected comes from the reference's own state; a DUT-only corruption is caught.
- What it does not prove: the reference model's own logic is correct — that needs review/redundancy.
- Bug signature:
expected_datawhose logic cone includes a DUT signal — a sampling reference.
13. Testbench — a design-only corruption must be caught
Drives a write then a read whose "design" value is corrupted, and checks the independent reference disagrees.
module tb_chi_ref_model;
localparam AW = 8, DW = 16, NMEM = 256;
logic clk = 0, rst_n = 0, wr_valid, rd_valid;
logic [AW-1:0] wr_addr, rd_addr;
logic [DW-1:0] wr_data, expected_data;
logic [DW-1:0] dut_read_data; // stand-in for the DUT's actual response
int errors = 0;
chi_ref_model #(.AW(AW), .DW(DW), .NMEM(NMEM)) ref_m (.*);
always #5 clk = ~clk;
initial begin
wr_valid = 0; rd_valid = 0; wr_addr = 0; rd_addr = 0; wr_data = 0;
@(posedge clk) rst_n = 1;
// Write X = 0x0011 (observed by the INDEPENDENT reference).
@(posedge clk) begin wr_valid = 1; wr_addr = 8'h10; wr_data = 16'h0011; end
@(posedge clk) wr_valid = 0;
// Read X: the reference expects 0x0011 from its OWN state.
@(posedge clk) begin rd_valid = 1; rd_addr = 8'h10; end
#1;
if (expected_data !== 16'h0011) begin errors++; $display("FAIL reference expected wrong value"); end
else $display("PASS reference expects 0x%04h (from its own state)", expected_data);
// The DUT returns a CORRUPTED value 0x0099 (a design-only bug).
dut_read_data = 16'h0099;
// Independent reference disagrees -> mismatch -> bug caught.
if (dut_read_data === expected_data) begin errors++; $display("FAIL reference agreed with buggy DUT (common-mode?)"); end
else $display("PASS independent reference DISAGREES: expected 0x%04h, actual 0x%04h -> caught", expected_data, dut_read_data);
// Sanity: a correct DUT value matches the reference.
dut_read_data = 16'h0011;
if (dut_read_data !== expected_data) begin errors++; $display("FAIL correct value not matched"); end
else $display("PASS correct DUT value matches reference");
@(posedge clk) rd_valid = 0;
if (errors == 0) $display("ALL TESTS PASSED");
else $display("%0d FAILURE(S)", errors);
$finish;
end
endmoduleExpected output:
PASS reference expects 0x0011 (from its own state)
PASS independent reference DISAGREES: expected 0x0011, actual 0x0099 -> caught
PASS correct DUT value matches reference
ALL TESTS PASSED14. DebugLab — a reference model that samples the design
A reference model that samples the design
REFERENCE MODEL SAMPLES THE DESIGN -> EXPECTED EQUALS ACTUAL BY CONSTRUCTION -> COMMON-MODE FALSE PASSReal data bugs ship despite a fully green scoreboard — the design returns wrong values in silicon, yet every simulation comparison passed. There are no mismatches ever, on any test, which is itself suspicious: a healthy environment occasionally catches injected or real bugs, but this one catches nothing, because the reference always agrees with the design.
Expected and actual came from the same place:
write X = 0x11; DUT bug corrupts X's stored value to 0x99
reference model (sampling): expected_data = DUT_memory[X] = 0x99 (reads the DUT!)
DUT read response (actual): 0x99
scoreboard compares: actual 0x99 vs expected 0x99 -> MATCH -> PASS (bug HIDDEN)
-> expected == actual by construction; the reference never predicted independently
correct: reference keeps its OWN ref_mem[X] = 0x11 -> expected 0x11 -> mismatch -> caughtThe reference reported the design's own (wrong) value as the expectation.
The reference model sampled the design's state to compute the expected value, instead of maintaining its own architectural model. From that point expected always equalled actual, and no comparison could ever fail.
A comparison catches a bug only if the two sides can disagree, so the reference model must compute the expected value from its own independent state, never by sampling the design; a sampling reference is a common-mode failure that makes expected equal actual by construction. The reference's entire value is being a second, independent computation — the moment it reads the design's state it becomes a copy, and a copy always agrees, so a shared (design) bug corrupts both sides identically and passes. Independence must be established by construction and review, not testing, because a sampling and an independent reference produce identical waveforms on a correct design — the difference shows only on the buggy design the sampling reference is hiding. The most valuable reference is the one least like the design: different state, code, author, and abstraction, so every dimension of difference is a dimension along which a bug is caught.
Make the reference maintain its own architectural state — its own memory and last-writer model — and compute the expected value from that state, never consulting the design, exactly as the reference model does. Establish independence by review: confirm expected_data's logic cone contains no DUT signal. A design-only bug then shows as a disagreement. Compute the expectation from first principles, not from the thing being checked.
15. Common Mistakes
- Sampling the design. Assumption: the design's state is the expectation. Bug: common-mode false pass (the DebugLab). Prevention: maintain own state.
- Same author for both. Assumption: one engineer is efficient. Bug: conceptual common-mode. Prevention: independent author/abstraction.
- Shared library for the checked behavior. Assumption: reuse saves effort. Bug: code common-mode. Prevention: independent implementation.
- Establishing independence by testing. Assumption: green means independent. Bug: invisible to tests. Prevention: review the logic cone.
- A reference as complex as the design. Assumption: match the RTL. Bug: same bugs, less trust. Prevention: keep it abstract and simple.
- Not reviewing the expected-value source. Assumption: it is obviously independent. Bug: hidden sampling. Prevention: audit where expected comes from.
16. Engineering Checklist
- Give the reference its own architectural state (memory / last-writer model).
- Compute the expected value from that state, never from the design.
- Keep the reference simpler (more abstract) than the RTL.
- Maximize independence — different author, abstraction, and spec reading where possible.
- Review that
expected's logic cone contains no DUT signal. - Confirm the environment occasionally catches injected bugs (not always green).
17. Key Takeaways
- A reference model computes each transaction's expected result for the scoreboard.
- It is a separate, simpler implementation of the architectural behavior.
- Its power comes entirely from independence of the design.
- A reference that samples the design makes expected equal actual — common-mode.
- A common-mode failure hides bugs silently while the scoreboard runs green.
- Compute the expectation from first principles; the model here is representative.
18. Quick Revision
Reference models. The scoreboard (Chapter 17.3) compares each actual response against an expected value, and that value comes from a reference (golden) model — a separate, simpler implementation of the architecture's behavior (an abstract memory and last-writer model that, for any read, computes the value it should return). Its entire power rests on independence: a comparison catches a bug only when the two sides can disagree, so the reference must share no logic, state, or common assumption with the design — ideally a different author, abstraction, and spec reading — so their errors do not correlate. The failure to avoid: a reference that computes the expected value by sampling the design's own state (reading the DUT's cache/memory) instead of maintaining its own. Then expected equals actual by construction — both come from the same place — so a design bug corrupts both identically, they match, the compare passes, and the bug is invisible: a common-mode failure that silently voids the whole environment while the scoreboard runs green. Independence cannot be established by testing (a sampling and an independent reference are indistinguishable on a correct design) — only by construction and review: confirm the expected value's logic depends on no DUT signal. The most valuable reference is the one least like the design. Compute the expectation from first principles, never from the thing being checked. Representative model; 17.5 covers SVA assertions and the vacuity trap.
Coming Next
Chapter 17.5 — CHI Assertions. Reference models and scoreboards check data; assertions check temporal rules directly in SystemVerilog. Chapter 17.5 covers CHI assertions — writing SVA properties for protocol and ordering rules, and the vacuity trap: an implication whose antecedent never triggers passes forever while checking nothing, so a passing assertion is worthless unless its trigger is proven to fire.