I²C · Module 7
I²C Byte and Bit Transmission — MSB First, Nine Clocks
Every byte on this bus costs nine clock pulses, not eight. Eight carry data most-significant-bit first, and the ninth belongs to somebody else — which makes the transmitter's most important job in that slot to stop driving.
Module 6 produced an ack_request in five different chapters and never once said what an acknowledge is. This module pays that debt, and it starts one level below the acknowledge — with the thing an acknowledge is attached to.
UM10204 §3.1.5 is four sentences, and three of them are this chapter:
Every byte put on the SDA line must be eight bits long. The number of bytes that can be transmitted per transfer is unrestricted. Each byte must be followed by an Acknowledge bit. Data is transferred with the Most Significant Bit (MSB) first.
1. Nine Clocks, Not Eight
The first consequence is arithmetic and it shapes everything downstream: a byte occupies nine clock pulses.
Eight of them carry data. The ninth carries the acknowledge, and the specification is explicit about who generates it — "The master generates all clock pulses, including the acknowledge ninth clock pulse." So the acknowledge is not an extra handshake bolted onto the side of a byte; it is a slot in the byte's own clock train, indistinguishable from a data bit as far as the clock generator of Chapter 4.1 is concerned.
clock pulses per byte 9
pulses carrying data 8
data efficiency = 8 / 9 = 88.9%
protocol overhead = 1 / 9 = 11.1% of every byte, forever
For a 100 kHz Standard-mode bus, ignoring framing and bus-free time:
bit slots per second = 100,000
BYTES per second = 100,000 / 9 = 11,111
DATA BYTES per second = 11,111 (the ninth slot carries no payload)
so the payload rate = 88.9 kbit/s on a "100 kbit/s" bus
And that is the OPTIMISTIC figure: it excludes the address byte, the framing
events, and the bus-free interval between transfers. Module 8 computes a real
burst rate; the point here is that the 11.1% is unavoidable and per byte.Notice what that overhead buys. Eleven percent of every byte is spent on a one-bit answer — which sounds expensive until you ask what the alternative is. A bus with no per-byte acknowledge would have to either assume every byte arrived, or carry a checksum and a retry mechanism. The acknowledge is the cheapest possible confirmation, and Chapter 7.2 is about exactly how little it confirms.
Nine clocks per byte — eight data bits then the acknowledge
9 cycles2. MSB First, and Why the Bus Had No Choice
The specification says most significant bit first, and it is worth knowing that this is not an arbitrary convention on a bus like this one.
Chapter 6.1 already relied on it: the address byte's seven address bits arrive before its direction bit, which is what lets a slave begin comparing its address immediately and lets the direction be latched last. That ordering only works because the byte is sent most significant first.
The deeper reason is about early decisions. On a serial bus where a receiver may need to act partway through a byte, the useful bits are the ones that discriminate soonest — and for an address, the high bits narrow the field fastest. Chapter 6.2's 10-bit prefix is the extreme case: the first five bits of the first byte tell every device on the bus whether the next byte is even going to be an address. Sent least significant first, that decision could not be made until the byte was over.
For a data byte the ordering is a free choice, and the bus takes the same one for consistency — one shift direction in every device, for every byte, in both directions.
The practical consequence is a shift-left register. A receiver shifts each arriving bit into the least significant position and lets earlier bits migrate upward; after eight shifts the first bit to arrive sits in bit 7. A transmitter presents bit 7 first. Getting this backwards produces a device that transfers bit-reversed bytes — which, for an address, means it simply never responds, and for data means values that look like noise but are perfectly reproducible.
3. When a Bit May Move, and When It May Be Read
This is where Module 4 becomes load-bearing rather than background.
The data-valid rule says SDA must be stable while SCL is high and may only change while SCL is low. That single rule fixes both halves of a byte transfer, and they are on opposite clock edges:
| role | acts on | why |
|---|---|---|
| receiver | SCL's rising edge | the start of the interval in which the line is guaranteed stable |
| transmitter | SCL's falling edge | the start of the interval in which the line is permitted to move |
So a byte transfer is two state machines running on opposite edges of the same clock, and neither is free to choose. A transmitter that changed SDA on the rising edge would be producing a framing event — Chapter 5.1 established that an SDA edge while SCL is high is reserved, so it would not be a marginal violation but a START or STOP appearing in the middle of a byte. A receiver that sampled on the falling edge would be reading a line at the precise moment it is allowed to be changing.
4. The Ninth Slot Is Not Yours
Here is the part of byte transmission that surprises people, and it is the reason this chapter's RTL exists.
UM10204 §3.1.6: "the transmitter releases the SDA line during the acknowledge clock pulse so the receiver can pull the SDA line LOW."
So in the ninth slot the transmitter's job is to stop driving. Not to drive a one — Chapter 2.3 established that no device ever drives a one on this bus — but to deassert its pull-low intent so that the line becomes available to somebody else.
This has a design consequence that is easy to miss and expensive to get wrong. A shift register that simply shifts nine times would present something in the ninth slot, and whatever it presented would be interpreted as an acknowledge. Worse, the failure is data-dependent:
Suppose a transmitter never releases, and just holds whatever it drove for bit 0.
byte ends in 1 -> it was RELEASING for that bit anyway
-> the slot reads HIGH -> looks like a NACK
-> which may be CORRECT, if nobody was going to answer
byte ends in 0 -> it was PULLING LOW for that bit
-> the slot reads LOW -> looks like an ACK
-> the transmitter FORGES an acknowledge from a receiver
that never answered, and believes its byte arrived
So the bug is invisible for every byte whose least significant bit is 1, and
catastrophic for every byte whose least significant bit is 0. On real traffic
that is roughly half the bytes -- and which half depends entirely on the data.
This is exactly the mutation that survived the first version of section 5's
testbench, because the only release check followed a byte ending in 1.A forged acknowledge is the worst possible failure on this bus, because it is a false positive on the only confirmation mechanism the protocol has. The transmitter proceeds confidently, having been told by itself that its data arrived.
5. The Byte Shifter in Three Languages
One receive shifter, one transmit shifter, a bit counter that goes to eight, and an explicit release in the ninth slot.
// One byte on the I2C bus: eight data bits MSB-first, then a ninth slot that this
// block deliberately does not drive. Receive samples on SCL RISING; transmit
// changes SDA on SCL FALLING, because that is the only legal time to move it.
module i2c_byte_shifter (
input logic clk,
input logic rst_n,
input logic scl_in, // observed bus level
input logic sda_in, // observed bus level
input logic begin_byte, // pulse: start a new nine-slot byte
input logic tx_enable, // 1 = this device transmits the eight data bits
input logic [7:0] tx_data,
output logic sda_drive_low, // DRIVE INTENT for the eight DATA bits only
output logic [7:0] rx_data,
output logic rx_valid, // pulse: the eighth bit has been sampled
output logic ack_slot, // high during the NINTH clock
output logic byte_done, // pulse: all nine slots complete
output logic [3:0] bit_index // 0..8; 8 is the acknowledge slot
);
logic [7:0] sh_rx, sh_tx;
logic scl_q, active;
logic scl_rise, scl_fall;
assign scl_rise = !scl_q && scl_in;
assign scl_fall = scl_q && !scl_in;
// The ninth slot is not a data bit and this block never drives it: the
// transmitter must RELEASE so the receiver can pull SDA low.
assign ack_slot = active && (bit_index == 4'd8);
always_ff @(posedge clk) begin
if (!rst_n) begin
sh_rx <= 8'h00;
sh_tx <= 8'h00;
scl_q <= 1'b1; // idle bus: SCL released, therefore high
active <= 1'b0;
bit_index <= 4'd0;
rx_data <= 8'h00;
rx_valid <= 1'b0;
byte_done <= 1'b0;
sda_drive_low <= 1'b0; // RELEASE on reset -- never jam the bus
end else begin
rx_valid <= 1'b0;
byte_done <= 1'b0;
scl_q <= scl_in;
if (begin_byte) begin
active <= 1'b1;
bit_index <= 4'd0;
sh_rx <= 8'h00;
sh_tx <= tx_data;
// A byte begins with SCL low, so the MSB can be presented at once.
// Pull low for a zero; RELEASE for a one -- never drive high.
sda_drive_low <= tx_enable ? ~tx_data[7] : 1'b0;
end else if (active) begin
if (scl_rise) begin
if (bit_index < 4'd8) begin
// A bit is valid on the rising edge: that is the window the
// data-valid rule protects.
sh_rx <= {sh_rx[6:0], sda_in}; // MSB first, shift left
if (bit_index == 4'd7) begin
rx_data <= {sh_rx[6:0], sda_in};
rx_valid <= 1'b1;
end
bit_index <= bit_index + 4'd1;
end else begin
// The ninth rising edge samples the acknowledge, which is
// somebody else's job to interpret.
byte_done <= 1'b1;
active <= 1'b0;
end
end else if (scl_fall) begin
if (bit_index < 4'd8) begin
// SCL is low: the ONLY legal moment to move SDA.
sda_drive_low <= tx_enable ? ~sh_tx[7 - bit_index] : 1'b0;
end else begin
// Entering the ninth slot: RELEASE unconditionally.
sda_drive_low <= 1'b0;
end
end
end else begin
sda_drive_low <= 1'b0;
end
end
end
endmodule module i2c_byte_shifter_tb;
logic clk = 1'b0, rst_n, scl_in;
logic begin_byte, tx_enable;
logic [7:0] tx_data;
logic sda_drive_low;
logic [7:0] rx_data;
logic rx_valid, ack_slot, byte_done;
logic [3:0] bit_index;
int errors = 0;
// The observed bus. The testbench owns a second open-drain driver so it can
// act as the far end; a released line reads HIGH because nothing pulls it down.
// NO RC behaviour is modelled: this proves bit order, sampling edge and edge
// ORDER, and proves nothing about analog rise time.
logic tb_drive_low = 1'b0;
wire sda_bus = ~(sda_drive_low | tb_drive_low);
i2c_byte_shifter dut (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_bus),
.begin_byte(begin_byte), .tx_enable(tx_enable), .tx_data(tx_data),
.sda_drive_low(sda_drive_low), .rx_data(rx_data), .rx_valid(rx_valid),
.ack_slot(ack_slot), .byte_done(byte_done), .bit_index(bit_index));
always #5 clk = ~clk;
initial begin #80000; $display("FAIL: watchdog expired"); $finish; end
// ---- a continuous data-valid monitor, straight out of Chapter 4.2 ----
// Any SDA edge while SCL is high on BOTH samples is a framing waveform, and a
// byte transfer must never produce one. This runs for the whole simulation.
logic scl_q2, sda_q2;
int dv_violations = 0;
always @(posedge clk) if (rst_n) begin
if (scl_q2 && scl_in && (sda_bus !== sda_q2)) dv_violations++;
scl_q2 <= scl_in;
sda_q2 <= sda_bus;
end
int n_rx_valid = 0, n_done = 0;
int base_rx = 0;
always @(posedge clk) if (rst_n) begin
if (rx_valid) n_rx_valid++;
if (byte_done) n_done++;
end
// One SCL pulse. SDA is only ever moved by the TB while SCL is low.
task automatic scl_pulse();
scl_in = 1'b0; repeat (2) @(negedge clk);
scl_in = 1'b1; repeat (2) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
endtask
// Drive one bit from the TB side (the DUT is receiving).
task automatic tb_send_bit(input logic b);
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = ~b; repeat (1) @(negedge clk); // change while SCL low
scl_in = 1'b1; repeat (2) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = 1'b0; repeat (1) @(negedge clk); // RELEASE after each bit
endtask
// Sample what the DUT is presenting, at the instant a receiver would.
logic [7:0] observed;
task automatic observe_tx_byte();
observed = 8'h00;
for (int i = 0; i < 8; i++) begin
scl_in = 1'b0; repeat (2) @(negedge clk);
scl_in = 1'b1; repeat (1) @(negedge clk);
observed = {observed[6:0], sda_bus}; // sample during SCL high
repeat (1) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
end
endtask
initial begin
rst_n = 1'b0; scl_in = 1'b1; begin_byte = 1'b0; tx_enable = 1'b0; tx_data = 8'h00;
scl_q2 = 1'b1; sda_q2 = 1'b1;
repeat (3) @(negedge clk);
if (sda_drive_low !== 1'b0) begin $display("FAIL: reset did not release SDA"); errors++; end
if (ack_slot !== 1'b0) begin $display("FAIL: ack_slot asserted out of reset"); errors++; end
rst_n = 1'b1; @(negedge clk);
// ---- 1: TRANSMIT 0xA5. MSB first means the bus must carry 1,0,1,0,0,1,0,1.
tx_data = 8'hA5; tx_enable = 1'b1;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'hA5) begin
$display("FAIL: transmitted 0x%02h, bus carried 0x%02h", 8'hA5, observed); errors++; end
// ---- 2: the NINTH slot. The transmitter must RELEASE so a receiver can ack.
if (bit_index !== 4'd8) begin
$display("FAIL: bit_index = %0d after eight bits, expected 8", bit_index); errors++; end
if (ack_slot !== 1'b1) begin $display("FAIL: ack_slot not asserted in the ninth slot"); errors++; end
scl_in = 1'b0; repeat (2) @(negedge clk);
if (sda_drive_low !== 1'b0) begin
$display("FAIL: transmitter still driving SDA in the acknowledge slot"); errors++; end
// the far end acknowledges
tb_drive_low = 1'b1; repeat (1) @(negedge clk);
scl_in = 1'b1; repeat (2) @(negedge clk);
if (sda_bus !== 1'b0) begin $display("FAIL: the acknowledge was not visible on the bus"); errors++; end
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = 1'b0; repeat (1) @(negedge clk);
if (n_done != 1) begin $display("FAIL: expected one byte_done, saw %0d", n_done); errors++; end
// ---- 3: RECEIVE 0x3C. A value whose bit pattern is not symmetric, so a
// reversed shift direction cannot pass.
// NOTE: a TRANSMITTING shifter also samples the bus, which is deliberate --
// reading back what the bus actually carried is exactly what arbitration
// needs (Module 13) and it costs nothing. So rx_valid is counted as a
// DELTA here rather than from zero.
base_rx = n_rx_valid;
tx_enable = 1'b0;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
tb_send_bit(1'b0); tb_send_bit(1'b0); tb_send_bit(1'b1); tb_send_bit(1'b1);
tb_send_bit(1'b1); tb_send_bit(1'b1); tb_send_bit(1'b0); tb_send_bit(1'b0);
if (rx_data !== 8'h3C) begin
$display("FAIL: received 0x%02h, expected 0x3C", rx_data); errors++; end
if ((n_rx_valid - base_rx) != 1) begin
$display("FAIL: expected one rx_valid for the received byte, saw %0d",
n_rx_valid - base_rx); errors++; end
// ---- 4: a RECEIVER must not drive the eight data bits at all.
if (sda_drive_low !== 1'b0) begin
$display("FAIL: a receiving shifter drove SDA"); errors++; end
// ---- 5: the ninth slot while RECEIVING -- this block still does not drive it.
if (ack_slot !== 1'b1) begin $display("FAIL: ack_slot missing after a receive"); errors++; end
scl_pulse();
if (n_done != 2) begin $display("FAIL: expected two byte_done, saw %0d", n_done); errors++; end
// ---- 6: back-to-back bytes. 0x00 then 0xFF proves nothing is stuck.
tx_data = 8'h00; tx_enable = 1'b1;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'h00) begin $display("FAIL: 0x00 came out as 0x%02h", observed); errors++; end
scl_pulse(); // its acknowledge slot
tx_data = 8'hFF;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'hFF) begin $display("FAIL: 0xFF came out as 0x%02h", observed); errors++; end
scl_pulse();
// ---- 6b: THE NINTH-SLOT RELEASE, after a byte whose LAST BIT IS ZERO.
// This is the case that matters: after a byte ending in 1 the driver was
// already released, so "hold" and "release" look identical. Only a byte
// ending in 0 can show that the transmitter actually lets go -- and if it
// does not, it holds SDA low through the slot and FORGES an acknowledge
// from a receiver that never answered.
tx_data = 8'hA4; // LSB = 0
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'hA4) begin $display("FAIL: 0xA4 came out as 0x%02h", observed); errors++; end
scl_in = 1'b0; repeat (2) @(negedge clk);
if (sda_drive_low !== 1'b0) begin
$display("FAIL: still driving SDA low in the ninth slot after a byte ending in 0");
errors++; end
scl_pulse();
// ---- 7: THE RULE THAT GOVERNS EVERYTHING. Not one SDA edge may have
// occurred while SCL was high, across the whole simulation.
if (dv_violations != 0) begin
$display("FAIL: %0d data-valid violations -- SDA moved while SCL was high", dv_violations);
errors++;
end
if (errors == 0)
$display("PASS: MSB first both directions, ninth slot released, %0d bytes, zero data-valid violations",
n_done);
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule // One byte on the I2C bus: eight data bits MSB-first, then a ninth slot that this
// block deliberately does not drive. Receive samples on SCL RISING; transmit
// changes SDA on SCL FALLING, because that is the only legal time to move it.
module i2c_byte_shifter (
input wire clk,
input wire rst_n,
input wire scl_in, // observed bus level
input wire sda_in, // observed bus level
input wire begin_byte, // pulse: start a new nine-slot byte
input wire tx_enable, // 1 = this device transmits the eight data bits
input wire [7:0] tx_data,
output reg sda_drive_low, // DRIVE INTENT for the eight DATA bits only
output reg [7:0] rx_data,
output reg rx_valid, // pulse: the eighth bit has been sampled
output wire ack_slot, // high during the NINTH clock
output reg byte_done, // pulse: all nine slots complete
output reg [3:0] bit_index // 0..8; 8 is the acknowledge slot
);
reg [7:0] sh_rx, sh_tx;
reg scl_q, active;
wire scl_rise = ~scl_q & scl_in;
wire scl_fall = scl_q & ~scl_in;
// The ninth slot is not a data bit and this block never drives it.
assign ack_slot = active && (bit_index == 4'd8);
always @(posedge clk) begin
if (!rst_n) begin
sh_rx <= 8'h00;
sh_tx <= 8'h00;
scl_q <= 1'b1; // idle bus: SCL released, therefore high
active <= 1'b0;
bit_index <= 4'd0;
rx_data <= 8'h00;
rx_valid <= 1'b0;
byte_done <= 1'b0;
sda_drive_low <= 1'b0; // RELEASE on reset -- never jam the bus
end else begin
rx_valid <= 1'b0;
byte_done <= 1'b0;
scl_q <= scl_in;
if (begin_byte) begin
active <= 1'b1;
bit_index <= 4'd0;
sh_rx <= 8'h00;
sh_tx <= tx_data;
// A byte begins with SCL low, so the MSB can be presented at once.
// Pull low for a zero; RELEASE for a one -- never drive high.
sda_drive_low <= tx_enable ? ~tx_data[7] : 1'b0;
end else if (active) begin
if (scl_rise) begin
if (bit_index < 4'd8) begin
// A bit is valid on the rising edge: the window the
// data-valid rule protects.
sh_rx <= {sh_rx[6:0], sda_in}; // MSB first, shift left
if (bit_index == 4'd7) begin
rx_data <= {sh_rx[6:0], sda_in};
rx_valid <= 1'b1;
end
bit_index <= bit_index + 4'd1;
end else begin
byte_done <= 1'b1;
active <= 1'b0;
end
end else if (scl_fall) begin
if (bit_index < 4'd8) begin
// SCL is low: the ONLY legal moment to move SDA.
sda_drive_low <= tx_enable ? ~sh_tx[7 - bit_index] : 1'b0;
end else begin
// Entering the ninth slot: RELEASE unconditionally.
sda_drive_low <= 1'b0;
end
end
end else begin
sda_drive_low <= 1'b0;
end
end
end
endmodule module i2c_byte_shifter_tb;
reg clk, rst_n, scl_in;
reg begin_byte, tx_enable;
reg [7:0] tx_data;
wire sda_drive_low;
wire [7:0] rx_data;
wire rx_valid, ack_slot, byte_done;
wire [3:0] bit_index;
integer errors;
// The observed bus. The testbench owns a second open-drain driver so it can
// act as the far end; a released line reads HIGH because nothing pulls it down.
// NO RC behaviour is modelled: this proves bit order, sampling edge and edge
// ORDER, and proves nothing about analog rise time.
reg tb_drive_low;
wire sda_bus = ~(sda_drive_low | tb_drive_low);
i2c_byte_shifter dut (
.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_bus),
.begin_byte(begin_byte), .tx_enable(tx_enable), .tx_data(tx_data),
.sda_drive_low(sda_drive_low), .rx_data(rx_data), .rx_valid(rx_valid),
.ack_slot(ack_slot), .byte_done(byte_done), .bit_index(bit_index));
initial clk = 1'b0;
always #5 clk = ~clk;
initial begin #80000; $display("FAIL: watchdog expired"); $finish; end
// ---- a continuous data-valid monitor, straight out of Chapter 4.2 ----
// Any SDA edge while SCL is high on BOTH samples is a framing waveform, and a
// byte transfer must never produce one. This runs for the whole simulation.
reg scl_q2, sda_q2;
integer dv_violations;
always @(posedge clk) if (rst_n) begin
if (scl_q2 && scl_in && (sda_bus !== sda_q2)) dv_violations = dv_violations + 1;
scl_q2 <= scl_in;
sda_q2 <= sda_bus;
end
integer n_rx_valid, n_done, base_rx, i;
always @(posedge clk) if (rst_n) begin
if (rx_valid) n_rx_valid = n_rx_valid + 1;
if (byte_done) n_done = n_done + 1;
end
// One SCL pulse. SDA is only ever moved by the TB while SCL is low.
task scl_pulse; begin
scl_in = 1'b0; repeat (2) @(negedge clk);
scl_in = 1'b1; repeat (2) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
end endtask
// Drive one bit from the TB side (the DUT is receiving).
task tb_send_bit; input b; begin
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = ~b; repeat (1) @(negedge clk); // change while SCL low
scl_in = 1'b1; repeat (2) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = 1'b0; repeat (1) @(negedge clk); // RELEASE after each bit
end endtask
// Sample what the DUT is presenting, at the instant a receiver would.
reg [7:0] observed;
task observe_tx_byte; begin
observed = 8'h00;
for (i = 0; i < 8; i = i + 1) begin
scl_in = 1'b0; repeat (2) @(negedge clk);
scl_in = 1'b1; repeat (1) @(negedge clk);
observed = {observed[6:0], sda_bus}; // sample during SCL high
repeat (1) @(negedge clk);
scl_in = 1'b0; repeat (1) @(negedge clk);
end
end endtask
initial begin
errors = 0; dv_violations = 0; n_rx_valid = 0; n_done = 0; base_rx = 0;
tb_drive_low = 1'b0;
rst_n = 1'b0; scl_in = 1'b1; begin_byte = 1'b0; tx_enable = 1'b0; tx_data = 8'h00;
scl_q2 = 1'b1; sda_q2 = 1'b1;
repeat (3) @(negedge clk);
if (sda_drive_low !== 1'b0) begin $display("FAIL: reset did not release SDA"); errors = errors + 1; end
if (ack_slot !== 1'b0) begin $display("FAIL: ack_slot asserted out of reset"); errors = errors + 1; end
rst_n = 1'b1; @(negedge clk);
// ---- 1: TRANSMIT 0xA5. MSB first means the bus must carry 1,0,1,0,0,1,0,1.
tx_data = 8'hA5; tx_enable = 1'b1;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'hA5) begin
$display("FAIL: transmitted 0x%02h, bus carried 0x%02h", 8'hA5, observed); errors = errors + 1; end
// ---- 2: the NINTH slot. The transmitter must RELEASE so a receiver can ack.
if (bit_index !== 4'd8) begin
$display("FAIL: bit_index = %0d after eight bits, expected 8", bit_index); errors = errors + 1; end
if (ack_slot !== 1'b1) begin $display("FAIL: ack_slot not asserted in the ninth slot"); errors = errors + 1; end
scl_in = 1'b0; repeat (2) @(negedge clk);
if (sda_drive_low !== 1'b0) begin
$display("FAIL: transmitter still driving SDA in the acknowledge slot"); errors = errors + 1; end
// the far end acknowledges
tb_drive_low = 1'b1; repeat (1) @(negedge clk);
scl_in = 1'b1; repeat (2) @(negedge clk);
if (sda_bus !== 1'b0) begin $display("FAIL: the acknowledge was not visible on the bus"); errors = errors + 1; end
scl_in = 1'b0; repeat (1) @(negedge clk);
tb_drive_low = 1'b0; repeat (1) @(negedge clk);
if (n_done != 1) begin $display("FAIL: expected one byte_done, saw %0d", n_done); errors = errors + 1; end
// ---- 3: RECEIVE 0x3C. A value whose bit pattern is not symmetric, so a
// reversed shift direction cannot pass.
// NOTE: a TRANSMITTING shifter also samples the bus, which is deliberate --
// reading back what the bus actually carried is exactly what arbitration
// needs (Module 13) and it costs nothing. So rx_valid is counted as a
// DELTA here rather than from zero.
base_rx = n_rx_valid;
tx_enable = 1'b0;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
tb_send_bit(1'b0); tb_send_bit(1'b0); tb_send_bit(1'b1); tb_send_bit(1'b1);
tb_send_bit(1'b1); tb_send_bit(1'b1); tb_send_bit(1'b0); tb_send_bit(1'b0);
if (rx_data !== 8'h3C) begin
$display("FAIL: received 0x%02h, expected 0x3C", rx_data); errors = errors + 1; end
if ((n_rx_valid - base_rx) != 1) begin
$display("FAIL: expected one rx_valid for the received byte, saw %0d",
n_rx_valid - base_rx); errors = errors + 1; end
// ---- 4: a RECEIVER must not drive the eight data bits at all.
if (sda_drive_low !== 1'b0) begin
$display("FAIL: a receiving shifter drove SDA"); errors = errors + 1; end
// ---- 5: the ninth slot while RECEIVING -- this block still does not drive it.
if (ack_slot !== 1'b1) begin $display("FAIL: ack_slot missing after a receive"); errors = errors + 1; end
scl_pulse();
if (n_done != 2) begin $display("FAIL: expected two byte_done, saw %0d", n_done); errors = errors + 1; end
// ---- 6: back-to-back bytes. 0x00 then 0xFF proves nothing is stuck.
tx_data = 8'h00; tx_enable = 1'b1;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'h00) begin $display("FAIL: 0x00 came out as 0x%02h", observed); errors = errors + 1; end
scl_pulse(); // its acknowledge slot
tx_data = 8'hFF;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'hFF) begin $display("FAIL: 0xFF came out as 0x%02h", observed); errors = errors + 1; end
scl_pulse();
// ---- 6b: THE NINTH-SLOT RELEASE, after a byte whose LAST BIT IS ZERO.
// After a byte ending in 1 the driver was already released, so "hold"
// and "release" look identical. Only a byte ending in 0 shows that the
// transmitter actually lets go -- if it does not, it holds SDA low
// through the slot and FORGES an acknowledge nobody gave.
tx_data = 8'hA4;
scl_in = 1'b0; @(negedge clk);
begin_byte = 1'b1; @(negedge clk); begin_byte = 1'b0;
observe_tx_byte();
if (observed !== 8'hA4) begin $display("FAIL: 0xA4 came out as 0x%02h", observed); errors = errors + 1; end
scl_in = 1'b0; repeat (2) @(negedge clk);
if (sda_drive_low !== 1'b0) begin
$display("FAIL: still driving SDA low in the ninth slot after a byte ending in 0");
errors = errors + 1; end
scl_pulse();
// ---- 7: THE RULE THAT GOVERNS EVERYTHING. Not one SDA edge may have
// occurred while SCL was high, across the whole simulation.
if (dv_violations != 0) begin
$display("FAIL: %0d data-valid violations -- SDA moved while SCL was high", dv_violations);
errors = errors + 1;
end
if (errors == 0)
$display("PASS: MSB first both directions, ninth slot released, %0d bytes, zero data-valid violations",
n_done);
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- One byte on the I2C bus: eight data bits MSB-first, then a ninth slot that this
-- block deliberately does not drive. Receive samples on SCL RISING; transmit
-- changes SDA on SCL FALLING, because that is the only legal time to move it.
entity i2c_byte_shifter 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
begin_byte : in std_logic; -- pulse: start a byte
tx_enable : in std_logic; -- 1 = we transmit the 8 bits
tx_data : in std_logic_vector(7 downto 0);
sda_drive_low : out std_logic; -- DRIVE INTENT, data bits only
rx_data : out std_logic_vector(7 downto 0);
rx_valid : out std_logic; -- pulse: eighth bit sampled
ack_slot : out std_logic; -- high in the NINTH clock
byte_done : out std_logic; -- pulse: nine slots complete
bit_index : out natural range 0 to 8
);
end entity;
architecture rtl of i2c_byte_shifter is
signal sh_rx : std_logic_vector(7 downto 0) := (others => '0');
signal sh_tx : std_logic_vector(7 downto 0) := (others => '0');
signal scl_q : std_logic := '1'; -- idle bus: SCL released, therefore high
signal active : std_logic := '0';
signal idx : natural range 0 to 8 := 0;
signal scl_rise, scl_fall : std_logic;
begin
scl_rise <= (not scl_q) and scl_in;
scl_fall <= scl_q and (not scl_in);
-- The ninth slot is not a data bit and this block never drives it.
ack_slot <= '1' when (active = '1' and idx = 8) else '0';
bit_index <= idx;
process (clk)
begin
if rising_edge(clk) then
if rst_n = '0' then
sh_rx <= (others => '0');
sh_tx <= (others => '0');
scl_q <= '1';
active <= '0';
idx <= 0;
rx_data <= (others => '0');
rx_valid <= '0';
byte_done <= '0';
sda_drive_low <= '0'; -- RELEASE on reset -- never jam the bus
else
rx_valid <= '0';
byte_done <= '0';
scl_q <= scl_in;
if begin_byte = '1' then
active <= '1';
idx <= 0;
sh_rx <= (others => '0');
sh_tx <= tx_data;
-- A byte begins with SCL low, so the MSB is presented at once.
-- Pull low for a zero; RELEASE for a one -- never drive high.
if tx_enable = '1' then
sda_drive_low <= not tx_data(7);
else
sda_drive_low <= '0';
end if;
elsif active = '1' then
if scl_rise = '1' then
if idx < 8 then
-- A bit is valid on the rising edge.
sh_rx <= sh_rx(6 downto 0) & sda_in; -- MSB first
if idx = 7 then
rx_data <= sh_rx(6 downto 0) & sda_in;
rx_valid <= '1';
end if;
idx <= idx + 1;
else
byte_done <= '1';
active <= '0';
end if;
elsif scl_fall = '1' then
if idx < 8 then
-- SCL is low: the ONLY legal moment to move SDA.
if tx_enable = '1' then
sda_drive_low <= not sh_tx(7 - idx);
else
sda_drive_low <= '0';
end if;
else
-- Entering the ninth slot: RELEASE unconditionally.
sda_drive_low <= '0';
end if;
end if;
else
sda_drive_low <= '0';
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_byte_shifter_tb is
end entity;
architecture sim of i2c_byte_shifter_tb is
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal scl_in : std_logic := '1';
signal begin_byte : std_logic := '0';
signal tx_enable : std_logic := '0';
signal tx_data : std_logic_vector(7 downto 0) := (others => '0');
signal sda_drive_low : std_logic;
signal rx_data : std_logic_vector(7 downto 0);
signal rx_valid, ack_slot, byte_done : std_logic;
signal bit_index : natural range 0 to 8;
-- The observed bus: the DUT's open-drain driver plus the testbench's, wired-AND
-- with a pull-up. A released line reads HIGH. NO RC behaviour is modelled.
signal tb_drive_low : std_logic := '0';
signal sda_bus : std_logic;
signal n_rx_valid, n_done, dv_violations : natural := 0;
signal test_done : std_logic := '0';
begin
sda_bus <= not (sda_drive_low or tb_drive_low);
dut : entity work.i2c_byte_shifter
port map (clk => clk, rst_n => rst_n, scl_in => scl_in, sda_in => sda_bus,
begin_byte => begin_byte, tx_enable => tx_enable, tx_data => tx_data,
sda_drive_low => sda_drive_low, rx_data => rx_data, rx_valid => rx_valid,
ack_slot => ack_slot, byte_done => byte_done, bit_index => bit_index);
clk <= not clk after 5 ns;
watchdog : process
begin
wait for 80 us;
if test_done = '0' then
report "watchdog expired -- the design never reached the expected state"
severity failure;
end if;
wait;
end process;
-- A continuous data-valid monitor, straight out of Chapter 4.2: any SDA edge
-- while SCL is high on BOTH samples is a framing waveform, and a byte transfer
-- must never produce one.
monitor : process (clk)
variable scl_q2 : std_logic := '1';
variable sda_q2 : std_logic := '1';
begin
if rising_edge(clk) then
if rst_n = '1' then
if scl_q2 = '1' and scl_in = '1' and sda_bus /= sda_q2 then
dv_violations <= dv_violations + 1;
end if;
scl_q2 := scl_in;
sda_q2 := sda_bus;
end if;
if rst_n = '1' and rx_valid = '1' then n_rx_valid <= n_rx_valid + 1; end if;
if rst_n = '1' and byte_done = '1' then n_done <= n_done + 1; end if;
end if;
end process;
stim : process
variable errs : natural := 0;
variable base_rx : natural := 0;
variable observed : std_logic_vector(7 downto 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 scl_pulse is
begin
scl_in <= '0'; waitn(2);
scl_in <= '1'; waitn(2);
scl_in <= '0'; waitn(1);
end procedure;
procedure tb_send_bit (b : in std_logic) is
begin
scl_in <= '0'; waitn(1);
tb_drive_low <= not b; waitn(1); -- change while SCL is low
scl_in <= '1'; waitn(2);
scl_in <= '0'; waitn(1);
tb_drive_low <= '0'; waitn(1); -- RELEASE after each bit
end procedure;
procedure observe_tx_byte is
begin
observed := (others => '0');
for i in 0 to 7 loop
scl_in <= '0'; waitn(2);
scl_in <= '1'; waitn(1);
observed := observed(6 downto 0) & sda_bus; -- sample during SCL high
waitn(1);
scl_in <= '0'; waitn(1);
end loop;
end procedure;
procedure start_byte is
begin
scl_in <= '0'; waitn(1);
begin_byte <= '1'; waitn(1); begin_byte <= '0';
end procedure;
begin
waitn(3);
if sda_drive_low /= '0' then
report "reset did not release SDA" severity error; errs := errs + 1; end if;
if ack_slot /= '0' then
report "ack_slot asserted out of reset" severity error; errs := errs + 1; end if;
rst_n <= '1'; waitn(1);
-- 1: TRANSMIT 0xA5, MSB first.
tx_data <= "10100101"; tx_enable <= '1';
start_byte;
observe_tx_byte;
if observed /= "10100101" then
report "transmitted byte did not appear on the bus MSB-first" severity error;
errs := errs + 1; end if;
-- 2: the NINTH slot -- the transmitter must RELEASE.
if bit_index /= 8 then
report "bit_index not 8 after eight bits" severity error; errs := errs + 1; end if;
if ack_slot /= '1' then
report "ack_slot not asserted in the ninth slot" severity error; errs := errs + 1; end if;
scl_in <= '0'; waitn(2);
if sda_drive_low /= '0' then
report "transmitter still driving SDA in the acknowledge slot" severity error;
errs := errs + 1; end if;
tb_drive_low <= '1'; waitn(1);
scl_in <= '1'; waitn(2);
if sda_bus /= '0' then
report "the acknowledge was not visible on the bus" severity error; errs := errs + 1; end if;
scl_in <= '0'; waitn(1);
tb_drive_low <= '0'; waitn(1);
if n_done /= 1 then
report "expected one byte_done" severity error; errs := errs + 1; end if;
-- 3: RECEIVE 0x3C. A transmitting shifter also samples the bus -- deliberate,
-- and what arbitration needs -- so rx_valid is measured as a DELTA.
base_rx := n_rx_valid;
tx_enable <= '0';
start_byte;
tb_send_bit('0'); tb_send_bit('0'); tb_send_bit('1'); tb_send_bit('1');
tb_send_bit('1'); tb_send_bit('1'); tb_send_bit('0'); tb_send_bit('0');
if rx_data /= "00111100" then
report "received byte wrong" severity error; errs := errs + 1; end if;
if (n_rx_valid - base_rx) /= 1 then
report "expected one rx_valid for the received byte" severity error; errs := errs + 1; end if;
-- 4: a RECEIVER must not drive the eight data bits.
if sda_drive_low /= '0' then
report "a receiving shifter drove SDA" severity error; errs := errs + 1; end if;
-- 5: the ninth slot after a receive.
if ack_slot /= '1' then
report "ack_slot missing after a receive" severity error; errs := errs + 1; end if;
scl_pulse;
if n_done /= 2 then
report "expected two byte_done" severity error; errs := errs + 1; end if;
-- 6: back-to-back 0x00 then 0xFF proves nothing is stuck in either direction.
tx_data <= "00000000"; tx_enable <= '1';
start_byte; observe_tx_byte;
if observed /= "00000000" then
report "0x00 did not appear correctly" severity error; errs := errs + 1; end if;
scl_pulse;
tx_data <= "11111111";
start_byte; observe_tx_byte;
if observed /= "11111111" then
report "0xFF did not appear correctly" severity error; errs := errs + 1; end if;
scl_pulse;
-- 6b: THE NINTH-SLOT RELEASE, after a byte whose LAST BIT IS ZERO. After a
-- byte ending in 1 the driver was already released, so "hold" and
-- "release" look identical. Only a byte ending in 0 shows that the
-- transmitter actually lets go -- if it does not, it holds SDA low
-- through the slot and FORGES an acknowledge nobody gave.
tx_data <= "10100100"; -- 0xA4, LSB = 0
start_byte; observe_tx_byte;
if observed /= "10100100" then
report "0xA4 did not appear correctly" severity error; errs := errs + 1; end if;
scl_in <= '0'; waitn(2);
if sda_drive_low /= '0' then
report "still driving SDA low in the ninth slot after a byte ending in 0"
severity error; errs := errs + 1; end if;
scl_pulse;
-- 7: THE RULE THAT GOVERNS EVERYTHING.
if dv_violations /= 0 then
report "data-valid violations: SDA moved while SCL was high" severity error;
errs := errs + 1; end if;
if errs = 0 then
report "i2c_byte_shifter self-check complete: MSB first both directions, ninth "
& "slot released, " & integer'image(n_done)
& " bytes, zero data-valid violations" severity note;
else
report "i2c_byte_shifter self-check FAILED" severity error;
end if;
test_done <= '1';
wait;
end process;
end architecture;5a. Three Design Decisions Worth Defending
The block does not drive the ninth slot at all, in either direction. It asserts ack_slot and releases. Deciding what goes in that slot needs to know who the receiver is, and that is Chapter 7.3's question — so putting it here would mean this block needed to know about device roles, which it does not.
A transmitting shifter still samples the bus. rx_valid fires even when tx_enable is set, and that is deliberate rather than an oversight. Reading back the level the bus actually reached — as opposed to the level you intended — is exactly what arbitration needs, and it costs one register that already exists. Module 13 builds on it; here it is simply free.
The MSB is presented on begin_byte, not on the first falling edge. A byte begins with SCL low, so the first bit can be established immediately. Waiting for a falling edge that has already passed would delay the first bit by a whole clock period and lose it.
5b. Verified Execution and Cross-Language Parity
| language | simulator | result | completes at |
|---|---|---|---|
| SystemVerilog | Icarus Verilog, -g2012 | PASS | 2510 ns |
| Verilog-2005 | Icarus Verilog, -g2005 | PASS | 2510 ns |
| VHDL | nvc 1.23.0 | PASS | 2510 ns |
| SystemVerilog | Verilog-2005 | VHDL | |
|---|---|---|---|
| shifters | logic [7:0] | reg [7:0] | std_logic_vector(7 downto 0) |
| bit counter | logic [3:0] | reg [3:0] | natural range 0 to 8 |
| transmit bit select | sh_tx[7 - bit_index] | sh_tx[7 - bit_index] | sh_tx(7 - idx) |
| edge detect | assign | wire | concurrent assignment |
| bus model in the TB | ~(a | b) | ~(a | b) | not (a or b) |
The VHDL counter is a constrained natural range 0 to 8, so a design error that pushed it past eight is a runtime range error rather than a silent wrap. The Verilog versions get a four-bit vector and the same protection by review only — the same trade Chapter 5.3 noted for its interval counter.
i2c_byte_shifter — releasing for the ninth slot
10 cycles6. What the Testbench Proves
| step | stimulus | required result |
|---|---|---|
| 1 | transmit 0xA5 | the bus carries 1,0,1,0,0,1,0,1 — MSB first |
| 2 | the ninth slot after it | ack_slot high, drive intent released, far end's ACK visible |
| 3 | receive 0x3C | rx_data = 0x3C, exactly one rx_valid |
| 4 | while receiving | the shifter drives nothing |
| 5 | the ninth slot after a receive | still not driven by this block |
| 6 | transmit 0x00, then 0xFF | both appear correctly |
| 6b | transmit 0xA4, then check the release | the release happens after a byte ending in 0 |
| 7 | the whole simulation | zero SDA edges while SCL was high |
Step 6's pair is not arbitrary. 0x00 and 0xFF are the two bytes that a stuck driver, an inverted driver, or a shifter that never shifts can most easily fake. During development, step 6 passed for the wrong reason: the testbench's own far-end driver had been left asserted after the previous receive, so the bus read low regardless — and 0x00 "passed" while 0xFF exposed it. A single value would have hidden a real testbench fault.
Step 6b exists because a mutation survived. §7 tells that story.
Step 7 is the check worth copying. A continuous monitor watching for any SDA edge while SCL is high on both samples, running for the entire simulation, reports zero. It is four lines, it never needs maintenance, and it is the only check in the suite that would catch a transmitter moving on the wrong edge.
7. Mutation Testing
Seven faults injected into the verified RTL.
| mutation | what it breaks | result |
|---|---|---|
| sample data on SCL's falling edge | reads a line permitted to be moving | FAIL — bus read 0xFF |
| transmit changes SDA on the rising edge | emits framing events inside a byte | FAIL — bus read 0xFF |
| stop after seven data bits | the byte is short and misaligned | FAIL — received 0x1E |
| shift right — LSB first | every byte is bit-reversed | FAIL — received 0xF0 |
| the eighth bit is never published | rx_data never updates | FAIL — received 0x00 |
| reset holds SDA low | a device in reset jams the bus | FAIL — reset check |
| keep driving through the ninth slot | forges acknowledges | initially PASSED |
Six caught immediately. The seventh is the one worth the space, and §4 already derived why it is dangerous.
Why it survived. The only ninth-slot release check followed the transmission of 0xA5, whose least significant bit is 1. For that byte the transmitter was already releasing — so "hold the previous drive" and "release" produce identical bus behaviour, and no observation can tell them apart. The test was checking the right signal after the wrong byte.
The fix is step 6b: transmit 0xA4, whose least significant bit is 0, and check the release then. With that byte the mutant holds SDA low through the acknowledge slot and forges an ACK; the test now fails immediately.
The transferable lesson. When a behaviour is conditional on data, the test has to be run with data that makes the condition bite. "Check that the transmitter releases" is not a test — "check that the transmitter releases after a byte whose last bit was 0" is. The general form: for any output whose correctness depends on a preceding value, enumerate which preceding values could mask the fault, and use one that cannot.
8. Clock Stretching, Previewed and Deferred
§3.1.5's fourth sentence is the one this chapter has not used:
If a slave cannot receive or transmit another complete byte of data until it has performed some other function, for example servicing an internal interrupt, it can hold the clock line SCL LOW to force the master into a wait state. Data transfer then continues when the slave is ready for another byte of data and releases clock line SCL.
Two things are worth noting now, and the rest is Module 12.
It falls straight out of the wired-AND. Chapter 2.5 established that any device can hold a line low and every other device sees it. A slave holding SCL low is not a special protocol feature; it is the electrical layer being used deliberately.
The byte shifter in §5 already tolerates it, for free. The design is driven entirely by observed SCL edges rather than by an internal timer, so if SCL simply stops, the shifter stops with it and resumes when the edges resume. That is not an accident of this implementation — it is the reason a bit-level engine should be edge-driven rather than counter-driven. A design that counted internal cycles between bits would break the moment a slave stretched the clock, and Chapter 4.1 §7 made the same point about observing the bus rather than trusting a count.
9. Verification Connection — The Byte Is the Unit a Monitor Publishes
A protocol monitor does not publish bits. It publishes bytes, because a byte is the smallest thing with a meaning — and the acknowledge is part of that meaning rather than a separate event.
// A byte and its acknowledge travel together. Publishing them separately forces
// every consumer to re-pair them, and a scoreboard that mis-pairs them reports
// the wrong byte as unacknowledged.
class i2c_byte_item extends uvm_sequence_item;
`uvm_object_utils(i2c_byte_item)
rand bit [7:0] data;
bit acked; // filled in by the monitor from the ninth slot
bit is_address; // set by the monitor, not by the test
time t_first_bit; // for the timing checker of Chapter 5.5
time t_ack_bit;
function string convert2string();
return $sformatf("%s 0x%02h %s", is_address ? "ADDR" : "DATA",
data, acked ? "ACK" : "NACK");
endfunction
endclass
// And the property that catches the section 7 mutation from the outside -- worth
// having even when the DUT is somebody else's IP.
property transmitter_releases_in_ack_slot;
@(posedge clk) disable iff (!rst_n)
(ack_slot && we_are_transmitting) |-> !sda_drive_low;
endproperty
assert property (transmitter_releases_in_ack_slot)
else $error("transmitter held SDA during the acknowledge slot -- a forged ACK");Two observations.
Pairing the byte with its answer in one item is a design decision with consequences. The alternative — a byte_received event and a separate ack_seen event — is more flexible and makes every consumer responsible for matching them up. On a bus where the answer always follows the byte in a fixed slot, that flexibility buys nothing and costs a class of scoreboard bug.
The assertion is one line and it is unconditional. It does not care which byte, which direction, or which device — if we are transmitting and the slot is the ninth, we are not driving. That is exactly the shape that survives someone later adding a feature, and it is the check that the procedural testbench needed a specially-chosen data value to make.
10. FPGA and ASIC Implications
The engine must be edge-driven, not timer-driven. §8 is the reason: a slave may stretch the clock at any byte boundary, and an engine that counts internal cycles between bits will lose synchronisation the first time it happens. Every state change in §5's design is gated on an observed SCL edge.
The internal clock must be fast enough to resolve both edges of every SCL pulse. This is a sampled design: it needs to see the rising edge and the falling edge distinctly. That is a far stronger requirement than "faster than the bus" — at minimum a few internal cycles per SCL half-period, and more once the input filter of Module 19 adds latency.
The ninth-slot release is a single bit of logic and the most important one in the block. On an FPGA it is the tristate enable going inactive. A design that instead drove the buffer's data input high while leaving the enable asserted would drive a one onto an open-drain bus — Chapter 5.1 §9 covered why that is the wiring mistake that most often turns a correct design into a dead bus.
Both shift registers can be one register on an ASIC, and usually should not be. Sharing the receive and transmit shifter saves eight flip-flops and costs the read-back behaviour of §5a — which arbitration needs. Eight flip-flops is not a saving worth a feature.
11. Debugging — The Write That Always Succeeded
A logger that reported every byte delivered, on a bus with nothing attached
Pitfall — a nine-slot shift register that never releases for the acknowledge
// A master's byte engine shifts nine times and calls it a byte:
//
// if (scl_falling && bit_count < 9) begin
// sda_drive_low <= ~shifter[7]; // present the next bit
// shifter <= {shifter[6:0], 1'b1};
// bit_count <= bit_count + 1;
// end
//
// Nine shifts, nine clocks, and the ninth "bit" shifted in is a 1 -- so the
// engineer reasons that the transmitter will release for the acknowledge, because
// a 1 means release on an open-drain bus.
//
// And that reasoning is correct, for the ninth bit. The bug is that bit_count
// reaches 9 only AFTER the ninth falling edge, so the value presented for the
// ninth slot is the one shifted in on the EIGHTH falling edge -- which is data
// bit 0, not the padding 1.Every write succeeds. The driver reports an acknowledge for every byte of every transfer, and the application layer is entirely happy.
It is discovered during a bring-up of a NEW board where the target device had not yet been populated. Writes to the empty address still succeeded. That is the only reason anybody looked -- a fully-populated board gives the same answer whether the mechanism works or not, so the fault had been shipping for a year.
Once suspected, it is still confusing to pin down, because it is intermittent in a way that tracks DATA rather than time. Writing 0xFF repeatedly reports honest NACKs on the empty address. Writing 0x00 repeatedly reports ACKs. Writing real payload reports a mixture, correlated with nothing the engineer is looking at.
A capture shows SDA low in the acknowledge slot -- which looks like a target acknowledging, on a board where the target is not fitted.
The master was acknowledging its own bytes.
Section 4 derives it: if a transmitter fails to release in the ninth slot and instead holds whatever it drove for bit 0, the slot reads LOW whenever the byte ended in 0 -- and a LOW in the ninth slot is, by definition, an acknowledge. The master was pulling SDA low and then reading it back as somebody else's answer.
That explains the data dependence exactly:
byte ends in 1 -> the master was releasing anyway -> honest NACK byte ends in 0 -> the master holds SDA low -> FORGED ACK
0xFF ends in 1, so it reported truthfully. 0x00 ends in 0, so it lied. Real payload is a mixture, and the mixture follows the low bit of each byte -- which is why it correlated with nothing the engineer thought to plot.
The off-by-one is the immediate cause. A bit_count < 9 test with a post-increment means the ninth slot is driven with the value established on the eighth falling edge. The padding 1 that was supposed to release does get shifted in -- one clock too late to be used.
And note what made this survive a year of production: a forged ACK is a FALSE POSITIVE on the only confirmation the protocol offers. Every other bus fault produces a failure somewhere. This one produces silence and confidence, and it can only be detected by transferring to an address that should NOT answer -- which is the one test nobody runs, because why would you write to a device that is not there.
// Make the ninth slot an explicit state, not the tail of a loop:
//
// if (scl_falling) begin
// if (bit_index < 8) sda_drive_low <= ~sh_tx[7 - bit_index]; // data
// else sda_drive_low <= 1'b0; // RELEASE
// end
//
// Section 5's shifter is that design, and it makes the release unconditional and
// impossible to reach by falling off the end of a count.
//
// The verification lessons, in order of transferable value.
//
// 1. TEST WITH DATA THAT MAKES THE CONDITION BITE. The fault is invisible for
// every byte whose last bit is 1. Section 6's step 6b transmits 0xA4
// specifically so the release can be observed, and section 7 confirms the
// mutation is caught by that step and by nothing else. A checklist item
// reading "check the transmitter releases" is not a test until it names the
// byte.
//
// 2. TRANSFER TO AN ADDRESS THAT SHOULD NOT ANSWER. This is the cheapest and
// most neglected bring-up check on an I2C bus. A master that reports success
// writing to an unpopulated address has a broken acknowledge path, and one
// transfer proves it. It is the same instinct as Chapter 6.4's advice to read
// an identifying register rather than trusting data.
//
// 3. PREFER AN ASSERTION FOR "WE MUST NOT DRIVE HERE". Section 9's property --
// (ack_slot && we_are_transmitting) |-> !sda_drive_low -- is unconditional and
// needs no special data. A procedural test needed a carefully chosen byte to
// see this; the assertion sees it on the first byte of the first test.
//
// The design habit: when a slot in a sequence belongs to somebody else, make
// "release" an explicit action in an explicit state rather than a value that
// happens to fall out of a shift. A forged acknowledge is worse than a missing one,
// because the protocol has no second confirmation to cross-check it against.12. Common Misconceptions
"A byte is eight clock pulses." It is nine. Eight carry data and the ninth is the acknowledge, and the master generates all nine identically.
"The acknowledge is a separate handshake after the byte." It is a slot inside the byte's own clock train. Nothing in the clock generator distinguishes it.
"The transmitter drives a one in the ninth slot to let the receiver answer." It releases. No device ever drives a one on this bus; the pull-up produces the high level when nobody is pulling low.
"If the shift register pads with a 1, the release happens automatically." Only if the padding reaches the output in time. §11's off-by-one shifts the padding in one clock too late, so the slot carries data bit 0 — and half of all bytes then forge an acknowledge.
"Bit order is a convention and either would work." Either would work for data. MSB-first is what lets a receiver make early decisions — most visibly the 10-bit prefix of Chapter 6.2, which tells every device whether the next byte is an address before the first byte is even finished.
"Sampling anywhere in the bit is fine because SDA is stable." It is stable only while SCL is high. The low phase is when the transmitter is permitted to move it.
"A transmitter has no reason to read the bus." Reading back what the bus actually reached, rather than what you intended, is precisely how arbitration works — and it costs nothing, because the receive shifter already exists.
"An engine can count internal cycles between bits." Not on a bus where a slave may hold SCL low at any byte boundary. Every state change has to be gated on an observed edge.
13. Reason It Through
A 400 kHz bus transfers a 32-byte payload. Ignoring framing, roughly what is the payload rate, and why is it not 400 kbit/s?
Each byte costs nine bit slots, so the byte rate is 400,000 / 9 ≈ 44,400 bytes/s, and the payload rate is about 355 kbit/s — 88.9% of the signalling rate. The missing 11.1% is the acknowledge slot, which carries no payload and is paid on every byte. Adding the address byte and the framing makes the real figure lower still.
Why do the transmitter and the receiver act on opposite clock edges?
Because the data-valid rule assigns them different intervals. The receiver needs the guaranteed-stable window, which begins at the rising edge; the transmitter needs the permitted-to-change window, which begins at the falling edge. It is one rule producing two obligations, not two conventions.
A device transfers bytes that arrive bit-reversed. Where would you look first, and what would the symptom be for an address?
At the shift direction — a receiver shifting toward the MSB instead of away from it, or a transmitter presenting bit 0 first. For an address the symptom is not corrupted data but silence: the device never recognises its own address, so it never responds at all, which looks like an absent or dead device rather than a bit-order bug.
A master reports that every write succeeds, including writes to an address with nothing attached. What single mechanism explains it?
The master is not releasing SDA in the ninth slot, so it pulls the line low and reads its own drive back as an acknowledge. §4 shows the failure is data-dependent — only bytes ending in 0 forge the ACK — which is why it appears intermittent and correlates with payload rather than with time.
Why is a forged acknowledge worse than a missed one?
Because it is a false positive on the protocol's only confirmation. A missed acknowledge makes a working transfer look broken, which is annoying and self-announcing. A forged one makes a broken transfer look working, and there is no second mechanism to cross-check it against — so it can ship.
Your byte engine counts internal clock cycles between bits and works perfectly on the bench. What will break it in the field?
A slave stretching the clock. §8 quotes the specification's permission for a slave to hold SCL low to force a wait state, and a counter-driven engine will continue on its own schedule while the bus is stopped — losing bit alignment for the rest of the transfer. An edge-driven engine stops and resumes with the bus and needs no special handling at all.
14. Understanding Check
15. Summary
A byte is nine clock pulses. Eight carry data, the ninth is the acknowledge, and the master generates all of them identically — the acknowledge is a slot in the byte, not a handshake beside it.
That costs 11.1% of every byte, permanently, which is the price of per-byte confirmation and is the cheapest such mechanism available.
Data goes MSB first, which is what lets a receiver decide things early — most visibly the 10-bit prefix, which resolves before the first byte is over.
The transmitter changes SDA on the falling edge; the receiver samples on the rising edge. One rule from Module 4, two obligations, opposite edges — and the two failure modes look nothing alike.
The ninth slot belongs to somebody else, so the transmitter's job there is to stop driving. Not to drive a one; to release.
A forgotten release forges acknowledges, data-dependently — only for bytes ending in 0 — and a forged acknowledge is worse than a missing one because it is a false positive on the protocol's only confirmation.
A data-dependent behaviour needs a test with data that makes it bite. §7's mutation survived a suite that checked the release after the wrong byte.
Edge-driven, not counter-driven, so that a slave stretching the clock costs nothing to support.
16. What Comes Next
The byte now transfers correctly in both directions, and the ninth slot is reliably empty and available. What goes into it, and what it actually proves, has been deferred in every section of this chapter.
Chapter 7.2 fills the slot: acknowledge as a pulled-low bit and not-acknowledge as an absent one, the five conditions the specification lists for generating a NACK, and a careful account of what an acknowledge does not guarantee — which is considerably more than most engineers expect.
Browse the full path on the I²C tutorials index. For the rule that fixes both edges, see The Data-Valid Rule; for the byte this chapter generalises, The Address Byte.
Continue learning
Related tutorials
- 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.
- 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 START Condition
START is SDA falling while SCL is high, it is generated only by the controller, and it makes the bus busy. Derive what every device must do in response, then build a detector in three languages and find out why its two guard terms and its reset value are all load-bearing.
