I²C · Module 20
The Protocol Feature Matrix — Deciding What Must Be Covered
How to decompose a protocol into features that can be individually broken, why clock stretching and repeated START are dimensions rather than rows, and why the uncovered table is the one that tells the truth. Includes the honest matrix for this environment — twenty-five verified rows and eleven features it does not exercise at all.
Chapter 20.1 produced twenty-one objectives. An objective says what must be true. It does not say which parts of the protocol it touches, whether anything else touches them, or who is responsible for proving it — and a plan that answers only the first question is a plan in which a whole protocol feature can go unmentioned without anyone noticing.
A feature matrix is the document that makes an omission visible. It is not a coverage report. A coverage report is produced by running the environment and says what was exercised; a matrix is written before the environment exists and says what should be. The difference matters because a coverage report can only ever report on features somebody thought to instrument.
1. What Counts as a Feature
The decomposition rule that makes a matrix useful, rather than a restatement of the table of contents:
A feature is something that can be individually broken.
Test it by asking whether you can describe a defect that breaks this feature and nothing else. "Addressing" fails the test — a defect in address matching and a defect in the direction bit are unrelated, fixed in different places, and caught by different observations. "Seven-bit address match" and "direction from the address byte's low bit" both pass it.
The rule cuts the other way too. Do not split a feature into pieces that cannot fail independently. "Bit 3 of the address matches" is not a feature; no plausible defect breaks bit 3 while leaving bits 2 and 4 correct — except one, a mask with a wrong constant, and that is caught by the seven-bit sweep the single feature already demands.
2. Where the Features Come From
Three sources, and conflating them is the most common structural error in a matrix.
The specification. NXP UM10204 defines framing, byte format, acknowledgement, addressing, arbitration, stretching, the reserved addresses and the bus modes. Features from here are true of every conforming device, so their checks are reusable across every I²C design you will verify. They belong in a monitor or a protocol checker, never in a test.
The device datasheet. The register map, which registers are read-only, what the pointer does after a refused write, whether the part supports repeated START mid-transfer. None of this is in UM10204. Module 18 recorded six such decisions as D1 to D6, and Chapter 20.8's reference model is a transcription of them.
The integration. How the block is clocked, what its reset does, which pins it uses, what happens at power-on before software configures anything. Module 19's subject. These features are real and are frequently absent from matrices entirely, because neither of the first two documents mentions them.
Keeping the three separated is what lets a matrix be reused. Swap the device and the datasheet column changes; the specification column does not.
3. The Features That Multiply Everything Else
Three I²C features are not rows. They are dimensions, and they are where matrices quietly fail.
Clock stretching can happen at any acknowledge boundary. Every other feature therefore has a stretched variant, and the stretched variant is a different test because it exercises different logic: a driver that counts cycles works perfectly until something stretches.
Repeated START can replace any STOP. So every feature has a variant in which the transfer ends without releasing the bus, and the question "what state survives" gets a different answer for each.
Arbitration loss can happen at any bit while transmitting. Every transmit-side feature has a variant in which the device stops mid-operation through no fault of its own.
Three dimensions across roughly twenty rows is sixty combinations at minimum, and the honest response is not to fill in a sixty-cell grid. It is to ask, for each combination, whether there is a mechanism by which the interaction could fail — and to test the ones where there is.
| interaction | is there a mechanism? | in the matrix? |
|---|---|---|
| stretching × register write | yes — a driver that counts cycles clocks data into a busy target | yes, 20.9 T5b |
| stretching × address match | yes — the address is latched across the stretched acknowledge | yes, 20.6 T5 |
| repeated START × pointer state | yes — framing may or may not clear application state | yes, 20.8 T9 |
| repeated START × read direction | yes — the classic write-pointer-then-read sequence depends on it | yes, 20.3 T9 |
| stretching × wired-AND resolution | no — resolution is combinational and has no notion of time | no, and say so |
| arbitration × register map | no — a lost arbitration ends the transfer before any register is touched | no, and say so |
The last two rows are doing real work. A matrix cell marked "no mechanism" is a decision with a reason attached, which can be reviewed and overturned. A blank cell is indistinguishable from an oversight.
4. Ownership
Every row needs a named owner, and "the testbench" is not a name. The owner is the component that will contain the check, because that decides whether the check is written once or many times.
| owner | what belongs there | why |
|---|---|---|
| monitor | protocol rules true of every device | written once, reused for every I²C design, and cannot be forgotten by a test author |
| reference model | device-contract predictions | the contract is one document; it should have one implementation |
| scoreboard | the comparison, and only the comparison | it owns no knowledge, which is what keeps it from acquiring the DUT's bugs |
| test | the situation, never the expected value | a test that contains expected values is a test that has to be updated when the device changes |
| nobody | — | this is the row to look at |
That last line is the point of the column. A feature whose owner is a test is verified only in the tests that remember it. A feature with no owner is verified by nobody, and it will still be absent from the coverage report, because nothing was instrumented to notice.
5. How to Read a Status Column Honestly
Four statuses, and the distinction between the middle two is where most matrices overstate.
- EXERCISED AND CHECKED — a simulation drove this feature and a check on its outcome could have failed.
- EXERCISED, NOT CHECKED — it happened, and nothing looked. Common, and worse than not exercising it, because it produces coverage.
- IMPLEMENTED, NOT EXERCISED — the environment can do it and no test asks. This module had two of these until Section 6 was written, and they are the reason this section exists.
- NOT SUPPORTED — the environment cannot do it at all. Honest and fine, provided it is in the matrix.
6. The Matrix
Features, sources, owners and status for the environment built in this module against Module 18's target. The Source column is S for the specification, D for the datasheet or device contract, I for integration.
| feature | src | owner | status | evidence |
|---|---|---|---|---|
| a released line reads high only when all have released | S | monitor | exercised and checked | 16.4 |
| no participant drives a line high | S | structural | exercised and checked | 20.6 T4, 20.9 T6 |
| START is an SDA fall while SCL is high | S | monitor | exercised and checked | 20.7 T1 |
| STOP is an SDA rise while SCL is high | S | monitor | exercised and checked | 20.7 T1 |
| repeated START ends a transfer without releasing the bus | S | monitor | exercised and checked | 20.3 T9 |
| SDA changes only while SCL is low, except framing | S | monitor | exercised and checked | 20.7 T8 |
| a byte is eight bits, most significant first | S | monitor | exercised and checked | 20.7 T2 |
| the ninth slot is the acknowledge, owned by the receiver | S | monitor | exercised and checked | 20.6 T1 |
| a transfer interrupted mid-byte transfers nothing | S | monitor | exercised and checked | 20.7 T7, 20.9 T3 |
| seven-bit address match, all seven bits discriminated | D | reference model | exercised and checked | 18.4, 20.8 T11 |
| direction from the address byte's low bit | S | reference model | exercised and checked | 20.3 T7 |
| a foreign address is not answered | D | reference model | exercised and checked | 20.8 T4, 20.3 T8 |
| clock stretching by the target | S | responder, driver | exercised and checked | 20.6 T5, 20.9 T5b |
| a stretch is bounded, and an unbounded hold is a timeout | I | driver | exercised and checked | 20.9 T5, T5b |
| a write lands in the register the pointer selects | D | reference model | exercised and checked | 20.8 T1 |
| the pointer advances only on an accepted write | D | reference model | exercised and checked | 20.8 T8 |
| the pointer wraps modulo the register count | D | reference model | exercised and checked | 20.8 T10 |
| the pointer survives framing | D | reference model | exercised and checked | 20.8 T9 |
| a write to a read-only register is refused | D | reference model | exercised and checked | 20.3 T1b, 20.8 |
| after a read the pointer sits one past the last byte | D | reference model | exercised and checked | 20.3 T7 |
| read data comes from the selected register | D | reference model | exercised and checked | 20.8 T3 |
| a stuck line is abandoned rather than waited on | I | driver, target | exercised and checked | 20.9 T4, T5 |
| a disturbance narrower than the sampling window is survived | I | injector | exercised and checked | 20.9 T2 |
| a disturbance spanning a sampling edge corrupts data silently | I | injector | exercised and checked | 20.9 T2b |
| the same DUT behaviour in three HDLs | I | environment | exercised and checked | env_vs_dut, 3 languages |
Twenty-five rows, every one with an observation behind it. That is the matrix's optimistic half, and on its own it is the half that misleads.
7. What This Environment Does Not Cover
This is the column that makes a matrix worth writing. Eleven features of I²C that the environment built in this module does not exercise, each with why and with what would be needed.
| feature | status | why not, and what it would take |
|---|---|---|
| multi-master arbitration | not supported by the environment | the controller driver is the only controller on the bus and has no arbitration logic. Module 17 verified the target's response to arbitration loss; the environment cannot generate a contended bus. Needs a second driver and a bit-level arbitration check |
| 10-bit addressing | not supported | the reference model and responder both decode seven bits. A 10-bit address is two bytes with a reserved prefix, so this is a new framing case, not a wider field |
| general call address 0x00 | not supported | a reserved address with defined semantics. The target NACKs it, which is legal, but nothing checks that it does so deliberately rather than incidentally |
| START byte, CBUS address, device ID | not supported | reserved-address behaviours in UM10204 §3.1.12. No objective references them |
| SMBus and PMBus layers | out of scope | timeouts, packet error checking and the ALERT line are a different specification layered on this one |
| bus timing parameters | not supported | the bus is modelled in clock cycles, not nanoseconds. There is no tHD;STA anywhere in this environment, so no timing compliance is checked at all. Needs a timing-annotated model, and on real hardware, measurement |
| Fast-mode Plus, High-speed, Ultra-Fast mode | not supported | mode differences are electrical and timing, and this environment has neither |
| software reset via the reserved sequence | not supported | not implemented in the target |
| two targets sharing one address | not supported | the bus model supports many devices, but no test creates the collision. The wired-AND result of two acknowledges is indistinguishable from one, which is exactly what makes it worth a test |
| power-on and reset-release behaviour | partly | the target is reset before every test. Nothing exercises reset during a transfer, which is the integration case that actually happens |
| metastability, and any real timing claim | not reachable here | an RTL simulator has no sampling aperture. 19.4 argues this at length; no amount of stimulus changes it |
Reading the two tables together is the point. The environment verifies the target's protocol and application behaviour thoroughly and in three languages, and it says nothing whatsoever about timing compliance, arbitration, or any addressing mode beyond plain seven-bit. Both halves are true, and only the first half is what "verified" is usually taken to mean.
The matrix said 100% covered, and the feature had never run
Pitfall — a feature with an owner, a parameter, an output, and no test
// From this module's own environment, before the matrix was written.
//
// The responder model supports clock stretching. It has a policy parameter, a
// countdown, a release, and an exported count:
//
// i2c_resp_bfm #(.MY_ADDR(ADDR), .ACK_ADDR(1'b1), .NACK_AFTER(0),
// .STRETCH_CLKS(0), // <-- zero
// .READ_BASE(8'h00)) resp (...);
//
// The controller driver supports it too, correctly, with a bounded wait:
//
// while (!scl_in && w < STRETCH_TIMEOUT) begin ... end
//
// And the Module 18 target has a stall input:
//
// .stall_req(1'b0), // <-- tied low
//
// Every piece is present and every piece is correct. The feature has never
// executed. Line coverage on the responder shows the stretch block as unhit,
// which nobody looked at, because the component list said "stretching: resp_bfm"
// and the component existed.Pitfall — the sixty-cell grid nobody filled in
// A matrix with three cross-cutting dimensions, filled in by policy:
//
// | plain | stretched | rep-START | arb-loss
// address match | yes | TODO | TODO | TODO
// register write | yes | TODO | TODO | TODO
// register read | yes | TODO | TODO | TODO
// ... 20 rows ...
//
// Sixty cells, forty-five of them TODO. Six months later the TODOs are still
// there, and the review discussion is about whether to lower the target to 80%.
//
// The real problem is that the grid asserts all sixty interactions matter
// equally, which is false, and gives no way to say so. The team's actual
// knowledge -- that stretching cannot affect wired-AND resolution because
// resolution is combinational -- has nowhere to go except a TODO that looks
// identical to the ones that do matter.8. What 20.2 Settled
A feature is something that can be individually broken. That single rule produces a decomposition fine enough to be useful and coarse enough to finish, and it rules out both "addressing" and "bit 3 of the address".
Features come from three documents, and they must stay separated. Specification rules are reusable across every I²C design; datasheet decisions change when the part changes; integration features appear in neither document and are the ones most often missing entirely.
Three features are dimensions, not rows. Stretching, repeated START and arbitration loss each multiply the table, and the way to handle the product honestly is to ask of each cell whether a mechanism exists — recording "no mechanism, because" as a reviewable decision rather than leaving a blank.
The status column is the one that finds things, and the uncovered table is the one that tells the truth. This module's matrix has twenty-five rows with observations behind them and eleven features it does not exercise at all, including every timing parameter in the specification. Writing the second table is what turned two complete, correct, idle stretch implementations into six killed mutations.
The next chapter takes the first architectural decision the matrix implies. Several rows are owned by a component that has to turn edges into transactions, and what a transaction is turns out to be the decision that determines whether the environment can check anything at all. Chapter 20.3 — What Should an I²C Transaction Object Represent?.
Continue learning
Related tutorials
- Related topic
The Byte Engine and the ACK Slot — Where SDA Changes Hands
A byte is nine bit slots and the ninth differs from the eight in exactly one respect: who owns SDA. Builds the ownership flip as a mirror image for reads and writes, keeps the inverted acknowledge polarity in one place, and shows why an engine that holds the line one slot too long is invisible to every per-byte check.
- Related topic
ACK Generation and Its Timing Window
Where most slave designs first fail. The acknowledge has three parts, the window is bounded at both ends by falling edges, and asserting early is not early — SDA falling while SCL is high is a START, so a premature acknowledge restarts the transaction instead of acknowledging a byte.
- Related topic
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.
- Related topic
UART vs Other Interfaces: Choosing the Right Link
Serial interfaces differ first in where the receiver's timing comes from, then in what organises a shared medium — and capability is paid for in what the system must already provide. A question order for choosing between UART, SPI, I2C, CAN, USB and Ethernet.
