Skip to content
VLSI Mentor

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.

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — what the ninth clock costs
   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 cycles
Nine clock periods, one per bit. SCL ticks once per interval. SDA carries one zero one zero zero one zero one across the first eight intervals, most significant bit first, and is low in the ninth interval, which is the acknowledge slot.eight data bitseight data bitsACKACKMSB firstMSB firstLSBLSBninth slot: the acknowledgeninth slot: the acknowledgesclsda101001010t0t1t2t3t4t5t6t7t8
Figure 1 — one byte: nine clock pulses, one per interval. Eight carry data, most significant bit first, and the ninth is the acknowledge slot. The data byte here is 0xA5, and the acknowledge is low, which means it was acknowledged. Nothing in the clock train distinguishes the ninth pulse from the other eight — the master generates all nine identically.

2. 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:

roleacts onwhy
receiverSCL's rising edgethe start of the interval in which the line is guaranteed stable
transmitterSCL's falling edgethe 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:

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — why a forgotten release 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.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_byte_shifter.sv — SYNTHESIZABLE RTL. Eight bits MSB-first both ways; the ninth slot is deliberately not driven.
   // 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
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_byte_shifter_tb.sv — SELF-CHECKING TESTBENCH, SIMULATION ONLY. Both directions, complementary values, and a continuous data-valid monitor.
   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
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_byte_shifter.v — SYNTHESIZABLE RTL. The same shifter in Verilog-2005.
   // 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
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_byte_shifter_tb.v — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same cases in Verilog idiom.
   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
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_byte_shifter.vhd — SYNTHESIZABLE RTL. The same shifter in VHDL.
   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;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_byte_shifter_tb.vhd — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same cases with assert report severity.
   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

languagesimulatorresultcompletes at
SystemVerilogIcarus Verilog, -g2012PASS2510 ns
Verilog-2005Icarus Verilog, -g2005PASS2510 ns
VHDLnvc 1.23.0PASS2510 ns
SystemVerilogVerilog-2005VHDL
shifterslogic [7:0]reg [7:0]std_logic_vector(7 downto 0)
bit counterlogic [3:0]reg [3:0]natural range 0 to 8
transmit bit selectsh_tx[7 - bit_index]sh_tx[7 - bit_index]sh_tx(7 - idx)
edge detectassignwireconcurrent 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 cycles
Ten internal clock cycles. The observed SCL input pulses twice. After the first rising edge the bit index changes from seven to eight and the ack slot output rises. On the following falling edge the SDA drive-low intent falls to zero and the observed SDA bus rises from low to high. A byte done pulse appears after the second rising edge.eighth bit sampledeighth bit sampledninth slot beginsninth slot beginstransmitter releases SDAtransmitter releases SDAbyte_donebyte_doneclkscl_insda_drive_lowsda_bus0000011111bit_index7778888888ack_slotbyte_donet0t1t2t3t4t5t6t7t8t9
Figure 2 — the handover into the ninth slot, from the simulation, on the internal clock. The eighth bit is sampled on SCL's rising edge and the bit index moves to eight, which raises ack_slot. On the very next falling edge the transmitter's drive intent is deasserted and the observed bus rises — the line is now available to whoever is going to answer. SIMULATION-DERIVED figure.

6. What the Testbench Proves

stepstimulusrequired result
1transmit 0xA5the bus carries 1,0,1,0,0,1,0,1 — MSB first
2the ninth slot after itack_slot high, drive intent released, far end's ACK visible
3receive 0x3Crx_data = 0x3C, exactly one rx_valid
4while receivingthe shifter drives nothing
5the ninth slot after a receivestill not driven by this block
6transmit 0x00, then 0xFFboth appear correctly
6btransmit 0xA4, then check the releasethe release happens after a byte ending in 0
7the whole simulationzero 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.

mutationwhat it breaksresult
sample data on SCL's falling edgereads a line permitted to be movingFAIL — bus read 0xFF
transmit changes SDA on the rising edgeemits framing events inside a byteFAIL — bus read 0xFF
stop after seven data bitsthe byte is short and misalignedFAIL — received 0x1E
shift right — LSB firstevery byte is bit-reversedFAIL — received 0xF0
the eighth bit is never publishedrx_data never updatesFAIL — received 0x00
reset holds SDA lowa device in reset jams the busFAIL — reset check
keep driving through the ninth slotforges acknowledgesinitially 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.

Azvya Education Pvt. Ltd.VLSI Mentor
UVM CONCEPT — VERIFICATION ONLY. The byte, with its answer, as one transaction item.
   // 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
Buggy Code
// 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.
Symptom

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.

Root Cause

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.

Fix
// 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