C++ Part II · Module 27 · C++ for Semiconductor Verification
Golden Models and Independence from the RTL
This is the most expensive mistake in the module and the hardest to see, because its symptom is a clean regression. A reference model's authority has to come from the specification; if instead it comes from reading the design, then wherever the design is wrong the model is wrong in the same way, the comparison agrees, and the bug ships with a green dashboard behind it. The chapter measures exactly that, on real co-simulation rather than an argument. A saturating adder was given a one-character boundary bug — a greater-than where the specification requires greater-than-or-equal — and two C++ models of the same single-sentence specification were compared against it over all 65,536 input pairs. The model written from the specification found 255 mismatches. The model written by reading the Verilog found zero, exhaustively, over the complete input space. Nothing in the regression output distinguishes that zero from the zero a correct design would produce, which is the whole problem: the failure mode of a non-independent model is indistinguishable from success.
Expert22 min readC++Part IIVerificationModeling
Part II · Module 27 · Chapter 27.4 · Golden Models and Independence from the RTL
1. The Word "Golden" Means One Thing
A reference model says what the answer should be. A golden model is a reference model with one additional property, and the property is not technical:
It was written without reading the implementation it will judge.
That is the whole definition. Not "more accurate", not "cycle-accurate", not "written in C++ rather than SystemVerilog" — independent. Its answers derive from the specification, so when the design departs from the specification the two disagree, which is the only reason a comparison is worth running.
The failure this prevents is easy to state and hard to notice. If the model was written by reading the RTL, then the model and the design share their mistakes. Everywhere the designer misread the specification and the modeller copied the designer, the two agree, the scoreboard is silent, and the bug has a passing regression as its alibi.
2. The Experiment
A single-sentence specification, chosen so there is no room to argue about what correct means:
An 8-bit unsigned saturating adder:
y = min(a + b, 255).
The design under test implements it with one character wrong:
module sat_add(input [7:0] a, input [7:0] b, output [7:0] y);
wire [8:0] sum = a + b;
// BUG: the carry case is detected with > rather than >=, so a+b == 256
// falls through to the truncated sum, which is 0.
assign y = (sum > 9'd256) ? 8'd255 : sum[7:0];
endmodulesum is nine bits, so it holds the true total. For a + b above 256 the condition is true and the output saturates correctly. For a + b exactly 256 the condition is false, so the output is sum[7:0] — the low eight bits of 256, which are zero. The specification says 255; the design says 0. This is the archetypal hardware bug: a boundary, one comparison operator, correct on either side of it.
Then two C++ models of that same sentence.
Model A, written from the specification. The sentence says the minimum of the sum and 255:
unsigned modelFromSpec(unsigned a, unsigned b)
{
const unsigned sum = a + b;
return sum > 255u ? 255u : sum; // min(a+b, 255)
}Model B, written by reading the Verilog. This is what happens when the modeller opens the design to find out what it does — a completely normal thing to do, and often encouraged as a way to "make sure the model matches":
unsigned modelFromRtl(unsigned a, unsigned b)
{
const unsigned sum = a + b; // 9 bits, as in the RTL
return sum > 256u ? 255u : (sum & 0xFFu);// the SAME > that the RTL uses
}Both models are compiled into one VPI module and both are compared against the DUT on every call, so the two arms see identical stimulus from the identical simulation. The testbench sweeps all 65,536 input pairs — this is an exhaustive test, not a random sample, which matters for how the result must be read.
3. The Result
pairs checked : 65536
spec-derived : 255 mismatches
a= 1 b=255 model=255 dut= 0
a= 2 b=254 model=255 dut= 0
a= 3 b=253 model=255 dut= 0
RTL-derived : 0 mismatches| model | authority | mismatches over 65,536 pairs |
|---|---|---|
| spec-derived | the one-sentence specification | 255 |
| RTL-derived | reading the Verilog | 0 |
Same DUT. Same stimulus. Same simulation. 255 against 0.
Three things to take from the numbers.
255 is exactly right, and that is worth checking. The mismatching pairs are those where a + b == 256 with both operands in 0 to 255: a from 1 to 255, with b = 256 − a. That is 255 pairs, and I confirmed the count independently rather than trusting the harness — a one-line Python sum over the same space returns 255. When a measurement produces a suspiciously round-looking number, the cheapest audit is to derive it a second way.
The failing rows name the boundary immediately. a=1 b=255, a=2 b=254, a=3 b=253 — every one sums to 256, and the DUT returns 0 where the model says 255. Any engineer reading three lines of that report knows where to look. Chapter 27.6 is about making a scoreboard produce reports of this shape rather than "FAIL at index 1".
And the zero is exhaustive. The RTL-derived model did not get lucky with the stimulus; it agreed with the design over the complete input space. No amount of extra testing with that model can ever find this bug, because there is no input left to try. That is what makes this failure different from inadequate coverage — more vectors, longer runs, better randomisation and higher coverage targets are all useless. The oracle is wrong in exactly the places the design is wrong.
4. Why the Mistake Is So Easy to Make
It is tempting to treat Model B as a straw man. It is not; it is what careful, well-intentioned people produce under four specific pressures.
The specification is ambiguous and the RTL is not. Real specifications leave things undefined. The RTL always has an answer, and reading it off feels like resolving the ambiguity rather than abandoning independence. The correct move is to escalate the ambiguity to whoever owns the specification; the cheap move is to copy the design.
Debugging a mismatch. A mismatch arrives, you open the RTL to understand it, you discover the design's intent differs from your reading, and you "fix" the model. Sometimes the model was wrong. Sometimes you have just deleted a real bug from the regression. Nothing in the mismatch tells you which, and the deciding evidence is the specification, not the RTL.
The model is written by the designer. Efficient — nobody understands the block better — and it guarantees that every misreading of the specification is present in both artefacts. Independence is a property of the derivation, and one person cannot derive the same thing twice independently.
Schedule. The RTL exists, the specification is 300 pages, and the model is due. This is the real reason it happens.
The common thread: every one of those pressures makes copying the RTL look like increasing accuracy, and accuracy against the implementation is precisely the property a golden model must not have.
5. How to Get Independence, Concretely
Independence is a property of process, so these are process controls — and they are cheap relative to a missed bug.
Different person. The model's author is not the RTL's author. This is the single highest-value control and the one most often traded away for schedule.
Different document. The model is written with the specification open and the RTL closed. If the specification is insufficient, that is a specification bug to be raised, not a reason to open the design.
Different structure, and review for it. The reviewer's question is not "is this model correct?" but "could this model have been written without reading the design?" Nine-bit intermediates, the design's own signal names, its state encodings, its pipeline stages leaking into an untimed model — all are tells.
Write it first, where you can. A model written before the RTL exists cannot have copied it. This is the strongest form, it costs nothing extra when the specification precedes the design, and it has the useful side effect of finding specification bugs early — the modeller is often the first person who has to make every case concrete.
Validate against an outside authority. Published check values, conformance vectors, a reference implementation from the standards body. This is the control that works even when the first four have failed, because an external vector set does not care who wrote your model — Chapter 27.11 §2 makes it the first step of the flow and it is the reason that chapter's CRC model can be trusted.
And when independence is genuinely impossible, say so in the model's documentation. A model marked "derived from the RTL; cannot detect specification-conformance errors in the saturation logic" is far more useful than one that silently has that property, because it tells the team which checks must come from somewhere else — a directed test from the specification, a formal property, or a review.
6. What Independence Does Not Mean
Four clarifications, because the principle is easy to over-apply.
Not cycle-accuracy. A golden model usually models no timing at all. Independence is about the derivation of the answers, not their timing — Chapter 27.1 §6's exclusion list stands.
Not a different language. Writing Model B in Python instead of C++ changes nothing; the mistake is copied regardless. Language diversity is not independence, and the belief that it is may be the most common confusion here.
Not never reading the RTL. The verification engineer debugging a failure must read it. The rule is narrower: the RTL is not the model's authority. Read it to understand a mismatch, then resolve the mismatch against the specification.
Not independence from other models. Two models derived from the same ambiguous specification paragraph share that paragraph's ambiguity. Independence from the implementation is the property under discussion; a specification bug needs a different control, and the usual one is that the model's author must make every case concrete and raise the ones that are not.
7. The Shapes of Bug This Finds, and Misses
Worth being precise, since the chapter's own experiment is one specific shape.
| the bug | does an independent model find it? |
|---|---|
a boundary wrong by one — §2's > for >= | yes — this is the archetype |
| a misread specification value (wrong polynomial, wrong reset) | yes, if the model read the specification |
| an off-by-one in a length or count | yes |
| an unimplemented corner case | yes, if stimulus reaches it |
| a timing violation | no — the model has no time; use SVA |
| an X-propagation bug | no — C++ has no X, Chapter 27.1 §6 |
| a specification bug both artefacts implement faithfully | no — nothing here helps |
| a bug outside the stimulus you generated | no — this is coverage, a separate problem |
The last two rows are the honest limits. An independent model raises the ceiling of what a comparison can detect; it does not make a comparison complete. Coverage still has to answer whether you reached the case, and a specification bug is immune to all of this — which is why the model-written-first control in §5 is doubly valuable, since the modeller is frequently the person who discovers that the specification does not say.
8. Reading a Zero Honestly
Given §4's callout, the practical question is what to do with a passing regression. Three habits make a zero worth something.
Prove the comparison can fail. Inject a bug deliberately and confirm the scoreboard reports it. This is the single cheapest check in verification and it directly tests the one thing a green run cannot: that the mechanism is live. Chapter 27.11 §4 runs the correct and the mutated DUT side by side for exactly this reason, and the I²C track's Module 20 made a whole discipline of it.
Count the work, not just the failures. A report of 0 mismatches from a run that checked nothing looks the same as one from a run that checked 65,536 pairs. Chapter 23.8 §2's rule — assert a floor on the amount of work before reporting the findings — is why the transcript in §3 prints pairs checked : 65536 on the line above the verdict.
Know where the model's answers came from. Not a measurement, a fact about the project. If nobody can say which document was open when the model was written, the zero means less than the dashboard suggests.
9. The Table
| the question | the answer | measured |
|---|---|---|
| what makes a model "golden" | it was written without reading the implementation | §1 |
| the DUT's bug | > where the spec requires >=, so a+b == 256 gives 0 | §2 |
| what the spec-derived model found | 255 mismatches of 65,536 | §3 |
| what the RTL-derived model found | 0 mismatches, exhaustively | §3 |
| is 255 the right number | yes — the pairs with a+b == 256, confirmed a second way | §3 |
| can more testing fix the RTL-derived model | no — the input space was exhausted | §3 |
| why does the mistake happen | ambiguous spec, mismatch debugging, designer-authored, schedule | §4 |
what does 0 mismatches prove | that the DUT and the model agree — nothing more | §4 callout |
| the structural tell | the model mirrors the design's internal decomposition | §4 callout |
| the controls | different author, different document, review, write-first, outside vectors | §5 |
| is a different language independence | no | §6 |
| what it cannot find | timing, X, specification bugs, unreached cases | §7 |
| how to make a zero mean something | inject a bug, count the work, know the derivation | §8 |
10. What to Actually Do
- Derive the model from the specification, with the RTL closed. That is the entire property.
- Have a different person write the model from the one who wrote the design, whenever the schedule permits it.
- Write the model before the RTL where the specification allows — it cannot copy what does not exist, and it finds specification bugs early.
- Review for structural resemblance. Ask whether the model could have been written without the design open; nine-bit intermediates and the design's signal names are tells.
- When a mismatch appears, resolve it against the specification, not by making the model agree with the RTL.
- Validate against vectors from outside the project — published check values or conformance suites — because that control survives the failure of every other one.
- Prove the comparison can fail by injecting a bug, every time you build one.
- Print the amount of work checked next to the verdict, so an empty run cannot look like a clean one.
- If independence is impossible, document it and name the checks that must therefore come from elsewhere.
- Expect some false mismatches. A model that never disagrees has been tuned into agreement.
11. Mistakes Worth Making Once
Writing the model by reading the RTL. 0 mismatches over an exhaustive sweep of a design with a real bug.
"Fixing" the model when a mismatch appears, without going back to the specification.
Letting the designer write the golden model. One derivation cannot be independent of itself.
Believing a different language makes it independent. The copied mistake does not care.
Resolving a specification ambiguity by reading the design. That is the moment independence is lost.
Treating 0 mismatches as evidence the design is correct. It is evidence that two things agree.
Never injecting a bug to prove the scoreboard works. The cheapest check in verification, routinely skipped.
Reporting a verdict without the work count. A run that checked nothing reads as a pass.
Expecting an independent model to find a timing or X bug. Different instrument required.
Tuning the model until mismatches stop. You have rebuilt Model B incrementally.
12. Interview Relevance
The frozen curriculum marks this Critical and Expert, and it is a strong discriminator because the answer is conceptual rather than syntactic. The question is usually what is a golden model? or how do you know your reference model is right?
Lead with the definition that carries the content: a golden model is a reference model written without reading the implementation it judges, and the reason is that a model derived from the RTL reproduces the RTL's mistakes, so the comparison agrees and the bug ships. Then the measurement, which is short and hard to argue with: a saturating adder with a > where the specification requires >=, two C++ models of the same one-sentence specification, exhaustive comparison over all 65,536 input pairs — 255 mismatches from the spec-derived model and 0 from the RTL-derived one, against the same DUT and the same stimulus.
The point that marks a senior answer is what the zero means. 0 mismatches proves the DUT and the model agree; concluding anything about the specification needs the second premise that the model matches the specification, and that premise is established by the model's derivation and cannot be measured from the regression at all. Which is why no amount of extra coverage rescues it — the RTL-derived model agreed over the complete input space.
Then the controls, briefly: different author, specification open and RTL closed, review for structural resemblance, write the model first where possible, and validate against published vectors because that survives the failure of the others. Finishing with "and I would inject a bug to prove the scoreboard can fail" is the kind of concrete habit interviewers remember.
13. What You Should Be Able to Say Now
- That golden means written without reading the implementation, and that this is a property of the derivation rather than of the language, accuracy or timing.
- That the measured DUT bug was a
>where the specification requires>=, soa + b == 256produced 0 instead of 255. - That over all 65,536 pairs the spec-derived model found 255 mismatches and the RTL-derived model found 0.
- That 255 is exactly the number of pairs summing to 256, confirmed independently rather than taken from the harness.
- That the RTL-derived model's zero is exhaustive, so more stimulus, more seeds and more coverage cannot help.
- That
0 mismatcheslicenses only "the DUT and the model agree", and that the second premise is unmeasurable from the results. - That a mismatch is self-evidencing and agreement is not — the same asymmetry as a sanitizer's finding versus its silence.
- The structural tell: a model that mirrors the design's internal decomposition probably came from it.
- The controls: different author, specification open and RTL closed, review, write-first, and outside vectors as the one that survives the others failing.
- That independence is not cycle-accuracy, not a different language, and not a ban on ever reading the RTL — only on treating it as the authority.
- That it finds boundary, misread-value and off-by-one bugs, and cannot find timing, X, unreached cases or a faithful implementation of a wrong specification.
- That a zero is worth something only if you injected a bug to prove the comparison fires and printed the work count beside the verdict.
14. Exercises
Exercise 1 — reproduce it. Write the saturating adder with the > bug, then two models: one from the sentence y = min(a+b, 255) and one by reading your Verilog. Compare both against the DUT exhaustively.
Exercise 2 — confirm the count. Predict the number of mismatching pairs from the specification alone, then check it against the harness. They must agree before you believe either.
Exercise 3 — the exhaustive zero. Convince yourself that no additional stimulus can make the RTL-derived model find the bug, by noting that the sweep already covered every input.
Exercise 4 — the structural tell. Give both models to someone who has not seen the RTL and ask which one was written from it. Record what gave it away.
Exercise 5 — the ambiguity trap. Find a genuinely ambiguous sentence in a specification you have. Write down what you would do instead of opening the RTL.
Exercise 6 — prove the comparison fires. Take your correct DUT, inject a different bug, and confirm the spec-derived model catches it and reports the boundary clearly.
Exercise 7 — the empty run. Make your testbench check zero pairs and confirm your report still says 0 mismatches. Then add the work count that makes that impossible to misread.
15. Summary
- A golden model is a reference model written without reading the implementation it judges. That is the definition, and it is about derivation, not language or accuracy.
- The failure it prevents is shared mistakes: wherever the designer misread the specification and the modeller copied the designer, the two agree and the comparison is silent.
- Measured on real co-simulation, exhaustively over 65,536 input pairs against a DUT whose carry test uses
>instead of>=: the spec-derived model found 255 mismatches, the RTL-derived model found 0. - 255 is exactly the count of pairs where
a + b == 256, confirmed by a second independent derivation rather than trusted from the harness. - That zero is exhaustive, so no additional stimulus, seed, or coverage target can recover the bug. This is categorically different from a coverage hole.
0 mismatchesproves only that the DUT and the model agree. Everything else requires the model to match the specification, which the regression cannot measure — a mismatch is self-evidencing, agreement is not.- The mistake is made by careful people under four pressures: an ambiguous specification, debugging a mismatch, the designer writing the model, and schedule. All four make copying look like improving accuracy.
- The controls are procedural: a different author, the specification open and the RTL closed, review for structural resemblance, write the model first, and validate against vectors from outside the project.
- The structural tell is a model shaped like the implementation — nine-bit intermediates and masks where the specification says "the minimum of the sum and 255".
- It cannot find timing bugs, X-propagation, unreached cases, or a specification bug both artefacts implement faithfully.
- Make a zero mean something: inject a bug to prove the comparison can fail, and print the amount of work checked next to the verdict.
Chapter 27.5 turns to the other input of that comparison — producing stimulus that a failure can be replayed from.
Standards & specifications
- Governing standard
- ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)
Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.
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 C++ Programming curriculum.
