I²C · Module 7
Reading a Complete I²C Frame
Every chapter so far has worked on a fragment — one edge, one byte, one slot. This one assembles them: a method for reading a raw capture by eye, and a passive decoder that turns two wires into framing, address, direction, data and every acknowledge.
Seven modules have built this bus one mechanism at a time. Framing, addressing, the byte, the acknowledge — each derived on its own, each with its own hardware. This chapter puts them together, because the thing an engineer actually faces is never a fragment. It is a capture, or a monitor log, containing a whole transfer at once.
The deliverable here is twofold: a reading method that works on a screen with no tooling, and a decoder that does the same job in hardware and is assembled entirely from blocks already built.
1. The Frame Map
Every I²C frame has the same skeleton, and knowing its shape is most of the reading skill.
| position | what it is | who drives the 8 bits | who drives the 9th | established in |
|---|---|---|---|---|
| S | framing: START | master | — | 5.2 |
| byte 0 | address + R/W | master | addressed slave | 6.1 |
| byte 1..n | data | depends on R/W | the receiver | 7.3 |
| Sr | framing: repeated START — optional, may appear anywhere | master | — | 5.4 |
| P | framing: STOP | master | — | 5.3 |
Three structural facts follow from that table, and they are what make a capture readable.
The byte count is not in the frame. There is no length field anywhere. A frame ends when framing says it does, which means the only way to know how many bytes it contained is to count them.
A repeated START can appear between any two bytes, and when it does, the byte following it is an address again — not data. That is the single most important thing to spot when reading a capture, because mistaking a post-Sr address byte for data shifts the interpretation of everything after it.
The direction can change mid-frame, but only at an Sr. Within one addressing, the direction bit is fixed. Module 10 builds the combined transactions that exploit this.
2. Reading a Capture by Eye
Given a screen showing SDA and SCL and nothing else, the following order works and any other order costs time. It is the same discipline as Chapter 4.3's passes, applied to content rather than timing.
Pass 1 — find the framing first, and only the framing. Scan SDA for edges that occur while SCL is high. Ignore everything else on the first pass. Every such edge is a framing event: falling is S or Sr, rising is P. This partitions the whole capture into frames before any byte is decoded, and it is the pass people skip.
Pass 2 — count nine clocks from each S or Sr. The nine pulses after a framing fall are the address byte and its acknowledge. Mark the boundary. Then nine more, and nine more, until the next framing event.
Pass 3 — read the address byte and split it. Seven bits, then the direction bit. Shift right by one to get the seven-bit address the datasheet uses, and read bit 0 as the direction — 0 is a write, 1 is a read. Chapter 6.1 §2 is the conversion, and getting it backwards is the most common reading error.
Pass 4 — decide who owns each byte, then read the data. With the direction known, Chapter 7.3's table says who drove each byte and who answered it. This matters for interpretation: a byte the slave drove is device data, and a byte the master drove is a command or a register pointer.
Pass 5 — read every ninth bit. Not just the failures. The acknowledge pattern is diagnostic on its own, and §3 is a table of what each pattern means.
Pass 6 — check the ending. A read should end with a NACK on its final byte (Chapter 7.4); a write should end with every byte acknowledged. An ending that does not match the direction is the finding.
3. The Acknowledge Pattern Is a Diagnosis
Pass 5 deserves expanding, because the ninth bits taken together identify the failure faster than the payload does.
| pattern | reading | most likely cause |
|---|---|---|
| address NACKed, nothing follows | nobody answered | device absent, unpowered, or at another address — 6.4 |
| address ACKed, first data byte NACKed | present but declined the content | wrong register pointer, or the device is busy |
| address ACKed, byte k NACKed mid-write | it stopped accepting | buffer full, or it did not understand byte k |
| a write with every byte ACKed | structurally healthy | the frame is fine; a data problem is elsewhere |
| a read whose last byte is ACKed | the master forgot its NACK | 7.4 — expect the bus to hang next |
| a read whose last byte is NACKed | normal termination | correct, condition 5 — not an error |
| a read with an early NACK | short read | the master stopped before it meant to |
The fifth row is the one worth memorising, because it is a prediction: a read ending in an acknowledge means the slave was asked for another byte, so the next thing in the capture will be the slave transmitting and the bus failing to release. Spotting it in the acknowledge column tells you what you are about to see.
Address byte, read direction — the slave still answers
9 cycles4. What a Decoder Must Record, and What It Must Not Infer
Doing the same job in hardware raises a design question that is easy to get wrong: what should the output be?
The temptation is to publish a verdict — transfer succeeded or transfer failed. That is the wrong shape, for the reason Chapter 7.2 §3 established: one acknowledge bit has five possible meanings and the decoder cannot distinguish them. A decoder that collapses the frame to a boolean has thrown away the information needed to diagnose it.
So the decoder records observations, not conclusions:
| recorded | why it cannot be inferred later |
|---|---|
| the seven-bit address | needed to know which device the frame was for |
| the direction bit | determines who owned every subsequent byte |
| whether the address was acknowledged | separates "absent device" from "present but declined" |
| each data byte | the payload |
| each data byte's acknowledge | the pattern of §3 is the diagnosis |
| the count of data bytes | there is no length field; counting is the only source |
| that a STOP ended the frame | distinguishes a completed frame from an abandoned one |
| that a byte arrived with no framing | a capture that is not a frame at all |
And one deliberate non-behaviour: a STOP does not clear the decoded fields. A consumer reads them after the frame completes, so clearing on the STOP would make the decoder useless to everything downstream. §7 injects a fault that clears them and the testbench catches it.
5. The Decoder in Three Languages
It instantiates Chapter 7.1's byte shifter with tx_enable tied low and Chapter 7.2's acknowledge slot with we_acknowledge tied low — a passive observer built from two blocks that can both drive, simply by not asking them to. All it adds is the sequencing that says which byte is the address.
// A passive frame observer: it decodes a complete I2C frame off the two wires and
// never drives either of them. ASSEMBLED from the byte shifter and the acknowledge
// slot built earlier in this module -- it adds only the sequencing that says which
// byte is the address and which are data.
module i2c_frame_decoder (
input logic clk,
input logic rst_n,
input logic scl_in, // observed bus level
input logic sda_in, // observed bus level
input logic frame_start, // pulse: S or Sr observed (Module 5)
input logic frame_stop, // pulse: P observed (Module 5)
output logic [6:0] addr, // the seven address bits of this frame
output logic dir_is_read, // the latched R/W
output logic addr_acked, // did anybody answer the address?
output logic addr_valid, // pulse: the address phase is decoded
output logic [7:0] data_byte, // the most recent data byte
output logic data_acked, // was that byte acknowledged?
output logic data_valid, // pulse: a data byte is decoded
output logic [7:0] byte_count, // data bytes seen so far in this frame
output logic frame_complete,// pulse: a STOP ended the frame
output logic frame_error // a byte arrived with no framing START
);
// ---- the two blocks from earlier in this module ----
logic sh_rx_valid, sh_ack_slot, sh_byte_done;
logic [7:0] sh_rx_data;
logic [3:0] sh_bit_index;
logic sh_drive_low; // tied off: an observer never drives
logic begin_byte;
i2c_byte_shifter u_shift (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_in),
.begin_byte(begin_byte), .tx_enable(1'b0), .tx_data(8'h00),
.sda_drive_low(sh_drive_low), .rx_data(sh_rx_data), .rx_valid(sh_rx_valid),
.ack_slot(sh_ack_slot), .byte_done(sh_byte_done), .bit_index(sh_bit_index)
);
logic ak_drive_low; // tied off: an observer never drives
logic ak_bit, ak_received, ak_nack, ak_valid;
i2c_ack_slot u_ack (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_in),
.ack_slot(sh_ack_slot), .we_acknowledge(1'b0), .send_ack(1'b0),
.sda_drive_low(ak_drive_low), .ack_bit(ak_bit), .ack_received(ak_received),
.nack_received(ak_nack), .ack_valid(ak_valid)
);
// ---- the sequencing this block adds ----
typedef enum logic [1:0] {
FD_IDLE, // no frame in progress
FD_ADDR, // the next byte to complete is the address
FD_DATA // subsequent bytes are data
} state_e;
state_e state;
always_ff @(posedge clk) begin
if (!rst_n) begin
state <= FD_IDLE;
addr <= 7'h00;
dir_is_read <= 1'b0;
addr_acked <= 1'b0;
addr_valid <= 1'b0;
data_byte <= 8'h00;
data_acked <= 1'b0;
data_valid <= 1'b0;
byte_count <= 8'h00;
frame_complete <= 1'b0;
frame_error <= 1'b0;
begin_byte <= 1'b0;
end else begin
addr_valid <= 1'b0;
data_valid <= 1'b0;
frame_complete <= 1'b0;
begin_byte <= 1'b0;
if (frame_stop) begin
// A STOP ends the frame. Note it does NOT clear the decoded fields:
// a consumer reads them after the frame, so they must survive it.
if (state != FD_IDLE) frame_complete <= 1'b1;
state <= FD_IDLE;
end else if (frame_start) begin
// S or Sr both open an address phase, and a repeated START restarts
// the byte count -- the bytes after it belong to a new addressing.
state <= FD_ADDR;
byte_count <= 8'h00;
begin_byte <= 1'b1;
end else if (ak_valid) begin
// The ninth bit has been sampled, so the byte AND its answer are both
// known. Triggering here rather than on byte_done is what makes the
// acknowledge available in the same cycle as the data.
case (state)
FD_ADDR: begin
addr <= sh_rx_data[7:1];
dir_is_read <= sh_rx_data[0];
addr_acked <= ak_received;
addr_valid <= 1'b1;
state <= FD_DATA;
begin_byte <= 1'b1; // arm for the first data byte
end
FD_DATA: begin
data_byte <= sh_rx_data;
data_acked <= ak_received;
data_valid <= 1'b1;
byte_count <= byte_count + 8'd1;
// A NACKed byte must be followed by a STOP or a repeated
// START, so there is no point arming for another byte --
// but the bus decides that, not this observer, so it arms
// anyway and lets the framing event override.
begin_byte <= 1'b1;
end
default: begin
// A byte completed with no framing START before it. That is
// not a decodable frame, and reporting it is more useful than
// inventing an address for it.
frame_error <= 1'b1;
end
endcase
end
end
end
endmodule module i2c_frame_decoder_tb;
logic clk = 1'b0, rst_n, scl_in, frame_start, frame_stop;
logic [6:0] addr;
logic dir_is_read, addr_acked, addr_valid;
logic [7:0] data_byte;
logic data_acked, data_valid;
logic [7:0] byte_count;
logic frame_complete, frame_error;
int errors = 0;
// The testbench is the only driver: the decoder is a passive observer.
logic tb_drive_low = 1'b0;
wire sda_bus = ~tb_drive_low;
i2c_frame_decoder dut (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_bus),
.frame_start(frame_start), .frame_stop(frame_stop),
.addr(addr), .dir_is_read(dir_is_read), .addr_acked(addr_acked),
.addr_valid(addr_valid), .data_byte(data_byte), .data_acked(data_acked),
.data_valid(data_valid), .byte_count(byte_count),
.frame_complete(frame_complete), .frame_error(frame_error));
always #5 clk = ~clk;
initial begin #200000; $display("FAIL: watchdog expired"); $finish; end
int n_addr = 0, n_data = 0, n_frame = 0;
logic [7:0] data_log [0:7];
logic ack_log [0:7];
always @(posedge clk) if (rst_n) begin
if (addr_valid) n_addr++;
if (data_valid) begin
if (n_data < 8) begin data_log[n_data] = data_byte; ack_log[n_data] = data_acked; end
n_data++;
end
if (frame_complete) n_frame++;
end
// Drive one bit, changing SDA only while SCL is low.
task automatic send_bit(input logic b);
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = ~b; repeat (1) @(negedge clk);
scl_in = 1'b1; repeat (2) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
endtask
// A whole byte plus its acknowledge slot. `ack` is what the far end answers.
task automatic send_byte(input logic [7:0] v, input logic ack);
for (int i = 7; i >= 0; i--) send_bit(v[i]);
send_bit(~ack); // ACK is a LOW, so an ack of 1 drives a 0 bit
tb_drive_low = 1'b0; repeat (1) @(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
initial begin
rst_n = 1'b0; scl_in = 1'b1; frame_start = 1'b0; frame_stop = 1'b0;
repeat (3) @(negedge clk);
if (frame_error !== 1'b0) begin $display("FAIL: frame_error out of reset"); errors++; end
rst_n = 1'b1; @(negedge clk);
// ---- 1: a complete WRITE frame. Address 0x48 + W, two data bytes, all ACKed.
pulse_start();
send_byte({7'h48, 1'b0}, 1'b1); // address byte, acknowledged
if (n_addr != 1) begin $display("FAIL: address phase not decoded once"); errors++; end
if (addr !== 7'h48) begin $display("FAIL: decoded addr 0x%02h, expected 0x48", addr); errors++; end
if (dir_is_read !== 1'b0) begin $display("FAIL: direction should be write"); errors++; end
if (addr_acked !== 1'b1) begin $display("FAIL: address ACK not recorded"); errors++; end
send_byte(8'hDE, 1'b1);
send_byte(8'hAD, 1'b1);
if (n_data != 2) begin $display("FAIL: expected 2 data bytes, saw %0d", n_data); errors++; end
if (byte_count !== 8'd2) begin $display("FAIL: byte_count = %0d, expected 2", byte_count); errors++; end
if (data_log[0] !== 8'hDE || data_log[1] !== 8'hAD) begin
$display("FAIL: data bytes 0x%02h,0x%02h, expected 0xDE,0xAD", data_log[0], data_log[1]);
errors++; end
pulse_stop();
if (n_frame != 1) begin $display("FAIL: frame_complete not pulsed at the STOP"); errors++; end
// The decoded fields must SURVIVE the STOP -- a consumer reads them after.
if (addr !== 7'h48 || byte_count !== 8'd2) begin
$display("FAIL: decoded fields were cleared by the STOP"); errors++; end
// ---- 2: a READ frame, and the final byte NACKed -- Chapter 7.4's pattern.
n_data = 0;
pulse_start();
send_byte({7'h50, 1'b1}, 1'b1); // address + R, acknowledged by the slave
if (dir_is_read !== 1'b1) begin $display("FAIL: direction should be read"); errors++; end
send_byte(8'h11, 1'b1); // master ACKs -- another byte please
send_byte(8'h22, 1'b0); // master NACKs -- that was the last
if (n_data != 2) begin $display("FAIL: expected 2 read bytes, saw %0d", n_data); errors++; end
if (ack_log[0] !== 1'b1) begin $display("FAIL: first read byte should be ACKed"); errors++; end
if (ack_log[1] !== 1'b0) begin
$display("FAIL: the final read byte must be recorded as NACKed"); errors++; end
pulse_stop();
// ---- 3: an address that NOBODY answers. The frame is still well formed;
// the decoder must report the NACK rather than hiding it.
n_data = 0;
pulse_start();
send_byte({7'h7A, 1'b0}, 1'b0); // nothing at this address
if (addr_acked !== 1'b0) begin
$display("FAIL: an unanswered address must be recorded as NACKed"); errors++; end
if (addr !== 7'h7A) begin $display("FAIL: address still decodes on a NACK"); errors++; end
pulse_stop();
// ---- 4: a repeated START mid-frame restarts the addressing and the count.
n_data = 0;
pulse_start();
send_byte({7'h48, 1'b0}, 1'b1);
send_byte(8'h01, 1'b1);
if (byte_count !== 8'd1) begin $display("FAIL: byte_count should be 1 here"); errors++; end
pulse_start(); // Sr
send_byte({7'h48, 1'b1}, 1'b1); // re-addressed for a read
if (dir_is_read !== 1'b1) begin
$display("FAIL: the repeated START's direction was not latched"); errors++; end
if (byte_count !== 8'd0) begin
$display("FAIL: a repeated START must restart the data byte count, got %0d", byte_count);
errors++; end
send_byte(8'hFF, 1'b0);
pulse_stop();
// ---- 5: a byte with NO framing START is not a frame and must be reported.
if (frame_error !== 1'b0) begin $display("FAIL: spurious frame_error"); errors++; end
send_byte(8'h5A, 1'b1);
if (frame_error !== 1'b1) begin
$display("FAIL: a byte outside any frame was not reported"); errors++; end
if (errors == 0)
$display("PASS: address, direction, data and every acknowledge decoded; Sr restarts; stray byte reported");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule // A passive frame observer: it decodes a complete I2C frame off the two wires and
// never drives either of them. ASSEMBLED from the byte shifter and the acknowledge
// slot built earlier in this module -- it adds only the sequencing that says which
// byte is the address and which are data.
module i2c_frame_decoder (
input wire clk,
input wire rst_n,
input wire scl_in, // observed bus level
input wire sda_in, // observed bus level
input wire frame_start, // pulse: S or Sr observed (Module 5)
input wire frame_stop, // pulse: P observed (Module 5)
output reg [6:0] addr, // the seven address bits of this frame
output reg dir_is_read, // the latched R/W
output reg addr_acked, // did anybody answer the address?
output reg addr_valid, // pulse: the address phase is decoded
output reg [7:0] data_byte, // the most recent data byte
output reg data_acked, // was that byte acknowledged?
output reg data_valid, // pulse: a data byte is decoded
output reg [7:0] byte_count, // data bytes seen so far in this frame
output reg frame_complete,// pulse: a STOP ended the frame
output reg frame_error // a byte arrived with no framing START
);
// ---- the two blocks from earlier in this module ----
wire sh_rx_valid, sh_ack_slot, sh_byte_done;
wire [7:0] sh_rx_data;
wire [3:0] sh_bit_index;
wire sh_drive_low; // tied off: an observer never drives
reg begin_byte;
i2c_byte_shifter u_shift (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_in),
.begin_byte(begin_byte), .tx_enable(1'b0), .tx_data(8'h00),
.sda_drive_low(sh_drive_low), .rx_data(sh_rx_data), .rx_valid(sh_rx_valid),
.ack_slot(sh_ack_slot), .byte_done(sh_byte_done), .bit_index(sh_bit_index)
);
wire ak_drive_low; // tied off: an observer never drives
wire ak_bit, ak_received, ak_nack, ak_valid;
i2c_ack_slot u_ack (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_in),
.ack_slot(sh_ack_slot), .we_acknowledge(1'b0), .send_ack(1'b0),
.sda_drive_low(ak_drive_low), .ack_bit(ak_bit), .ack_received(ak_received),
.nack_received(ak_nack), .ack_valid(ak_valid)
);
// ---- the sequencing this block adds ----
localparam FD_IDLE = 2'd0; // no frame in progress
localparam FD_ADDR = 2'd1; // the next byte to complete is the address
localparam FD_DATA = 2'd2; // subsequent bytes are data
reg [1:0] state;
always @(posedge clk) begin
if (!rst_n) begin
state <= FD_IDLE;
addr <= 7'h00;
dir_is_read <= 1'b0;
addr_acked <= 1'b0;
addr_valid <= 1'b0;
data_byte <= 8'h00;
data_acked <= 1'b0;
data_valid <= 1'b0;
byte_count <= 8'h00;
frame_complete <= 1'b0;
frame_error <= 1'b0;
begin_byte <= 1'b0;
end else begin
addr_valid <= 1'b0;
data_valid <= 1'b0;
frame_complete <= 1'b0;
begin_byte <= 1'b0;
if (frame_stop) begin
// A STOP ends the frame. Note it does NOT clear the decoded fields:
// a consumer reads them after the frame, so they must survive it.
if (state != FD_IDLE) frame_complete <= 1'b1;
state <= FD_IDLE;
end else if (frame_start) begin
// S or Sr both open an address phase, and a repeated START restarts
// the byte count -- the bytes after it belong to a new addressing.
state <= FD_ADDR;
byte_count <= 8'h00;
begin_byte <= 1'b1;
end else if (ak_valid) begin
// The ninth bit has been sampled, so the byte AND its answer are both
// known. Triggering here rather than on byte_done is what makes the
// acknowledge available in the same cycle as the data.
case (state)
FD_ADDR: begin
addr <= sh_rx_data[7:1];
dir_is_read <= sh_rx_data[0];
addr_acked <= ak_received;
addr_valid <= 1'b1;
state <= FD_DATA;
begin_byte <= 1'b1; // arm for the first data byte
end
FD_DATA: begin
data_byte <= sh_rx_data;
data_acked <= ak_received;
data_valid <= 1'b1;
byte_count <= byte_count + 8'd1;
// A NACKed byte must be followed by a STOP or a repeated
// START, so there is no point arming for another byte --
// but the bus decides that, not this observer, so it arms
// anyway and lets the framing event override.
begin_byte <= 1'b1;
end
default: begin
// A byte completed with no framing START before it. That is
// not a decodable frame, and reporting it is more useful than
// inventing an address for it.
frame_error <= 1'b1;
end
endcase
end
end
end
endmodule module i2c_frame_decoder_tb;
reg clk, rst_n, scl_in, frame_start, frame_stop;
wire [6:0] addr;
wire dir_is_read, addr_acked, addr_valid;
wire [7:0] data_byte;
wire data_acked, data_valid;
wire [7:0] byte_count;
wire frame_complete, frame_error;
integer errors, n_addr, n_data, n_frame, i;
// The testbench is the only driver: the decoder is a passive observer.
reg tb_drive_low;
wire sda_bus = ~tb_drive_low;
i2c_frame_decoder dut (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_bus),
.frame_start(frame_start), .frame_stop(frame_stop),
.addr(addr), .dir_is_read(dir_is_read), .addr_acked(addr_acked),
.addr_valid(addr_valid), .data_byte(data_byte), .data_acked(data_acked),
.data_valid(data_valid), .byte_count(byte_count),
.frame_complete(frame_complete), .frame_error(frame_error));
initial clk = 1'b0;
always #5 clk = ~clk;
initial begin #200000; $display("FAIL: watchdog expired"); $finish; end
reg [7:0] data_log [0:7];
reg ack_log [0:7];
always @(posedge clk) if (rst_n) begin
if (addr_valid) n_addr = n_addr + 1;
if (data_valid) begin
if (n_data < 8) begin data_log[n_data] = data_byte; ack_log[n_data] = data_acked; end
n_data = n_data + 1;
end
if (frame_complete) n_frame = n_frame + 1;
end
// Drive one bit, changing SDA only while SCL is low.
task send_bit; input b; begin
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = ~b; repeat (1) @(negedge clk);
scl_in = 1'b1; repeat (2) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
end endtask
// A whole byte plus its acknowledge slot. `ack` is what the far end answers.
task send_byte; input [7:0] v; input ack; begin
for (i = 7; i >= 0; i = i - 1) send_bit(v[i]);
send_bit(~ack); // ACK is a LOW, so an ack of 1 drives a 0 bit
tb_drive_low = 1'b0; repeat (1) @(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
initial begin
errors = 0; n_addr = 0; n_data = 0; n_frame = 0; tb_drive_low = 1'b0;
rst_n = 1'b0; scl_in = 1'b1; frame_start = 1'b0; frame_stop = 1'b0;
repeat (3) @(negedge clk);
if (frame_error !== 1'b0) begin $display("FAIL: frame_error out of reset"); errors = errors + 1; end
rst_n = 1'b1; @(negedge clk);
// ---- 1: a complete WRITE frame. Address 0x48 + W, two data bytes, all ACKed.
pulse_start();
send_byte({7'h48, 1'b0}, 1'b1); // address byte, acknowledged
if (n_addr != 1) begin $display("FAIL: address phase not decoded once"); errors = errors + 1; end
if (addr !== 7'h48) begin $display("FAIL: decoded addr 0x%02h, expected 0x48", addr); errors = errors + 1; end
if (dir_is_read !== 1'b0) begin $display("FAIL: direction should be write"); errors = errors + 1; end
if (addr_acked !== 1'b1) begin $display("FAIL: address ACK not recorded"); errors = errors + 1; end
send_byte(8'hDE, 1'b1);
send_byte(8'hAD, 1'b1);
if (n_data != 2) begin $display("FAIL: expected 2 data bytes, saw %0d", n_data); errors = errors + 1; end
if (byte_count !== 8'd2) begin $display("FAIL: byte_count = %0d, expected 2", byte_count); errors = errors + 1; end
if (data_log[0] !== 8'hDE || data_log[1] !== 8'hAD) begin
$display("FAIL: data bytes 0x%02h,0x%02h, expected 0xDE,0xAD", data_log[0], data_log[1]);
errors = errors + 1; end
pulse_stop();
if (n_frame != 1) begin $display("FAIL: frame_complete not pulsed at the STOP"); errors = errors + 1; end
// The decoded fields must SURVIVE the STOP -- a consumer reads them after.
if (addr !== 7'h48 || byte_count !== 8'd2) begin
$display("FAIL: decoded fields were cleared by the STOP"); errors = errors + 1; end
// ---- 2: a READ frame, and the final byte NACKed -- Chapter 7.4's pattern.
n_data = 0;
pulse_start();
send_byte({7'h50, 1'b1}, 1'b1); // address + R, acknowledged by the slave
if (dir_is_read !== 1'b1) begin $display("FAIL: direction should be read"); errors = errors + 1; end
send_byte(8'h11, 1'b1); // master ACKs -- another byte please
send_byte(8'h22, 1'b0); // master NACKs -- that was the last
if (n_data != 2) begin $display("FAIL: expected 2 read bytes, saw %0d", n_data); errors = errors + 1; end
if (ack_log[0] !== 1'b1) begin $display("FAIL: first read byte should be ACKed"); errors = errors + 1; end
if (ack_log[1] !== 1'b0) begin
$display("FAIL: the final read byte must be recorded as NACKed"); errors = errors + 1; end
pulse_stop();
// ---- 3: an address that NOBODY answers. The frame is still well formed;
// the decoder must report the NACK rather than hiding it.
n_data = 0;
pulse_start();
send_byte({7'h7A, 1'b0}, 1'b0); // nothing at this address
if (addr_acked !== 1'b0) begin
$display("FAIL: an unanswered address must be recorded as NACKed"); errors = errors + 1; end
if (addr !== 7'h7A) begin $display("FAIL: address still decodes on a NACK"); errors = errors + 1; end
pulse_stop();
// ---- 4: a repeated START mid-frame restarts the addressing and the count.
n_data = 0;
pulse_start();
send_byte({7'h48, 1'b0}, 1'b1);
send_byte(8'h01, 1'b1);
if (byte_count !== 8'd1) begin $display("FAIL: byte_count should be 1 here"); errors = errors + 1; end
pulse_start(); // Sr
send_byte({7'h48, 1'b1}, 1'b1); // re-addressed for a read
if (dir_is_read !== 1'b1) begin
$display("FAIL: the repeated START's direction was not latched"); errors = errors + 1; end
if (byte_count !== 8'd0) begin
$display("FAIL: a repeated START must restart the data byte count, got %0d", byte_count);
errors = errors + 1; end
send_byte(8'hFF, 1'b0);
pulse_stop();
// ---- 5: a byte with NO framing START is not a frame and must be reported.
if (frame_error !== 1'b0) begin $display("FAIL: spurious frame_error"); errors = errors + 1; end
send_byte(8'h5A, 1'b1);
if (frame_error !== 1'b1) begin
$display("FAIL: a byte outside any frame was not reported"); errors = errors + 1; end
if (errors == 0)
$display("PASS: address, direction, data and every acknowledge decoded; Sr restarts; stray byte reported");
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 frame observer: it decodes a complete I2C frame off the two wires and
-- never drives either of them. ASSEMBLED from the byte shifter and the acknowledge
-- slot built earlier in this module -- it adds only the sequencing that says which
-- byte is the address and which are data.
entity i2c_frame_decoder is
port (
clk : in std_logic;
rst_n : in std_logic;
scl_in : in std_logic; -- observed bus level
sda_in : in std_logic; -- observed bus level
frame_start : in std_logic; -- pulse: S or Sr (Module 5)
frame_stop : in std_logic; -- pulse: P (Module 5)
addr : out std_logic_vector(6 downto 0);
dir_is_read : out std_logic;
addr_acked : out std_logic;
addr_valid : out std_logic; -- pulse: address decoded
data_byte : out std_logic_vector(7 downto 0);
data_acked : out std_logic;
data_valid : out std_logic; -- pulse: a data byte decoded
byte_count : out unsigned(7 downto 0);
frame_complete : out std_logic; -- pulse: a STOP ended it
frame_error : out std_logic -- byte with no framing START
);
end entity;
architecture rtl of i2c_frame_decoder is
signal sh_rx_data : std_logic_vector(7 downto 0);
signal sh_rx_valid, sh_ack_slot, sh_byte_done, sh_drive_low : std_logic;
signal sh_bit_index : natural range 0 to 8;
signal begin_byte : std_logic := '0';
signal ak_drive_low, ak_bit, ak_received, ak_nack, ak_valid : std_logic;
type state_t is (
FD_IDLE, -- no frame in progress
FD_ADDR, -- the next byte to complete is the address
FD_DATA -- subsequent bytes are data
);
signal state : state_t := FD_IDLE;
signal count : unsigned(7 downto 0) := (others => '0');
begin
u_shift : entity work.i2c_byte_shifter
port map (clk => clk, rst_n => rst_n, scl_in => scl_in, sda_in => sda_in,
begin_byte => begin_byte, tx_enable => '0', tx_data => x"00",
sda_drive_low => sh_drive_low, rx_data => sh_rx_data,
rx_valid => sh_rx_valid, ack_slot => sh_ack_slot,
byte_done => sh_byte_done, bit_index => sh_bit_index);
u_ack : entity work.i2c_ack_slot
port map (clk => clk, rst_n => rst_n, scl_in => scl_in, sda_in => sda_in,
ack_slot => sh_ack_slot, we_acknowledge => '0', send_ack => '0',
sda_drive_low => ak_drive_low, ack_bit => ak_bit,
ack_received => ak_received, nack_received => ak_nack,
ack_valid => ak_valid);
byte_count <= count;
process (clk)
begin
if rising_edge(clk) then
if rst_n = '0' then
state <= FD_IDLE;
addr <= (others => '0');
dir_is_read <= '0';
addr_acked <= '0';
addr_valid <= '0';
data_byte <= (others => '0');
data_acked <= '0';
data_valid <= '0';
count <= (others => '0');
frame_complete <= '0';
frame_error <= '0';
begin_byte <= '0';
else
addr_valid <= '0';
data_valid <= '0';
frame_complete <= '0';
begin_byte <= '0';
if frame_stop = '1' then
-- A STOP ends the frame. It does NOT clear the decoded fields:
-- a consumer reads them after the frame, so they must survive it.
if state /= FD_IDLE then frame_complete <= '1'; end if;
state <= FD_IDLE;
elsif frame_start = '1' then
-- S or Sr both open an address phase, and a repeated START
-- restarts the byte count.
state <= FD_ADDR;
count <= (others => '0');
begin_byte <= '1';
elsif ak_valid = '1' then
-- The ninth bit has been sampled, so the byte AND its answer are
-- both known. Triggering here rather than on byte_done is what
-- makes the acknowledge available in the same cycle as the data.
case state is
when FD_ADDR =>
addr <= sh_rx_data(7 downto 1);
dir_is_read <= sh_rx_data(0);
addr_acked <= ak_received;
addr_valid <= '1';
state <= FD_DATA;
begin_byte <= '1'; -- arm for the first data byte
when FD_DATA =>
data_byte <= sh_rx_data;
data_acked <= ak_received;
data_valid <= '1';
count <= count + 1;
begin_byte <= '1';
when others =>
-- A byte completed with no framing START before it. That
-- is not a decodable frame, and reporting it is more
-- useful than inventing an address for it.
frame_error <= '1';
end case;
end if;
end if;
end if;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_frame_decoder_tb is
end entity;
architecture sim of i2c_frame_decoder_tb is
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 addr : std_logic_vector(6 downto 0);
signal dir_is_read, addr_acked, addr_valid : std_logic;
signal data_byte : std_logic_vector(7 downto 0);
signal data_acked, data_valid : std_logic;
signal byte_count : unsigned(7 downto 0);
signal frame_complete, frame_error : std_logic;
-- The testbench is the only driver: the decoder is a passive observer.
signal tb_drive_low : std_logic := '0';
signal sda_bus : std_logic;
signal n_addr, n_data, n_frame : natural := 0;
type byte_log_t is array (0 to 7) of std_logic_vector(7 downto 0);
type ack_log_t is array (0 to 7) of std_logic;
signal data_log : byte_log_t := (others => (others => '0'));
signal ack_log : ack_log_t := (others => '0');
signal test_done : std_logic := '0';
begin
sda_bus <= not tb_drive_low;
dut : entity work.i2c_frame_decoder
port map (clk => clk, rst_n => rst_n, scl_in => scl_in, sda_in => sda_bus,
frame_start => frame_start, frame_stop => frame_stop,
addr => addr, dir_is_read => dir_is_read, addr_acked => addr_acked,
addr_valid => addr_valid, data_byte => data_byte,
data_acked => data_acked, data_valid => data_valid,
byte_count => byte_count, frame_complete => frame_complete,
frame_error => frame_error);
clk <= not clk after 5 ns;
watchdog : process
begin
wait for 200 us;
if test_done = '0' then
report "watchdog expired -- the design never reached the expected state"
severity failure;
end if;
wait;
end process;
count : process (clk)
begin
if rising_edge(clk) then
if rst_n = '1' then
if addr_valid = '1' then n_addr <= n_addr + 1; end if;
if data_valid = '1' then
if n_data < 8 then
data_log(n_data) <= data_byte;
ack_log(n_data) <= data_acked;
end if;
n_data <= n_data + 1;
end if;
if frame_complete = '1' then n_frame <= n_frame + 1; end if;
end if;
end if;
end process;
stim : process
variable errs : natural := 0;
variable base : natural := 0;
procedure waitn (n : in positive) is
begin
for i in 1 to n loop wait until falling_edge(clk); end loop;
end procedure;
procedure send_bit (b : in std_logic) is
begin
scl_in <= '0'; waitn(1);
tb_drive_low <= not b; waitn(1); -- change only while SCL is low
scl_in <= '1'; waitn(2);
scl_in <= '0'; waitn(1);
end procedure;
-- A whole byte plus its acknowledge slot. `ack` is what the far end answers;
-- ACK is a LOW, so an ack of '1' drives a '0' bit.
procedure send_byte (v : in std_logic_vector(7 downto 0); ack : in std_logic) is
begin
for i in 7 downto 0 loop send_bit(v(i)); end loop;
send_bit(not ack);
tb_drive_low <= '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;
begin
waitn(3);
if frame_error /= '0' then
report "frame_error out of reset" severity error; errs := errs + 1; end if;
rst_n <= '1'; waitn(1);
-- 1: a complete WRITE frame. Address 0x48 + W, two data bytes, all ACKed.
pulse_start;
send_byte("10010000", '1'); -- 0x48 shifted left, write
if n_addr /= 1 then
report "address phase not decoded once" severity error; errs := errs + 1; end if;
if addr /= "1001000" then
report "decoded address wrong" severity error; errs := errs + 1; end if;
if dir_is_read /= '0' then
report "direction should be write" severity error; errs := errs + 1; end if;
if addr_acked /= '1' then
report "address ACK not recorded" severity error; errs := errs + 1; end if;
send_byte(x"DE", '1');
send_byte(x"AD", '1');
if n_data /= 2 then
report "expected 2 data bytes" severity error; errs := errs + 1; end if;
if byte_count /= 2 then
report "byte_count wrong" severity error; errs := errs + 1; end if;
if data_log(0) /= x"DE" or data_log(1) /= x"AD" then
report "data bytes wrong" severity error; errs := errs + 1; end if;
pulse_stop;
if n_frame /= 1 then
report "frame_complete not pulsed at the STOP" severity error; errs := errs + 1; end if;
-- The decoded fields must SURVIVE the STOP.
if addr /= "1001000" or byte_count /= 2 then
report "decoded fields were cleared by the STOP" severity error; errs := errs + 1; end if;
-- 2: a READ frame with the final byte NACKed -- Chapter 7.4's pattern.
base := n_data;
pulse_start;
send_byte("10100001", '1'); -- 0x50 + R
if dir_is_read /= '1' then
report "direction should be read" severity error; errs := errs + 1; end if;
send_byte(x"11", '1'); -- ACK: another byte please
send_byte(x"22", '0'); -- NACK: that was the last
if (n_data - base) /= 2 then
report "expected 2 read bytes" severity error; errs := errs + 1; end if;
if ack_log(2) /= '1' then
report "first read byte should be ACKed" severity error; errs := errs + 1; end if;
if ack_log(3) /= '0' then
report "the final read byte must be recorded as NACKed" severity error;
errs := errs + 1; end if;
pulse_stop;
-- 3: an address NOBODY answers -- report the NACK rather than hide it.
pulse_start;
send_byte("11110100", '0'); -- 0x7A + W, unanswered
if addr_acked /= '0' then
report "an unanswered address must be recorded as NACKed" severity error;
errs := errs + 1; end if;
if addr /= "1111010" then
report "address must still decode on a NACK" severity error; errs := errs + 1; end if;
pulse_stop;
-- 4: a repeated START restarts the addressing and the data byte count.
pulse_start;
send_byte("10010000", '1');
send_byte(x"01", '1');
if byte_count /= 1 then
report "byte_count should be 1 here" severity error; errs := errs + 1; end if;
pulse_start; -- Sr
send_byte("10010001", '1'); -- re-addressed for a read
if dir_is_read /= '1' then
report "the repeated START's direction was not latched" severity error;
errs := errs + 1; end if;
if byte_count /= 0 then
report "a repeated START must restart the data byte count" severity error;
errs := errs + 1; end if;
send_byte(x"FF", '0');
pulse_stop;
-- 5: a byte with NO framing START is not a frame and must be reported.
if frame_error /= '0' then
report "spurious frame_error" severity error; errs := errs + 1; end if;
send_byte(x"5A", '1');
if frame_error /= '1' then
report "a byte outside any frame was not reported" severity error;
errs := errs + 1; end if;
if errs = 0 then
report "i2c_frame_decoder self-check complete: address, direction, data and every "
& "acknowledge decoded; Sr restarts; stray byte reported" severity note;
else
report "i2c_frame_decoder self-check FAILED" severity error;
end if;
test_done <= '1';
wait;
end process;
end architecture;5a. Three Decisions Worth Defending
It triggers on ack_valid, not on byte_done. Both pulse on the ninth rising edge, so the choice looks arbitrary — and §7's mutation confirms they are behaviourally identical. It is still the right choice, because ack_valid names the reason: the byte and its answer are both known at that instant. A reader of the code learns why the trigger is there, which byte_done would not convey. This is a readability decision made deliberately in the knowledge that no test can enforce it.
It re-arms the shifter itself. begin_byte is generated by the decoder on a framing START and after every completed byte, so the shifter never needs to know what a frame is. That keeps the byte-level block free of frame-level concepts and is why it was reusable here at all.
A framing START restarts the byte count. After a repeated START the bytes belong to a new addressing, so counting them with the previous phase's total would misreport both. §7 injects the fault and the testbench catches it with a mid-frame Sr.
5b. Verified Execution
Each language's testbench was compiled with the decoder and both sub-blocks:
| language | files together | simulator | result | completes at |
|---|---|---|---|---|
| SystemVerilog | decoder + shifter + ack slot + TB | Icarus Verilog, -g2012 | PASS | 5740 ns |
| Verilog-2005 | decoder + shifter + ack slot + TB | Icarus Verilog, -g2005 | PASS | 5740 ns |
| VHDL | all three analysed, then elaborated | nvc 1.23.0 | PASS | 5740 ns |
| SystemVerilog | Verilog-2005 | VHDL | |
|---|---|---|---|
| states | typedef enum logic [1:0] | localparam constants | type state_t is (...) |
| instantiation | named ports | named ports | entity work.<name> direct |
| tie-offs | 1'b0 / 8'h00 in the port map | same | '0' / x"00" in the port map |
| byte count | logic [7:0] | reg [7:0] | internal unsigned, driven to the port |
i2c_frame_decoder — the address phase resolves
10 cyclesi2c_frame_decoder — a data byte and its answer, together
10 cycles6. What the Testbench Proves
Five frames, chosen so that each exercises a different structural case rather than a different payload.
| # | frame | what only it establishes |
|---|---|---|
| 1 | write to 0x48, two bytes, all ACKed | the ordinary case, the byte count, and that the fields survive the STOP |
| 2 | read from 0x50, two bytes, final one NACKed | the direction latches, and 7.4's pattern is recorded per byte |
| 3 | address 0x7A, nobody answers | the address still decodes on a NACK — the frame is well formed, the device is absent |
| 4 | write, one byte, Sr, re-address for read | a repeated START restarts the count and re-latches the direction |
| 5 | a byte with no framing START | reported as frame_error rather than decoded into an invented frame |
Frame 3 is the one that separates a decoder from a verdict machine. The address is decoded, the direction is decoded, and addr_acked is low. All three are true and useful — a decoder that reported only "failed" would have discarded the address, which is the field you need to know which device was absent.
Frame 4 is the case §1 called the most important thing to spot in a capture. After the Sr the byte count returns to zero and the direction is re-read from the new address byte. A decoder that carried the count across would report one frame of three bytes where there were two phases of one byte each.
Frame 5 is a decoder refusing to guess. A byte with no preceding framing is not a frame, and inventing an address for it would produce a plausible-looking wrong answer — which is worse than an error flag.
7. Mutation Testing
Five faults injected into the verified RTL. Four caught; one is provably equivalent.
| mutation | what it breaks | result |
|---|---|---|
| treat the address byte as data | every frame is shifted by one byte | FAIL — address never decoded |
| a repeated START does not restart the count | two phases reported as one | FAIL — count wrong after the Sr |
| a STOP clears the decoded fields | the consumer reads zeros | FAIL — fields cleared by the STOP |
| a stray byte is not reported | a non-frame decoded as a frame | FAIL — no frame_error |
trigger on byte_done instead of ack_valid | — | survived, provably equivalent |
The survivor is equivalent by construction, and the proof is two lines of RTL. In the byte shifter, byte_done is set on the ninth scl_rise. In the acknowledge slot, ack_valid is set on the same scl_rise, because ack_slot is combinational and high throughout the ninth slot. Both are registered at the same clock edge, and ack_bit is latched at that same edge — so the acknowledge is readable in the cycle either trigger fires. No stimulus can separate them.
The ack_valid form is still the better code, for the reason in §5a: it names why the trigger exists. But it is a readability choice, and being honest about that is the difference between reporting four of five caught with one equivalence proven, and quietly claiming five.
Module 7 totals: 27 mutations injected, 26 caught, 1 proven equivalent. Two of the 26 required new tests before they could be caught — Chapter 7.1's ninth-slot release, which needed a byte ending in zero, and Chapter 7.2's sampling edge, which needed the far end to release exactly as SCL falls. Both are the same shape: a fault that is only observable under stimulus that makes its condition bite.
8. Verification Connection — The Frame Is the Transaction
Everything in this module has been building toward the object a protocol monitor publishes.
// The frame is the unit a scoreboard reasons about. Note what is stored: every
// acknowledge, not a summary -- because Chapter 7.2 section 3 established that
// one bit has five meanings and only POSITION narrows them.
class i2c_frame extends uvm_sequence_item;
`uvm_object_utils(i2c_frame)
bit [6:0] addr;
bit is_read;
bit addr_acked;
bit [7:0] data [$]; // the payload, in order
bit data_acked [$]; // one answer per byte, same indices
bit ended_with_stop; // false if an Sr continued the transaction
bit malformed; // a byte arrived outside any framing
// A frame is well formed if its ENDING matches its direction. This is the
// check that catches Chapter 7.4's hang, and it cannot be expressed as
// "no unexpected NACKs occurred".
function bit ending_is_legal();
if (data_acked.size() == 0) return 1; // address-only frame
if (is_read) return (data_acked[$] == 0); // a read MUST end NACKed
else return 1; // a write may end either way
endfunction
function string convert2string();
string s = $sformatf("%s 0x%02h %s |", is_read ? "RD" : "WR",
addr, addr_acked ? "A" : "N");
foreach (data[i]) s = {s, $sformatf(" %02h%s", data[i], data_acked[i] ? "A" : "N")};
return {s, ended_with_stop ? " P" : " Sr"};
endfunction
endclassThree observations, and the last is the practical one.
Per-byte acknowledges are stored, not summarised. A boolean "all acknowledged" destroys §3's diagnostic table. Storing the vector costs nothing and is what lets a scoreboard say which byte declined.
ending_is_legal is asymmetric on purpose. A read must end with a NACK; a write may legitimately end either way, because a write's final byte is acknowledged by the slave and a NACK there is condition 3 or 4 rather than a termination. Encoding that asymmetry is what makes the check usable without false positives.
convert2string is the highest-value line in the class. A log reading RD 0x50 A | 11A 22N P is a complete frame in eleven characters — address, direction, every acknowledge, and the ending. An engineer scanning a thousand of those spots the anomalous one instantly, which no amount of structured reporting achieves as quickly.
9. FPGA and ASIC Implications
A passive decoder is the cheapest debug instrument available. The whole thing is the byte shifter, the acknowledge slot and a three-state machine — under a hundred flip-flops. Instantiating one alongside a controller, with its outputs wired to a small FIFO, gives a bring-up engineer the frame log of §8 without an oscilloscope. It is the single highest-return piece of debug hardware on an I²C interface and it is routinely omitted.
Tie-offs are how a bidirectional block becomes an observer. The shifter and the acknowledge slot are both capable of driving; wiring tx_enable and we_acknowledge to zero makes them incapable of it. That is a stronger guarantee than trusting a state machine never to assert them, and it is worth doing deliberately rather than by omission — a reviewer can see a tie-off.
The decoder needs the same synchronised inputs as everything else. It samples SDA and SCL, so Module 19's synchroniser sits in front of it. A monitor with its own unsynchronised sampling path can disagree with the controller about what the bus did, which is the worst possible property in a debug instrument.
A monitor enabled mid-transfer has no valid state, exactly as Chapter 5.4 §9 and Chapter 6.5 established for their state. The decoder's frame_error output is the honest response: a byte with no preceding framing is reported rather than decoded. The clean recovery is to treat the next STOP as a resynchronisation point.
Expose the byte count and the last acknowledge to software. A driver that can read how far a frame got, and whether the last byte was answered, can distinguish a NACK on the address from a NACK on byte twelve — which is the difference between "the device is absent" and "the device stopped accepting", and those lead to different recovery actions.
10. Debugging — The Capture That Read Backwards
A register dump that made sense only if the sensor had two register maps
Pitfall — missing a repeated START and reading an address byte as data
// An engineer captures a failing sensor access and decodes it by hand from the
// waveform. The transfer on the wire is a standard register-pointer read:
//
// S 0x90 A 0x00 A Sr 0x91 A 0x1C A 0x2F N P
// ^^^^ ^^^^ ^^^^^^^ ^^^^ ^^^^
// addr+W pointer addr+R data data
//
// The hand decode, done by counting nine-clock groups from the START and reading
// them off in order, produces:
//
// addr 0x48 W, data = { 0x00, 0x91, 0x1C, 0x2F }
// ^^^^ ^^^^
// pointer, then... 0x91 as DATA
//
// The repeated START was not spotted -- it is a single SDA falling edge in the
// middle of what looks like a continuous stream of clock pulses, and the counting
// method never looked for it.The decode is internally consistent and completely wrong.
It says the master wrote four bytes to register 0x00 of device 0x48. The engineer checks the datasheet: register 0x00 is a read-only ID register, and writing four bytes to it is meaningless. So the conclusion is that the DRIVER is writing garbage to a read-only register, and the investigation moves to the driver.
The driver's source clearly performs a register-pointer read -- write the pointer, repeated START, read two bytes. It does not write four bytes to anything. So now the driver and the capture disagree, and the natural assumption is that the peripheral hardware is malfunctioning: generating spurious bytes, or losing the repeated START.
Considerable time goes into the controller's Sr generation, which is working perfectly. The value 0x91 appearing in the middle of the data is the clue that resolves it, and it is sitting in plain sight: 0x91 is 0x48 shifted left with the read bit set -- it is the device's own address, and an address byte has no business being in a data stream.
A repeated START was missed, so one address byte was decoded as data and everything after it shifted.
Section 1 states it as the most important thing to spot in a capture: an Sr can appear between any two bytes, and the byte following it is an ADDRESS, not data. Section 2's pass 1 exists precisely to find these before any byte is counted -- scan for SDA edges during SCL high FIRST, and only then count nine-clock groups within each resulting segment.
The counting method used here inverted that order. It found the first START, counted groups of nine from there, and never re-examined SDA for framing. A repeated START is exactly one falling SDA edge during one high phase, visually indistinguishable at low zoom from a data bit going low -- so a scan that is looking for byte boundaries rather than for framing will pass straight over it.
Note that every individual byte was decoded CORRECTLY. The bits were read right, the nine-clock grouping was right, and the acknowledges were right. The single error was in the frame STRUCTURE, and a structural error relabels everything downstream without corrupting anything -- which is why the result was internally consistent and why it survived scrutiny.
And note the confirming detail that was available immediately: the acknowledge pattern. The decode claimed a WRITE whose last byte was NACKed, which section 3's table lists as "it stopped accepting". But the real frame was a READ, and a read's final NACK is normal termination. A pass-5 reading of the acknowledge column would have flagged the inconsistency between the claimed direction and the ending before anybody opened the datasheet.
// Decode in the order of section 2, and let the tooling do pass 1:
//
// PASS 1 first, always. Scan for SDA edges while SCL is HIGH. Mark every one.
// Falling = S or Sr. Rising = P. Do this before counting a single byte.
// THEN count nine-clock groups WITHIN each segment between framing events.
//
// The frame decoder of section 5 does exactly this -- frame_start is an input that
// overrides everything, so a repeated START restarts the address phase and the byte
// count unconditionally. Section 7 injects the fault of NOT restarting the count and
// the testbench catches it with a mid-frame Sr, which is this bug in RTL form.
//
// The lessons, ordered by how far they generalise.
//
// 1. STRUCTURE BEFORE CONTENT. A structural misreading produces a self-consistent
// wrong answer, which is far more expensive than a corrupted one because
// nothing looks wrong. Find the frame boundaries before decoding what is
// inside them.
//
// 2. AN ADDRESS BYTE IN A DATA STREAM IS A MISSED Sr. If a decoded data byte
// equals the device's own address shifted left -- 0x90 or 0x91 for a device at
// 0x48 -- you have almost certainly missed a repeated START. This is a
// two-second check and it is the single most useful sanity test on a hand
// decode.
//
// 3. CHECK THE ENDING AGAINST THE DIRECTION. Section 3's table: a read must end
// with a NACK, a write normally does not. A decode claiming a write that ends
// in a NACK, or a read that ends in an ACK, is either a real bug or a
// misreading -- and in both cases it is the next thing to investigate.
//
// 4. USE A DECODER RATHER THAN COUNTING BY HAND. Section 9's point: a passive
// frame decoder is under a hundred flip-flops and it cannot miss a framing
// event, because framing is its first input rather than something it infers
// from byte boundaries. Hand decoding is a skill worth having for when there is
// no instrument, not a method to prefer when there is one.
//
// The habit: before believing any hand decode, count the framing events and check
// that the number of address bytes equals the number of S and Sr events. This decode
// had two framing falls and claimed one address byte, and that arithmetic alone
// would have found it.11. Common Misconceptions
"A frame contains a length field." It contains no length anywhere. A frame ends when framing ends it, and the byte count exists only if somebody counted.
"You can find byte boundaries by counting nine clocks from the start." Only within one segment. A repeated START anywhere resets the structure, and a count that ignores framing shifts everything after it — §10 is that failure.
"A repeated START is visually obvious." It is one SDA falling edge during one high phase, and at low zoom it looks like a data bit. Pass 1 exists because it is not obvious.
"After a repeated START the next byte continues the data." It is an address byte. That single fact is what §10's decode got wrong.
"A decoder should report whether the transfer succeeded." One acknowledge bit has five meanings, so a verdict discards the information needed to choose between them. Record every acknowledge and let the consumer diagnose.
"A NACKed address means there is nothing to decode." The address and direction are still there and still useful — knowing which device did not answer is the whole point.
"The decoded frame can be cleared at the STOP." A consumer reads it after the frame completes. Clearing on the STOP makes the decoder useless, and §7 injects exactly that fault.
"A read ending in an acknowledge is a minor anomaly." It is a prediction that the bus is about to hang, because the slave has been asked for another byte and will hold SDA. §3's fifth row.
12. Reason It Through
A capture shows two SDA falling edges during high phases and one rising edge. How many address bytes should your decode contain?
Two. Each framing fall — one START and one repeated START — begins an address phase, so there are two address bytes and one STOP. A decode claiming a single address byte has missed a framing event, and that arithmetic is the fastest check on a hand decode.
You decode a data byte as 0x91 in a transfer with a device at 0x48. What should you suspect immediately?
A missed repeated START. 0x91 is 0x48 shifted left with the read bit set — it is the device's own address, and an address byte appearing in a data stream almost always means a framing event was not spotted, so everything after it has been relabelled.
Your decode says a write whose final byte was NACKed. Why is that worth a second look?
Because a NACK on a write's last byte is condition 3 or 4 — the device did not understand it or could not take it — which is a real anomaly. But it is also exactly what a read looks like if you got the direction bit backwards, since a read's final NACK is normal termination. So it is either a device problem or a decoding error, and checking the direction bit again costs nothing.
Why does the frame decoder keep its decoded fields after the STOP?
Because a consumer reads them once the frame is complete — that is when a monitor publishes a transaction and when software services an interrupt. Clearing on the STOP would mean the outputs are only valid during a window that has already closed by the time anything looks at them.
A monitor is enabled in the middle of a transfer. What should it do with the first byte it completes?
Report it as an error rather than decode it, because it has no preceding framing and therefore no way to know whether it is an address or data. Inventing a frame would produce a plausible wrong answer. The clean recovery is to treat the next STOP as a resynchronisation point and decode confidently from then on.
Why is it worth instantiating a passive frame decoder in a design that already has a working controller?
Because it cannot miss a framing event — framing is its input rather than something it infers — and it produces the frame log of §8 without an oscilloscope. It costs under a hundred flip-flops, and it turns the class of bug in §10 from an afternoon of hand decoding into a line of log output.
13. Understanding Check
14. Summary
Every frame has the same skeleton: START, address plus direction, data bytes each with an acknowledge, optional repeated STARTs each beginning a new address phase, and a STOP. There is no length field.
Read a capture structure-first. Find the framing edges before counting bytes; a count that ignores framing relabels everything after a missed repeated START — and the result is self-consistent and wrong.
After a repeated START the next byte is an address. An address byte appearing in a decoded data stream is the signature of a missed one.
The acknowledge column is a diagnosis. Which byte NACKed, and whether the ending matches the direction, identifies the failure faster than the payload does — and a read ending in an acknowledge predicts a hung bus.
A decoder records observations, not verdicts. Every acknowledge, the byte count, the address even when it was not answered, and whether framing ended the frame. One bit with five meanings cannot be collapsed to a boolean.
The decoded frame must survive the STOP, because that is when anything reads it.
The decoder is assembled from two bidirectional blocks made passive by tie-offs — a stronger and more visible guarantee than logic that merely never drives.
Module 7 totals: 27 mutations, 26 caught, 1 proven equivalent — and two of the 26 needed new tests first, both of the same shape: a fault observable only under stimulus that makes its condition bite.
15. What Comes Next
Module 7 is complete. The bus now has a fully accounted-for byte: nine clock pulses, MSB first, an acknowledge whose owner is determined by direction, a defined way for a master-receiver to end a read, and a decoder that reconstructs the whole frame from the two wires.
What has been assembled in fragments is a transaction. Module 8 — Write Transactions takes the simpler direction first and walks a complete write end to end as one continuous story — START, address plus W, data, acknowledge, STOP — with every bit accounted for, then splits the master-transmitter and slave-receiver responsibilities and computes what a multi-byte burst really costs once the ninth clock is counted.
Browse the full path on the I²C tutorials index. For the blocks this chapter assembles, see Byte and Bit Transmission and The ACK/NACK Cycle; for the framing that gives a frame its boundaries, Bus Idle and the Framing Primitives.
Continue learning
Related tutorials
- Related topic
Address Decoding Inside a Slave
The first piece of slave hardware in the curriculum, and it is assembled rather than written: byte capture, reserved-map classification, one equality test, and a decision whose ordering matters — because a prohibition has to beat an address match.
- 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.
- Related topic
START/STOP Timing and Malformed Framing
Three framing margins, each with two anchor events, all of them minimums: the hold after a START, the setup before a repeated START, and the setup before a STOP. Build a sequencer that generates all three and refuses an illegal configuration, then catalogue the malformed framing the margins exist to prevent.
- Related topic
The Address Byte — Seven Address Bits and the R/W Bit
The first byte after a START is not an address followed by a direction bit. It is one eight-bit field that the bus, the slave and the datasheet all treat as a unit — and treating it as two things is the single most common source of I²C address confusion.
