I²C · Module 9
The I²C Read Transaction End to End
A read reverses every role a write established, and it does so mid-frame on a bus where neither end can drive high. This chapter walks a read from START to STOP, derives the master's acknowledge policy from the one sentence that governs it, and builds the transaction-level sequencer.
Module 8 built the write and said plainly why it was the easier transaction: nothing hands over. The master transmits from the first address bit to the last payload bit, and the addressed slave receives throughout.
A read is the other one. On the wire it is almost the same shape — the same nine-pulse bytes, the same framing, the same address byte with one bit different. What changes is that the direction reverses in the middle of the frame, on a bus where both ends can pull low and neither can drive high, and where the reversal is announced by nothing except the bit that has already gone past.
Two consequences follow, and they are the whole of this module:
- the master must now acknowledge, and its acknowledges are the only way it can end the transfer;
- the slave must now transmit, and it has no way to refuse anything.
This chapter takes the master's side. Chapter 9.2 takes the slave's.
1. The Read, in One Paragraph
The specification devotes one paragraph to the read format, and unusually for UM10204 every clause in it is load-bearing.
Read that clause by clause, because each one is a design requirement.
"At the moment of the first acknowledge, the master-transmitter becomes a master-receiver." The handover happens at a known instant, and that instant is the end of the address byte. Not at the START, not at the first data bit — at the ninth slot of byte zero. Everything before it is a write; everything after it is a read.
"This first acknowledge is still generated by the slave." The address byte's ninth bit belongs to the slave even in a read, because at the time that slot occurs the slave is still the receiver — it has just received the address. This single clause is the most common source of off-by-one in read monitors, and §7's mutation C1 is exactly that error.
"The master generates subsequent acknowledges." Every data byte's ninth bit is the master's. The slave never answers again for the rest of the frame.
"The master... sends a not-acknowledge just before the STOP condition." This is not advice about where to put a NACK. It is the only mechanism the master has for ending the transfer, and §3 derives why.
And the specification says the same thing about the other terminator, in the very next paragraph — which is the clause that stops people from thinking of the final NACK as "the NACK before the STOP":
So the NACK precedes both endings. It does not mean I am stopping; it means I want no further byte. What follows it — a STOP or a repeated START — is a separate decision. The design in §6 takes that seriously and makes the terminator a request input, because a sequencer that hard-wires STOP cannot implement the combined format at all.
2. The Frame, Event by Event
Here is a complete two-byte read from device 0x48. Compare it against Chapter 8.1 §2's write: the events are nearly identical and the arrows are not.
Three things that diagram makes visible and a table of bits would not.
The data arrows point the other way. In a write they all run master to slave. Here the address runs master to slave and the payload runs slave to master, and the reversal happens once, at the third arrow.
The acknowledge arrows change owner mid-diagram. The first dashed arrow runs slave to master. Every subsequent one runs master to slave. That is the specification's "this first acknowledge is still generated by the slave" drawn rather than stated, and it is the asymmetry the instrument in Chapter 9.3 exists to check.
The last acknowledge is a different kind of thing from the others. The ACK on byte 1 is a continuation — keep going. The NACK on byte 2 is a termination. They occupy identical slots and differ only in value, which means the difference between "read two bytes" and "read three bytes" is one bit, decided one byte in advance.
3. The Acknowledge Is the Only Way to Stop
This is the section that explains why a read is harder than a write, and the argument is short.
There is no length field. An I²C frame contains no count of how many bytes will follow. Chapter 7.5 §1 established this for reading captures; here it bites the design. The slave-transmitter does not know how many bytes the master wants, and there is no field in which the master could have told it.
There is no "stop sending" command. The bus has framing conditions, address bytes and data bytes. That is all. There is no control channel, no out-of-band signal, and no reserved byte value meaning halt — a data byte of 0xFF is just data.
The slave cannot decide to stop. It has a register file of some size and a pointer; it will keep producing bytes as long as it is clocked. Nothing in the protocol lets it say "that is all I have" — Chapter 9.2 is largely about this.
So the master must end the transfer, using only the signalling available to it — and the only thing a master-receiver drives is the ninth bit of each byte. Hence:
Chapter 7.4 introduced this as one of five NACK conditions. In this module it is the mechanism the whole transaction is built on, and the consequence is worth stating as a rule:
In a read, the acknowledge is flow control, not error reporting. An ACK means send me another. A NACK means I want no more. Neither says anything about whether the byte was any good — the master has no way to express that and no reason to, because it asked for the data.
That is the exact inverse of a write, where every acknowledge is the receiver's verdict on the byte and a NACK is always information about a problem. Same slot, same wire, opposite meaning — decided entirely by the direction bit sent one or more bytes earlier.
4. One Byte at Bit Resolution
Here is the single data byte of a one-byte read. It is the most instructive byte on the bus, because three different things own SDA inside nine slots.
Read data byte 0x3C — 0011 1100 — then the master's NACK
9 cyclesThe drives SDA row reads S eight times and then a dash, and the dash is the point.
A NACK has no driver. Chapter 7.2 established that an ACK is the receiver pulling SDA low and a NACK is the receiver leaving it alone. So in the ninth slot of this byte: the slave has released, because the transmitter must release for the slot; and the master is not pulling low, because it is NACKing. Nobody is driving SDA. The high level is the pull-up resistor doing its job and nothing else.
That is why the failure in §3's callout is a hang. The master's "no" is the absence of an action, so it cannot be made more forceful. If the slave is pulling low, the master has no stronger move available.
And the sampling row flips too. The master samples the eight data bits; the slave samples the ninth. Both halves matter, and Chapter 9.2 §2 tabulates the whole thing.
5. The One Structural Difference on the Wire
Between a write frame and a read frame there is exactly one structural difference, and everything in this module follows from it:
| write | read | |
|---|---|---|
| address byte | addr << 1 | 0 | addr << 1 | 1 |
| bytes on the wire | n + 1 | n + 1 |
| SCL pulses | 9(n + 1) | 9(n + 1) |
| who drives data bits 1–8 | master, every byte | master for byte 0, slave thereafter |
| who drives the 9th bit | slave, every byte | slave for byte 0, master thereafter |
| what the 9th bit means | the receiver's verdict | flow control |
| the final 9th bit | should be ACK | must be NACK |
One bit in one byte, and the right-hand column is a different transaction with a different failure mode. This is worth internalising because it is what makes read bugs hard to spot in code review: a driver that reads and a driver that writes differ by a single | 1, and everything else that must change is a consequence rather than a visible difference.
6. The Read Sequencer in Three Languages
The design is the master-receiver's transaction layer. It is the sibling of Chapter 8.1's write sequencer and deliberately shares its shape — issue commands, consume completions, hold transaction state, touch no wires.
| state | waiting for | note |
|---|---|---|
RS_IDLE | a request | |
RS_START | framing_done | |
RS_ADDR | byte_done | the address is in flight; the slave answers it |
RS_DATA | byte_done | a payload byte is arriving; this master answers it |
RS_END | framing_done | the terminating P or Sr |
And the policy, which is the design's entire content, is one expression:
// Acknowledge byte `idx` if and only if another byte is wanted after it.
return ((idx + 1'b1) < req_len); // A master-receiver's TRANSACTION-level sequencer. Like its write counterpart in
// Chapter 8.1 it issues framing and byte commands to the layers built in Modules 5
// and 7 and contains no bit-level logic; unlike that one, it must ANSWER every byte
// it receives, and the answer is the only means it has of ending the transfer.
//
// The specification's read format, verbatim: "At the moment of the first acknowledge,
// the master-transmitter becomes a master-receiver and the slave-receiver becomes a
// slave-transmitter. This first acknowledge is still generated by the slave. The
// master generates subsequent acknowledges. The STOP condition is generated by the
// master, which sends a not-acknowledge just before the STOP condition."
//
// And for the other terminator: "If a master-receiver sends a repeated START
// condition, it sends a not-acknowledge just before the repeated START condition."
// So the final NACK is NOT "the NACK before the STOP" -- it precedes BOTH endings,
// because it means "I want no further byte", not "I am stopping". req_stop selects
// which terminator follows it, and the acknowledge policy is identical either way.
module i2c_read_sequencer #(
parameter int LEN_W = 8
)(
input logic clk,
input logic rst_n,
// ---- transaction request ----
input logic req, // pulse: begin a read
input logic [6:0] req_addr, // seven-bit address, as the datasheet states it
input logic [LEN_W-1:0] req_len, // bytes to read; ZERO is a legal probe
input logic req_stop, // 1: terminate with P. 0: terminate with Sr.
// ---- commands to the framing sequencer (5.5) and byte engine (7.1) ----
output logic cmd_start, // pulse: emit S
output logic cmd_stop, // pulse: emit P
output logic cmd_restart, // pulse: emit Sr
output logic cmd_byte, // pulse: TRANSMIT one byte (the address only)
output logic [7:0] cmd_byte_data,
output logic cmd_recv, // pulse: RECEIVE one byte
// The answer this master will put in the received byte's ninth slot. It is issued
// WITH cmd_recv, one whole byte ahead of the slot, because by the time the slot
// opens there is no longer time to decide -- Chapter 7.2, section 2.
output logic cmd_ack,
// ---- completions from those layers ----
input logic framing_done, // pulse: an S, Sr or P completed
input logic byte_done, // pulse: a byte AND its ninth slot completed
input logic ack_received, // the SLAVE's answer -- meaningful for the address
input logic [7:0] byte_in, // the byte just received
// ---- payload sink ----
output logic [LEN_W-1:0] data_index,
output logic [7:0] data_out,
output logic data_valid, // pulse: data_out/data_index are good
// ---- status ----
output logic busy,
output logic done, // pulse: the transaction has ended
output logic addr_nacked,// nobody answered the address byte
output logic [LEN_W-1:0] bytes_read
);
typedef enum logic [2:0] {
RS_IDLE,
RS_START, // waiting for the S to complete
RS_ADDR, // the address byte is in flight; the SLAVE answers it
RS_DATA, // a payload byte is arriving; THIS master answers it
RS_END // waiting for the terminating P or Sr to complete
} state_e;
state_e state;
// The address byte is the seven-bit address with R/W = 1 appended. This is the
// ONLY structural difference from Chapter 8.1's write on the wire, and it is the
// difference that reverses every role for the rest of the frame.
logic [7:0] addr_byte;
assign addr_byte = {req_addr, 1'b1};
// THE ACKNOWLEDGE POLICY, as one expression.
//
// Reading byte number `idx` (zero-based) of `req_len`: acknowledge it if and only
// if another byte is still wanted afterwards. The final byte gets a NACK, which is
// the fifth NACK condition -- "a master-receiver must signal the end of the
// transfer to the slave transmitter" -- and is the only mechanism available for
// saying so. There is no length field on this bus and no command for "stop".
function automatic logic ack_for(input logic [LEN_W-1:0] idx);
return ((idx + 1'b1) < req_len);
endfunction
always_ff @(posedge clk) begin
if (!rst_n) begin
state <= RS_IDLE;
cmd_start <= 1'b0;
cmd_stop <= 1'b0;
cmd_restart <= 1'b0;
cmd_byte <= 1'b0;
cmd_recv <= 1'b0;
cmd_ack <= 1'b0;
cmd_byte_data <= 8'h00;
data_index <= '0;
data_out <= 8'h00;
data_valid <= 1'b0;
busy <= 1'b0;
done <= 1'b0;
addr_nacked <= 1'b0;
bytes_read <= '0;
end else begin
// Every command is a single-cycle pulse. Holding one asserted would make
// the lower layer act twice; Chapter 8.1's mutation A7 is that bug.
cmd_start <= 1'b0;
cmd_stop <= 1'b0;
cmd_restart <= 1'b0;
cmd_byte <= 1'b0;
cmd_recv <= 1'b0;
data_valid <= 1'b0;
done <= 1'b0;
case (state)
RS_IDLE:
if (req) begin
busy <= 1'b1;
addr_nacked <= 1'b0;
bytes_read <= '0;
data_index <= '0;
cmd_start <= 1'b1;
state <= RS_START;
end
RS_START:
if (framing_done) begin
cmd_byte <= 1'b1;
cmd_byte_data <= addr_byte;
state <= RS_ADDR;
end
RS_ADDR:
if (byte_done) begin
if (!ack_received) begin
// Condition 1: nobody claimed the address. Identical to the
// write's abort -- there is no device to read from.
addr_nacked <= 1'b1;
state <= RS_END;
if (req_stop) cmd_stop <= 1'b1;
else cmd_restart <= 1'b1;
end else if (req_len == '0) begin
// An address-only probe with R/W = 1. The master never
// becomes a receiver of data, so it generates NO
// acknowledges at all -- and therefore issues no NACK
// either, because there is no byte to refuse.
state <= RS_END;
if (req_stop) cmd_stop <= 1'b1;
else cmd_restart <= 1'b1;
end else begin
// The direction has now changed. From here the master is
// the receiver and owns every ninth bit.
cmd_recv <= 1'b1;
cmd_ack <= ack_for('0);
state <= RS_DATA;
end
end
RS_DATA:
if (byte_done) begin
data_out <= byte_in;
data_valid <= 1'b1;
bytes_read <= bytes_read + 1'b1;
if (!ack_for(data_index)) begin
// This was the final byte and it was NACKed, so the slave
// has released SDA and the bus is free for the terminator.
state <= RS_END;
if (req_stop) cmd_stop <= 1'b1;
else cmd_restart <= 1'b1;
end else begin
// data_index is advanced and the NEXT answer is computed
// from the advanced value, in the same cycle. That is safe
// here and was not in Chapter 8.1: ack_for() is a function
// of the index alone, not of a memory read, so there is no
// fetch latency to wait for and no WS_FETCH equivalent.
data_index <= data_index + 1'b1;
cmd_recv <= 1'b1;
cmd_ack <= ack_for(data_index + 1'b1);
state <= RS_DATA;
end
end
RS_END:
if (framing_done) begin
busy <= 1'b0;
done <= 1'b1;
state <= RS_IDLE;
end
default: state <= RS_IDLE;
endcase
end
end
endmodule `timescale 1ns/1ps
// The two layers below the sequencer are modelled as two INDEPENDENT processes, one
// per layer. Chapter 8.1's section 11 records why: a single always block containing
// blocking waits is one thread of control, and it drops any command that arrives
// while it is suspended.
module i2c_read_sequencer_tb;
localparam int LEN_W = 8;
localparam int FRAMING_CYCLES = 4;
localparam int BYTE_CYCLES = 9;
logic clk = 1'b0;
always #5 clk = ~clk;
logic rst_n = 1'b0;
logic req = 1'b0, req_stop = 1'b1;
logic [6:0] req_addr = 7'h48;
logic [LEN_W-1:0] req_len = '0;
logic cmd_start, cmd_stop, cmd_restart, cmd_byte, cmd_recv, cmd_ack;
logic [7:0] cmd_byte_data;
logic framing_done = 1'b0, byte_done = 1'b0, ack_received = 1'b0;
logic [7:0] byte_in = 8'h00;
logic [LEN_W-1:0] data_index;
logic [7:0] data_out;
logic data_valid, busy, done, addr_nacked;
logic [LEN_W-1:0] bytes_read;
int errors = 0;
i2c_read_sequencer #(.LEN_W(LEN_W)) dut (.*);
initial begin #200000; $display("FAIL: watchdog expired"); $finish; end
// ---- observation -------------------------------------------------------------
int n_s, n_p, n_sr, n_tx, n_rx, n_done;
logic [7:0] tx_log [0:15]; // bytes the master TRANSMITTED (the address)
logic ack_log [0:15]; // the answer the master gave to each byte it READ
logic [7:0] rx_log [0:15]; // bytes delivered to the payload sink
int n_rxlog;
// The stimulus asks for these to be cleared rather than clearing them itself, so
// that this testbench has the same single-writer structure as the VHDL one -- which
// is what makes the matching finish time between the three mean something. n_done is
// deliberately NOT cleared: it is the baseline the transaction wait is built on.
logic obs_clear = 1'b0;
always @(posedge clk) if (rst_n) begin
if (obs_clear) begin
n_s = 0; n_p = 0; n_sr = 0; n_tx = 0; n_rx = 0; n_rxlog = 0;
end else begin
if (cmd_start) n_s++;
if (cmd_stop) n_p++;
if (cmd_restart) n_sr++;
if (cmd_byte) begin if (n_tx < 16) tx_log[n_tx] = cmd_byte_data; n_tx++; end
if (cmd_recv) begin if (n_rx < 16) ack_log[n_rx] = cmd_ack; n_rx++; end
if (data_valid) begin if (n_rxlog < 16) rx_log[n_rxlog] = data_out; n_rxlog++; end
end
if (done) n_done++;
end
// ---- the framing layer, an independent responder -----------------------------
initial forever begin
wait (cmd_start || cmd_stop || cmd_restart);
repeat (FRAMING_CYCLES) @(posedge clk);
framing_done <= 1'b1; @(posedge clk); framing_done <= 1'b0;
end
// ---- the byte layer, an independent responder --------------------------------
// It serves BOTH transmit and receive. slave_answer is what the far end puts in
// the ninth slot of a TRANSMITTED byte; slave_data is what it sends us when we
// receive. The distinction matters: for a received byte the ninth bit is OURS,
// and this model must not invent an answer for it.
logic slave_answer = 1'b1;
logic is_recv;
// The payload source the far end reads out of. It has exactly ONE owning process,
// because VHDL permits one driver per signal and the three testbenches are kept
// structurally identical so that the finish-time match between them means
// something. The stimulus asks for a new seed with a request pulse rather than
// writing the signal itself.
logic [7:0] slave_seed = 8'h00;
logic seed_load = 1'b0;
logic rx_taken = 1'b0;
logic [7:0] slave_data = 8'h00;
always @(posedge clk) begin
if (seed_load) slave_data <= slave_seed;
else if (rx_taken) slave_data <= slave_data + 8'h11; // distinct payload bytes
end
initial forever begin
wait (cmd_byte || cmd_recv);
// Latch WHICH command is being served: both are one-cycle pulses and are gone
// by the time this model finishes counting out the byte.
is_recv = cmd_recv;
if (is_recv) byte_in <= slave_data;
repeat (BYTE_CYCLES) @(posedge clk);
byte_done <= 1'b1;
// For a byte the master TRANSMITTED, ack_received is the slave's answer. For a
// byte the master RECEIVED, the ninth bit is the master's own -- so there is no
// slave answer, and this model drives X rather than a plausible value. A design
// that consults ack_received on a received byte then propagates X instead of
// silently agreeing with whatever the model happened to leave there.
ack_received <= is_recv ? 1'bx : slave_answer;
// The advance is requested in the SAME cycle as byte_done, not after it. The
// DUT issues the next cmd_recv on the byte_done edge, so an advance one cycle
// later would be latched after this model had already picked up the next
// byte -- and the payload would repeat its first value. That is the same
// index-and-data hazard as Chapter 8.1's WS_FETCH, in the testbench.
rx_taken <= is_recv;
@(posedge clk);
byte_done <= 1'b0;
rx_taken <= 1'b0;
end
// Progress record: bytes_read sampled one cycle AFTER each data_valid, which is when
// that byte's increment has landed. A running count must read 1, 2, 3 across a
// three-byte read. A design that reported the FINAL total from the first byte would
// read 3, 3, 3 -- indistinguishable at the end of the transfer, and useless to a
// consumer tracking progress or draining a stream.
logic [LEN_W-1:0] br_log [0:15];
int n_brlog;
logic dv_q = 1'b0;
always @(posedge clk) if (rst_n) begin
if (obs_clear) begin n_brlog = 0; dv_q = 1'b0; end
else begin
if (dv_q) begin if (n_brlog < 16) br_log[n_brlog] = bytes_read; n_brlog++; end
dv_q = data_valid;
end
end
task automatic start_txn(input logic [LEN_W-1:0] len, input logic stop_term,
input logic [7:0] first_data);
int base;
base = n_done;
obs_clear = 1'b1; @(negedge clk); obs_clear = 1'b0; @(negedge clk);
req_len = len; req_stop = stop_term;
slave_seed = first_data;
seed_load = 1'b1; @(negedge clk); seed_load = 1'b0; @(negedge clk);
// Stimulus changes on the NEGEDGE. Driving a DUT input with a blocking
// assignment at the posedge it is sampled on is a race, and it cost a
// watchdog timeout before this comment existed.
req = 1'b1; @(negedge clk); req = 1'b0;
// A COUNT-based wait, never `wait (done)`: done is a one-cycle pulse and is
// still asserted at the following negedge, so a level wait is satisfied by
// the PREVIOUS transaction's pulse. Chapter 8.1 section 7 records the bug.
wait (n_done == base + 1);
repeat (2) @(negedge clk);
endtask
initial begin
repeat (3) @(negedge clk);
if (busy !== 1'b0) begin $display("FAIL: busy out of reset"); errors++; end
rst_n = 1'b1; @(negedge clk);
// ---- 1: a 3-byte read. The address byte carries R/W = 1, and the master's
// acknowledge pattern must be ACK, ACK, NACK.
slave_answer = 1'b1;
start_txn(8'd3, 1'b1, 8'hA0);
if (tx_log[0] !== 8'h91) begin
$display("FAIL: address byte was 0x%02h, expected 0x91 (0x48 << 1 | R)", tx_log[0]);
errors++; end
if (n_tx !== 1) begin
$display("FAIL: master transmitted %0d bytes, expected only the address", n_tx);
errors++; end
if (n_rx !== 3) begin
$display("FAIL: master received %0d bytes, expected 3", n_rx); errors++; end
if (ack_log[0] !== 1'b1 || ack_log[1] !== 1'b1 || ack_log[2] !== 1'b0) begin
$display("FAIL: ack pattern was %b%b%b, expected 110 (ACK ACK NACK)",
ack_log[0], ack_log[1], ack_log[2]); errors++; end
if (rx_log[0] !== 8'hA0 || rx_log[1] !== 8'hB1 || rx_log[2] !== 8'hC2) begin
$display("FAIL: payload was 0x%02h,0x%02h,0x%02h, expected 0xa0,0xb1,0xc2",
rx_log[0], rx_log[1], rx_log[2]); errors++; end
if (bytes_read !== 8'd3) begin
$display("FAIL: bytes_read = %0d, expected 3", bytes_read); errors++; end
if (n_s !== 1 || n_p !== 1 || n_sr !== 0) begin
$display("FAIL: framing was %0d S, %0d P, %0d Sr -- expected 1, 1, 0",
n_s, n_p, n_sr); errors++; end
if (addr_nacked !== 1'b0) begin $display("FAIL: spurious addr_nacked"); errors++; end
// bytes_read must be a RUNNING count, not the final total announced early.
if (br_log[0] !== 8'd1 || br_log[1] !== 8'd2 || br_log[2] !== 8'd3) begin
$display("FAIL: bytes_read progressed %0d,%0d,%0d -- expected 1,2,3",
br_log[0], br_log[1], br_log[2]); errors++; end
// ---- 2: a ONE-byte read. The master's only acknowledge is a NACK, because
// the first byte is also the last. An off-by-one in the policy shows up
// here and nowhere else.
start_txn(8'd1, 1'b1, 8'h5A);
if (n_rx !== 1) begin
$display("FAIL: 1-byte read received %0d bytes", n_rx); errors++; end
if (ack_log[0] !== 1'b0) begin
$display("FAIL: the only byte of a 1-byte read was ACKed, not NACKed"); errors++; end
if (rx_log[0] !== 8'h5A) begin
$display("FAIL: 1-byte payload was 0x%02h, expected 0x5a", rx_log[0]); errors++; end
if (bytes_read !== 8'd1) begin
$display("FAIL: bytes_read = %0d, expected 1", bytes_read); errors++; end
// ---- 3: an ADDRESS-ONLY probe with R/W = 1. The master never becomes a
// receiver, so it must generate NO acknowledges whatsoever -- not even
// a NACK, because there is no byte to refuse.
start_txn(8'd0, 1'b1, 8'h00);
if (n_tx !== 1) begin
$display("FAIL: a read probe put %0d bytes on the wire, expected 1", n_tx);
errors++; end
if (n_rx !== 0) begin
$display("FAIL: a read probe generated %0d acknowledges, expected 0", n_rx);
errors++; end
if (bytes_read !== 8'd0) begin
$display("FAIL: a read probe reported %0d bytes read", bytes_read); errors++; end
if (n_p !== 1) begin
$display("FAIL: a read probe emitted %0d STOPs", n_p); errors++; end
// ---- 4: the address is NACKED. Nobody is there, so no byte is ever read,
// and a terminator is still emitted so the bus is not left held.
slave_answer = 1'b0;
start_txn(8'd4, 1'b1, 8'h00);
if (addr_nacked !== 1'b1) begin
$display("FAIL: addr_nacked not reported"); errors++; end
if (n_rx !== 0) begin
$display("FAIL: read %0d bytes from an address nobody answered", n_rx); errors++; end
if (bytes_read !== 8'd0) begin
$display("FAIL: bytes_read = %0d after an address NACK", bytes_read); errors++; end
if (n_p !== 1) begin
$display("FAIL: an aborted read emitted %0d STOPs, expected 1", n_p); errors++; end
slave_answer = 1'b1;
// ---- 5: terminate with a REPEATED START instead of a STOP. The acknowledge
// policy must be IDENTICAL -- the specification says a master-receiver
// sends a not-acknowledge just before the repeated START too.
start_txn(8'd2, 1'b0, 8'h33);
if (n_sr !== 1 || n_p !== 0) begin
$display("FAIL: Sr-terminated read emitted %0d Sr and %0d P, expected 1 and 0",
n_sr, n_p); errors++; end
if (ack_log[0] !== 1'b1 || ack_log[1] !== 1'b0) begin
$display("FAIL: Sr-terminated ack pattern was %b%b, expected 10 (ACK NACK)",
ack_log[0], ack_log[1]); errors++; end
if (bytes_read !== 8'd2) begin
$display("FAIL: bytes_read = %0d, expected 2", bytes_read); errors++; end
// ---- 6: back-to-back transactions. done must be a pulse and the second
// transaction must start from a clean state.
start_txn(8'd2, 1'b1, 8'h77);
if (bytes_read !== 8'd2 || n_s !== 1 || n_p !== 1) begin
$display("FAIL: the transaction after an Sr-terminated one did not start clean");
errors++; end
if (n_done !== 6) begin
$display("FAIL: %0d done pulses across 6 transactions", n_done); errors++; end
if (errors == 0)
$display("PASS: address carries R/W=1, ack policy is n-1 ACKs then one NACK, probe generates none, both terminators");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule // A master-receiver's TRANSACTION-level sequencer. (Verilog-2001) Like its write counterpart in
// Chapter 8.1 it issues framing and byte commands to the layers built in Modules 5
// and 7 and contains no bit-level logic; unlike that one, it must ANSWER every byte
// it receives, and the answer is the only means it has of ending the transfer.
//
// The specification's read format, verbatim: "At the moment of the first acknowledge,
// the master-transmitter becomes a master-receiver and the slave-receiver becomes a
// slave-transmitter. This first acknowledge is still generated by the slave. The
// master generates subsequent acknowledges. The STOP condition is generated by the
// master, which sends a not-acknowledge just before the STOP condition."
//
// And for the other terminator: "If a master-receiver sends a repeated START
// condition, it sends a not-acknowledge just before the repeated START condition."
// So the final NACK is NOT "the NACK before the STOP" -- it precedes BOTH endings,
// because it means "I want no further byte", not "I am stopping". req_stop selects
// which terminator follows it, and the acknowledge policy is identical either way.
module i2c_read_sequencer #(
parameter LEN_W = 8
)(
input wire clk,
input wire rst_n,
// ---- transaction request ----
input wire req, // pulse: begin a read
input wire [6:0] req_addr, // seven-bit address, as the datasheet states it
input wire [LEN_W-1:0] req_len, // bytes to read; ZERO is a legal probe
input wire req_stop, // 1: terminate with P. 0: terminate with Sr.
// ---- commands to the framing sequencer (5.5) and byte engine (7.1) ----
output reg cmd_start, // pulse: emit S
output reg cmd_stop, // pulse: emit P
output reg cmd_restart, // pulse: emit Sr
output reg cmd_byte, // pulse: TRANSMIT one byte (the address only)
output reg [7:0] cmd_byte_data,
output reg cmd_recv, // pulse: RECEIVE one byte
// The answer this master will put in the received byte's ninth slot. It is issued
// WITH cmd_recv, one whole byte ahead of the slot, because by the time the slot
// opens there is no longer time to decide -- Chapter 7.2, section 2.
output reg cmd_ack,
// ---- completions from those layers ----
input wire framing_done, // pulse: an S, Sr or P completed
input wire byte_done, // pulse: a byte AND its ninth slot completed
input wire ack_received, // the SLAVE's answer -- meaningful for the address
input wire [7:0] byte_in, // the byte just received
// ---- payload sink ----
output reg [LEN_W-1:0] data_index,
output reg [7:0] data_out,
output reg data_valid, // pulse: data_out/data_index are good
// ---- status ----
output reg busy,
output reg done, // pulse: the transaction has ended
output reg addr_nacked,// nobody answered the address byte
output reg [LEN_W-1:0] bytes_read
);
localparam RS_IDLE = 3'd0; // waiting for a request
localparam RS_START = 3'd1; // waiting for the S to complete
localparam RS_ADDR = 3'd2; // the address byte is in flight; the SLAVE answers it
localparam RS_DATA = 3'd3; // a payload byte is arriving; THIS master answers it
localparam RS_END = 3'd4; // waiting for the terminating P or Sr to complete
reg [2:0] state;
// The address byte is the seven-bit address with R/W = 1 appended. This is the
// ONLY structural difference from Chapter 8.1's write on the wire, and it is the
// difference that reverses every role for the rest of the frame.
wire [7:0] addr_byte;
assign addr_byte = {req_addr, 1'b1};
// THE ACKNOWLEDGE POLICY, as one expression.
//
// Reading byte number `idx` (zero-based) of `req_len`: acknowledge it if and only
// if another byte is still wanted afterwards. The final byte gets a NACK, which is
// the fifth NACK condition -- "a master-receiver must signal the end of the
// transfer to the slave transmitter" -- and is the only mechanism available for
// saying so. There is no length field on this bus and no command for "stop".
function ack_for;
input [LEN_W-1:0] idx;
begin
ack_for = ((idx + 1'b1) < req_len);
end
endfunction
always @(posedge clk) begin
if (!rst_n) begin
state <= RS_IDLE;
cmd_start <= 1'b0;
cmd_stop <= 1'b0;
cmd_restart <= 1'b0;
cmd_byte <= 1'b0;
cmd_recv <= 1'b0;
cmd_ack <= 1'b0;
cmd_byte_data <= 8'h00;
data_index <= {LEN_W{1'b0}};
data_out <= 8'h00;
data_valid <= 1'b0;
busy <= 1'b0;
done <= 1'b0;
addr_nacked <= 1'b0;
bytes_read <= {LEN_W{1'b0}};
end else begin
// Every command is a single-cycle pulse. Holding one asserted would make
// the lower layer act twice; Chapter 8.1's mutation A7 is that bug.
cmd_start <= 1'b0;
cmd_stop <= 1'b0;
cmd_restart <= 1'b0;
cmd_byte <= 1'b0;
cmd_recv <= 1'b0;
data_valid <= 1'b0;
done <= 1'b0;
case (state)
RS_IDLE:
if (req) begin
busy <= 1'b1;
addr_nacked <= 1'b0;
bytes_read <= {LEN_W{1'b0}};
data_index <= {LEN_W{1'b0}};
cmd_start <= 1'b1;
state <= RS_START;
end
RS_START:
if (framing_done) begin
cmd_byte <= 1'b1;
cmd_byte_data <= addr_byte;
state <= RS_ADDR;
end
RS_ADDR:
if (byte_done) begin
if (!ack_received) begin
// Condition 1: nobody claimed the address. Identical to the
// write's abort -- there is no device to read from.
addr_nacked <= 1'b1;
state <= RS_END;
if (req_stop) cmd_stop <= 1'b1;
else cmd_restart <= 1'b1;
end else if (req_len == {LEN_W{1'b0}}) begin
// An address-only probe with R/W = 1. The master never
// becomes a receiver of data, so it generates NO
// acknowledges at all -- and therefore issues no NACK
// either, because there is no byte to refuse.
state <= RS_END;
if (req_stop) cmd_stop <= 1'b1;
else cmd_restart <= 1'b1;
end else begin
// The direction has now changed. From here the master is
// the receiver and owns every ninth bit.
cmd_recv <= 1'b1;
cmd_ack <= ack_for({LEN_W{1'b0}});
state <= RS_DATA;
end
end
RS_DATA:
if (byte_done) begin
data_out <= byte_in;
data_valid <= 1'b1;
bytes_read <= bytes_read + 1'b1;
if (!ack_for(data_index)) begin
// This was the final byte and it was NACKed, so the slave
// has released SDA and the bus is free for the terminator.
state <= RS_END;
if (req_stop) cmd_stop <= 1'b1;
else cmd_restart <= 1'b1;
end else begin
// data_index is advanced and the NEXT answer is computed
// from the advanced value, in the same cycle. That is safe
// here and was not in Chapter 8.1: ack_for() is a function
// of the index alone, not of a memory read, so there is no
// fetch latency to wait for and no WS_FETCH equivalent.
data_index <= data_index + 1'b1;
cmd_recv <= 1'b1;
cmd_ack <= ack_for(data_index + 1'b1);
state <= RS_DATA;
end
end
RS_END:
if (framing_done) begin
busy <= 1'b0;
done <= 1'b1;
state <= RS_IDLE;
end
default: state <= RS_IDLE;
endcase
end
end
endmodule `timescale 1ns/1ps
// The two layers below the sequencer are modelled as two INDEPENDENT processes, one
// per layer. Chapter 8.1's section 11 records why: a single always block containing
// blocking waits is one thread of control, and it drops any command that arrives
// while it is suspended.
module i2c_read_sequencer_tb; // Verilog-2001
localparam LEN_W = 8;
localparam FRAMING_CYCLES = 4;
localparam BYTE_CYCLES = 9;
reg clk = 1'b0;
always #5 clk = ~clk;
reg rst_n = 1'b0;
reg req = 1'b0, req_stop = 1'b1;
reg [6:0] req_addr = 7'h48;
reg [LEN_W-1:0] req_len = {LEN_W{1'b0}};
wire cmd_start, cmd_stop, cmd_restart, cmd_byte, cmd_recv, cmd_ack;
wire [7:0] cmd_byte_data;
reg framing_done = 1'b0, byte_done = 1'b0, ack_received = 1'b0;
reg [7:0] byte_in = 8'h00;
wire [LEN_W-1:0] data_index;
wire [7:0] data_out;
wire data_valid, busy, done, addr_nacked;
wire [LEN_W-1:0] bytes_read;
integer errors = 0;
i2c_read_sequencer #(.LEN_W(LEN_W)) dut (
.clk(clk), .rst_n(rst_n), .req(req), .req_addr(req_addr), .req_len(req_len),
.req_stop(req_stop), .cmd_start(cmd_start), .cmd_stop(cmd_stop),
.cmd_restart(cmd_restart), .cmd_byte(cmd_byte), .cmd_byte_data(cmd_byte_data),
.cmd_recv(cmd_recv), .cmd_ack(cmd_ack), .framing_done(framing_done),
.byte_done(byte_done), .ack_received(ack_received), .byte_in(byte_in),
.data_index(data_index), .data_out(data_out), .data_valid(data_valid),
.busy(busy), .done(done), .addr_nacked(addr_nacked), .bytes_read(bytes_read));
initial begin #200000; $display("FAIL: watchdog expired"); $finish; end
// ---- observation -------------------------------------------------------------
integer n_s = 0, n_p = 0, n_sr = 0, n_tx = 0, n_rx = 0, n_done = 0;
reg [7:0] tx_log [0:15]; // bytes the master TRANSMITTED (the address)
reg ack_log [0:15]; // the answer the master gave to each byte it READ
reg [7:0] rx_log [0:15]; // bytes delivered to the payload sink
integer n_rxlog = 0;
// The stimulus asks for these to be cleared rather than clearing them itself, so
// that this testbench has the same single-writer structure as the VHDL one -- which
// is what makes the matching finish time between the three mean something. n_done is
// deliberately NOT cleared: it is the baseline the transaction wait is built on.
reg obs_clear = 1'b0;
always @(posedge clk) if (rst_n) begin
if (obs_clear) begin
n_s = 0; n_p = 0; n_sr = 0; n_tx = 0; n_rx = 0; n_rxlog = 0;
end else begin
if (cmd_start) n_s = n_s + 1;
if (cmd_stop) n_p = n_p + 1;
if (cmd_restart) n_sr = n_sr + 1;
if (cmd_byte) begin if (n_tx < 16) tx_log[n_tx] = cmd_byte_data; n_tx = n_tx + 1; end
if (cmd_recv) begin if (n_rx < 16) ack_log[n_rx] = cmd_ack; n_rx = n_rx + 1; end
if (data_valid) begin if (n_rxlog < 16) rx_log[n_rxlog] = data_out; n_rxlog = n_rxlog + 1; end
end
if (done) n_done = n_done + 1;
end
// ---- the framing layer, an independent responder -----------------------------
initial forever begin
wait (cmd_start || cmd_stop || cmd_restart);
repeat (FRAMING_CYCLES) @(posedge clk);
framing_done <= 1'b1; @(posedge clk); framing_done <= 1'b0;
end
// ---- the byte layer, an independent responder --------------------------------
// It serves BOTH transmit and receive. slave_answer is what the far end puts in
// the ninth slot of a TRANSMITTED byte; slave_data is what it sends us when we
// receive. The distinction matters: for a received byte the ninth bit is OURS,
// and this model must not invent an answer for it.
reg slave_answer = 1'b1;
reg is_recv;
// The payload source the far end reads out of. It has exactly ONE owning process,
// because VHDL permits one driver per signal and the three testbenches are kept
// structurally identical so that the finish-time match between them means
// something. The stimulus asks for a new seed with a request pulse rather than
// writing the signal itself.
reg [7:0] slave_seed = 8'h00;
reg seed_load = 1'b0;
reg rx_taken = 1'b0;
reg [7:0] slave_data = 8'h00;
always @(posedge clk) begin
if (seed_load) slave_data <= slave_seed;
else if (rx_taken) slave_data <= slave_data + 8'h11; // distinct payload bytes
end
initial forever begin
wait (cmd_byte || cmd_recv);
// Latch WHICH command is being served: both are one-cycle pulses and are gone
// by the time this model finishes counting out the byte.
is_recv = cmd_recv;
if (is_recv) byte_in <= slave_data;
repeat (BYTE_CYCLES) @(posedge clk);
byte_done <= 1'b1;
// For a byte the master TRANSMITTED, ack_received is the slave's answer. For a
// byte the master RECEIVED, the ninth bit is the master's own -- so there is no
// slave answer, and this model drives X rather than a plausible value. A design
// that consults ack_received on a received byte then propagates X instead of
// silently agreeing with whatever the model happened to leave there.
ack_received <= is_recv ? 1'bx : slave_answer;
// The advance is requested in the SAME cycle as byte_done, not after it. The
// DUT issues the next cmd_recv on the byte_done edge, so an advance one cycle
// later would be latched after this model had already picked up the next
// byte -- and the payload would repeat its first value. That is the same
// index-and-data hazard as Chapter 8.1's WS_FETCH, in the testbench.
rx_taken <= is_recv;
@(posedge clk);
byte_done <= 1'b0;
rx_taken <= 1'b0;
end
// Progress record: bytes_read sampled one cycle AFTER each data_valid, which is when
// that byte's increment has landed. A running count must read 1, 2, 3 across a
// three-byte read. A design that reported the FINAL total from the first byte would
// read 3, 3, 3 -- indistinguishable at the end of the transfer, and useless to a
// consumer tracking progress or draining a stream.
reg [LEN_W-1:0] br_log [0:15];
integer n_brlog = 0;
reg dv_q = 1'b0;
always @(posedge clk) if (rst_n) begin
if (obs_clear) begin n_brlog = 0; dv_q = 1'b0; end
else begin
if (dv_q) begin if (n_brlog < 16) br_log[n_brlog] = bytes_read; n_brlog = n_brlog + 1; end
dv_q = data_valid;
end
end
integer base;
task start_txn;
input [LEN_W-1:0] len;
input stop_term;
input [7:0] first_data;
begin
base = n_done;
obs_clear = 1'b1; @(negedge clk); obs_clear = 1'b0; @(negedge clk);
req_len = len; req_stop = stop_term;
slave_seed = first_data;
seed_load = 1'b1; @(negedge clk); seed_load = 1'b0; @(negedge clk);
// Stimulus changes on the NEGEDGE. Driving a DUT input with a blocking
// assignment at the posedge it is sampled on is a race, and it cost a
// watchdog timeout before this comment existed.
req = 1'b1; @(negedge clk); req = 1'b0;
// A COUNT-based wait, never `wait (done)`: done is a one-cycle pulse and is
// still asserted at the following negedge, so a level wait is satisfied by
// the PREVIOUS transaction's pulse. Chapter 8.1 section 7 records the bug.
wait (n_done == base + 1);
repeat (2) @(negedge clk);
end
endtask
initial begin
repeat (3) @(negedge clk);
if (busy !== 1'b0) begin $display("FAIL: busy out of reset"); errors = errors + 1; end
rst_n = 1'b1; @(negedge clk);
// ---- 1: a 3-byte read. The address byte carries R/W = 1, and the master's
// acknowledge pattern must be ACK, ACK, NACK.
slave_answer = 1'b1;
start_txn(8'd3, 1'b1, 8'hA0);
if (tx_log[0] !== 8'h91) begin
$display("FAIL: address byte was 0x%02h, expected 0x91 (0x48 << 1 | R)", tx_log[0]);
errors = errors + 1; end
if (n_tx !== 1) begin
$display("FAIL: master transmitted %0d bytes, expected only the address", n_tx);
errors = errors + 1; end
if (n_rx !== 3) begin
$display("FAIL: master received %0d bytes, expected 3", n_rx); errors = errors + 1; end
if (ack_log[0] !== 1'b1 || ack_log[1] !== 1'b1 || ack_log[2] !== 1'b0) begin
$display("FAIL: ack pattern was %b%b%b, expected 110 (ACK ACK NACK)",
ack_log[0], ack_log[1], ack_log[2]); errors = errors + 1; end
if (rx_log[0] !== 8'hA0 || rx_log[1] !== 8'hB1 || rx_log[2] !== 8'hC2) begin
$display("FAIL: payload was 0x%02h,0x%02h,0x%02h, expected 0xa0,0xb1,0xc2",
rx_log[0], rx_log[1], rx_log[2]); errors = errors + 1; end
if (bytes_read !== 8'd3) begin
$display("FAIL: bytes_read = %0d, expected 3", bytes_read); errors = errors + 1; end
if (n_s !== 1 || n_p !== 1 || n_sr !== 0) begin
$display("FAIL: framing was %0d S, %0d P, %0d Sr -- expected 1, 1, 0",
n_s, n_p, n_sr); errors = errors + 1; end
if (addr_nacked !== 1'b0) begin $display("FAIL: spurious addr_nacked"); errors = errors + 1; end
// bytes_read must be a RUNNING count, not the final total announced early.
if (br_log[0] !== 8'd1 || br_log[1] !== 8'd2 || br_log[2] !== 8'd3) begin
$display("FAIL: bytes_read progressed %0d,%0d,%0d -- expected 1,2,3",
br_log[0], br_log[1], br_log[2]); errors = errors + 1; end
// ---- 2: a ONE-byte read. The master's only acknowledge is a NACK, because
// the first byte is also the last. An off-by-one in the policy shows up
// here and nowhere else.
start_txn(8'd1, 1'b1, 8'h5A);
if (n_rx !== 1) begin
$display("FAIL: 1-byte read received %0d bytes", n_rx); errors = errors + 1; end
if (ack_log[0] !== 1'b0) begin
$display("FAIL: the only byte of a 1-byte read was ACKed, not NACKed"); errors = errors + 1; end
if (rx_log[0] !== 8'h5A) begin
$display("FAIL: 1-byte payload was 0x%02h, expected 0x5a", rx_log[0]); errors = errors + 1; end
if (bytes_read !== 8'd1) begin
$display("FAIL: bytes_read = %0d, expected 1", bytes_read); errors = errors + 1; end
// ---- 3: an ADDRESS-ONLY probe with R/W = 1. The master never becomes a
// receiver, so it must generate NO acknowledges whatsoever -- not even
// a NACK, because there is no byte to refuse.
start_txn(8'd0, 1'b1, 8'h00);
if (n_tx !== 1) begin
$display("FAIL: a read probe put %0d bytes on the wire, expected 1", n_tx);
errors = errors + 1; end
if (n_rx !== 0) begin
$display("FAIL: a read probe generated %0d acknowledges, expected 0", n_rx);
errors = errors + 1; end
if (bytes_read !== 8'd0) begin
$display("FAIL: a read probe reported %0d bytes read", bytes_read); errors = errors + 1; end
if (n_p !== 1) begin
$display("FAIL: a read probe emitted %0d STOPs", n_p); errors = errors + 1; end
// ---- 4: the address is NACKED. Nobody is there, so no byte is ever read,
// and a terminator is still emitted so the bus is not left held.
slave_answer = 1'b0;
start_txn(8'd4, 1'b1, 8'h00);
if (addr_nacked !== 1'b1) begin
$display("FAIL: addr_nacked not reported"); errors = errors + 1; end
if (n_rx !== 0) begin
$display("FAIL: read %0d bytes from an address nobody answered", n_rx); errors = errors + 1; end
if (bytes_read !== 8'd0) begin
$display("FAIL: bytes_read = %0d after an address NACK", bytes_read); errors = errors + 1; end
if (n_p !== 1) begin
$display("FAIL: an aborted read emitted %0d STOPs, expected 1", n_p); errors = errors + 1; end
slave_answer = 1'b1;
// ---- 5: terminate with a REPEATED START instead of a STOP. The acknowledge
// policy must be IDENTICAL -- the specification says a master-receiver
// sends a not-acknowledge just before the repeated START too.
start_txn(8'd2, 1'b0, 8'h33);
if (n_sr !== 1 || n_p !== 0) begin
$display("FAIL: Sr-terminated read emitted %0d Sr and %0d P, expected 1 and 0",
n_sr, n_p); errors = errors + 1; end
if (ack_log[0] !== 1'b1 || ack_log[1] !== 1'b0) begin
$display("FAIL: Sr-terminated ack pattern was %b%b, expected 10 (ACK NACK)",
ack_log[0], ack_log[1]); errors = errors + 1; end
if (bytes_read !== 8'd2) begin
$display("FAIL: bytes_read = %0d, expected 2", bytes_read); errors = errors + 1; end
// ---- 6: back-to-back transactions. done must be a pulse and the second
// transaction must start from a clean state.
start_txn(8'd2, 1'b1, 8'h77);
if (bytes_read !== 8'd2 || n_s !== 1 || n_p !== 1) begin
$display("FAIL: the transaction after an Sr-terminated one did not start clean");
errors = errors + 1; end
if (n_done !== 6) begin
$display("FAIL: %0d done pulses across 6 transactions", n_done); errors = errors + 1; end
if (errors == 0)
$display("PASS: address carries R/W=1, ack policy is n-1 ACKs then one NACK, probe generates none, both terminators");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- A master-receiver's TRANSACTION-level sequencer. Like its write counterpart in
-- Chapter 8.1 it issues framing and byte commands to the layers built in Modules 5
-- and 7 and contains no bit-level logic; unlike that one, it must ANSWER every byte
-- it receives, and the answer is the only means it has of ending the transfer.
--
-- The specification's read format, verbatim: "At the moment of the first acknowledge,
-- the master-transmitter becomes a master-receiver and the slave-receiver becomes a
-- slave-transmitter. This first acknowledge is still generated by the slave. The
-- master generates subsequent acknowledges. The STOP condition is generated by the
-- master, which sends a not-acknowledge just before the STOP condition."
--
-- And for the other terminator: "If a master-receiver sends a repeated START
-- condition, it sends a not-acknowledge just before the repeated START condition."
-- So the final NACK is NOT "the NACK before the STOP" -- it precedes BOTH endings,
-- because it means "I want no further byte", not "I am stopping". req_stop selects
-- which terminator follows it, and the acknowledge policy is identical either way.
entity i2c_read_sequencer is
generic (
LEN_W : positive := 8
);
port (
clk : in std_logic;
rst_n : in std_logic;
-- transaction request
req : in std_logic; -- pulse: begin a read
req_addr : in std_logic_vector(6 downto 0); -- as the datasheet states it
req_len : in unsigned(LEN_W - 1 downto 0); -- bytes; ZERO is a legal probe
req_stop : in std_logic; -- 1: end with P. 0: end with Sr.
-- commands to the framing sequencer (5.5) and byte engine (7.1)
cmd_start : out std_logic; -- pulse: emit S
cmd_stop : out std_logic; -- pulse: emit P
cmd_restart : out std_logic; -- pulse: emit Sr
cmd_byte : out std_logic; -- pulse: TRANSMIT one byte
cmd_byte_data : out std_logic_vector(7 downto 0);
cmd_recv : out std_logic; -- pulse: RECEIVE one byte
-- The answer this master will put in the received byte's ninth slot. Issued
-- WITH cmd_recv, one whole byte ahead of the slot, because by the time the
-- slot opens there is no longer time to decide -- Chapter 7.2, section 2.
cmd_ack : out std_logic;
-- completions from those layers
framing_done : in std_logic; -- pulse: an S, Sr or P completed
byte_done : in std_logic; -- pulse: a byte and its slot ended
ack_received : in std_logic; -- the SLAVE's answer
byte_in : in std_logic_vector(7 downto 0);
-- payload sink
data_index : out unsigned(LEN_W - 1 downto 0);
data_out : out std_logic_vector(7 downto 0);
data_valid : out std_logic; -- pulse: data_out is good
-- status
busy : out std_logic;
done : out std_logic; -- pulse: the transaction ended
addr_nacked : out std_logic;
bytes_read : out unsigned(LEN_W - 1 downto 0)
);
end entity;
architecture rtl of i2c_read_sequencer is
type state_t is (
RS_IDLE,
RS_START, -- waiting for the S to complete
RS_ADDR, -- the address byte is in flight; the SLAVE answers it
RS_DATA, -- a payload byte is arriving; THIS master answers it
RS_END -- waiting for the terminating P or Sr to complete
);
signal state : state_t := RS_IDLE;
constant ZERO_LEN : unsigned(LEN_W - 1 downto 0) := (others => '0');
-- The address byte is the seven-bit address with R/W = 1 appended. This is the
-- ONLY structural difference from Chapter 8.1's write on the wire, and it is the
-- difference that reverses every role for the rest of the frame.
signal addr_byte : std_logic_vector(7 downto 0);
signal idx : unsigned(LEN_W - 1 downto 0) := (others => '0');
-- THE ACKNOWLEDGE POLICY, as one expression.
--
-- Reading byte number i (zero-based) of req_len: acknowledge it if and only if
-- another byte is still wanted afterwards. The final byte gets a NACK, which is
-- the fifth NACK condition -- "a master-receiver must signal the end of the
-- transfer to the slave transmitter" -- and is the only mechanism available for
-- saying so. There is no length field on this bus and no command for "stop".
function ack_for (i : unsigned; len : unsigned) return std_logic is
begin
if (i + 1) < len then return '1'; else return '0'; end if;
end function;
begin
addr_byte <= req_addr & '1';
data_index <= idx;
process (clk)
begin
if rising_edge(clk) then
if rst_n = '0' then
state <= RS_IDLE;
cmd_start <= '0';
cmd_stop <= '0';
cmd_restart <= '0';
cmd_byte <= '0';
cmd_recv <= '0';
cmd_ack <= '0';
cmd_byte_data <= (others => '0');
idx <= (others => '0');
data_out <= (others => '0');
data_valid <= '0';
busy <= '0';
done <= '0';
addr_nacked <= '0';
bytes_read <= (others => '0');
else
-- Every command is a single-cycle pulse. Holding one asserted would
-- make the lower layer act twice; Chapter 8.1's mutation A7 is that bug.
cmd_start <= '0';
cmd_stop <= '0';
cmd_restart <= '0';
cmd_byte <= '0';
cmd_recv <= '0';
data_valid <= '0';
done <= '0';
case state is
when RS_IDLE =>
if req = '1' then
busy <= '1';
addr_nacked <= '0';
bytes_read <= (others => '0');
idx <= (others => '0');
cmd_start <= '1';
state <= RS_START;
end if;
when RS_START =>
if framing_done = '1' then
cmd_byte <= '1';
cmd_byte_data <= addr_byte;
state <= RS_ADDR;
end if;
when RS_ADDR =>
if byte_done = '1' then
if ack_received /= '1' then
-- Condition 1: nobody claimed the address. Identical to
-- the write's abort -- there is nothing to read from.
addr_nacked <= '1';
state <= RS_END;
if req_stop = '1' then cmd_stop <= '1';
else cmd_restart <= '1'; end if;
elsif req_len = ZERO_LEN then
-- An address-only probe with R/W = 1. The master never
-- becomes a receiver of data, so it generates NO
-- acknowledges at all -- and therefore issues no NACK
-- either, because there is no byte to refuse.
state <= RS_END;
if req_stop = '1' then cmd_stop <= '1';
else cmd_restart <= '1'; end if;
else
-- The direction has now changed. From here the master
-- is the receiver and owns every ninth bit.
cmd_recv <= '1';
cmd_ack <= ack_for(ZERO_LEN, req_len);
state <= RS_DATA;
end if;
end if;
when RS_DATA =>
if byte_done = '1' then
data_out <= byte_in;
data_valid <= '1';
bytes_read <= bytes_read + 1;
if ack_for(idx, req_len) = '0' then
-- This was the final byte and it was NACKed, so the
-- slave has released SDA and the bus is free for the
-- terminator.
state <= RS_END;
if req_stop = '1' then cmd_stop <= '1';
else cmd_restart <= '1'; end if;
else
-- idx is advanced and the NEXT answer is computed from
-- the advanced value, in the same cycle. That is safe
-- here and was not in Chapter 8.1: ack_for is a
-- function of the index alone, not of a memory read, so
-- there is no fetch latency and no WS_FETCH equivalent.
idx <= idx + 1;
cmd_recv <= '1';
cmd_ack <= ack_for(idx + 1, req_len);
state <= RS_DATA;
end if;
end if;
when RS_END =>
if framing_done = '1' then
busy <= '0';
done <= '1';
state <= RS_IDLE;
end if;
end case;
end if;
end if;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- The two layers below the sequencer are modelled as two INDEPENDENT processes, one
-- per layer. Chapter 8.1's section 11 records why: a single process containing waits
-- is one thread of control, and it drops any command that arrives while suspended.
--
-- 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_sequencer_tb is
end entity;
architecture sim of i2c_read_sequencer_tb is
constant LEN_W : positive := 8;
constant FRAMING_CYCLES : positive := 4;
constant BYTE_CYCLES : positive := 9;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal req : std_logic := '0';
signal req_stop : std_logic := '1';
signal req_addr : std_logic_vector(6 downto 0) := "1001000"; -- 0x48
signal req_len : unsigned(LEN_W - 1 downto 0) := (others => '0');
signal cmd_start, cmd_stop, cmd_restart, cmd_byte, cmd_recv, cmd_ack : std_logic;
signal cmd_byte_data : std_logic_vector(7 downto 0);
signal framing_done : std_logic := '0';
signal byte_done : std_logic := '0';
signal ack_received : std_logic := '0';
signal byte_in : std_logic_vector(7 downto 0) := (others => '0');
signal data_index : unsigned(LEN_W - 1 downto 0);
signal data_out : std_logic_vector(7 downto 0);
signal data_valid, busy, done, addr_nacked : std_logic;
signal bytes_read : unsigned(LEN_W - 1 downto 0);
-- observation, owned solely by the observe process
type byte_arr is array (0 to 15) of std_logic_vector(7 downto 0);
type bit_arr is array (0 to 15) of std_logic;
signal n_s, n_p, n_sr, n_tx, n_rx, n_done, n_rxlog : natural := 0;
signal tx_log : byte_arr := (others => (others => '0'));
signal ack_log : bit_arr := (others => '0');
signal rx_log : byte_arr := (others => (others => '0'));
-- the far end, owned solely by the byte process
signal slave_answer : std_logic := '1';
signal is_recv : std_logic := '0';
signal rx_taken : std_logic := '0';
-- the payload source: one owning process, fed by a request pulse from the stimulus
signal slave_seed : std_logic_vector(7 downto 0) := (others => '0');
signal seed_load : std_logic := '0';
signal slave_data : std_logic_vector(7 downto 0) := (others => '0');
-- The stimulus does not own the observation counters, so it asks for them to be
-- cleared rather than clearing them. n_done is deliberately NOT cleared: it is the
-- baseline the count-based transaction wait is built on.
signal obs_clear : std_logic := '0';
-- Progress record: bytes_read sampled one cycle AFTER each data_valid, which is when
-- that byte's increment has landed. A running count must read 1, 2, 3 across a
-- three-byte read. A design that reported the FINAL total from the first byte would
-- read 3, 3, 3 -- indistinguishable at the end of the transfer, and useless to a
-- consumer tracking progress or draining a stream.
type len_arr is array (0 to 15) of unsigned(LEN_W - 1 downto 0);
signal br_log : len_arr := (others => (others => '0'));
signal n_brlog : natural := 0;
signal dv_q : std_logic := '0';
signal test_done : std_logic := '0';
begin
dut : entity work.i2c_read_sequencer
generic map (LEN_W => LEN_W)
port map (clk => clk, rst_n => rst_n, req => req, req_addr => req_addr,
req_len => req_len, req_stop => req_stop,
cmd_start => cmd_start, cmd_stop => cmd_stop, cmd_restart => cmd_restart,
cmd_byte => cmd_byte, cmd_byte_data => cmd_byte_data,
cmd_recv => cmd_recv, cmd_ack => cmd_ack,
framing_done => framing_done, byte_done => byte_done,
ack_received => ack_received, byte_in => byte_in,
data_index => data_index, data_out => data_out, data_valid => data_valid,
busy => busy, done => done, addr_nacked => addr_nacked,
bytes_read => bytes_read);
clk <= not clk after 5 ns;
watchdog : process
begin
wait for 400 us;
if test_done = '0' then
report "watchdog expired -- the design never reached the expected state"
severity failure;
end if;
wait;
end process;
-- ---- observation -------------------------------------------------------------
observe : process (clk)
begin
if rising_edge(clk) and rst_n = '1' then
if obs_clear = '1' then
n_s <= 0; n_p <= 0; n_sr <= 0; n_tx <= 0; n_rx <= 0; n_rxlog <= 0;
else
if cmd_start = '1' then n_s <= n_s + 1; end if;
if cmd_stop = '1' then n_p <= n_p + 1; end if;
if cmd_restart = '1' then n_sr <= n_sr + 1; end if;
if cmd_byte = '1' then
if n_tx < 16 then tx_log(n_tx) <= cmd_byte_data; end if;
n_tx <= n_tx + 1;
end if;
if cmd_recv = '1' then
if n_rx < 16 then ack_log(n_rx) <= cmd_ack; end if;
n_rx <= n_rx + 1;
end if;
if data_valid = '1' then
if n_rxlog < 16 then rx_log(n_rxlog) <= data_out; end if;
n_rxlog <= n_rxlog + 1;
end if;
end if;
if done = '1' then n_done <= n_done + 1; end if;
end if;
end process;
progress : process (clk)
begin
if rising_edge(clk) and rst_n = '1' then
if obs_clear = '1' then
n_brlog <= 0; dv_q <= '0';
else
if dv_q = '1' then
if n_brlog < 16 then br_log(n_brlog) <= bytes_read; end if;
n_brlog <= n_brlog + 1;
end if;
dv_q <= data_valid;
end if;
end if;
end process;
-- ---- the framing layer, an independent responder -----------------------------
framing : process
begin
wait until cmd_start = '1' or cmd_stop = '1' or cmd_restart = '1';
for i in 1 to FRAMING_CYCLES loop wait until rising_edge(clk); end loop;
framing_done <= '1';
wait until rising_edge(clk);
framing_done <= '0';
end process;
-- ---- the byte layer, an independent responder --------------------------------
-- It serves BOTH transmit and receive. slave_answer is what the far end puts in
-- the ninth slot of a TRANSMITTED byte; slave_data is what it sends us when we
-- receive. The distinction matters: for a received byte the ninth bit is OURS,
-- and this model must not invent an answer for it.
bytelayer : process
variable recv : std_logic;
begin
wait until cmd_byte = '1' or cmd_recv = '1';
-- Latch WHICH command is being served: both are one-cycle pulses and are gone
-- by the time this model finishes counting out the byte.
recv := cmd_recv;
is_recv <= recv;
if recv = '1' then byte_in <= slave_data; end if;
for i in 1 to BYTE_CYCLES loop wait until rising_edge(clk); end loop;
byte_done <= '1';
-- For a byte the master TRANSMITTED, ack_received is the slave's answer. For a
-- byte the master RECEIVED, the ninth bit is the master's own -- so there is no
-- slave answer, and this model drives X rather than a plausible value. A design
-- that consults ack_received on a received byte then propagates X instead of
-- silently agreeing with whatever the model happened to leave there.
if recv = '1' then ack_received <= 'X'; else ack_received <= slave_answer; end if;
-- The advance is requested in the SAME cycle as byte_done, not after it. The
-- DUT issues the next cmd_recv on the byte_done edge, so an advance one cycle
-- later would be latched after this model had already picked up the next byte --
-- and the payload would repeat its first value. That is the same index-and-data
-- hazard as Chapter 8.1's WS_FETCH, in the testbench.
rx_taken <= recv;
wait until rising_edge(clk);
byte_done <= '0';
rx_taken <= '0';
end process;
-- ---- the payload source, single owner ----------------------------------------
payload : process (clk)
begin
if rising_edge(clk) then
if seed_load = '1' then
slave_data <= slave_seed;
elsif rx_taken = '1' then
slave_data <= std_logic_vector(unsigned(slave_data) + 16#11#);
end if;
end if;
end process;
-- ---- stimulus ----------------------------------------------------------------
stim : process
variable errs : natural := 0;
variable base : 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 start_txn (len : in natural; stop_term : in std_logic;
first_data : in std_logic_vector(7 downto 0)) is
begin
base := n_done;
obs_clear <= '1'; waitn(1); obs_clear <= '0'; waitn(1);
req_len <= to_unsigned(len, LEN_W);
req_stop <= stop_term;
slave_seed <= first_data;
seed_load <= '1'; waitn(1); seed_load <= '0'; waitn(1);
-- Stimulus changes on the FALLING edge. Driving a DUT input at the rising
-- edge it is sampled on is a race, and it cost a watchdog timeout before
-- this comment existed.
req <= '1'; waitn(1); req <= '0';
-- A COUNT-based wait, never "wait until done = '1'": done is a one-cycle
-- pulse and is still asserted at the following falling edge, so a level
-- wait is satisfied by the PREVIOUS transaction's pulse.
wait until n_done = base + 1;
waitn(2);
end procedure;
begin
waitn(3);
if busy /= '0' then
report "busy out of reset" severity error; errs := errs + 1; end if;
rst_n <= '1'; waitn(1);
-- 1: a 3-byte read. The address byte carries R/W = 1, and the master's
-- acknowledge pattern must be ACK, ACK, NACK.
slave_answer <= '1';
start_txn(3, '1', x"A0");
if tx_log(0) /= x"91" then
report "address byte wrong -- expected 0x91 (0x48 shifted, R)" severity error;
errs := errs + 1; end if;
if n_tx /= 1 then
report "master transmitted more than the address byte" severity error;
errs := errs + 1; end if;
if n_rx /= 3 then
report "master received the wrong number of bytes" severity error;
errs := errs + 1; end if;
if ack_log(0) /= '1' or ack_log(1) /= '1' or ack_log(2) /= '0' then
report "ack pattern wrong -- expected ACK ACK NACK" severity error;
errs := errs + 1; end if;
if rx_log(0) /= x"A0" or rx_log(1) /= x"B1" or rx_log(2) /= x"C2" then
report "payload wrong -- expected 0xa0, 0xb1, 0xc2" severity error;
errs := errs + 1; end if;
if bytes_read /= to_unsigned(3, LEN_W) then
report "bytes_read wrong, expected 3" severity error; errs := errs + 1; end if;
if n_s /= 1 or n_p /= 1 or n_sr /= 0 then
report "framing wrong -- expected one S, one P, no Sr" severity error;
errs := errs + 1; end if;
if addr_nacked /= '0' then
report "spurious addr_nacked" severity error; errs := errs + 1; end if;
-- bytes_read must be a RUNNING count, not the final total announced early.
if br_log(0) /= to_unsigned(1, LEN_W) or br_log(1) /= to_unsigned(2, LEN_W)
or br_log(2) /= to_unsigned(3, LEN_W) then
report "bytes_read did not progress 1, 2, 3" severity error;
errs := errs + 1; end if;
-- 2: a ONE-byte read. The master's only acknowledge is a NACK, because the
-- first byte is also the last. An off-by-one in the policy shows up here and
-- nowhere else.
start_txn(1, '1', x"5A");
if n_rx /= 1 then
report "1-byte read received the wrong number of bytes" severity error;
errs := errs + 1; end if;
if ack_log(0) /= '0' then
report "the only byte of a 1-byte read was ACKed, not NACKed" severity error;
errs := errs + 1; end if;
if rx_log(0) /= x"5A" then
report "1-byte payload wrong" severity error; errs := errs + 1; end if;
if bytes_read /= to_unsigned(1, LEN_W) then
report "bytes_read wrong, expected 1" severity error; errs := errs + 1; end if;
-- 3: an ADDRESS-ONLY probe with R/W = 1. The master never becomes a receiver,
-- so it must generate NO acknowledges whatsoever -- not even a NACK, because
-- there is no byte to refuse.
start_txn(0, '1', x"00");
if n_tx /= 1 then
report "a read probe put the wrong number of bytes on the wire" severity error;
errs := errs + 1; end if;
if n_rx /= 0 then
report "a read probe generated acknowledges" severity error; errs := errs + 1; end if;
if bytes_read /= to_unsigned(0, LEN_W) then
report "a read probe reported bytes read" severity error; errs := errs + 1; end if;
if n_p /= 1 then
report "a read probe emitted the wrong number of STOPs" severity error;
errs := errs + 1; end if;
-- 4: the address is NACKED. Nobody is there, so no byte is ever read, and a
-- terminator is still emitted so the bus is not left held.
slave_answer <= '0';
start_txn(4, '1', x"00");
if addr_nacked /= '1' then
report "addr_nacked not reported" severity error; errs := errs + 1; end if;
if n_rx /= 0 then
report "read bytes from an address nobody answered" severity error;
errs := errs + 1; end if;
if bytes_read /= to_unsigned(0, LEN_W) then
report "bytes_read nonzero after an address NACK" severity error;
errs := errs + 1; end if;
if n_p /= 1 then
report "an aborted read emitted the wrong number of STOPs" severity error;
errs := errs + 1; end if;
slave_answer <= '1';
-- 5: terminate with a REPEATED START instead of a STOP. The acknowledge policy
-- must be IDENTICAL -- the specification says a master-receiver sends a
-- not-acknowledge just before the repeated START too.
start_txn(2, '0', x"33");
if n_sr /= 1 or n_p /= 0 then
report "Sr-terminated read framed wrongly" severity error; errs := errs + 1; end if;
if ack_log(0) /= '1' or ack_log(1) /= '0' then
report "Sr-terminated ack pattern wrong -- expected ACK NACK" severity error;
errs := errs + 1; end if;
if bytes_read /= to_unsigned(2, LEN_W) then
report "bytes_read wrong, expected 2" severity error; errs := errs + 1; end if;
-- 6: back-to-back transactions. done must be a pulse and the second
-- transaction must start from a clean state.
start_txn(2, '1', x"77");
if bytes_read /= to_unsigned(2, LEN_W) or n_s /= 1 or n_p /= 1 then
report "the transaction after an Sr-terminated one did not start clean"
severity error; errs := errs + 1; end if;
if n_done /= 6 then
report "wrong number of done pulses across six transactions" severity error;
errs := errs + 1; end if;
if errs = 0 then
report "i2c_read_sequencer self-check complete: address carries R/W=1, ack "
& "policy is n-1 ACKs then one NACK, probe generates none, both terminators"
severity note;
else
report "i2c_read_sequencer self-check FAILED" severity error;
end if;
test_done <= '1';
wait;
end process;
end architecture;6a. Five Decisions Worth Defending
cmd_ack is issued WITH cmd_recv, one whole byte ahead of the slot. The answer must be a registered value already sitting on the output when the ninth slot opens, because the slot begins with SCL falling and is sampled on the next rising edge. A design that computed the answer during the slot would be moving SDA while SCL is high, which Chapter 5.1 established is reserved for framing — so the fault would not be a late acknowledge but a START or STOP appearing inside a byte. Chapter 8.2 §5a made the same argument for the slave-receiver; it is symmetric and it is not negotiable on either side.
The terminator is a request input, not a constant. req_stop selects a STOP or a repeated START, and the acknowledge policy is identical either way — because the specification says a master-receiver NACKs before a repeated START too. Hard-wiring the STOP would make the combined format of §1 unimplementable on top of this block, and the combined format is how essentially every register read on a real device works.
A zero-length read is legal and generates no acknowledges at all. req_len = 0 sends S, the address with R/W = 1, and the terminator. The master never becomes a receiver of data, so it issues no ACK and — this is the part worth pausing on — no NACK either. There is no byte to refuse. A sequencer that emitted a NACK anyway would be answering a slot that does not exist. The mutation suite tests this as A5.
The next answer is computed from the ADVANCED index, in the same cycle. ack_for(data_index + 1'b1) rather than ack_for(data_index). Note that this is safe here and was not safe in Chapter 8.1: that design needed a whole extra state, WS_FETCH, because its payload came from a memory whose output lagged its index. Here ack_for is a pure function of the index — no memory, no latency — so the advanced value is available immediately. The two designs differ because the data source differs, not because one author was more careful, and recognising which situation you are in is the transferable skill. Mutation A6 injects the wrong one and the failure is ack pattern was 111, expected 110.
bytes_read is a running count, not the requested length. In this design every completed read delivers exactly req_len bytes, so the two are equal at the end of any transfer — which makes bytes_read <= req_len an almost undetectable mutation. It is distinguishable only in progress: a consumer draining the stream needs to know it is on byte one of three, not that three are coming. §7 records what it took to kill it.
6b. Verified Execution
$ iverilog -g2012 -o a0 i2c_read_sequencer.sv i2c_read_sequencer_tb.sv && ./a0
PASS: address carries R/W=1, ack policy is n-1 ACKs then one NACK, probe generates
none, both terminators
i2c_read_sequencer_tb.sv:249: $finish called at 2460 (1ps)
$ iverilog -g2005 -o a1 i2c_read_sequencer.v i2c_read_sequencer_tb.v && ./a1
PASS: address carries R/W=1, ack policy is n-1 ACKs then one NACK, probe generates
none, both terminators
i2c_read_sequencer_tb.v:260: $finish called at 2460 (1ps)
$ nvc -a i2c_read_sequencer.vhd i2c_read_sequencer_tb.vhd && nvc -e i2c_read_sequencer_tb
$ nvc -r i2c_read_sequencer_tb --stop-time=500us
** Note: 2460ns+0: i2c_read_sequencer self-check complete: address carries R/W=1, ack
policy is n-1 ACKs then one NACK, probe generates none, both terminatorsAll three finish at 2460 ns. That match is the evidence that the three testbenches apply identical stimulus with identical cycle counts, so the three implementations are being compared against each other rather than each passing a private test. Two structural changes were made to the SystemVerilog and Verilog testbenches purely to keep that true, and §8's callout explains why the VHDL constraint improved all three.
7. What the Testbench Proves
The framing and byte layers are modelled as two independent processes, for the reason Chapter 8.1 §11 documents at length: a single clocked block containing blocking waits is one thread of control and silently drops commands that arrive while it is suspended.
| # | stimulus | what it establishes |
|---|---|---|
| 1 | a 3-byte read from 0x48 | the address byte is 0x91 — R/W set |
| 2 | the same read | the master transmits one byte only; it receives three |
| 3 | the same read | the acknowledge pattern is ACK, ACK, NACK |
| 4 | the same read | the payload arrives in order, and bytes_read progresses 1, 2, 3 |
| 5 | a 1-byte read | the only acknowledge is a NACK — master_acks is zero |
| 6 | req_len = 0 | a probe: one byte, and no acknowledges whatsoever |
| 7 | the address NACKed | addr_nacked, no bytes read, and a terminator still emitted |
| 8 | req_stop = 0 | terminated with Sr, and the NACK still precedes it |
| 9 | back-to-back | done is a pulse; six transactions produce six pulses |
Test 5 is the one that earns its place by being an edge case the general case hides. In a one-byte read the first byte is also the last, so the master's only acknowledge is a NACK and master_acks is zero. Any off-by-one in the policy expression shows up here and nowhere else — the mutation < → <= reads four bytes for a three-byte request but passes every test that only checks "at least one ACK, then a NACK".
Test 4's progress check exists because of a mutation that survived, and it is worth being precise about why. Replacing the running increment with bytes_read <= req_len cannot be caught at the end of a transfer, because in this design every completed read delivers exactly what was requested — the two values are provably equal there. The only observable difference is during the transfer, so the testbench samples bytes_read one cycle after each data_valid and requires 1, 2, 3. The mutant reports 3, 3, 3.
8. Mutation Testing
Ten defects injected into the SystemVerilog sequencer, one at a time.
| # | injected defect | outcome |
|---|---|---|
| A1 | the address byte's R/W bit says write | killed — address byte was 0x90, expected 0x91 |
| A2 | the final byte is ACKed instead of NACKed | killed — received 4 bytes, expected 3 |
| A3 | the NACK comes one byte early | killed — received 2 bytes, expected 3 |
| A4 | an address NACK is ignored and bytes are read anyway | killed |
| A5 | a zero-length probe reads a byte anyway | killed — the probe generated 1 acknowledge |
| A6 | the next answer is computed from the old index | killed — ack pattern 111, expected 110 |
| A7 | commands are held instead of pulsed | killed — 47 bytes transmitted |
| A8 | req_stop ignored: always STOP, never Sr | killed — 0 Sr and 1 P, expected 1 and 0 |
| A9 | no terminator after the final byte | killed — the watchdog expired |
| A10 | bytes_read reports the requested length | killed — progressed 3,3,3, expected 1,2,3 |
Ten injected, ten killed — with A10 requiring a new test and A7 requiring a repaired mutation, both recorded here rather than smoothed over.
A2 and A3 are a pair and neither alone is enough. A2 makes the policy one byte too permissive and A3 one byte too strict. A single test that counted bytes would catch one of them depending on which direction it was written to expect. Injecting both directions of an off-by-one is the general form of testing a boundary expression, and it is cheaper than reasoning about which direction is more likely.
A7's first version was not a valid mutation. The five-line command-clearing block appears twice in the source — once in the reset branch and once as the per-cycle pulse clear — so the anchor matched two places and the harness correctly refused to inject it rather than silently mutating the wrong one. The fix was to anchor on the comment line above the pulse clear. A mutation harness that does not verify anchor uniqueness will happily mutate the wrong site and report a kill for the wrong reason; this one asserts on it, which is why the problem surfaced as a refusal instead of as a misleading pass.
A9 was killed by the watchdog, and that is a legitimate kill. A sequencer that never emits its terminator never asserts done, so the count-based wait never returns and the test stops making progress rather than failing a check. Every testbench in this module carries a watchdog for exactly this class of defect.
9. Verification Connection — A Read Item Carries a Policy, Not Just a Length
The write's sequence_item in Chapter 8.1 §9 was almost a copy of its port list. A read item needs one thing the write item did not: the terminator, because §1 established that a read can legally end two different ways and the acknowledge policy is identical for both.
class i2c_read_item extends uvm_sequence_item;
`uvm_object_utils(i2c_read_item)
// ---- the request ----
rand bit [6:0] addr;
rand int len; // payload bytes; zero is the R/W=1 probe
// The terminator is part of the REQUEST, not an implementation detail. A read
// that ends in a repeated START is the first half of a combined transfer, and
// a generator that could not produce one could never exercise the format that
// essentially every real register read uses.
rand bit end_with_stop;
// ---- the response ----
bit addr_nacked;
int bytes_read;
bit [7:0] payload [];
// A zero-length read is the address probe of section 6a. It is admitted
// deliberately: the interesting property of a probe is that it generates NO
// acknowledges, and a constraint of [1:16] would make that untestable.
constraint c_len { len inside {[0:16]}; }
// Chapter 6.3's reserved ranges, excluded structurally rather than filtered in
// the driver, so an illegal address is UNGENERATABLE rather than merely unused.
constraint c_addr {
addr != 7'h00;
addr[6:3] != 4'b0000; // 0x00-0x07 reserved
addr[6:3] != 4'b1111; // 0x78-0x7F reserved
}
function new(string name = "i2c_read_item");
super.new(name);
endfunction
function string convert2string();
return $sformatf("READ addr=0x%02h len=%0d -> got=%0d term=%s%s",
addr, len, bytes_read,
end_with_stop ? "P" : "Sr",
addr_nacked ? " ADDR-NACK" : "");
endfunction
endclassAnd the properties. For a read these are not optional niceties — the first one is the difference between a working bus and a hung one.
// THE property of a read. The final data byte must be NACKed, because a master
// that ACKs it has asked for another byte, and the slave that supplies it holds
// SDA low through the window in which SDA must RISE to form the STOP. The STOP
// cannot be formed, so this is not a failed transfer -- it is a held bus.
//
// Written on the terminator rather than on "the last byte", because nothing in
// the frame marks a byte as last. The terminator is the only evidence that the
// byte before it was final, which is exactly why the property has to look
// backwards from the framing event.
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");
// The address byte's acknowledge belongs to the SLAVE, even in a read. A monitor
// or scoreboard that attributes it to the master reports one master acknowledge
// too many on every single read, and the error is uniform -- which makes it look
// like a convention difference rather than a bug.
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");10. FPGA and ASIC Implications
The block is smaller than the write sequencer. Five states, one LEN_W index, one LEN_W count, one byte register. There is no fetch state, because — §6a — the acknowledge policy is a function of the index rather than a memory read. At LEN_W = 8 this is roughly 25 flops.
The comparator is the only arithmetic. (idx + 1) < req_len synthesises to an incrementer and a magnitude compare, both LEN_W wide, on a path with a whole byte time to settle. Nothing here constrains the clock.
The payload sink is the integration concern, and it is easier than the write's source. The write sequencer had to fetch a byte and wait for it. This one produces a byte with a data_valid pulse and a data_index, so the consumer can be a FIFO write port, a memory write, or a register file — all of which accept a valid/data pair directly. If the sink can ever apply back-pressure, though, this interface cannot express it: there is no ready signal, and the byte arrives whether or not anywhere has room. A design that needs back-pressure must either buffer a byte or stretch the clock, and the latter is the protocol's own answer.
Reset mid-read leaves the bus in the one state worth worrying about. Resetting this block stops it issuing commands; it does not emit a STOP and it does not NACK. So a reset during the data phase leaves a slave-transmitter that was last told "send another" — which is precisely the hang of §3, arrived at by a different route. A master that can be reset asynchronously needs the bus-recovery mechanism of Chapter 5.5, and this is the strongest argument for having one.
11. Debugging — The Read That Returned One Byte Too Many
Pitfall — an off-by-one in the acknowledge policy is a length bug, not an acknowledge bug
// A driver reads three bytes of sensor data. The policy expression was written as:
//
// ack_next = ((index + 1) <= req_len); // <= instead of <
//
// which is the ordinary off-by-one, and it does something specific: it ACKs the
// THIRD byte as well, because for index 2 and req_len 3 the test 3 <= 3 is true.
//
// The capture shows:
//
// S 0x91 A d0 A d1 A d2 A d3 N P
// ^^ ^ ^^ ^ ^^ ^ ^^ ^
// three bytes requested ... and FOUR on the wire
//
// The transfer completes cleanly. There is no NACK out of place, no framing error,
// and the STOP forms correctly -- because the FOURTH byte did get NACKed.The driver reports success and returns three bytes. The first three bytes are correct. Everything downstream works.
And the sensor's register pointer is one further along than the driver believes.
That is the whole bug, and it surfaces on the NEXT transfer rather than this one. The following read starts from a pointer that has already been advanced by the extra byte, so it returns data shifted by one register -- with no error anywhere, in a transfer that is itself completely well-formed.
The investigation therefore starts in the wrong place: the SECOND read is the one that returns wrong data, so the second read is what gets examined, and it is perfect. Its capture is textbook. Considerable time went into the sensor's auto-increment behaviour and into the driver's pointer arithmetic, both correct.
The clue was the byte count of the FIRST read. Four nine-clock groups between the address and the STOP, for a three-byte request. Nothing in the second transfer can explain that, and a transfer that reads one byte more than it was asked for has side effects on a device with an auto-incrementing pointer -- even though the extra byte was discarded.
The acknowledge policy was one byte too permissive, so the master asked for a fourth byte, received it, discarded it, and NACKed that one instead.
Because the NACK still arrived -- just one byte late -- every structural check passes. The frame is well-formed, the STOP forms, no device misbehaves. The only evidence is the byte count, and the only CONSEQUENCE is a side effect on the slave's pointer that surfaces in a later transfer.
12. Common Misconceptions
"A read is a write with the direction bit flipped." On the wire, structurally, almost — §5 is that table. But every role reverses, the acknowledge changes from a verdict into flow control, and the failure mode changes from a reported error into a hung bus. One bit, a different transaction.
"The slave acknowledges the data bytes of a read." It acknowledges the address byte and nothing else. The master answers every data byte, because the master is the receiver. Attributing the address byte's acknowledge to the master is the single most common monitor bug and it inflates every read's count by one.
"The master NACKs the last byte to report it was bad." It NACKs to say stop sending. A master-receiver has no mechanism for reporting a bad byte and no reason to want one — it asked for the data. In a read the ninth bit is flow control; in a write it is a verdict.
"If the master forgets the final NACK, the transfer fails." The bus fails. The slave sends another byte and holds SDA low, so the STOP cannot be formed at all — not delayed, not corrupted, impossible. Every other device on the bus is affected.
"A zero-length read is meaningless." It is the address probe with R/W = 1, and it is one of the two standard ways to scan a bus. It generates no master acknowledges at all, which is a genuinely distinct case worth testing.
"The final NACK is the NACK before the STOP." It is the NACK before either terminator. The specification says a master-receiver sends one before a repeated START too, and a sequencer that hard-wires the STOP cannot implement the combined format that real register reads depend on.
13. Reason It Through
A capture shows S, 0x91, A, then four nine-clock groups, then P. The driver asked for three bytes. What happened and where will the symptom appear?
The master read four bytes — an off-by-one in the acknowledge policy, ACKing the third byte instead of NACKing it. The frame is well-formed and the transfer reports success, because the fourth byte was NACKed. The symptom appears in the next read, because the slave's auto-incrementing pointer advanced four instead of three, so the following transfer returns data shifted by one register. §11 is that bug end to end.
Why can the master's final NACK not be made "more forceful" if the slave is holding SDA low?
Because a NACK is the absence of an action. §4's figure shows the ninth slot of a NACKed byte having no driver at all — the slave released and the master is not pulling low, so the pull-up alone holds the line high. There is no stronger version of not driving. If the slave is pulling low the master has already done everything it can, which is why the consequence is a hang rather than a degraded signal.
A one-byte read generates zero master ACKs. Why is that the most valuable test in §7?
Because it is the only length at which the first byte is also the last, so it pins down both ends of the boundary expression (idx + 1) < req_len. A policy that is one byte too permissive reads two bytes; one that is too strict NACKs before any byte arrives. Every longer read has at least one ACK and one NACK and will pass a loosely-written check in either direction.
Why does this design need no equivalent of the write sequencer's WS_FETCH state?
Because the thing it computes one byte ahead — the acknowledge decision — is a pure function of the index, with no memory behind it. The write sequencer's payload came from a source whose output lagged its index by a cycle, so advancing the index and latching the data in the same cycle re-sent the previous byte. The difference is in the data source, not in the care taken, and knowing which situation applies is what stops you from either shipping the bug or adding a state you do not need.
The specification says the master NACKs before a repeated START as well as before a STOP. What does that tell you about what the NACK means?
That it is about the data flow, not the framing. It means "I want no further byte", and what the master does next — stop, or re-address for a different direction — is a separate decision. This is why req_stop is an input in §6 rather than a constant, and why the combined format is expressible on top of this block at all.
14. Understanding Check
15. Summary
A read differs from a write by one bit and reverses everything. Same framing, same nine-pulse bytes, same byte count. The direction bit is set, so the master becomes the receiver and the slave becomes the transmitter — and the specification puts that handover precisely "at the moment of the first acknowledge", which is why the address byte's acknowledge still belongs to the slave.
The acknowledge is flow control, not a verdict. An ACK means send another; a NACK means no more. Neither says anything about the quality of the byte, because the master asked for the data and has no way to complain about it. This is the exact inverse of a write and it is decided by a bit sent one or more bytes earlier.
The final NACK is the only way to end a read. No length field, no stop command, and a slave that will keep producing bytes as long as it is clocked. Get it wrong and the failure is not a failed transfer but a held bus, because a NACK is the absence of an action and there is no stronger version of not driving.
The NACK precedes both terminators. A STOP or a repeated START — the specification requires the not-acknowledge before either, which is why the terminator belongs in the transaction request rather than being hard-wired.
Check the boundary at length one. A one-byte read is the only case where the first byte is also the last, so it is the only test that pins down both ends of the acknowledge policy. Off-by-one in either direction survives every longer read.
16. What Comes Next
Chapter 9.2 takes the slave's side of this transfer, and the asymmetry it finds is sharper than the write's. Chapter 8.2's slave-receiver owned the ninth bit and could therefore refuse — two of the specification's five NACK conditions were its to produce. A slave-transmitter owns bits one through eight and nothing else, so it has no way to say no: no NACK, no error code, and no field in the frame to put one in. What a device does when a read runs past the end of its register file is therefore a design decision with no help from the protocol, and one of the available answers is actively harmful.
Chapter 9.3 takes the acknowledge policy across a burst and builds the instrument that checks it. A well-formed read of n bytes contains exactly n−1 ACKs and one NACK, always in the final slot — a pattern narrow enough that a passive monitor can verify it, and, more usefully, can predict the hang of §3 from the acknowledge pattern alone, before it happens.
Continue learning
Related tutorials
- Related topic
The I²C Write Transaction End to End
Seven modules built the pieces. This one runs a write from START to STOP as a single continuous story, counts every bit on the wire, and builds the transaction-level sequencer that issues it — in SystemVerilog, Verilog and VHDL.
- Related topic
The End-of-Transfer NACK
A master reading from a slave has no length field and no command for 'stop'. What it has is the acknowledge slot — so it ends a read by withholding one. The fifth NACK condition is not an error; it is the only way the conversation can be terminated.
- Related topic
Write-Then-Read — The I²C Register-Pointer Pattern
Almost every real I²C access is this one shape: write the register pointer, repeated START, read the data, without ever letting the bus go. This chapter derives it from the specification's own example and builds the transaction sequencer.
- 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.
