I²C · Module 9
Multi-Byte I²C Reads — ACK Policy, Timing and Waveforms
A well-formed read of n bytes contains exactly n−1 ACKs and one NACK, always last. That pattern is narrow enough to check in hardware — and narrow enough to predict a bus hang before the STOP that cannot form is even attempted.
Chapter 9.1 established that a master ends a read by withholding an acknowledge, and Chapter 9.2 established that the slave has no say in the matter. Put those together across a burst and something useful falls out: the acknowledge pattern of a correct read is completely determined by its length.
Not constrained. Determined. There is exactly one legal pattern for a read of n bytes, which means a passive observer can check the policy with no knowledge of the device, the driver, or what the data means — and can do something better than check it. It can predict the bus hang before it happens, because the hang is caused by the acknowledge pattern and the acknowledge pattern is visible one byte before the STOP is attempted.
This chapter derives that pattern, prices a read against the equivalent write, and builds the instrument.
1. The Pattern Is Determined by the Length
A read of n payload bytes puts n + 1 bytes on the wire and therefore has n + 1 acknowledge slots. Their owners and required values are fixed:
| slot | belongs to | required value | why |
|---|---|---|---|
| byte 0 — address + R | the slave | ACK | otherwise nobody is at that address |
| bytes 1 … n−1 | the master | ACK | "send me another" |
| byte n — the last | the master | NACK | the fifth NACK condition: end the transfer |
So for any n ≥ 1 the master produces exactly one NACK and exactly n−1 ACKs, and the NACK is always in the final slot. For n = 0 — the address-only probe with R/W = 1 — the master produces nothing at all, because it never became a receiver of data and there is no slot that belongs to it.
| n | master ACKs | master NACKs | the pattern, in order |
|---|---|---|---|
| 0 | 0 | 0 | — (the slave's ACK only) |
| 1 | 0 | 1 | N |
| 2 | 1 | 1 | A N |
| 3 | 2 | 1 | A A N |
| 8 | 7 | 1 | A A A A A A A N |
The n = 1 row is the one that breaks naive checkers. A one-byte read contains zero master ACKs — its only acknowledge is the NACK, because the first byte is also the last. An instrument that assumed "at least one ACK, then a NACK" reports a policy violation on the single most common read in existence, and §9's mutation C10 is the sibling error for n = 0.
2. What a Read Costs, and Why It Costs More Than a Write
The pulse arithmetic is identical to Chapter 8.3's. Nine pulses per byte, no pulses for framing, n + 1 bytes on the wire for a plain read:
| quantity | closed form |
|---|---|
| bytes on the wire | n + 1 |
| SCL pulses | 9(n + 1) |
| payload bits | 8n |
| overhead pulses | n + 9 |
Identical to a write, and for the same reason: the acknowledge costs one pulse in nine whichever direction the data is going, and the address byte costs nine pulses and carries no payload whichever direction follows it. The 8/9 ceiling is direction-independent.
But that is the plain read, and a plain read is rare. Almost every real read is a register read, which means the combined format of Chapter 9.1 §1: write the pointer, repeated START, read the data. And that costs one more byte-slot than the equivalent write.
| transfer | bytes on the wire | SCL pulses |
|---|---|---|
| write n bytes to a register pointer | S, addr+W, ptr, n data, P → n + 2 | 9(n + 2) |
| read n bytes from a register pointer | S, addr+W, ptr, Sr, addr+R, n data, P → n + 3 | 9(n + 3) |
A register read always costs exactly nine pulses more than the equivalent write — one extra byte-slot, which is the second address byte. The repeated START itself is free in pulses, as all framing is. Here is what that does to the numbers:
| n | write pulses | write efficiency | read pulses | read efficiency |
|---|---|---|---|---|
| 1 | 27 | 29.6% | 36 | 22.2% |
| 2 | 36 | 44.4% | 45 | 35.6% |
| 4 | 54 | 59.3% | 63 | 50.8% |
| 8 | 90 | 71.1% | 99 | 64.6% |
| 16 | 162 | 79.0% | 171 | 74.9% |
| 32 | 306 | 83.7% | 315 | 81.3% |
| → ∞ | — | 88.9% | — | 88.9% |
Three readings worth having.
A single-byte register read is 22% efficient. Four bytes on the wire to move one. This is the most-issued transaction on many real buses — read one status register — and it is the least efficient thing I²C does. At 100 kHz it takes 360 µs to retrieve eight bits.
The penalty shrinks fast and the asymptote is the same. The extra nine pulses are a fixed cost, so they matter enormously at n = 1 and hardly at all at n = 32. Both directions converge on 8/9, because in the limit the acknowledges dominate and the acknowledges are symmetric.
The engineering conclusion is the same as Chapter 8.3's, only more so. Do not read status registers one at a time in a loop. Most devices lay related registers out contiguously precisely so that one burst can fetch them, and the gain is larger for reads than for writes because the fixed cost being amortised is larger.
3. Reading an Annotated Burst
Here is a six-byte register read at byte resolution — one interval per byte slot. At bit resolution this frame would be 81 intervals wide.
Register read: write the pointer, Sr, then read six bytes
10 cyclesThe figure has no clock row, deliberately: at byte resolution one interval is nine SCL pulses, so a clock row would draw one period where nine belong. The pulses row carries that information in the correct units, as it does in Chapter 8.3 §5.
The driven by row is what makes this figure worth the space. Read it across: S S S for three byte slots, then M M M M. The acknowledge changes owner once, at the transition from the second address byte to the first data byte — and that is the handover the specification places "at the moment of the first acknowledge". Note also that the row reads S for the address byte after the Sr, even though that byte requests a read: at the moment of its acknowledge slot the slave is still the receiver, because it has just received an address. That is Chapter 9.1 §1's clause drawn rather than argued, and it is the single most common source of monitor off-by-one.
Four things to read off a capture of this shape, in order:
Count the intervals between framing events. Ten intervals, three of which are framing, so seven bytes on the wire. The pulse total is 7 × 9 = 63 — and 63 divides by nine, which is the sanity check that costs one division.
Find the Sr and check what follows it. A byte following a repeated START is an address, not data. Reading it as data shifts the interpretation of everything after it, which Chapter 7.5 §10 documents as the most expensive structural misreading available.
Read the ninth-bit row as a pattern. One N, in the last data slot. Any other arrangement is malformed, and §1's callout covers the two ways it can be.
Check who drove each acknowledge. Three slave acknowledges then four master ones. A monitor reporting five master acknowledges for this frame has attributed the second address byte's slot to the master, and its byte accounting will be wrong by one on every read it ever sees.
4. The Hang, Derived From the Pattern
Chapter 7.4 introduced the read hang and Chapter 9.1 §3 explained why it is unrecoverable. Here it is as a derivation, because the instrument in §6 implements exactly this reasoning.
Premise 1. A master that ACKs a byte has told the slave to send another (§1).
Premise 2. A slave-transmitter that has been told to send another will drive the next byte's eight data bits (Chapter 9.2 §2). It cannot decline — it has no NACK.
Premise 3. Driving a bit means pulling SDA low or releasing it; no device drives SDA high (Chapter 2.3). So whenever a driven bit is 0, SDA is held low by the slave.
Premise 4. A STOP requires SDA to rise while SCL is high (Chapter 5.3).
Conclusion. If the master ACKs what it intended to be the final byte, the slave drives another byte, and for any byte whose most significant bit is 0 the master cannot form its STOP. Not delayed, not corrupted — impossible, because the master has no way to overpower a device pulling low.
Two consequences of the derivation matter for the instrument:
The fault is visible one byte early. The acknowledge that causes the hang happens in the slot before the byte that holds the line. So an observer that watches the acknowledge pattern knows the transfer is doomed before the STOP is attempted, which is the difference between a diagnosis and an autopsy.
The hang is data-dependent, and that makes it worse. It manifests only when the extra byte's MSB is 0 — so roughly half the time, depending on what the device happens to hold. A driver with this bug works intermittently, and "works intermittently on a shared bus" is the hardest class of fault to attribute, because the symptom lands on whichever device is unlucky enough to need the bus next.
5. Why the Instrument Is Passive
The design below observes and never participates. It has no output that reaches SDA or SCL, so it cannot be the cause of a bus problem — which is the entire requirement for a debug block that ships in silicon. A monitor that can perturb the bus is a monitor people disable, and then it is not there on the day it is needed.
It measures four quantities and produces two verdicts:
| output | what it is |
|---|---|
bytes_seen, scl_pulses, payload_bytes | the cost accounting of §2 |
master_acks, master_nacks | the pattern of §1, with the address slot excluded |
addr_acked | the slave's answer to the address byte, kept separate |
policy_ok | the pattern was n−1 ACKs then exactly one NACK |
hang_risk | the final data byte was ACKed — the derivation of §4 |
6. The Instrument in Three Languages
// A PASSIVE instrument that checks a read transfer's acknowledge policy and measures
// what the transfer cost. It drives nothing and cannot perturb what it observes.
//
// A read of n payload bytes has the same shape on the wire as a write -- n+1 bytes and
// 9(n+1) SCL pulses -- and a completely different acknowledge pattern:
//
// byte 0 (address + R) : ninth bit driven by the SLAVE -> must be ACK
// bytes 1 .. n-1 : ninth bit driven by the MASTER -> ACK "send another"
// byte n (the last) : ninth bit driven by the MASTER -> NACK "no more"
//
// so a well-formed read contains EXACTLY ONE master NACK and it is always in the final
// slot. That is the fifth NACK condition of section 3.1.6 -- "a master-receiver must
// signal the end of the transfer to the slave transmitter" -- and it is the only
// mechanism the protocol offers for saying so: there is no length field and no command
// for "stop".
//
// The instrument therefore checks two things a simple pass/fail monitor cannot:
// policy_ok -- the pattern was n-1 ACKs then one NACK, nothing else
// hang_risk -- the final data byte was ACKED, which means the slave will transmit
// ANOTHER byte and hold SDA low through the window in which SDA must
// rise to form the STOP. The STOP cannot form and the bus hangs.
// This is Chapter 7.4's failure, detected from the acknowledge pattern
// alone, before the hang has happened.
module i2c_read_ack_policy #(
parameter int CNT_W = 16
)(
input logic clk,
input logic rst_n,
input logic scl_in, // observed bus level
input logic frame_start, // pulse: S or Sr
input logic frame_stop, // pulse: P
input logic byte_done, // pulse: a byte AND its ninth slot completed
// The ninth bit AS OBSERVED ON THE WIRE: 0 means SDA was pulled LOW, which is an
// ACK; 1 means SDA stayed HIGH, which is a NACK. The polarity is the bus's, not a
// convenience -- section 3.1.6 defines the acknowledge as the receiver pulling the
// line low, so "low is yes" is the physical fact and inverting it here would put a
// translation between the instrument and the thing it measures.
input logic ack_bit,
output logic [CNT_W-1:0] scl_pulses,
output logic [CNT_W-1:0] bytes_seen, // INCLUDING the address byte
output logic [CNT_W-1:0] payload_bytes, // bytes_seen - 1, floored at zero
output logic [CNT_W-1:0] master_acks, // "send me another"
output logic [CNT_W-1:0] master_nacks, // should be exactly one
output logic addr_acked, // the slave answered the address
output logic policy_ok, // n-1 ACKs then exactly one final NACK
output logic hang_risk, // the final data byte was ACKed
output logic transfer_active,
output logic metrics_valid // pulse: a STOP closed the transfer
);
logic scl_q;
logic scl_rise;
assign scl_rise = !scl_q && scl_in;
// The first byte after an S or Sr is the address byte. Nothing in the frame marks
// it, so position is the only way to know -- which is exactly why a decoder that
// misses a repeated START reads an address byte as data (Chapter 7.5, section 10).
logic expect_address;
// Whether the most recent DATA byte was acknowledged. This is the whole basis of
// the hang prediction, and it must be the LAST one rather than "any of them".
logic last_data_acked;
// The address byte carries no payload, so it is excluded -- and the subtraction is
// guarded, because bytes_seen is zero before the first byte completes and an
// unguarded 0 - 1 would wrap to a very large number rather than to zero.
assign payload_bytes = (bytes_seen == '0) ? '0 : (bytes_seen - 1'b1);
// A well-formed read: every data byte but the last was ACKed, the last was NACKed,
// and there was exactly one NACK in total. An address-only probe (no data bytes at
// all) is well-formed too and produces no master acknowledges whatsoever, so it is
// admitted explicitly rather than falling out of the arithmetic by accident.
assign policy_ok = (payload_bytes == '0)
? ((master_acks == '0) && (master_nacks == '0))
: ((master_nacks == {{(CNT_W-1){1'b0}}, 1'b1})
&& (master_acks == (payload_bytes - 1'b1))
&& !last_data_acked);
// The prediction. If the final data byte was ACKed, the slave-transmitter has been
// told "send another" and will drive the next byte's MSB. Whenever that bit is a
// zero the slave holds SDA low, so the master's STOP -- which needs SDA to RISE
// while SCL is high -- cannot be formed at all.
assign hang_risk = (payload_bytes != '0) && last_data_acked;
always_ff @(posedge clk) begin
if (!rst_n) begin
scl_q <= 1'b1; // idle bus: SCL released, therefore high
scl_pulses <= '0;
bytes_seen <= '0;
master_acks <= '0;
master_nacks <= '0;
addr_acked <= 1'b0;
expect_address <= 1'b1;
last_data_acked <= 1'b0;
transfer_active <= 1'b0;
metrics_valid <= 1'b0;
end else begin
metrics_valid <= 1'b0;
scl_q <= scl_in;
if (frame_stop) begin
// The counters are deliberately NOT cleared here: a consumer reads them
// AFTER the transfer. The next frame_start clears them instead.
if (transfer_active) metrics_valid <= 1'b1;
transfer_active <= 1'b0;
end else if (frame_start) begin
// An S or Sr begins a new measurement AND a new addressing. Both halves
// matter: the byte after an Sr is an address again, so expect_address
// must be re-armed or every post-Sr read would be counted as data.
scl_pulses <= '0;
bytes_seen <= '0;
master_acks <= '0;
master_nacks <= '0;
addr_acked <= 1'b0;
expect_address <= 1'b1;
last_data_acked <= 1'b0;
transfer_active <= 1'b1;
end else if (transfer_active) begin
if (scl_rise) scl_pulses <= scl_pulses + 1'b1;
if (byte_done) begin
bytes_seen <= bytes_seen + 1'b1;
if (expect_address) begin
// The address byte's ninth bit is the SLAVE's, not the master's.
// Counting it as a master acknowledge is the single easiest way
// to get this instrument wrong, and it would make every read
// report one acknowledge too many.
addr_acked <= !ack_bit;
expect_address <= 1'b0;
end else begin
// From here the ninth bit is the master's.
last_data_acked <= !ack_bit;
if (!ack_bit) master_acks <= master_acks + 1'b1;
else master_nacks <= master_nacks + 1'b1;
end
end
end
end
end
endmodule `timescale 1ns/1ps
module i2c_read_ack_policy_tb;
localparam int CNT_W = 16;
logic clk = 1'b0;
always #5 clk = ~clk;
logic rst_n = 1'b0;
logic scl_in = 1'b1;
logic frame_start = 1'b0, frame_stop = 1'b0, byte_done = 1'b0, ack_bit = 1'b0;
logic [CNT_W-1:0] scl_pulses, bytes_seen, payload_bytes, master_acks, master_nacks;
logic addr_acked, policy_ok, hang_risk, transfer_active, metrics_valid;
int errors = 0;
i2c_read_ack_policy #(.CNT_W(CNT_W)) dut (.*);
initial begin #400000; $display("FAIL: watchdog expired"); $finish; end
int n_valid;
logic obs_clear = 1'b0;
always @(posedge clk) if (rst_n) begin
if (obs_clear) n_valid = 0;
else if (metrics_valid) n_valid++;
end
task automatic clear_obs();
obs_clear = 1'b1; @(negedge clk); obs_clear = 1'b0; @(negedge clk);
endtask
task automatic scl_pulse();
scl_in = 1'b0; @(negedge clk);
scl_in = 1'b1; @(negedge clk);
scl_in = 1'b0; @(negedge clk);
endtask
// One byte: nine SCL pulses, then the byte_done the engine of Chapter 7.1 emits,
// carrying the ninth bit AS OBSERVED -- 0 for ACK, 1 for NACK.
task automatic wire_byte(input logic ninth);
repeat (9) scl_pulse();
ack_bit = ninth;
byte_done = 1'b1; @(negedge clk); byte_done = 1'b0; @(negedge clk);
endtask
task automatic pulse_start(); frame_start = 1'b1; @(negedge clk); frame_start = 1'b0; @(negedge clk); endtask
task automatic pulse_stop(); frame_stop = 1'b1; @(negedge clk); frame_stop = 1'b0; @(negedge clk); endtask
// A WELL-FORMED read of n payload bytes: address ACKed by the slave, then n-1
// master ACKs, then one master NACK.
task automatic read_burst(input int n);
pulse_start();
wire_byte(1'b0); // address + R, slave ACKs
for (int i = 0; i < n; i++)
wire_byte((i == n - 1) ? 1'b1 : 1'b0); // NACK only the last
pulse_stop();
endtask
task automatic check_read(input int n);
int exp_pulses, exp_acks;
exp_pulses = 9 * (n + 1);
exp_acks = (n == 0) ? 0 : n - 1;
if (bytes_seen !== CNT_W'(n + 1)) begin
$display("FAIL: n=%0d -- bytes_seen %0d, expected %0d", n, bytes_seen, n + 1);
errors++; end
if (scl_pulses !== CNT_W'(exp_pulses)) begin
$display("FAIL: n=%0d -- scl_pulses %0d, expected 9(n+1) = %0d",
n, scl_pulses, exp_pulses); errors++; end
if (payload_bytes !== CNT_W'(n)) begin
$display("FAIL: n=%0d -- payload_bytes %0d, expected %0d", n, payload_bytes, n);
errors++; end
if (master_acks !== CNT_W'(exp_acks)) begin
$display("FAIL: n=%0d -- master_acks %0d, expected n-1 = %0d",
n, master_acks, exp_acks); errors++; end
if (master_nacks !== CNT_W'((n == 0) ? 0 : 1)) begin
$display("FAIL: n=%0d -- master_nacks %0d, expected %0d",
n, master_nacks, (n == 0) ? 0 : 1); errors++; end
if (addr_acked !== 1'b1) begin
$display("FAIL: n=%0d -- addr_acked not set", n); errors++; end
if (policy_ok !== 1'b1) begin
$display("FAIL: n=%0d -- a well-formed read was reported as policy violation", n);
errors++; end
if (hang_risk !== 1'b0) begin
$display("FAIL: n=%0d -- spurious hang_risk on a well-formed read", n);
errors++; end
endtask
initial begin
repeat (3) @(negedge clk);
if (transfer_active !== 1'b0) begin $display("FAIL: active out of reset"); errors++; end
if (payload_bytes !== '0) begin
$display("FAIL: payload_bytes %0d before any byte -- the guarded subtraction wrapped",
payload_bytes); errors++; end
if (hang_risk !== 1'b0) begin
$display("FAIL: hang_risk out of reset"); errors++; end
rst_n = 1'b1; @(negedge clk);
clear_obs();
// ---- 1: well-formed reads across a range of lengths.
read_burst(1); check_read(1);
read_burst(2); check_read(2);
read_burst(4); check_read(4);
read_burst(8); check_read(8);
// ---- 2: a ONE-byte read. Its only master acknowledge is the NACK, so
// master_acks is ZERO. An instrument that assumed at least one ACK per
// read reports a policy violation here and nowhere else.
read_burst(1);
if (master_acks !== '0) begin
$display("FAIL: a 1-byte read reported %0d master ACKs, expected 0", master_acks);
errors++; end
if (master_nacks !== 16'd1 || policy_ok !== 1'b1) begin
$display("FAIL: a 1-byte read is well-formed and was not reported so"); errors++; end
// ---- 3: an ADDRESS-ONLY probe with R/W = 1. One byte, the slave's ACK, and NO
// master acknowledges at all -- the master never became a receiver.
pulse_start();
wire_byte(1'b0);
pulse_stop();
check_read(0);
if (master_acks !== '0 || master_nacks !== '0) begin
$display("FAIL: a read probe produced master acknowledges"); errors++; end
// ---- 4: THE HANG. The master ACKs the final byte instead of NACKing it. The
// slave has been told "send another", will drive the next byte, and holds
// SDA low -- so the STOP cannot form. The instrument must predict this
// from the acknowledge pattern, before the hang happens.
pulse_start();
wire_byte(1'b0); // address + R, slave ACKs
wire_byte(1'b0); wire_byte(1'b0); // two data bytes, BOTH ACKed
if (hang_risk !== 1'b1) begin
$display("FAIL: the final byte was ACKed and hang_risk was not raised"); errors++; end
if (policy_ok !== 1'b0) begin
$display("FAIL: ACKing the final byte is a policy violation and was not flagged");
errors++; end
if (master_acks !== 16'd2 || master_nacks !== '0) begin
$display("FAIL: hang case counted %0d ACKs and %0d NACKs, expected 2 and 0",
master_acks, master_nacks); errors++; end
pulse_stop();
// ---- 5: TWO NACKs. A master that NACKs a middle byte and then keeps reading
// has violated the policy even though the final slot is a NACK, so the
// final-slot check alone is not sufficient.
pulse_start();
wire_byte(1'b0); // address
wire_byte(1'b1); // data byte 1 NACKed -- ends it, but...
wire_byte(1'b1); // ...another byte read anyway
if (master_nacks !== 16'd2) begin
$display("FAIL: counted %0d NACKs, expected 2", master_nacks); errors++; end
if (policy_ok !== 1'b0) begin
$display("FAIL: two NACKs in one read is a violation and was not flagged");
errors++; end
pulse_stop();
// ---- 6: the ADDRESS was NACKed. Nobody is there. addr_acked must be clear,
// and this must NOT be counted as a master acknowledge -- the address
// byte's ninth bit belongs to the slave.
pulse_start();
wire_byte(1'b1); // address + R, NOBODY answers
pulse_stop();
if (addr_acked !== 1'b0) begin
$display("FAIL: addr_acked set on an unanswered address"); errors++; end
if (master_nacks !== '0 || master_acks !== '0) begin
$display("FAIL: the address byte's NACK was counted as the master's (%0d/%0d)",
master_acks, master_nacks); errors++; end
if (bytes_seen !== 16'd1) begin
$display("FAIL: bytes_seen %0d after an unanswered address", bytes_seen); errors++; end
// ---- 7: the metrics must SURVIVE the STOP, because that is when they are read.
if (transfer_active !== 1'b0) begin $display("FAIL: still active after the STOP"); errors++; end
if (bytes_seen === '0) begin
$display("FAIL: the counters were cleared by the STOP"); errors++; end
// ---- 8: a repeated START re-arms the ADDRESS expectation. The byte after an Sr
// is an address again, so a design that did not re-arm would count it as a
// data byte and report one master acknowledge too many.
pulse_start();
wire_byte(1'b0); wire_byte(1'b0); wire_byte(1'b1); // a 2-byte read
pulse_start(); // Sr
if (bytes_seen !== '0) begin
$display("FAIL: a repeated START did not restart the measurement"); errors++; end
wire_byte(1'b0); // the post-Sr ADDRESS
wire_byte(1'b0); wire_byte(1'b1); // two data bytes
pulse_stop();
check_read(2);
// ---- 9: an IDLE BUS must not be measured. Between transfers SCL keeps moving,
// and counting any of it would corrupt the totals a consumer reads.
begin
int snap_pulses, snap_bytes;
snap_pulses = scl_pulses; snap_bytes = bytes_seen;
repeat (6) scl_pulse();
wire_byte(1'b0);
if (scl_pulses !== snap_pulses[CNT_W-1:0] || bytes_seen !== snap_bytes[CNT_W-1:0]) begin
$display("FAIL: idle-bus activity was counted -- %0d/%0d, expected %0d/%0d",
scl_pulses, bytes_seen, snap_pulses, snap_bytes); errors++; end
if (transfer_active !== 1'b0) begin
$display("FAIL: clocking an idle bus opened a transfer"); errors++; end
end
// ---- 10: a STOP that closed nothing must not announce a measurement.
begin
int snap_valid;
snap_valid = n_valid;
pulse_stop();
if (n_valid !== snap_valid) begin
$display("FAIL: a STOP that closed nothing produced metrics_valid"); errors++; end
end
// one metrics_valid per completed transfer: 4 bursts + 1 + probe + hang + two-NACK
// + addr-NACK + the Sr transfer = 10.
if (n_valid !== 10) begin
$display("FAIL: %0d metrics_valid pulses, expected 10", n_valid); errors++; end
if (errors == 0)
$display("PASS: ack pattern is n-1 ACKs then one NACK, the address byte's answer is the slave's, the hang is predicted, both malformed patterns rejected");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule // A PASSIVE instrument (Verilog-2001) that checks a read transfer's acknowledge policy and measures
// what the transfer cost. It drives nothing and cannot perturb what it observes.
//
// A read of n payload bytes has the same shape on the wire as a write -- n+1 bytes and
// 9(n+1) SCL pulses -- and a completely different acknowledge pattern:
//
// byte 0 (address + R) : ninth bit driven by the SLAVE -> must be ACK
// bytes 1 .. n-1 : ninth bit driven by the MASTER -> ACK "send another"
// byte n (the last) : ninth bit driven by the MASTER -> NACK "no more"
//
// so a well-formed read contains EXACTLY ONE master NACK and it is always in the final
// slot. That is the fifth NACK condition of section 3.1.6 -- "a master-receiver must
// signal the end of the transfer to the slave transmitter" -- and it is the only
// mechanism the protocol offers for saying so: there is no length field and no command
// for "stop".
//
// The instrument therefore checks two things a simple pass/fail monitor cannot:
// policy_ok -- the pattern was n-1 ACKs then one NACK, nothing else
// hang_risk -- the final data byte was ACKED, which means the slave will transmit
// ANOTHER byte and hold SDA low through the window in which SDA must
// rise to form the STOP. The STOP cannot form and the bus hangs.
// This is Chapter 7.4's failure, detected from the acknowledge pattern
// alone, before the hang has happened.
module i2c_read_ack_policy #(
parameter CNT_W = 16
)(
input wire clk,
input wire rst_n,
input wire scl_in, // observed bus level
input wire frame_start, // pulse: S or Sr
input wire frame_stop, // pulse: P
input wire byte_done, // pulse: a byte AND its ninth slot completed
// The ninth bit AS OBSERVED ON THE WIRE: 0 means SDA was pulled LOW, which is an
// ACK; 1 means SDA stayed HIGH, which is a NACK. The polarity is the bus's, not a
// convenience -- section 3.1.6 defines the acknowledge as the receiver pulling the
// line low, so "low is yes" is the physical fact and inverting it here would put a
// translation between the instrument and the thing it measures.
input wire ack_bit,
output reg [CNT_W-1:0] scl_pulses,
output reg [CNT_W-1:0] bytes_seen, // INCLUDING the address byte
output wire [CNT_W-1:0] payload_bytes, // bytes_seen - 1, floored at zero
output reg [CNT_W-1:0] master_acks, // "send me another"
output reg [CNT_W-1:0] master_nacks, // should be exactly one
output reg addr_acked, // the slave answered the address
output wire policy_ok, // n-1 ACKs then exactly one final NACK
output wire hang_risk, // the final data byte was ACKed
output reg transfer_active,
output reg metrics_valid // pulse: a STOP closed the transfer
);
reg scl_q;
wire scl_rise;
assign scl_rise = !scl_q && scl_in;
// The first byte after an S or Sr is the address byte. Nothing in the frame marks
// it, so position is the only way to know -- which is exactly why a decoder that
// misses a repeated START reads an address byte as data (Chapter 7.5, section 10).
reg expect_address;
// Whether the most recent DATA byte was acknowledged. This is the whole basis of
// the hang prediction, and it must be the LAST one rather than "any of them".
reg last_data_acked;
// The address byte carries no payload, so it is excluded -- and the subtraction is
// guarded, because bytes_seen is zero before the first byte completes and an
// unguarded 0 - 1 would wrap to a very large number rather than to zero.
assign payload_bytes = (bytes_seen == {CNT_W{1'b0}}) ? {CNT_W{1'b0}} : (bytes_seen - 1'b1);
// A well-formed read: every data byte but the last was ACKed, the last was NACKed,
// and there was exactly one NACK in total. An address-only probe (no data bytes at
// all) is well-formed too and produces no master acknowledges whatsoever, so it is
// admitted explicitly rather than falling out of the arithmetic by accident.
assign policy_ok = (payload_bytes == {CNT_W{1'b0}})
? ((master_acks == {CNT_W{1'b0}}) && (master_nacks == {CNT_W{1'b0}}))
: ((master_nacks == {{(CNT_W-1){1'b0}}, 1'b1})
&& (master_acks == (payload_bytes - 1'b1))
&& !last_data_acked);
// The prediction. If the final data byte was ACKed, the slave-transmitter has been
// told "send another" and will drive the next byte's MSB. Whenever that bit is a
// zero the slave holds SDA low, so the master's STOP -- which needs SDA to RISE
// while SCL is high -- cannot be formed at all.
assign hang_risk = (payload_bytes != {CNT_W{1'b0}}) && last_data_acked;
always @(posedge clk) begin
if (!rst_n) begin
scl_q <= 1'b1; // idle bus: SCL released, therefore high
scl_pulses <= {CNT_W{1'b0}};
bytes_seen <= {CNT_W{1'b0}};
master_acks <= {CNT_W{1'b0}};
master_nacks <= {CNT_W{1'b0}};
addr_acked <= 1'b0;
expect_address <= 1'b1;
last_data_acked <= 1'b0;
transfer_active <= 1'b0;
metrics_valid <= 1'b0;
end else begin
metrics_valid <= 1'b0;
scl_q <= scl_in;
if (frame_stop) begin
// The counters are deliberately NOT cleared here: a consumer reads them
// AFTER the transfer. The next frame_start clears them instead.
if (transfer_active) metrics_valid <= 1'b1;
transfer_active <= 1'b0;
end else if (frame_start) begin
// An S or Sr begins a new measurement AND a new addressing. Both halves
// matter: the byte after an Sr is an address again, so expect_address
// must be re-armed or every post-Sr read would be counted as data.
scl_pulses <= {CNT_W{1'b0}};
bytes_seen <= {CNT_W{1'b0}};
master_acks <= {CNT_W{1'b0}};
master_nacks <= {CNT_W{1'b0}};
addr_acked <= 1'b0;
expect_address <= 1'b1;
last_data_acked <= 1'b0;
transfer_active <= 1'b1;
end else if (transfer_active) begin
if (scl_rise) scl_pulses <= scl_pulses + 1'b1;
if (byte_done) begin
bytes_seen <= bytes_seen + 1'b1;
if (expect_address) begin
// The address byte's ninth bit is the SLAVE's, not the master's.
// Counting it as a master acknowledge is the single easiest way
// to get this instrument wrong, and it would make every read
// report one acknowledge too many.
addr_acked <= !ack_bit;
expect_address <= 1'b0;
end else begin
// From here the ninth bit is the master's.
last_data_acked <= !ack_bit;
if (!ack_bit) master_acks <= master_acks + 1'b1;
else master_nacks <= master_nacks + 1'b1;
end
end
end
end
end
endmodule `timescale 1ns/1ps
module i2c_read_ack_policy_tb; // Verilog-2001
localparam CNT_W = 16;
reg clk = 1'b0;
always #5 clk = ~clk;
reg rst_n = 1'b0;
reg scl_in = 1'b1;
reg frame_start = 1'b0, frame_stop = 1'b0, byte_done = 1'b0, ack_bit = 1'b0;
wire [CNT_W-1:0] scl_pulses, bytes_seen, payload_bytes, master_acks, master_nacks;
wire addr_acked, policy_ok, hang_risk, transfer_active, metrics_valid;
integer errors = 0;
integer i;
integer exp_pulses, exp_acks;
integer snap_pulses, snap_bytes, snap_valid;
i2c_read_ack_policy #(.CNT_W(CNT_W)) dut (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .frame_start(frame_start),
.frame_stop(frame_stop), .byte_done(byte_done), .ack_bit(ack_bit),
.scl_pulses(scl_pulses), .bytes_seen(bytes_seen), .payload_bytes(payload_bytes),
.master_acks(master_acks), .master_nacks(master_nacks), .addr_acked(addr_acked),
.policy_ok(policy_ok), .hang_risk(hang_risk), .transfer_active(transfer_active),
.metrics_valid(metrics_valid));
initial begin #400000; $display("FAIL: watchdog expired"); $finish; end
integer n_valid = 0;
reg obs_clear = 1'b0;
always @(posedge clk) if (rst_n) begin
if (obs_clear) n_valid = 0;
else if (metrics_valid) n_valid = n_valid + 1;
end
task clear_obs; begin
obs_clear = 1'b1; @(negedge clk); obs_clear = 1'b0; @(negedge clk);
end endtask
task scl_pulse; begin
scl_in = 1'b0; @(negedge clk);
scl_in = 1'b1; @(negedge clk);
scl_in = 1'b0; @(negedge clk);
end endtask
// One byte: nine SCL pulses, then the byte_done the engine of Chapter 7.1 emits,
// carrying the ninth bit AS OBSERVED -- 0 for ACK, 1 for NACK.
task wire_byte;
input ninth;
begin
repeat (9) scl_pulse;
ack_bit = ninth;
byte_done = 1'b1; @(negedge clk); byte_done = 1'b0; @(negedge clk);
end
endtask
task pulse_start; begin frame_start = 1'b1; @(negedge clk); frame_start = 1'b0; @(negedge clk); end endtask
task pulse_stop; begin frame_stop = 1'b1; @(negedge clk); frame_stop = 1'b0; @(negedge clk); end endtask
// A WELL-FORMED read of n payload bytes: address ACKed by the slave, then n-1
// master ACKs, then one master NACK.
task read_burst;
input integer n;
begin
pulse_start;
wire_byte(1'b0); // address + R, slave ACKs
for (i = 0; i < n; i = i + 1)
wire_byte((i == n - 1) ? 1'b1 : 1'b0); // NACK only the last
pulse_stop;
end
endtask
task check_read;
input integer n;
begin
exp_pulses = 9 * (n + 1);
exp_acks = (n == 0) ? 0 : n - 1;
if (bytes_seen !== (n + 1)) begin
$display("FAIL: n=%0d -- bytes_seen %0d, expected %0d", n, bytes_seen, n + 1);
errors = errors + 1; end
if (scl_pulses !== exp_pulses) begin
$display("FAIL: n=%0d -- scl_pulses %0d, expected 9(n+1) = %0d",
n, scl_pulses, exp_pulses); errors = errors + 1; end
if (payload_bytes !== n) begin
$display("FAIL: n=%0d -- payload_bytes %0d, expected %0d", n, payload_bytes, n);
errors = errors + 1; end
if (master_acks !== exp_acks) begin
$display("FAIL: n=%0d -- master_acks %0d, expected n-1 = %0d",
n, master_acks, exp_acks); errors = errors + 1; end
if (master_nacks !== ((n == 0) ? 0 : 1)) begin
$display("FAIL: n=%0d -- master_nacks %0d, expected %0d",
n, master_nacks, (n == 0) ? 0 : 1); errors = errors + 1; end
if (addr_acked !== 1'b1) begin
$display("FAIL: n=%0d -- addr_acked not set", n); errors = errors + 1; end
if (policy_ok !== 1'b1) begin
$display("FAIL: n=%0d -- a well-formed read was reported as policy violation", n);
errors = errors + 1; end
if (hang_risk !== 1'b0) begin
$display("FAIL: n=%0d -- spurious hang_risk on a well-formed read", n);
errors = errors + 1; end
end
endtask
initial begin
repeat (3) @(negedge clk);
if (transfer_active !== 1'b0) begin $display("FAIL: active out of reset"); errors = errors + 1; end
if (payload_bytes !== {CNT_W{1'b0}}) begin
$display("FAIL: payload_bytes %0d before any byte -- the guarded subtraction wrapped",
payload_bytes); errors = errors + 1; end
if (hang_risk !== 1'b0) begin
$display("FAIL: hang_risk out of reset"); errors = errors + 1; end
rst_n = 1'b1; @(negedge clk);
clear_obs;
// ---- 1: well-formed reads across a range of lengths.
read_burst(1); check_read(1);
read_burst(2); check_read(2);
read_burst(4); check_read(4);
read_burst(8); check_read(8);
// ---- 2: a ONE-byte read. Its only master acknowledge is the NACK, so
// master_acks is ZERO. An instrument that assumed at least one ACK per
// read reports a policy violation here and nowhere else.
read_burst(1);
if (master_acks !== {CNT_W{1'b0}}) begin
$display("FAIL: a 1-byte read reported %0d master ACKs, expected 0", master_acks);
errors = errors + 1; end
if (master_nacks !== 16'd1 || policy_ok !== 1'b1) begin
$display("FAIL: a 1-byte read is well-formed and was not reported so"); errors = errors + 1; end
// ---- 3: an ADDRESS-ONLY probe with R/W = 1. One byte, the slave's ACK, and NO
// master acknowledges at all -- the master never became a receiver.
pulse_start;
wire_byte(1'b0);
pulse_stop;
check_read(0);
if (master_acks !== {CNT_W{1'b0}} || master_nacks !== {CNT_W{1'b0}}) begin
$display("FAIL: a read probe produced master acknowledges"); errors = errors + 1; end
// ---- 4: THE HANG. The master ACKs the final byte instead of NACKing it. The
// slave has been told "send another", will drive the next byte, and holds
// SDA low -- so the STOP cannot form. The instrument must predict this
// from the acknowledge pattern, before the hang happens.
pulse_start;
wire_byte(1'b0); // address + R, slave ACKs
wire_byte(1'b0); wire_byte(1'b0); // two data bytes, BOTH ACKed
if (hang_risk !== 1'b1) begin
$display("FAIL: the final byte was ACKed and hang_risk was not raised"); errors = errors + 1; end
if (policy_ok !== 1'b0) begin
$display("FAIL: ACKing the final byte is a policy violation and was not flagged");
errors = errors + 1; end
if (master_acks !== 16'd2 || master_nacks !== {CNT_W{1'b0}}) begin
$display("FAIL: hang case counted %0d ACKs and %0d NACKs, expected 2 and 0",
master_acks, master_nacks); errors = errors + 1; end
pulse_stop;
// ---- 5: TWO NACKs. A master that NACKs a middle byte and then keeps reading
// has violated the policy even though the final slot is a NACK, so the
// final-slot check alone is not sufficient.
pulse_start;
wire_byte(1'b0); // address
wire_byte(1'b1); // data byte 1 NACKed -- ends it, but...
wire_byte(1'b1); // ...another byte read anyway
if (master_nacks !== 16'd2) begin
$display("FAIL: counted %0d NACKs, expected 2", master_nacks); errors = errors + 1; end
if (policy_ok !== 1'b0) begin
$display("FAIL: two NACKs in one read is a violation and was not flagged");
errors = errors + 1; end
pulse_stop;
// ---- 6: the ADDRESS was NACKed. Nobody is there. addr_acked must be clear,
// and this must NOT be counted as a master acknowledge -- the address
// byte's ninth bit belongs to the slave.
pulse_start;
wire_byte(1'b1); // address + R, NOBODY answers
pulse_stop;
if (addr_acked !== 1'b0) begin
$display("FAIL: addr_acked set on an unanswered address"); errors = errors + 1; end
if (master_nacks !== {CNT_W{1'b0}} || master_acks !== {CNT_W{1'b0}}) begin
$display("FAIL: the address byte's NACK was counted as the master's (%0d/%0d)",
master_acks, master_nacks); errors = errors + 1; end
if (bytes_seen !== 16'd1) begin
$display("FAIL: bytes_seen %0d after an unanswered address", bytes_seen); errors = errors + 1; end
// ---- 7: the metrics must SURVIVE the STOP, because that is when they are read.
if (transfer_active !== 1'b0) begin $display("FAIL: still active after the STOP"); errors = errors + 1; end
if (bytes_seen === {CNT_W{1'b0}}) begin
$display("FAIL: the counters were cleared by the STOP"); errors = errors + 1; end
// ---- 8: a repeated START re-arms the ADDRESS expectation. The byte after an Sr
// is an address again, so a design that did not re-arm would count it as a
// data byte and report one master acknowledge too many.
pulse_start;
wire_byte(1'b0); wire_byte(1'b0); wire_byte(1'b1); // a 2-byte read
pulse_start; // Sr
if (bytes_seen !== {CNT_W{1'b0}}) begin
$display("FAIL: a repeated START did not restart the measurement"); errors = errors + 1; end
wire_byte(1'b0); // the post-Sr ADDRESS
wire_byte(1'b0); wire_byte(1'b1); // two data bytes
pulse_stop;
check_read(2);
// ---- 9: an IDLE BUS must not be measured. Between transfers SCL keeps moving,
// and counting any of it would corrupt the totals a consumer reads.
begin
snap_pulses = scl_pulses; snap_bytes = bytes_seen;
repeat (6) scl_pulse;
wire_byte(1'b0);
if (scl_pulses !== snap_pulses[CNT_W-1:0] || bytes_seen !== snap_bytes[CNT_W-1:0]) begin
$display("FAIL: idle-bus activity was counted -- %0d/%0d, expected %0d/%0d",
scl_pulses, bytes_seen, snap_pulses, snap_bytes); errors = errors + 1; end
if (transfer_active !== 1'b0) begin
$display("FAIL: clocking an idle bus opened a transfer"); errors = errors + 1; end
end
// ---- 10: a STOP that closed nothing must not announce a measurement.
begin
snap_valid = n_valid;
pulse_stop;
if (n_valid !== snap_valid) begin
$display("FAIL: a STOP that closed nothing produced metrics_valid"); errors = errors + 1; end
end
// one metrics_valid per completed transfer: 4 bursts + 1 + probe + hang + two-NACK
// + addr-NACK + the Sr transfer = 10.
if (n_valid !== 10) begin
$display("FAIL: %0d metrics_valid pulses, expected 10", n_valid); errors = errors + 1; end
if (errors == 0)
$display("PASS: ack pattern is n-1 ACKs then one NACK, the address byte's answer is the slave's, the hang is predicted, both malformed patterns rejected");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- A PASSIVE instrument that checks a read transfer's acknowledge policy and measures
-- what the transfer cost. It drives nothing and cannot perturb what it observes.
--
-- A read of n payload bytes has the same shape on the wire as a write -- n+1 bytes and
-- 9(n+1) SCL pulses -- and a completely different acknowledge pattern:
--
-- byte 0 (address + R) : ninth bit driven by the SLAVE -> must be ACK
-- bytes 1 .. n-1 : ninth bit driven by the MASTER -> ACK "send another"
-- byte n (the last) : ninth bit driven by the MASTER -> NACK "no more"
--
-- so a well-formed read contains EXACTLY ONE master NACK and it is always in the final
-- slot. That is the fifth NACK condition of section 3.1.6 -- "a master-receiver must
-- signal the end of the transfer to the slave transmitter" -- and it is the only
-- mechanism the protocol offers for saying so: there is no length field and no command
-- for "stop".
--
-- The instrument therefore checks two things a simple pass/fail monitor cannot:
-- policy_ok -- the pattern was n-1 ACKs then one NACK, nothing else
-- hang_risk -- the final data byte was ACKED, which means the slave will transmit
-- ANOTHER byte and hold SDA low through the window in which SDA must
-- rise to form the STOP. The STOP cannot form and the bus hangs.
entity i2c_read_ack_policy is
generic (
CNT_W : positive := 16
);
port (
clk : in std_logic;
rst_n : in std_logic;
scl_in : in std_logic; -- observed bus level
frame_start : in std_logic; -- pulse: S or Sr
frame_stop : in std_logic; -- pulse: P
byte_done : in std_logic; -- pulse: a byte AND its ninth slot completed
-- The ninth bit AS OBSERVED ON THE WIRE: 0 means SDA was pulled LOW, which is an
-- ACK; 1 means SDA stayed HIGH, which is a NACK. The polarity is the bus's, not
-- a convenience -- section 3.1.6 defines the acknowledge as the receiver pulling
-- the line low, so "low is yes" is the physical fact.
ack_bit : in std_logic;
scl_pulses : out unsigned(CNT_W - 1 downto 0);
bytes_seen : out unsigned(CNT_W - 1 downto 0); -- INCLUDING the address
payload_bytes : out unsigned(CNT_W - 1 downto 0); -- bytes_seen - 1, floored
master_acks : out unsigned(CNT_W - 1 downto 0); -- "send me another"
master_nacks : out unsigned(CNT_W - 1 downto 0); -- should be exactly one
addr_acked : out std_logic;
policy_ok : out std_logic;
hang_risk : out std_logic;
transfer_active : out std_logic;
metrics_valid : out std_logic
);
end entity;
architecture rtl of i2c_read_ack_policy is
constant ZERO : unsigned(CNT_W - 1 downto 0) := (others => '0');
constant ONE : unsigned(CNT_W - 1 downto 0) := to_unsigned(1, CNT_W);
signal scl_q : std_logic := '1'; -- idle bus: SCL released, therefore high
signal scl_rise : std_logic;
signal pulses : unsigned(CNT_W - 1 downto 0) := (others => '0');
signal nbytes : unsigned(CNT_W - 1 downto 0) := (others => '0');
signal n_ack : unsigned(CNT_W - 1 downto 0) := (others => '0');
signal n_nack : unsigned(CNT_W - 1 downto 0) := (others => '0');
signal active : std_logic := '0';
signal payload : unsigned(CNT_W - 1 downto 0);
-- The first byte after an S or Sr is the address byte. Nothing in the frame marks
-- it, so position is the only way to know -- which is exactly why a decoder that
-- misses a repeated START reads an address byte as data (Chapter 7.5, section 10).
signal expect_address : std_logic := '1';
-- Whether the most recent DATA byte was acknowledged. This is the whole basis of
-- the hang prediction, and it must be the LAST one rather than "any of them".
signal last_data_acked : std_logic := '0';
begin
scl_rise <= (not scl_q) and scl_in;
scl_pulses <= pulses;
bytes_seen <= nbytes;
master_acks <= n_ack;
master_nacks <= n_nack;
transfer_active <= active;
-- The address byte carries no payload, so it is excluded -- and the subtraction is
-- guarded, because nbytes is zero before the first byte completes and an unguarded
-- 0 - 1 would wrap to a very large number rather than to zero.
payload <= ZERO when nbytes = ZERO else nbytes - 1;
payload_bytes <= payload;
-- A well-formed read: every data byte but the last was ACKed, the last was NACKed,
-- and there was exactly one NACK in total. An address-only probe (no data bytes at
-- all) is well-formed too and produces no master acknowledges whatsoever, so it is
-- admitted explicitly rather than falling out of the arithmetic by accident.
policy_ok <= '1' when (payload = ZERO and n_ack = ZERO and n_nack = ZERO)
or (payload /= ZERO and n_nack = ONE and n_ack = payload - 1
and last_data_acked = '0')
else '0';
-- The prediction. If the final data byte was ACKed, the slave-transmitter has been
-- told "send another" and will drive the next byte's MSB. Whenever that bit is a
-- zero the slave holds SDA low, so the master's STOP -- which needs SDA to RISE
-- while SCL is high -- cannot be formed at all.
hang_risk <= '1' when payload /= ZERO and last_data_acked = '1' else '0';
process (clk)
begin
if rising_edge(clk) then
if rst_n = '0' then
scl_q <= '1';
pulses <= (others => '0');
nbytes <= (others => '0');
n_ack <= (others => '0');
n_nack <= (others => '0');
addr_acked <= '0';
expect_address <= '1';
last_data_acked <= '0';
active <= '0';
metrics_valid <= '0';
else
metrics_valid <= '0';
scl_q <= scl_in;
if frame_stop = '1' then
-- The counters are deliberately NOT cleared here: a consumer reads
-- them AFTER the transfer. The next frame_start clears them.
if active = '1' then metrics_valid <= '1'; end if;
active <= '0';
elsif frame_start = '1' then
-- An S or Sr begins a new measurement AND a new addressing. Both
-- halves matter: the byte after an Sr is an address again, so
-- expect_address must be re-armed or every post-Sr read would be
-- counted as data.
pulses <= (others => '0');
nbytes <= (others => '0');
n_ack <= (others => '0');
n_nack <= (others => '0');
addr_acked <= '0';
expect_address <= '1';
last_data_acked <= '0';
active <= '1';
elsif active = '1' then
if scl_rise = '1' then pulses <= pulses + 1; end if;
if byte_done = '1' then
nbytes <= nbytes + 1;
if expect_address = '1' then
-- The address byte's ninth bit is the SLAVE's, not the
-- master's. Counting it as a master acknowledge is the
-- single easiest way to get this instrument wrong, and it
-- would make every read report one acknowledge too many.
addr_acked <= not ack_bit;
expect_address <= '0';
else
-- From here the ninth bit is the master's.
last_data_acked <= not ack_bit;
if ack_bit = '0' then n_ack <= n_ack + 1;
else n_nack <= n_nack + 1; end if;
end if;
end if;
end if;
end if;
end if;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- Every signal here has exactly ONE driving process, because VHDL permits one driver
-- per signal -- and the SystemVerilog and Verilog testbenches were written to the same
-- discipline so that the matching finish time between the three means something.
entity i2c_read_ack_policy_tb is
end entity;
architecture sim of i2c_read_ack_policy_tb is
constant CNT_W : positive := 16;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal scl_in : std_logic := '1';
signal frame_start : std_logic := '0';
signal frame_stop : std_logic := '0';
signal byte_done : std_logic := '0';
signal ack_bit : std_logic := '0';
signal scl_pulses, bytes_seen, payload_bytes : unsigned(CNT_W - 1 downto 0);
signal master_acks, master_nacks : unsigned(CNT_W - 1 downto 0);
signal addr_acked, policy_ok, hang_risk : std_logic;
signal transfer_active, metrics_valid : std_logic;
-- owned solely by the observe process
signal n_valid : natural := 0;
signal obs_clear : std_logic := '0';
signal test_done : std_logic := '0';
begin
dut : entity work.i2c_read_ack_policy
generic map (CNT_W => CNT_W)
port map (clk => clk, rst_n => rst_n, scl_in => scl_in,
frame_start => frame_start, frame_stop => frame_stop,
byte_done => byte_done, ack_bit => ack_bit,
scl_pulses => scl_pulses, bytes_seen => bytes_seen,
payload_bytes => payload_bytes, master_acks => master_acks,
master_nacks => master_nacks, addr_acked => addr_acked,
policy_ok => policy_ok, hang_risk => hang_risk,
transfer_active => transfer_active, metrics_valid => metrics_valid);
clk <= not clk after 5 ns;
watchdog : process
begin
wait for 500 us;
if test_done = '0' then
report "watchdog expired -- the design never reached the expected state"
severity failure;
end if;
wait;
end process;
observe : process (clk)
begin
if rising_edge(clk) and rst_n = '1' then
if obs_clear = '1' then
n_valid <= 0;
elsif metrics_valid = '1' then
n_valid <= n_valid + 1;
end if;
end if;
end process;
stim : process
variable errs : natural := 0;
variable snap_pulses, snap_bytes : unsigned(CNT_W - 1 downto 0);
variable snap_valid : natural;
procedure waitn (n : in positive) is
begin
for i in 1 to n loop wait until falling_edge(clk); end loop;
end procedure;
procedure clear_obs is
begin
obs_clear <= '1'; waitn(1); obs_clear <= '0'; waitn(1);
end procedure;
procedure scl_pulse is
begin
scl_in <= '0'; waitn(1);
scl_in <= '1'; waitn(1);
scl_in <= '0'; waitn(1);
end procedure;
-- One byte: nine SCL pulses, then the byte_done the engine of Chapter 7.1
-- emits, carrying the ninth bit AS OBSERVED -- 0 for ACK, 1 for NACK.
procedure wire_byte (ninth : in std_logic) is
begin
for i in 1 to 9 loop scl_pulse; end loop;
ack_bit <= ninth;
byte_done <= '1'; waitn(1); byte_done <= '0'; waitn(1);
end procedure;
procedure pulse_start is
begin
frame_start <= '1'; waitn(1); frame_start <= '0'; waitn(1);
end procedure;
procedure pulse_stop is
begin
frame_stop <= '1'; waitn(1); frame_stop <= '0'; waitn(1);
end procedure;
-- A WELL-FORMED read of n payload bytes: address ACKed by the slave, then n-1
-- master ACKs, then one master NACK.
procedure read_burst (n : in natural) is
begin
pulse_start;
wire_byte('0'); -- address + R, slave ACKs
for i in 0 to n - 1 loop
if i = n - 1 then wire_byte('1'); -- NACK only the last
else wire_byte('0'); end if;
end loop;
pulse_stop;
end procedure;
procedure check_read (n : in natural) is
variable exp_pulses, exp_acks, exp_nacks : natural;
begin
exp_pulses := 9 * (n + 1);
if n = 0 then exp_acks := 0; exp_nacks := 0;
else exp_acks := n - 1; exp_nacks := 1; end if;
if bytes_seen /= to_unsigned(n + 1, CNT_W) then
report "n=" & integer'image(n) & ": bytes_seen wrong" severity error;
errs := errs + 1; end if;
if scl_pulses /= to_unsigned(exp_pulses, CNT_W) then
report "n=" & integer'image(n) & ": scl_pulses is not 9(n+1)" severity error;
errs := errs + 1; end if;
if payload_bytes /= to_unsigned(n, CNT_W) then
report "n=" & integer'image(n) & ": payload_bytes wrong" severity error;
errs := errs + 1; end if;
if master_acks /= to_unsigned(exp_acks, CNT_W) then
report "n=" & integer'image(n) & ": master_acks is not n-1" severity error;
errs := errs + 1; end if;
if master_nacks /= to_unsigned(exp_nacks, CNT_W) then
report "n=" & integer'image(n) & ": master_nacks wrong" severity error;
errs := errs + 1; end if;
if addr_acked /= '1' then
report "n=" & integer'image(n) & ": addr_acked not set" severity error;
errs := errs + 1; end if;
if policy_ok /= '1' then
report "n=" & integer'image(n) & ": a well-formed read was reported as a "
& "policy violation" severity error;
errs := errs + 1; end if;
if hang_risk /= '0' then
report "n=" & integer'image(n) & ": spurious hang_risk" severity error;
errs := errs + 1; end if;
end procedure;
begin
waitn(3);
if transfer_active /= '0' then
report "active out of reset" severity error; errs := errs + 1; end if;
if payload_bytes /= to_unsigned(0, CNT_W) then
report "payload_bytes nonzero before any byte -- the guarded subtraction wrapped"
severity error; errs := errs + 1; end if;
if hang_risk /= '0' then
report "hang_risk out of reset" severity error; errs := errs + 1; end if;
rst_n <= '1'; waitn(1);
clear_obs;
-- 1: well-formed reads across a range of lengths.
read_burst(1); check_read(1);
read_burst(2); check_read(2);
read_burst(4); check_read(4);
read_burst(8); check_read(8);
-- 2: a ONE-byte read. Its only master acknowledge is the NACK, so master_acks
-- is ZERO. An instrument that assumed at least one ACK per read reports a policy
-- violation here and nowhere else.
read_burst(1);
if master_acks /= to_unsigned(0, CNT_W) then
report "a 1-byte read reported master ACKs, expected none" severity error;
errs := errs + 1; end if;
if master_nacks /= to_unsigned(1, CNT_W) or policy_ok /= '1' then
report "a 1-byte read is well-formed and was not reported so" severity error;
errs := errs + 1; end if;
-- 3: an ADDRESS-ONLY probe with R/W = 1. One byte, the slave's ACK, and NO
-- master acknowledges at all -- the master never became a receiver.
pulse_start;
wire_byte('0');
pulse_stop;
check_read(0);
if master_acks /= to_unsigned(0, CNT_W) or master_nacks /= to_unsigned(0, CNT_W) then
report "a read probe produced master acknowledges" severity error;
errs := errs + 1; end if;
-- 4: THE HANG. The master ACKs the final byte instead of NACKing it. The slave
-- has been told "send another", will drive the next byte, and holds SDA low --
-- so the STOP cannot form. The instrument must predict this from the
-- acknowledge pattern, before the hang happens.
pulse_start;
wire_byte('0'); -- address + R, slave ACKs
wire_byte('0'); wire_byte('0'); -- two data bytes, BOTH ACKed
if hang_risk /= '1' then
report "the final byte was ACKed and hang_risk was not raised" severity error;
errs := errs + 1; end if;
if policy_ok /= '0' then
report "ACKing the final byte is a policy violation and was not flagged"
severity error; errs := errs + 1; end if;
if master_acks /= to_unsigned(2, CNT_W) or master_nacks /= to_unsigned(0, CNT_W) then
report "hang case counted the wrong acknowledges" severity error;
errs := errs + 1; end if;
pulse_stop;
-- 5: TWO NACKs. A master that NACKs a middle byte and then keeps reading has
-- violated the policy even though the final slot is a NACK, so the final-slot
-- check alone is not sufficient.
pulse_start;
wire_byte('0'); -- address
wire_byte('1'); -- data byte 1 NACKed -- ends it, but...
wire_byte('1'); -- ...another byte read anyway
if master_nacks /= to_unsigned(2, CNT_W) then
report "counted the wrong number of NACKs, expected 2" severity error;
errs := errs + 1; end if;
if policy_ok /= '0' then
report "two NACKs in one read is a violation and was not flagged" severity error;
errs := errs + 1; end if;
pulse_stop;
-- 6: the ADDRESS was NACKed. Nobody is there. addr_acked must be clear, and this
-- must NOT be counted as a master acknowledge -- the address byte's ninth bit
-- belongs to the slave.
pulse_start;
wire_byte('1'); -- address + R, NOBODY answers
pulse_stop;
if addr_acked /= '0' then
report "addr_acked set on an unanswered address" severity error;
errs := errs + 1; end if;
if master_nacks /= to_unsigned(0, CNT_W) or master_acks /= to_unsigned(0, CNT_W) then
report "the address byte's NACK was counted as the master's" severity error;
errs := errs + 1; end if;
if bytes_seen /= to_unsigned(1, CNT_W) then
report "bytes_seen wrong after an unanswered address" severity error;
errs := errs + 1; end if;
-- 7: the metrics must SURVIVE the STOP, because that is when they are read.
if transfer_active /= '0' then
report "still active after the STOP" severity error; errs := errs + 1; end if;
if bytes_seen = to_unsigned(0, CNT_W) then
report "the counters were cleared by the STOP" severity error;
errs := errs + 1; end if;
-- 8: a repeated START re-arms the ADDRESS expectation. The byte after an Sr is
-- an address again, so a design that did not re-arm would count it as a data
-- byte and report one master acknowledge too many.
pulse_start;
wire_byte('0'); wire_byte('0'); wire_byte('1'); -- a 2-byte read
pulse_start; -- Sr
if bytes_seen /= to_unsigned(0, CNT_W) then
report "a repeated START did not restart the measurement" severity error;
errs := errs + 1; end if;
wire_byte('0'); -- the post-Sr ADDRESS
wire_byte('0'); wire_byte('1'); -- two data bytes
pulse_stop;
check_read(2);
-- 9: an IDLE BUS must not be measured. Between transfers SCL keeps moving, and
-- counting any of it would corrupt the totals a consumer reads.
snap_pulses := scl_pulses; snap_bytes := bytes_seen;
for i in 1 to 6 loop scl_pulse; end loop;
wire_byte('0');
if scl_pulses /= snap_pulses or bytes_seen /= snap_bytes then
report "idle-bus activity was counted" severity error; errs := errs + 1; end if;
if transfer_active /= '0' then
report "clocking an idle bus opened a transfer" severity error;
errs := errs + 1; end if;
-- 10: a STOP that closed nothing must not announce a measurement.
snap_valid := n_valid;
pulse_stop;
if n_valid /= snap_valid then
report "a STOP that closed nothing produced metrics_valid" severity error;
errs := errs + 1; end if;
-- one metrics_valid per completed transfer: 4 bursts + 1 + probe + hang +
-- two-NACK + addr-NACK + the Sr transfer = 10.
if n_valid /= 10 then
report "wrong number of metrics_valid pulses, expected 10" severity error;
errs := errs + 1; end if;
if errs = 0 then
report "i2c_read_ack_policy self-check complete: ack pattern is n-1 ACKs then "
& "one NACK, the address byte's answer is the slave's, the hang is "
& "predicted, both malformed patterns rejected" severity note;
else
report "i2c_read_ack_policy self-check FAILED" severity error;
end if;
test_done <= '1';
wait;
end process;
end architecture;6a. Five Decisions Worth Defending
ack_bit uses the bus's polarity, not a convenient one. Zero means SDA was pulled low, which is an ACK. The specification defines the acknowledge as the receiver pulling the line low, so "low is yes" is the physical fact; inverting it at the instrument's boundary would insert a translation between the measurement and the thing measured. Mutation C4 inverts it and is killed immediately, but the real argument is reviewability: a reader comparing this port against §3.1.6 should not have to hold an inversion in their head.
expect_address is re-armed on every S and Sr. The first byte after a framing event is an address byte, and nothing in the frame marks it as one — position is the only evidence. So a repeated START must re-arm the expectation or every post-Sr address byte is counted as data, inflating master_acks by one for the rest of the transfer. Mutation C5 removes the re-arm and the failure is master_acks 2, expected n-1 = 1. This is the same structural fact that makes a missed repeated START the most expensive misreading of a capture (Chapter 7.5 §10), appearing here as a hardware requirement.
last_data_acked is the state of the LAST data byte, not "any byte was ACKed". The hang derivation in §4 turns entirely on the final acknowledge, so an accumulating flag would raise hang_risk on every multi-byte read — every read of two or more bytes contains an ACK. Mutation C2 makes exactly that change and is killed by a well-formed read being reported as a violation, which is the right failure: a checker that fires on correct behaviour is worse than no checker.
The subtraction is guarded and the guard is tested before any stimulus. payload_bytes is bytes_seen − 1, and bytes_seen is zero on an idle bus. Unguarded, that is not −1 but 65535 on a 16-bit unsigned counter. The hazard lives in the reset state, which is the state a stimulus-driven test passes through on its way to doing something interesting and never asserts on. Chapter 8.3 §7a made the same point; it recurs because unsigned subtraction recurs.
An address-only probe is admitted explicitly rather than by arithmetic. For n = 0 the policy expression would compute master_acks == payload_bytes − 1 — a comparison against a wrapped value. So policy_ok has a separate branch: zero payload bytes is well-formed if and only if there were no master acknowledges at all. Mutation C10 makes that branch return false and is killed by the probe test. Writing the degenerate case as its own clause costs two lines and removes a class of accidental correctness.
6b. Verified Execution
$ iverilog -g2012 -o c0 i2c_read_ack_policy.sv i2c_read_ack_policy_tb.sv && ./c0
PASS: ack pattern is n-1 ACKs then one NACK, the address byte's answer is the slave's,
the hang is predicted, both malformed patterns rejected
i2c_read_ack_policy_tb.sv:216: $finish called at 11120 (1ps)
$ iverilog -g2005 -o c1 i2c_read_ack_policy.v i2c_read_ack_policy_tb.v && ./c1
PASS: ack pattern is n-1 ACKs then one NACK, the address byte's answer is the slave's,
the hang is predicted, both malformed patterns rejected
i2c_read_ack_policy_tb.v:231: $finish called at 11120 (1ps)
$ nvc -a i2c_read_ack_policy.vhd i2c_read_ack_policy_tb.vhd
$ nvc -e i2c_read_ack_policy_tb && nvc -r i2c_read_ack_policy_tb --stop-time=600us
** Note: 11120ns+0: i2c_read_ack_policy self-check complete: ack pattern is n-1 ACKs
then one NACK, the address byte's answer is the slave's, the hang is predicted,
both malformed patterns rejectedAll three at 11120 ns. Every closed form in §2 is checked against arithmetic the testbench computes from n — exp_pulses = 9 * (n + 1), not a table of constants — so these are checks on the formulas rather than on a transcription of them.
7. What the Testbench Proves
| # | stimulus | what it establishes |
|---|---|---|
| 1 | well-formed reads, n = 1, 2, 4, 8 | all closed forms, and the pattern is n−1 ACKs then one NACK |
| 2 | a 1-byte read | master_acks is zero — the boundary case of §1 |
| 3 | an address-only probe, n = 0 | one byte, one slave ACK, and no master acknowledges |
| 4 | the final byte ACKed | hang_risk raised and policy_ok cleared, before any STOP |
| 5 | two NACKs in one read | malformed, even though the final slot is a NACK |
| 6 | the address NACKed | addr_acked clear, and not counted as a master acknowledge |
| 7 | after the STOP | the counters survive, because that is when they are read |
| 8 | an Sr mid-transfer | the measurement restarts and the address expectation re-arms |
| 9 | SCL toggled on an idle bus | nothing counted, no transfer opened |
| 10 | a STOP that closed nothing | no metrics_valid; ten transfers produce ten pulses |
Tests 2 and 3 are the boundary pair, and they fail in opposite directions. A checker built around "some ACKs then a NACK" breaks on test 2, where there are no ACKs. A checker built around "at least one acknowledge" breaks on test 3, where there are none. Both are single-line mistakes and both describe reads that happen constantly in real systems.
Test 6 is the one that keeps the instrument honest about §3's driven by row. The address byte's ninth bit belongs to the slave, so when nobody answers an address, that NACK must not appear in master_nacks. Mutation C1 removes the distinction and every read reports one acknowledge too many — uniformly, which is what makes it look like a convention difference rather than a bug.
Tests 9 and 10 exist because the equivalent mutations survived the first version of Chapter 8.3's testbench, for a reason worth carrying forward: a suite that only exercises the active case cannot test the gate that defines "active". Any design with an enable, a valid, or a state predicate needs stimulus that occurs while that predicate is false. Here those tests were written from the start rather than discovered by a survivor.
8. Mutation Testing
Ten defects injected into the SystemVerilog instrument.
| # | injected defect | outcome |
|---|---|---|
| C1 | the address byte's acknowledge counted as the master's | killed — master_acks 1, expected 0 |
| C2 | hang_risk keys off any ACKed byte, not the last | killed — a well-formed read flagged as a violation |
| C3 | policy_ok does not check the NACK count | killed — two NACKs accepted |
| C4 | the acknowledge polarity is inverted | killed |
| C5 | a repeated START does not re-arm the address expectation | killed — master_acks 2, expected 1 |
| C6 | pulses counted while no transfer is active | killed — idle clocking counted, 42 where 27 stood |
| C7 | the guard is dropped, so a zero byte count wraps | killed — payload_bytes 65535 before any byte |
| C8 | the STOP clears the counters | killed — bytes_seen 0 after the transfer |
| C9 | metrics_valid fires on a STOP that closed nothing | killed |
| C10 | an address-only probe is not admitted as well-formed | killed |
Across all three of this module's designs: 30 injected, 30 killed, no survivors and no invalid mutants. Two of the thirty needed work before they reported honestly, and both are recorded in the chapters they belong to — Chapter 9.1 §8 has the mutation whose anchor matched two sites and the one that required a new progress test.
C2's kill is the instructive one, because of the direction it fails in. Changing last_data_acked <= !ack_bit to an accumulating || makes the instrument report hang_risk on every read of two or more bytes — since every such read contains at least one ACK. The kill message is a well-formed read was reported as a policy violation, which is a false positive rather than a missed fault.
That is worth dwelling on. A checker that misses faults is inadequate; a checker that fires on correct behaviour is actively harmful, because it trains people to ignore it. Within a few weeks a monitor that cries wolf on every burst read is a monitor whose output nobody looks at, and at that point it is worse than absent — it occupies the place where a working checker would have gone. So the testbench asserts hang_risk == 0 on every well-formed read, not merely hang_risk == 1 on the broken one, and that is why C2 dies.
C6 and C7 are the two "nobody is looking" mutations and they fail in regions a stimulus-driven test naturally skips: the idle bus between transfers, and the reset state before any transfer. Both regions are where a design spends most of its life.
9. Verification Connection — The Policy Is an Assertion, the Lengths Are Coverage
The read's acknowledge policy is the clearest case in this course of a property that must be an assertion rather than a check: it holds at every moment of every read, including reads written years later by somebody who never opened this chapter.
// THE property. A read's final data byte must be NACKed. Written on the
// TERMINATOR rather than on "the last byte", because nothing in the frame marks a
// byte as last -- there is no length field, so "last" is only knowable once the
// STOP or repeated START arrives. The property therefore looks backwards from the
// framing event, which is the general move for any property about the final
// element of an unbounded sequence.
property p_read_ends_with_nack;
@(posedge clk) disable iff (!rst_n)
(is_read && (frame_stop || frame_restart) && data_bytes_seen > 0)
|-> last_ack_was_nack;
endproperty
assert property (p_read_ends_with_nack)
else $error("a read ended without NACKing its final byte -- the bus will hang");
// Exactly ONE master NACK per read. The final-slot check above does not imply
// this: a master that NACKs a middle byte and keeps clocking satisfies it while
// reading bytes off an undriven bus, because the slave stopped transmitting at
// the first NACK. See section 1.
property p_one_nack_per_read;
@(posedge clk) disable iff (!rst_n)
(is_read && (frame_stop || frame_restart)) |-> (master_nack_count <= 1);
endproperty
assert property (p_one_nack_per_read)
else $error("%0d master NACKs in one read -- bytes were read off a silent bus",
master_nack_count);
// The address byte's acknowledge is the SLAVE's, even in a read. A monitor that
// attributes it to the master is wrong by exactly one on every read, uniformly --
// which makes it look like a convention difference rather than a defect.
property p_address_ack_is_slaves;
@(posedge clk) disable iff (!rst_n)
(byte_done && byte_index == 0) |-> !master_drove_ack;
endproperty
assert property (p_address_ack_is_slaves)
else $error("the address byte's acknowledge was attributed to the master");
// A master-receiver must have RELEASED SDA in a slot it is NACKing. This is the
// structural half of the hang: the NACK is the absence of an action, so a master
// that is still driving low cannot express one at all.
property p_nack_means_released;
@(posedge clk) disable iff (!rst_n)
(ack_slot && is_read && !master_acking) |-> !master_drives_sda_low;
endproperty
assert property (p_nack_means_released)
else $error("the master intended a NACK while still pulling SDA low");And the coverage. For reads the interesting axis is length, because §1 showed the pattern is a function of it and §2 showed the cost is too.
covergroup i2c_read_len_cg with function sample(int n, bit combined, bit ended_with_sr);
// The bins follow the SHAPE of section 1's table rather than being spread
// evenly. Everything structurally distinctive happens at 0 and 1: a probe has
// no master acknowledges at all, and a one-byte read has a NACK and no ACK.
// Past 2 the pattern is self-similar and one bin covers it.
read_len: coverpoint n {
bins probe = {0}; // no master acknowledges whatsoever
bins single = {1}; // exactly one acknowledge, and it is a NACK
bins pair = {2}; // the shortest read with both an ACK and a NACK
bins burst = {[3:16]};
bins long = {[17:255]};
}
// A plain read and a combined register read are different frames with
// different costs -- section 2's nine extra pulses -- and a suite that only
// ever issued one of them has not exercised the format real devices use.
combined_fmt: coverpoint combined;
// Terminating with Sr rather than P is the other half of the specification's
// requirement, and the acknowledge policy is identical for both. Covering it
// is how you know the identical-policy claim was actually exercised.
sr_terminated: coverpoint ended_with_sr;
// The crosses are the point. A probe that is never combined and a long burst
// that always is would fill both coverpoints while leaving the interesting
// combinations untried.
len_x_combined: cross read_len, combined_fmt;
len_x_sr: cross read_len, sr_terminated;
endgroup10. FPGA and ASIC Implications
Two counters and two comparators. At CNT_W = 16 the instrument is roughly 50 flops — two byte/pulse counters, two acknowledge counters, an edge detector and five state bits. payload_bytes, policy_ok and hang_risk are all combinational.
Size CNT_W from the pulse count, not the byte count. A read of n bytes generates 9(n+1) pulses, so the pulse counter overflows nine times sooner than intuition based on bytes suggests. At CNT_W = 8 that is 27 bytes — reachable — and an overflow reports a small number rather than an error, which is the worst failure mode a counter has.
hang_risk is the output worth wiring to something. It is the one signal here that predicts a fault rather than recording one, and it is available a full byte before the STOP is attempted. Wiring it to an interrupt or a capture trigger turns it from a register somebody might read into a mechanism that catches the intermittent version of the fault — the one where the extra byte's MSB happened to be 1 and the bus survived by luck. That is the case a person will never reproduce on purpose.
It is genuinely passive, and keeping it so is the point. No output goes near SDA or SCL. This is the property that makes it safe to leave enabled in production silicon, and a debug block that could perturb the bus would be switched off in exactly the situations where it was needed.
In simulation the arithmetic is checked independently; in silicon nothing checks it. The testbench computes 9(n+1) from n. Nothing in the chip does, so the instrument's outputs are only as trustworthy as the verification that shipped with it. That is the argument for §9's assertions living in the environment permanently rather than being a one-off bring-up exercise.
11. Debugging — The Read Loop That Hung the Bus Every Few Hours
Pitfall — a data-dependent hang from an acknowledge policy that is right most of the time
// A monitoring task polls a sensor's four status registers once a second, using a
// register read: write the pointer, repeated START, read four bytes.
//
// The acknowledge policy was written as a loop with the NACK outside it:
//
// for (i = 0; i < len; i++)
// data[i] = i2c_read_byte(ACK); // ACK every byte...
// i2c_nack(); // ...then NACK
//
// which looks right and is not. The NACK is issued as a SEPARATE ninth slot, so the
// master reads len bytes with an ACK each and then clocks a FIFTH byte in order to
// have something to NACK. The capture:
//
// S 0x90 A ptr A Sr 0x91 A d0 A d1 A d2 A d3 A d4 N P
// ^^ ^
// a FIFTH byte, read only to be NACKedIt worked. For hours, and then the whole bus stopped -- not just the sensor. Other devices' transfers began failing, and the failures were reported by whichever driver happened to need the bus next, which was rarely the monitoring task.
The logs pointed everywhere. A display driver reported a timeout. An EEPROM write failed mid-page. The sensor task itself usually reported success, because by the time it noticed anything the bus had already been power-cycled by a watchdog.
And it was not reproducible on demand. Running the monitoring task in a tight loop for twenty minutes produced nothing. The hang needed the fifth byte -- register 4, whatever happened to be there -- to have its most significant bit CLEAR, and that register held a slowly-changing measurement that was usually above the midpoint. So the fault rate tracked a sensor reading, which is not a hypothesis anybody forms early.
What resolved it was a bus monitor with the hang_risk output of section 6. It flagged EVERY poll -- all of them, not one in a few hundred -- because the final byte of every read was ACKed. The flag fires on the CAUSE, which was present every time, rather than on the hang, which needed the data to cooperate.
The acknowledge is not a separate operation from reading a byte -- it IS the ninth slot of the byte being read. Issuing a NACK "after the loop" clocks an extra byte to attach it to, so the transfer reads len+1 bytes and ACKs the byte the driver believes was its last.
The slave, told to send another, drove a fifth byte. Whenever that byte's MSB was zero the slave held SDA low through the window in which the master's STOP needed SDA to rise, so no STOP could be formed and the bus stayed held -- for every device on it.
12. Common Misconceptions
"A read of n bytes contains n ACKs." It contains n−1 master ACKs and one master NACK, plus one slave ACK for the address byte. A one-byte read contains zero master ACKs.
"Checking that the last slot is a NACK is enough." It misses a master that NACKs a middle byte and keeps clocking — the final slot is a NACK there too, while the bytes after the first NACK were read off an undriven bus. Check the count.
"Reads and writes cost the same." A plain read does. A register read costs nine pulses more than the equivalent write, because it carries a second address byte after the repeated START. At one payload byte that is the difference between 30% and 22% efficiency.
"A repeated START costs clock pulses." No framing condition does. The nine extra pulses of a register read are the second address byte, not the Sr.
"Forgetting the final NACK produces a failed read." It produces a held bus, affecting every device — and only when the extra byte's MSB happens to be 0, so it is intermittent. §11 is that fault in the field.
"The address byte after a repeated START is answered by the master, since it requests a read." The slave answers it. At the moment of that slot the slave is still the receiver, having just received an address. The handover is at that acknowledge, not before it.
"An instrument should report a hang when the STOP fails." By then it is an autopsy. The acknowledge that causes the hang is visible a full byte earlier and is deterministic, while the failure itself is data-dependent. Flag the cause.
13. Reason It Through
A capture shows S, 0x90, A, one byte, A, Sr, 0x91, A, then five nine-clock groups, then P. The driver asked for four bytes. What happened?
Five data bytes were read for a four-byte request — the acknowledge was issued as a separate operation after the loop, so the master clocked a fifth byte to attach the NACK to and ACKed the byte it believed was its last. The frame is well-formed and the STOP did form, so this particular transfer succeeded; whether the next one does depends on whether the fifth byte's MSB was 1. §11 is this bug.
A well-formed read of one byte and a well-formed probe both have zero master ACKs. How does an instrument tell them apart?
By the NACK count and the byte count. The one-byte read has one master NACK and two bytes on the wire; the probe has zero master acknowledges of either kind and one byte on the wire. That is why policy_ok has a separate branch for zero payload bytes rather than deriving it — the two cases agree on master_acks and differ on everything else.
Why does a register read of one byte cost four bytes on the wire, and what does that imply for a polling loop?
S, address+W, pointer, Sr, address+R, data, P — four byte-slots for one payload byte, so 36 SCL pulses for 8 bits: 22% efficient, 360 µs at 100 kHz. For a polling loop the implication is direct: reading four status registers as four separate register reads costs 4 × 36 = 144 pulses, while reading them as one burst from a single pointer costs 9 × 7 = 63. The burst is better than twice as fast, and most devices lay related registers out contiguously precisely to make that possible.
An instrument reports hang_risk on every read, but the bus hangs only occasionally. Is the instrument wrong?
No — it is working exactly as designed, and the discrepancy is the point. The acknowledge pattern that causes the hang is present on every transfer and is deterministic; whether it manifests depends on the extra byte's most significant bit. An instrument that only flagged the transfers that actually hung would be as intermittent as the bug and correspondingly useless for finding it.
Why must expect_address be re-armed on a repeated START and not only on a START?
Because the byte after any framing event is an address byte, and nothing in the frame says so — position is the only evidence. Without the re-arm, the post-Sr address byte is counted as a data byte, its slave-driven acknowledge is attributed to the master, and every count after it is wrong by one for the rest of the transfer. It is the hardware form of the most expensive capture-reading error there is.
14. Understanding Check
15. Summary
The acknowledge pattern of a correct read is determined by its length, not merely constrained by it. n−1 master ACKs, then exactly one NACK in the final slot, with the address byte's acknowledge belonging to the slave. That narrowness is what makes a passive instrument able to verify the policy with no knowledge of the device.
Count the NACKs, not just the last one. A NACK in the final slot is necessary and not sufficient: a master that NACKs a middle byte and keeps clocking reads the remainder off an undriven bus and gets 0xFF, while satisfying any final-slot check.
A plain read costs the same as a write; a register read costs nine pulses more. The second address byte after the repeated START is one extra byte-slot. At one payload byte that is 22% efficiency — four bytes on the wire to move one — which is why polling status registers one at a time is the most wasteful common use of this bus.
The hang is derivable and predictable. ACKing the final byte tells the slave to send another; a slave that cannot refuse drives it; a driven zero holds SDA low; a STOP needs SDA to rise. So the fault is visible one byte early, and it is deterministic even though the hang it causes is data-dependent.
Flag the cause, not the symptom. The acknowledge pattern is present on every affected transfer; the hang appears only when the extra byte's MSB is zero. A checker on the pattern turns an unreproducible bus-wide failure into something visible on the first attempt.
A false positive is worse than a miss. A monitor that fires on correct behaviour gets ignored, and then it is worse than absent. Assert that the flag is clear on well-formed traffic, not only that it sets on broken traffic.
16. What Comes Next
Module 9 is complete, and with it both of I²C's transactions. Every claim in it was compiled and run in three languages, all three designs are at exact timing parity, and the mutation suite closed thirty out of thirty with the two awkward cases documented rather than smoothed over.
What remains is the composition. Module 10 builds the combined transaction properly — the format this chapter has been using informally since §2, where a write phase and a read phase are joined by a repeated START and the bus is never released between them. The pieces are all present now: Chapter 5.4 framed it, Chapter 9.2 §4 established the one piece of slave state that must survive it, and this chapter priced it. What Module 10 adds is the reason it exists at all — atomicity. On a multi-master bus, releasing the bus between the pointer write and the data read allows another master to change the pointer in between, and the repeated START is how a transaction becomes indivisible rather than merely adjacent.
Continue learning
Related tutorials
- Related topic
Multi-Byte I²C Writes — Timing, Throughput and Waveforms
A three-byte write spends a third of its bus time on overhead. This chapter derives the closed forms for what a burst costs, computes the real byte rate at both standard speeds, reads an annotated burst capture, and builds a passive instrument that measures all of it in hardware.
- Related topic
Repeated START in Practice — Why Not STOP Then START
Two sequences that look nearly identical in a driver's source are completely different on the wire. This chapter measures the difference, builds the passive monitor that tells them apart from two wires alone, and names exactly what a STOP costs on a shared bus.
- Related topic
Where an I²C Target May Stretch, and What It Costs
Stretching is free in correctness and expensive in throughput. Establishes exactly where it is legal — including the one speed mode that forbids half of it — and builds the hardware that prices it per byte and per transaction.
- Related topic
Repeated START — Holding the Bus Between Phases
A repeated START is not a new waveform. It is the START edge again, and what makes it a different event is that the bus was already busy. That single fact is why a classifier needs state and why a monitor that joins late cannot classify what it sees.
