C Part II · Module 24 · C Reference Models and Scoreboards
A CRC Reference Model, Validated Against Known Vectors
A CRC is the one thing in a testbench with a right answer published by somebody outside the project, which makes it the clearest demonstration of the habit this module is about: validate the oracle, then let it judge. Measured in that order — the model reaches the published 0xF4, its incremental form agrees with its one-shot form, a clean design matches on all nine bytes, and a design whose polynomial is one bit wrong mismatches on eight of nine and fails the published check on its own evidence.
Advanced22 min readCPart IIVerificationDPI-C
Part II · Module 24 · Chapter 24.5 · A CRC Reference Model, Validated Against Known Vectors
1. The Order Is the Lesson
Chapter 22.4 built a CRC in C and checked it against its published value, as part of learning how to import a function. This chapter uses the same file, unchanged, and is about something else: the moment it becomes an oracle.
Chapter 24.1 set out what that promotion requires. A CRC is the clearest possible demonstration, because it has the one thing most models never get — a right answer published by somebody outside the project.
So the chapter is an order of operations, and the order is the whole point:
1. validate the model against the published value
2. validate the form the testbench will actually call
3. then, and only then, let it judge a designAlmost every model in practice skips the first two, because the design is available and running something feels like progress. Chapter 24.3 measured where that ends up.
2. Validating the Oracle
Measured, before the model judges anything at all:
one-shot over the check string : 0xF4 (published 0xF4) matchOne comparison, against a number computed by whoever specified CRC-8/SMBUS, before this project existed. That is what Chapter 24.1 §3 means by the specification being the authority — here it is unusually concrete, because the specification shipped a test vector.
And the second check, which is the one people skip:
incremental, one byte at a time : 0xF4 equals the one-shotA testbench does not have the whole message in one place; it has a byte per cycle. So the function the scoreboard actually calls is the incremental one, and it is a different function from the one that was just validated. Chapter 22.4 §4 listed how the two can diverge — an initial value applied per byte instead of per message, a final step applied in the wrong place — all of which leave one form correct and the other wrong.
Two checks, because there are two functions.
Then the prefix table, which is not a check but a debugging asset:
97 72 C0 C2 CB FD 78 C7 F4Those are the nine intermediate values a correct design passes through. §4 is what they are for.
3. Judging
Only now does the model judge anything. Measured, a clean design, compared byte by byte through the comparison path Chapter 24.4 built:
clean DUT : 0xf4 after 9 bytes (published check value 0xF4): match
9 bytes checked against the model, 0 mismatch(es)Note that this design is checked twice, independently:
- its final value against the published
0xF4, with no model involved at all - its every intermediate byte against the model
That is unusual and worth having. Most blocks only get the second kind.
And a design whose polynomial is 0x06 where the specification says 0x07 — a one-bit transcription slip, the kind that gets written:
poly-bug DUT: 0x2a after 9 bytes (published check value 0xF4): MISMATCH
9 bytes checked, 8 mismatch(es), first at byte 1
[SB] first mismatch at byte 1: expected 0x97, actual 0xA6Caught twice. The model disagrees from the very first byte, and the final value 0x2A fails the published check on its own evidence — without the model, and without anything the project wrote.
4. Eight of Nine, and What a Rate Is Worth
The polynomial is wrong on every byte, and the mismatch count is 8 of 9. One byte happened to coincide anyway.
That is the third time this module has produced a rate that undersells the fault:
- Chapter 24.2: a real specification departure visible in 2 of 70 checks.
- Chapter 24.4: a comparison error producing 16 of 30 on a design with no bug.
- Here: a wholly wrong algorithm producing 8 of 9.
Put together, they say something useful and slightly uncomfortable:
A mismatch rate tells you almost nothing about severity. A catastrophic algorithmic error can read as 89%, a cosmetic-looking one as 3%, and a perfectly good design as 53% when the harness is wrong.
What is informative is where the first mismatch is, which is why every report in this module carries it. Byte 1 says the very first operation disagrees — an initial value, a polynomial, a bit order. Byte 28 (Chapter 24.2) says everything was fine until some condition was reached, which points at a boundary.
That is what the prefix table is for. Measured, the nine values a correct design passes through:
97 72 C0 C2 CB FD 78 C7 F4A design that diverges at byte 5 has a different problem from one that diverges at byte 1, and without the intermediate values a failing message is just a failing message.
5. The Misconception: The Model Matched the DUT, So the CRC Is Right
"We ran a thousand packets through the model and the RTL and every CRC matched. The model is correct."
This is the right sentence with its two halves swapped.
Agreement with the design is what the oracle is for. It is the output of the arrangement, the thing you are trying to establish. Using it as evidence that the model is correct uses the conclusion as the premise.
The published value is what makes the model an oracle. It is the only input to the arrangement that did not come from the project, and Chapter 24.1's independence obligation is exactly the claim that such an input exists.
And a model validated only against the design is Chapter 24.3's RTL-derived model arrived at slowly: not transcribed, but adjusted until it agrees, which reaches the same place. That chapter measured the destination — 51 mismatches against 0 on the same design.
The practical form, and it is one line in a test:
assert(crc8_smbus((const uint8_t *)"123456789", 9) == 0xF4);That assertion runs before anything else and costs nothing. If it ever fails, every result the model has produced since the last time it passed is in question — which is a far better thing to learn from one line than from a week of disagreements.
The accurate position:
Validate the oracle against something outside the project, then let it judge. A thousand matching packets confirm that two things agree; the published value is the only part of the arrangement that says what they ought to be.
6. What You Should Be Able to Say Now
- The order is the lesson: validate the model · validate the form the testbench calls · then judge.
- A CRC is the clearest case because its right answer was published by somebody outside the project.
- Measured, step 1: the one-shot model gives 0xF4, matching the published value.
- Measured, step 2: the incremental form gives
0xF4and equals the one-shot — two functions, two checks. - The clean design is checked twice independently: its final value against the published
0xF4, and every byte against the model — measured, 0 of 9 mismatches. - The polynomial-bug design is caught twice: 8 of 9 mismatches from byte 1, and
0x2Afails the published check on its own. - 8 of 9, not 9 of 9 — one byte coincided even with the wrong polynomial.
- A mismatch rate says almost nothing about severity — 2 of 70 was a real bug, 16 of 30 was a correct design, 8 of 9 is a wholly wrong algorithm.
- Where the first mismatch is, is informative — byte 1 points at an initial value, a polynomial or a bit order.
- The prefix table
97 72 C0 C2 CB FD 78 C7 F4turns a failing message into a failing byte. - Agreement with the design is the output, not the evidence — a model validated only against the DUT is 24.3's model reached slowly.
7. Exercises
- Validate a CRC implementation against its published check value and then against its own incremental form. Why are these two different checks?
- Build the prefix table for your CRC. A design first diverges at entry 5 — what does that rule out?
- Change a polynomial by one bit and count the mismatches over a nine-byte message. Is the rate what you expected?
- List the three mismatch rates in §4 with their causes. What would you now ask before treating a rate as a severity estimate?
- Write the one-line assertion from §5 and put it where it runs before any judging. What does its failure invalidate?
- For a block in your project with no published vectors, work through §4's callout and say which source of an independent answer you can actually get.
- Derive one expected value by hand from a format you work with, showing the arithmetic. How long did it take?
- A colleague validated a model by running it against the RTL on 10,000 packets. Explain, in two sentences, what that established and what it did not.
8. Summary
- Validate, then judge — and validate the form the testbench will actually call.
- Measured: one-shot 0xF4 against the published value; incremental 0xF4, equal to the one-shot.
- Clean design: 0xF4 and 0 of 9 mismatches, checked twice — by the published value and by the model.
- Polynomial-bug design: 0x2A, 8 of 9 mismatches from byte 1, failing the published check on its own evidence.
- 8 of 9, not 9 of 9 — even a wholly wrong algorithm leaves a coincidence.
- Rates say little about severity — 2 of 70 real, 16 of 30 a correct design, 8 of 9 catastrophic.
- The first mismatch's position is the informative part; the prefix table localises it.
- When there are no published vectors: a worked example · a hand calculation · a second author — never the design.
- Agreement with the design is the output, not the evidence.
A CRC has a published answer, which is what made this chapter short. Arithmetic rarely does — and the places where two defensible implementations disagree are exactly the ones a specification tends to describe in a single word.
Where this fits
Part of the C Programming curriculum.
