I²C · Module 20
Verification Objectives and the I²C Test Plan
Turns a specification into claims with falsifiers, separates the four levels at which an I²C claim can be true, and argues that 'all tests pass' is the state an environment starts in rather than an exit criterion. Ends with the plan this module was executed against and the evidence it produced, including the one mutation that survives with a proof.
Modules 16 through 19 built a working I²C target and proved things about it. Every one of those proofs answered a question somebody had already decided to ask.
This module is about deciding what to ask. That sounds like paperwork and it is not: almost every escaped bug in a verified IP is a question nobody wrote down, and the reason nobody wrote it down is almost never that it was hard. It is that the question sits between two components, or between two layers of abstraction, or between the design and the environment — in a place no single test naturally looks.
A test plan is the artifact that makes those places visible before they become silicon.
1. The Question a Plan Answers
Not "what tests will we write". That is a schedule. The plan answers:
What claims are we going to be able to make when this is done, and what would have to be observed for each claim to be false?
Both halves matter. A claim with no falsifying observation is not a claim — it is a hope, and a test written against it will pass for reasons unrelated to the design. This is the single most useful discipline in this chapter, and the rest of the module is its consequences.
Consider two ways of writing the same plan line.
| a schedule entry | an objective |
|---|---|
| "test address matching" | "the target responds to its own address and to no other; falsified by an acknowledge on any of the 127 other addresses, or a NACK on its own" |
| "test clock stretching" | "the target can hold SCL low to delay a transfer and the controller waits; falsified by the controller sampling a bit while SCL is held, or by the target releasing before it is ready" |
| "test the register map" | "a write lands in the register the pointer selects and a read returns the register the pointer selects, where the pointer's behaviour is Module 18's six documented decisions; falsified by a write landing anywhere else" |
The left column can be satisfied by a test that drives one address and checks one acknowledge. The right column cannot. And notice what the right column required: in the third row it forced a reference to a written-down contract, because "the register map works" has no meaning until somebody says what the pointer does after a refused write.
2. Why I²C Makes This Harder Than a Register Block
A synchronous register block has one clock, one direction of causality, and an interface whose meaning is fixed by the signal names. Verifying it is mostly a matter of coverage. I²C is not that, for six structural reasons, and each one creates a class of question that has to be written down deliberately because no signal name suggests it.
-
The bus has no driver, only pullers. SDA's value is the wired-AND of every participant. There is no signal anywhere in the system whose value is "what the target is transmitting" — only "what the bus resolved to". Every objective about transmitted data has to be phrased in terms of the resolved line, or it is phrased about something that does not exist.
-
The target has no clock of its own for the protocol. SCL is an input, generated by somebody else, at a rate the target does not control and cannot assume. Objectives phrased in terms of cycles are meaningless; they have to be phrased in terms of edges and their order.
-
The target can stop time. Clock stretching means the target holds SCL low and the transfer pauses. Any objective about "what happens next" has to survive an arbitrary pause inserted anywhere.
-
More than one participant may be a controller. Arbitration means a device can lose the bus mid-byte, through no fault of its own, and must handle that as a normal event rather than an error.
-
Framing lives on the data line. START and STOP are SDA transitions while SCL is high — the one time SDA is otherwise guaranteed not to change. So the same wire carries both the data and the punctuation, and the rule that separates them is a timing relationship, not a field. An objective about data integrity and an objective about framing integrity are about the same two wires.
-
Some state survives framing and some does not. Module 18's register pointer persists across a STOP; its shift register does not. Which is which is a design decision, not a protocol rule, and a plan that does not name it will be verified by accident in whichever direction the implementation happens to go.
Each of those is a reason a question can be missed. Together they are the reason this module exists as nine chapters rather than one.
3. Four Levels of Claim, and Why They Must Not Be Merged
The most consequential structural decision in the plan is to separate claims by the level of abstraction at which they are true. There are four, and a bug can be invisible at three of them while being fatal at the fourth.
Read the right-hand column as four bug reports that a single-level environment cannot all catch.
- Signal level. Two devices pulling SDA low at once. The resolved bus is low, which is a perfectly legal value, so nothing above this level can see it. Only a check on the drive intents can.
- Protocol level. SDA changing while SCL is high, mid-byte. The byte still assembles into a value and the value still arrives; a transaction-level checker sees a clean transfer.
- Transaction level. A write whose every byte is legal and acknowledged, landing in the wrong register because the pointer advanced when it should not have. Every protocol rule is obeyed.
- Application level. A data bit corrupted on the wire, with every acknowledge still present. Chapter 20.9 produces exactly this in simulation:
0xFFarrives as0x7F, acknowledged at every byte. A protocol-level checker reports a flawless transfer.
That last one is worth stating as a rule, because it is the trap that a single pass-or-fail bit walks straight into:
4. The Shape of an Objective
Four fields. The third and fourth are the ones usually skipped, and skipping them is how a plan comes to contain unfalsifiable lines.
| field | what it says | failure mode if omitted |
|---|---|---|
| claim | what will be true of the design | — |
| why it could fail | the mechanism, not the symptom | tests aimed at symptoms miss the mechanism's other symptoms |
| evidence | what observation would establish it, and by what tool | "verified" with no attached observation |
| falsifier | what observation would refute it | a test that cannot fail, which is the default outcome |
The falsifier field is the one that does real work. Write it and a whole class of bad test disappears, because you cannot write "falsified by X" without knowing how to make X happen — and an objective whose falsifier you cannot produce is an objective you have not verified, however many tests reference it.
5. The Objective List
Here is the plan Modules 16 through 19's target was verified against, as objectives rather than as a schedule. The Level column is Section 3's; the Oracle column is Section 6's and is the reason this table is not just a checklist.
| # | objective | level | oracle | verified in |
|---|---|---|---|---|
| O1 | a released line reads high only when every participant has released it | signal | protocol rule | 16.4 |
| O2 | no participant ever drives a line high | signal | protocol rule | 19.1 |
| O3 | a device that loses arbitration stops driving and does not corrupt the winner | signal | protocol rule | 17.10 |
| O4 | START and STOP are recognised only as SDA edges while SCL is high | protocol | protocol rule | 18.3 |
| O5 | SDA changes only while SCL is low, except for framing | protocol | protocol rule | 20.5 |
| O6 | a byte is eight bits followed by one acknowledge slot | protocol | protocol rule | 18.6 |
| O7 | the acknowledge slot is owned by the receiver, and by only one device | protocol | protocol rule | 18.7 |
| O8 | a repeated START ends a transfer without releasing the bus | protocol | protocol rule | 20.3 |
| O9 | a transfer interrupted mid-byte transfers nothing | protocol | protocol rule | 20.7 |
| O10 | the target acknowledges its own address and no other | transaction | device contract | 18.4 |
| O11 | direction is taken from the address byte's low bit | transaction | device contract | 18.4 |
| O12 | held SCL delays the transfer and loses no data | transaction | protocol rule | 18.10 |
| O13 | a write advances the pointer only when the byte was accepted | application | device contract | 18.9 |
| O14 | the pointer wraps modulo the register count | application | device contract | 18.9 |
| O15 | the pointer survives framing | application | device contract | 18.9 |
| O16 | a write to a read-only register is refused | application | device contract | 18.9 |
| O17 | a refused write does not advance the pointer | application | device contract | 20.8 |
| O18 | after a read the pointer sits one past the last byte served | application | device contract | 18.9 |
| O19 | a bus wedged by a stuck line is abandoned, not waited on forever | transaction | device contract | 20.9 |
| O20 | a rise time can move a sampling instant past a valid window | signal | physical law | 19.3 |
| O21 | a synchroniser's reliability cannot be established by simulation | signal | — not provable here | 19.4 |
Twenty-one objectives, and the last one is in the table on purpose. A plan that lists only what it can prove is a plan that quietly redefines the goal as whatever the available tools reach.
6. Where "Expected" Is Allowed to Come From
Every objective needs an expected value, and there are exactly three legitimate sources plus one that is used constantly and is worthless.
Legitimate: a protocol rule. "SDA must not change while SCL is high" comes from the specification and is true of every conforming device. Objectives with this oracle are reusable across every I²C design you will ever verify, which is why they belong in a monitor or a checker rather than in a test.
Legitimate: a device contract. "The pointer wraps modulo eight" is not in any specification. It is a decision someone made about this part, and it has to be written down somewhere the environment can read. Module 18 wrote six of them down as decisions D1 to D6, and Chapter 20.8's reference model is a direct transcription of those six.
Legitimate: physical law. tr = 0.8473 · Rp · Cb is arithmetic about a resistor and a capacitance. It predicts a number no simulation produced, and 19.3 uses it as an oracle for whether a bus can meet its rise-time budget.
Illegitimate: the stimulus. And this is the one that matters, because it is the default:
7. Exit Criteria, and Why "All Tests Pass" Is Not One
All tests passing is the state the environment is in on the day it is written, before it has been shown to be capable of failing. It is a necessary condition and it carries almost no information, because it is equally consistent with a complete environment and with an empty one.
What does carry information is whether the environment notices when the design is wrong. That is measurable, and this curriculum measures it the same way in every module:
Two cautions about that method, both learned by getting them wrong in this module and the last.
A survivor is a question, not a defect. The correct response is to classify it, and only one of the classifications leads to changing the environment. Section 9 reports a survivor in this module that turned out to be provably equivalent, and two that turned out to be errors in the mutation itself rather than in the design or the environment.
The mutation harness has to prove that the mutated source is the source that was compiled. Module 19 recorded eight survivors of which three were an artifact: the harness resolved dependencies from a shared directory, so files mutated in one place were built from another. The mutation driver used in this module computes the mutated file's hash and refuses to report any verdict unless that hash appears in the manifest of files the build actually compiled. A verdict from an unverified harness is not evidence of anything — including, and this is the dangerous case, when it says KILLED.
8. What the Plan Must Say It Cannot Prove
An honest plan has a section for claims outside the reach of the available tools, and it says so in the plan rather than in a footnote after tape-out. For this curriculum's environment that section contains at least:
| claim | why not reachable here | what would reach it |
|---|---|---|
| the synchroniser makes metastable capture acceptably rare | an RTL simulator has no sampling aperture; a flop always resolves | an MTBF calculation from library characterisation data |
| the interface meets setup and hold on a real device | no timing data, no netlist, no tool | static timing analysis after place and route |
| the design works against a real EEPROM | the responder is a model of a device, written from the same assumptions as the target | a bench measurement against the part |
| the bus meets its rise-time budget | depends on board capacitance | arithmetic from a measured Cb, or a scope |
| synthesis infers the intended I/O primitive | no synthesis tool available in this environment | a synthesis report, read |
Every one of those is labelled in the chapters that raise them, using the evidence vocabulary this curriculum uses throughout: SIMULATED, SYNTHESIZED, STATICALLY CHECKED, CALCULATED, CONCEPTUAL, VENDOR-SPECIFIC, HARDWARE-ONLY, NOT EXECUTED. The vocabulary exists so that a reader can tell, of any claim in any chapter, what produced it.
9. This Module's Own Plan, and What It Produced
The remaining eight chapters build an environment against the plan above. This section reports what executing that plan actually produced, so the module can be read against its own exit criterion rather than against its intentions.
The environment's structure follows Section 3's levels rather than the usual component list, and that is the one architectural decision worth taking from this chapter into the next eight:
- the monitor (20.7) reports what the bus did, and knows nothing about what should have happened;
- the assembler (20.3) raises that from bytes to transactions, and still judges nothing;
- the reference model (20.8) says what should have happened, from the contract, and never looks at the bus;
- the scoreboard (20.8) compares them, and keeps the protocol verdict separate from the application verdict.
Four components, four jobs. Each is wrong in a recognisably different way when it breaks, which is the property that makes an environment debuggable — and it is the direct consequence of not merging the levels.
Every test passed, the plan was signed off, and the customer found it in a week
Pitfall — a 100% passing regression on an IP with a two-line bug
// An I2C target IP. 240 directed tests, 4 hours of constrained-random, every
// test green for six weeks. Shipped. The customer's first integration fails:
// after the host writes a byte that the target refuses, every subsequent write
// lands one register too high.
//
// The regression contains a test for refusal:
//
// write(RO_REG, 0x55);
// check(nack_seen == 1); // passes
// check(read(RO_REG) == old); // passes
//
// Both checks are correct and both pass. The bug is not in refusal. It is in
// what the pointer did afterwards -- and NO TEST WRITES AGAIN AFTER A REFUSAL,
// because the plan line said "test that read-only registers are protected",
// which those two checks completely satisfy.Pitfall — the scoreboard that agreed with itself for eleven months
// A verification environment for the same target. The scoreboard compares
// every transaction and has never reported a mismatch.
//
// task run();
// txn = seq.next(); // the sequence decides: write 0xA5 to reg 3
// driver.send(txn); // the driver puts it on the bus
// sb.expect(txn); // <-- the SAME object
// observed = mon.get(); // the monitor reads it back off the wire
// sb.compare(observed); // 0xA5 == 0xA5. PASS.
// endtask
//
// Comment out the DUT instantiation. Every test still passes.
//
// That is the whole diagnosis. The comparison closes a loop from the sequence
// through the driver to the monitor and back, and the DUT is not on that loop.
// It measures whether the driver transmits what it was told and whether the
// monitor reads what was transmitted -- both worth knowing, neither being
// verification of the design.10. What 20.1 Settled
Four things, and the module is built on them.
An objective is a claim with a falsifier. Without the second half it cannot be checked, and the test written against it will pass for reasons unconnected to the design. Section 4's worked pair shows two objectives where a plan without falsifiers would have written one — and the missing one is a mutation that survived this module's own first scoreboard run.
Claims live at four levels, and merging them destroys information. Not just diagnostic detail: it makes silent application-level corruption indistinguishable from a clean transfer, which is the failure 20.9 produces on purpose.
Expected values come from a protocol rule, a device contract, or physical law — never from the stimulus. The diagnostic is one line long: remove the DUT and see whether the environment still passes.
The exit criterion is zero valid non-equivalent survivors, from a harness that can prove it compiled what it mutated. All tests passing is where you start, not where you stop.
The next chapter turns the objective list into a feature matrix — which parts of the protocol each objective touches, who owns proving it, and, most usefully, which features appear in no objective at all. Chapter 20.2 — The Protocol Feature Matrix.
Continue learning
Related tutorials
- Related topic
The START Condition
START is SDA falling while SCL is high, it is generated only by the controller, and it makes the bus busy. Derive what every device must do in response, then build a detector in three languages and find out why its two guard terms and its reset value are all load-bearing.
- Related topic
f(SCL), tLOW and tHIGH — The I²C Clock Envelope
A legal I²C clock is three constraints, not one frequency — and a perfectly compliant 400 kHz clock with a symmetric duty cycle is illegal in Fast-mode. Derives the envelope and an exact identity hidden in Table 10.
- Related topic
tVD;DAT and tVD;ACK — I²C Data Valid Time
The module's first maximum, and it inverts everything: the comparison, the worst-case tracker, and what a passing measurement means. Plus why the acknowledge gets its own parameter when Table 10 gives it an identical number.
- Related topic
The I²C Stretching Mechanism — Holding SCL Low
Stretching needed no new mechanism: the specification already described it for multi-master synchronization. One sentence decides whether a master survives it — and getting it wrong collapses the high phase on the bit a stretch ended.
