Skip to content
VLSI Mentor

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:

defectproperties that fired
acknowledge never releasedSTOP with no transfer open · STOP mid-byte · sustained two-driver SDA
acknowledge driven earlySTART 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.

ruleviolated by a waveform alone?belongs in
SDA must not change while SCL is highyesassertion
a STOP requires an open transferyesassertion
a START needs a bus-free interval firstyesassertion
a transfer must not end mid-byteyesassertion
one device drives the acknowledge slotyes, given drive intentsassertion
the target acknowledges its own addressno — a NACK is legalscoreboard
a read returns the selected registerno — any byte is legalscoreboard
a write to a read-only register is refusedno — either answer is legalscoreboard
the pointer wraps rather than clampsnoscoreboard

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
Buggy Code
// 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.
Root Cause

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.

Fix
// 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
Buggy Code
// 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.
Root Cause

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.

Fix
// 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