I²C · Module 22
Assertion or Scoreboard? Deciding Where a Rule Lives
Answers the question by measurement rather than opinion: eight defects injected into a verified target, and a three-way split — illegal traffic 2 of 2 caught, protocol misreading 0 of 3, data 0 of 3. Ends with a one-question test for where any rule belongs.
Every verification plan reaches the same argument. A rule has to be checked — does it belong in an assertion or in a scoreboard? The usual answers are slogans: assertions are for temporal things, scoreboards are for data. Both are roughly true and neither tells you what to do with "the target must acknowledge its own address", which is temporal, about data, and about neither.
This chapter answers it by measurement. The property set built across 22.2 to 22.5 is run against Module 18's verified target with defects deliberately injected into it, and what the properties catch is counted.
1. This Module Can Actually Run Its Assertions
Module 21 published a complete UVM environment that has never been compiled, because nothing in this environment can execute UVM. Assertions are a different situation and it is worth stating plainly at the top.
2. The Experiment
Eight single-line defects injected into the target's VHDL, one at a time, and grouped by what kind of defect they are rather than by which file they are in. For each, the bench drives ordinary legal traffic and the question is whether any property fires.
Only legal traffic counts. A property firing on a deliberately illegal trace proves the property works — that is 22.2's subject — but it says nothing about whether the property would catch a broken device.
That is the answer, and it is sharper than the slogan.
3. What Assertions Caught, and Why Only Those
Both defects the properties caught have one thing in common: the target itself drove the bus illegally.
- The acknowledge is never released. SDA stays pulled after the acknowledge slot, so the line is held by two devices across a bit boundary and the next framing attempt cannot produce an edge.
- The acknowledge is driven without waiting for SCL to fall. The target pulls SDA low while SCL is high — which is the definition of a START — so the bus now carries framing nobody asked for.
The properties that fired name the damage precisely:
| defect | properties that fired |
|---|---|
| acknowledge never released | STOP with no transfer open · STOP mid-byte · sustained two-driver SDA |
| acknowledge driven early | START inside the bus-free interval · STOP with no transfer open · STOP mid-byte · sustained two-driver SDA |
Notice that neither list contains a property about acknowledgement. The target's acknowledge logic is broken and what the property set reports is framing damage — because that is what appeared on the wire. An assertion describes the bus, so it can only report what the bus shows.
4. What They Missed, and the Distinction That Matters
The three "protocol misreading" defects are the interesting ones, because they look exactly like what assertions are supposed to be for.
START detected without requiring SCL high makes the target treat any SDA fall as a START — so during an ordinary write it reframes on every data bit, loses track of the transfer, and stops acknowledging.
Every property stayed quiet. Not because the property set is weak, but because:
The three data defects fail for the same reason, more obviously. A target that returns register 3's contents when register 2 was selected emits a flawless waveform carrying the wrong byte. Chapter 20.9 makes the same point from the other direction: a disturbance spanning a sampling edge turns 0xFF into 0x7F with every byte acknowledged, and only a content comparison sees it.
5. Where a Rule Belongs
The rule that falls out of the experiment, and it is about the observation rather than the subject matter:
If the rule can be violated by a waveform, it is an assertion. If it can only be violated by a disagreement between a waveform and a contract, it is a scoreboard.
Applied to I²C's rules, that cuts cleanly — and it cuts across the temporal/data distinction rather than along it.
| rule | violated by a waveform alone? | belongs in |
|---|---|---|
| SDA must not change while SCL is high | yes | assertion |
| a STOP requires an open transfer | yes | assertion |
| a START needs a bus-free interval first | yes | assertion |
| a transfer must not end mid-byte | yes | assertion |
| one device drives the acknowledge slot | yes, given drive intents | assertion |
| the target acknowledges its own address | no — a NACK is legal | scoreboard |
| a read returns the selected register | no — any byte is legal | scoreboard |
| a write to a read-only register is refused | no — either answer is legal | scoreboard |
| the pointer wraps rather than clamps | no | scoreboard |
Note the fifth row. "One device drives the acknowledge slot" is a legal-waveform question only because the bus model exports a count of drive intents — the resolved line cannot distinguish one puller from two, since low is low. Without that count the rule would move to the right-hand column purely for lack of an observable. Where a rule belongs depends partly on what you can see.
6. The Cost of the Overlap Being Wrong
Neither mechanism is free, and the failure that costs most is duplication with a gap.
A rule checked in both places is not twice as safe. It is a rule with two implementations that can disagree, and when they do, the debate is about which checker is right rather than about the design. Worse, the version in the scoreboard tends to be the one that gets relaxed — because it is the one that fires during integration, when everyone is under pressure and the assertion is harder to argue with.
The discipline that avoids it is to write down, per rule, which mechanism owns it and why — the middle column of the table above is that reason, and it is checkable in a way "temporal things go in assertions" is not.
The property that was right and got waived
Pitfall — a contract rule written as an assertion
// A property added after a bug where the target failed to acknowledge its own
// address. It looks temporal, so it went into the property file.
//
// -- psl ADDR_MUST_ACK : assert always (
// -- (addr_byte_done = '1' and addr_matches = '1') -> next (sda = '0'))
// -- report "the target did not acknowledge its own address";
//
// It fires constantly. Not on the bug -- on ordinary traffic:
//
// * every transfer addressed to a DIFFERENT device on the bus
// * every transfer while the target is held in reset
// * every read-only write, where a NACK is the CORRECT answer
//
// Within a week it has a waiver:
//
// # waive: ADDR_MUST_ACK -- too noisy, scoreboard covers it
//
// The waiver is permanent, and it now hides the original bug too.The rule is about whether an answer was correct, and correctness is a relation between the trace and a contract rather than a property of the trace. Written as an assertion it fires on every legal transfer that was never addressed to this device, which guarantees a waiver — and the waiver removes the check entirely, including for the case it was added for. The drawable-counterexample test separates the two kinds of rule in one question.
// Ask the deciding question: can this rule be violated by a WAVEFORM ALONE?
//
// A released line in an acknowledge slot is a NACK.
// A NACK is legal, meaningful, and frequently correct.
// => there is no illegal waveform here. It is not an assertion.
//
// It belongs where the CONTRACT lives, with an expectation computed from the
// datasheet rather than from the trace:
//
// -- predictor, from the datasheet: this device, this address
// exp_addr_ack := '1' when observed_addr = MY_ADDR else '0';
//
// -- scoreboard: compare the OBSERVED acknowledge against that
// if observed_addr_acked /= exp_addr_ack then
// n_ack_mismatch := n_ack_mismatch + 1;
// end if;
//
// Now a transfer addressed elsewhere expects no acknowledge and produces no
// error, and a target that fails to answer its OWN address is a mismatch.
//
// THE TEST that tells the two apart, before writing either:
// "Show me a waveform that violates this rule, with no device contract."
// If you cannot draw one, it is not an assertion.Pitfall — a framing rule left to the scoreboard, and consumed before it arrives
// A scoreboard-only environment. The monitor reconstructs transactions and the
// scoreboard compares them against a reference model. No assertions.
//
// The target has a defect: it drives its acknowledge without waiting for SCL to
// fall, so it pulls SDA LOW WHILE SCL IS HIGH -- which is a START condition.
//
// What the monitor does with that: it sees an SDA fall while SCL is high, and
// correctly -- by the specification -- treats it as a repeated START. So it ends
// the current transaction and begins a new one.
//
// observed: [write 0x50, 1 byte] [START] [write 0x??, ...]
//
// The scoreboard receives two well-formed transactions. It compares them against
// the model, finds the second one addressed to nonsense, and reports a DATA
// mismatch on an address byte.
//
// Nobody is told that the DUT generated a START. The violation was CONSUMED
// during reconstruction, and what arrives downstream is a plausible-looking
// transaction whose address happens to be wrong. Debugging goes to addressing.Reconstruction is lossy by design: it exists to turn edges into transactions, and a transaction cannot represent an edge that should not have happened. So an illegal waveform is either dropped or reinterpreted, and in both cases the scoreboard downstream sees something well-formed. The failure is expensive because the resulting report is plausible and points at whatever field the reinterpretation corrupted, which is never the real fault.
// Keep the waveform rule at the waveform, where the violation still exists:
//
// -- psl SDA_STABLE_WHILE_SCL_HIGH : assert always (
// -- (scl = '1' and prev(scl) = '1' and sda /= prev(sda)
// -- and in_byte = '1' and start_now = '0' and stop_now = '0')
// -- -> false)
// -- report "SDA changed while SCL was high, mid-byte";
//
// Now the run reports the illegal edge AT THE CYCLE IT HAPPENS, with a line
// number, before any reconstruction has had a chance to launder it into a
// well-formed transaction.
//
// THE GENERAL SHAPE, and it is why a scoreboard cannot replace assertions: a
// monitor's job is to turn a waveform into transactions, which means DISCARDING
// waveform detail. Anything illegal at the signal level is either rejected by the
// monitor (and vanishes) or reinterpreted by it (and vanishes differently).
//
// This module's mutation campaign is exactly this case measured: the two defects
// the assertions caught are both the DUT driving the bus illegally, and what
// fired was FRAMING properties -- never an acknowledge property -- because
// framing damage is what reached the wire.7. What 22.1 Settled
The split is measured, not asserted. Eight injected defects: illegal traffic 2 of 2 caught, protocol misreading 0 of 3, data 0 of 3 — and Module 20's scoreboard killed the data ones.
The deciding question is whether a waveform alone can violate the rule. That cuts across the temporal/data distinction rather than along it, and it puts "the target acknowledges its own address" — temporal in form — firmly in the scoreboard.
A missing acknowledge is legal traffic. There is no rule to violate, which is why an entire category of device misbehaviour is invisible to any bus-level property.
Where a rule belongs depends partly on what is observable. The one-driver-per-slot rule is an assertion only because drive intents are counted; from the resolved line alone it would be uncheckable.
A waveform rule delegated to a scoreboard disappears, because reconstruction launders it into a well-formed transaction, and the resulting failure report points somewhere else entirely.
Next, the central I²C property — and the three PSL constructs that this toolchain accepts and never evaluates, which would have made it silently vacuous. Chapter 22.2 — Asserting the Data-Valid Rule.
Continue learning
Related tutorials
- Related topic
The Receive Datapath — Capturing Written Bytes
The subject is the hand-off, not the value. A byte delivered twice is as bad as one dropped and both are invisible to a test that only compares bytes — and the acknowledge pulse is not a data bit, so a receive path left enabled through it displaces every byte after the first.
- Related topic
Reference Model and Scoreboard Strategy
Where an expected value legitimately comes from, why the prediction must be a function and only the state a register, and why a scoreboard needs one negative test per comparison path. Runs against Module 18's target in three languages, and works through six mutations that survived a structurally correct scoreboard.
- Related topic
Verification Completeness Review
Judging whether an I²C testbench has proven anything, by asking whether it is capable of failing. Two measured experiments: a scoreboard that performs seven comparisons and reports zero mismatches with the device physically absent, and a defect that survives at 100 % coverage until one extra transfer is added.
- Related topic
The Data-Valid Rule — SDA Stable While SCL Is High
One sentence governs every bit on an I²C bus, and it is derived rather than decreed: the receiver needs a settled value at the instant it looks. What falls out is that an SDA edge while SCL is HIGH cannot be data — which is why the bus reserves it for framing.
