Skip to content
VLSI Mentor

I²C · Module 18

ACK Generation and Its Timing Window

Where most slave designs first fail. The acknowledge has three parts, the window is bounded at both ends by falling edges, and asserting early is not early — SDA falling while SCL is high is a START, so a premature acknowledge restarts the transaction instead of acknowledging a byte.

The target knows it is addressed and in which direction (18.4). It now has to answer — and this is the block where most slave designs first fail.

1. Three Obligations, Not One

Read as obligations on the receiver, that is three things:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1. PULL SDA LOW   before the ninth SCL rise
2. HOLD IT LOW    for the whole of the ninth HIGH period
3. RELEASE IT     before the master can change SDA for the next bit

A design that gets the first right and misses either of the others is broken in a way that presents as data corruption — which is why the investigation goes to the datapath and the defect is here.

2. The Window Is Bounded at Both Ends by Falling Edges

§3.1.2 permits SDA to change only while SCL is LOW. There are exactly two low phases adjacent to the ninth high phase, and each is the only legal place for one of the two transitions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assert   in the low phase BEFORE the ninth rise   (after the eighth fall)
release  in the low phase AFTER  the ninth fall

That is the single most destructive timing error a slave can make, and it is why the assert is gated on scl_fall rather than on the byte-complete event that precedes it. Mutation M1 removes the gate and fails seventeen checks.

3. One Slot, Not a Level

The output is a drive-low intent true for exactly one SCL period. A design that held the acknowledge as a level until the next byte would be driving through the next byte's data bits — mutation M2, seven failing checks.

4. What This Block Does Not Decide

And a NACK is the absence of drive, not a driven one. There is no drive-high, so declining is what the pull-up does when nobody objects. Mutation M4 drives a NACK and fails three checks.

5. The Interface Assumption, Stated

byte_done always arrives while SCL is HIGH. That is guaranteed rather than hoped for: a byte completes when its eighth bit is sampled, and sampling happens at scl_rise (18.2, 18.4).

So the next scl_fall is always the low phase immediately before the ninth rise — which is the one instant the assert is allowed to happen.

6. The Window, Measured

Armed while SCL is high, asserted in the low phase, held through the high phase

10 cycles
Ten cycles. The SCL bus is high for two cycles, low for cycles two through six, then high again for the last three. The SDA bus is high until cycle five and low thereafter. An armed flag is high for cycles two through four. The slave's drive-low output asserts in cycle five, while SCL is still low, and remains asserted through the final high phase.waiting: must not assert yetwaiting: must not assert yetthe acknowledgethe acknowledgebyte done — SCL still highbyte done — SCL still higharmed, waiting for the fallarmed, waiting for the fallassert — inside the LOW phaseassert — inside the LOWphaseholds through the high phaseholds through the highphasescl_bussda_busack_armedsda_drive_lowt0t1t2t3t4t5t6t7t8t9
Figure 1 — from the running RTL. The byte completes while SCL is high and the slot arms; the assert waits for the falling edge and happens inside the low phase; the drive then holds through the whole ninth high phase. Simulation-derived from i2c_slave_ack behind the real front end.

Note interval 2. The byte is complete, the slot is armed, and SDA has not moved. That gap — between knowing an acknowledge is owed and being allowed to drive it — is the whole content of §2.

7. The Clock Ratio Is a Correctness Constraint, and It Is Measurable

Chapter 18.1 §8 claimed the system-to-SCL clock ratio is a design constraint rather than a free choice. This is where it bites, and the constraint is visible in simulation.

Capturing the same block with SCL phases only two system clocks long produced this:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
scl_bus        1 1 1 0 0 1 1 1 0 0
ack_armed      0 0 1 1 1 1 0 0 0 0
sda_drive_low  0 0 0 0 0 0 1 1 1 1
                         ^ SCL has ALREADY risen again

The synchroniser reports the falling edge two cycles late (18.2 §7), so by the time the assert happens the low phase is over. The acknowledge lands in the high phase — which is both too late to be sampled and, being an SDA fall while SCL is high, a START.

8. The Acknowledge Generator, in Three Languages

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_ack.sv — the acknowledge slot, bounded at both ends by falling edges
   // -----------------------------------------------------------------------------
   // i2c_slave_ack.sv
   // The acknowledge bit: the right slot, and -- harder -- released in time.
   //
   // §3.1.4, verbatim: "the transmitter releases the SDA line during the acknowledge clock
   // pulse so the receiver can pull the SDA line LOW and it remains stable LOW during the
   // HIGH period of this clock pulse."
   //
   // So the obligation has three parts, and a slave that gets the first and misses either
   // of the others is broken in a way that looks like data corruption:
   //
   //   1. PULL SDA LOW  -- before the ninth SCL rise
   //   2. HOLD IT LOW   -- for the whole of the ninth HIGH period
   //   3. RELEASE IT    -- before the master can change SDA for the next bit
   //
   // WHY THE WINDOW IS BOUNDED AT BOTH ENDS BY SCL FALLING EDGES. §3.1.2 permits SDA to
   // change only while SCL is LOW. There are exactly two low phases adjacent to the ninth
   // high phase, and each one is the only legal place for one of the two transitions:
   //
   //   assert  in the low phase BEFORE the ninth rise   (after the eighth fall)
   //   release in the low phase AFTER  the ninth fall
   //
   // ASSERTING EARLY IS NOT "EARLY", IT IS ILLEGAL. If the slave pulls SDA low while SCL is
   // still HIGH -- during the eighth bit's high phase, say -- then SDA has fallen while SCL
   // is high, and that is a START condition (§3.1.1). Every device on the bus, including
   // the master, is entitled to read it as one. The slave has not acknowledged a byte; it
   // has restarted the transaction. This is the single most destructive timing error a
   // slave can make, and it is why the assert is gated on `scl_fall` rather than on the
   // byte-complete event that precedes it.
   //
   // RELEASING LATE CORRUPTS THE NEXT BIT. The master will drive the next data bit in the
   // low phase that follows the ninth fall. If the slave is still pulling SDA down, the
   // wired-AND means the master's one becomes a zero -- and on a read the master will see
   // its own bit come back wrong, which on a multi-master bus it is entitled to interpret
   // as losing arbitration (§3.1.8). A slave that releases late can therefore knock a
   // master off a bus it was winning.
   //
   // ONE SLOT, NOT A LEVEL. The output is a drive-low intent that is true for exactly one
   // SCL period. A design that held ACK as a level until the next byte would be driving
   // through the next byte's data bits.
   //
   // WHAT THIS BLOCK DOES NOT DECIDE. Whether to acknowledge at all. That is policy --
   // address match (18.4), a register that refuses a write (18.9), a receiver with nowhere
   // to put the byte (18.6) -- and it arrives here as `ack_en`. Separating the decision
   // from the timing is what lets each be tested on its own.
   // -----------------------------------------------------------------------------

   module i2c_slave_ack #(
      parameter int CNT_W = 16
   ) (
      input  logic clk,
      input  logic rst_n,

      // From Chapter 18.2. The two falling edges that bound the window.
      input  logic scl_fall,

      // One cycle, from whichever block just completed a byte: the address block (18.4) or
      // the receive datapath (18.6). It means "the eighth bit has been sampled".
      //
      // INTERFACE ASSUMPTION, and it is guaranteed rather than hoped for: this pulse always
      // arrives while SCL is HIGH, because a byte completes when its eighth bit is SAMPLED
      // and sampling happens at `scl_rise` (§3.1.2). So the next `scl_fall` is always the
      // low phase immediately before the ninth rise -- which is the one instant the assert
      // is allowed to happen. A producer that raised `byte_done` in a low phase would arm a
      // slot whose assert landed a full phase late, and the acknowledge would miss its
      // pulse entirely.
      input  logic byte_done,

      // The DECISION, sampled when the slot is armed. 1 = acknowledge (pull SDA low),
      // 0 = do not (leave it released, which the master reads as a NACK).
      input  logic ack_en,

      // Framing aborts the slot outright. A START or STOP mid-acknowledge means the
      // transaction this acknowledge belonged to no longer exists.
      input  logic start_pulse,
      input  logic stop_pulse,

      // DRIVE INTENT, never a level to be driven high. 1 = pull SDA low, 0 = release.
      output logic sda_drive_low,

      // Exposed so Chapter 18.11 can see the slot and so a bench can check the boundaries.
      output logic ack_active,      // the slot is in progress
      output logic ack_armed,       // the byte is done, waiting for the eighth SCL fall
      output logic [CNT_W-1:0] n_acks,
      output logic [CNT_W-1:0] n_nacks
   );

      localparam [1:0] A_IDLE  = 2'd0,   // nothing owed
                       A_ARMED = 2'd1,   // byte complete, waiting for SCL to fall
                       A_DRIVE = 2'd2;   // driving through the ninth pulse

      logic [1:0] state;
      logic       will_ack;

      always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
            // Reset RELEASES. A slave that came out of reset pulling SDA low would hold the
            // whole bus down and nothing else could lift it.
            sda_drive_low <= 1'b0;
            ack_active    <= 1'b0;
            ack_armed     <= 1'b0;
            will_ack      <= 1'b0;
            state         <= A_IDLE;
            n_acks        <= {CNT_W{1'b0}};
            n_nacks       <= {CNT_W{1'b0}};
         end else if (start_pulse || stop_pulse) begin
            // Framing wins over everything. Release immediately: the framing edge has
            // already happened, so this release cannot itself produce one.
            sda_drive_low <= 1'b0;
            ack_active    <= 1'b0;
            ack_armed     <= 1'b0;
            state         <= A_IDLE;
         end else begin
            case (state)
               A_IDLE: begin
                  if (byte_done) begin
                     // Arm, and latch the decision NOW. The inputs that produced it -- an
                     // address comparison, a register's writability -- are valid at byte
                     // completion and need not still be valid two SCL phases later.
                     will_ack   <= ack_en;
                     ack_armed  <= 1'b1;
                     state      <= A_ARMED;
                  end
               end

               A_ARMED: begin
                  // Wait for SCL to FALL. This is the whole of the "not early" rule: the
                  // assert may only happen in a low phase, because SDA falling while SCL
                  // is high is a START and not an acknowledge.
                  if (scl_fall) begin
                     ack_armed     <= 1'b0;
                     ack_active    <= 1'b1;
                     // A NACK is the ABSENCE of drive, not a driven one. There is no
                     // drive-high anywhere in a conforming I²C interface.
                     sda_drive_low <= will_ack;
                     if (will_ack) n_acks  <= n_acks  + 1'b1;
                     else          n_nacks <= n_nacks + 1'b1;
                     state         <= A_DRIVE;
                  end
               end

               A_DRIVE: begin
                  // The ninth pulse happens here: SCL rises, the master samples, SCL falls.
                  // Release on that fall -- the next low phase belongs to the master, which
                  // will drive the next bit into it.
                  if (scl_fall) begin
                     sda_drive_low <= 1'b0;
                     ack_active    <= 1'b0;
                     state         <= A_IDLE;
                  end
               end

               default: state <= A_IDLE;
            endcase
         end
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_ack.v — the same design in Verilog-2001
   // -----------------------------------------------------------------------------
   // i2c_slave_ack.v
   // The acknowledge bit: the right slot, and -- harder -- released in time.
   //
   // §3.1.4, verbatim: "the transmitter releases the SDA line during the acknowledge clock
   // pulse so the receiver can pull the SDA line LOW and it remains stable LOW during the
   // HIGH period of this clock pulse."
   //
   // So the obligation has three parts, and a slave that gets the first and misses either
   // of the others is broken in a way that looks like data corruption:
   //
   //   1. PULL SDA LOW  -- before the ninth SCL rise
   //   2. HOLD IT LOW   -- for the whole of the ninth HIGH period
   //   3. RELEASE IT    -- before the master can change SDA for the next bit
   //
   // WHY THE WINDOW IS BOUNDED AT BOTH ENDS BY SCL FALLING EDGES. §3.1.2 permits SDA to
   // change only while SCL is LOW. There are exactly two low phases adjacent to the ninth
   // high phase, and each one is the only legal place for one of the two transitions:
   //
   //   assert  in the low phase BEFORE the ninth rise   (after the eighth fall)
   //   release in the low phase AFTER  the ninth fall
   //
   // ASSERTING EARLY IS NOT "EARLY", IT IS ILLEGAL. If the slave pulls SDA low while SCL is
   // still HIGH -- during the eighth bit's high phase, say -- then SDA has fallen while SCL
   // is high, and that is a START condition (§3.1.1). Every device on the bus, including
   // the master, is entitled to read it as one. The slave has not acknowledged a byte; it
   // has restarted the transaction. This is the single most destructive timing error a
   // slave can make, and it is why the assert is gated on `scl_fall` rather than on the
   // byte-complete event that precedes it.
   //
   // RELEASING LATE CORRUPTS THE NEXT BIT. The master will drive the next data bit in the
   // low phase that follows the ninth fall. If the slave is still pulling SDA down, the
   // wired-AND means the master's one becomes a zero -- and on a read the master will see
   // its own bit come back wrong, which on a multi-master bus it is entitled to interpret
   // as losing arbitration (§3.1.8). A slave that releases late can therefore knock a
   // master off a bus it was winning.
   //
   // ONE SLOT, NOT A LEVEL. The output is a drive-low intent that is true for exactly one
   // SCL period. A design that held ACK as a level until the next byte would be driving
   // through the next byte's data bits.
   //
   // WHAT THIS BLOCK DOES NOT DECIDE. Whether to acknowledge at all. That is policy --
   // address match (18.4), a register that refuses a write (18.9), a receiver with nowhere
   // to put the byte (18.6) -- and it arrives here as `ack_en`. Separating the decision
   // from the timing is what lets each be tested on its own.
   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   // -----------------------------------------------------------------------------

   module i2c_slave_ack #(
      parameter integer CNT_W = 16
   ) (
      input  wire  clk,
      input  wire  rst_n,

      // From Chapter 18.2. The two falling edges that bound the window.
      input  wire  scl_fall,

      // One cycle, from whichever block just completed a byte: the address block (18.4) or
      // the receive datapath (18.6). It means "the eighth bit has been sampled".
      //
      // INTERFACE ASSUMPTION, and it is guaranteed rather than hoped for: this pulse always
      // arrives while SCL is HIGH, because a byte completes when its eighth bit is SAMPLED
      // and sampling happens at `scl_rise` (§3.1.2). So the next `scl_fall` is always the
      // low phase immediately before the ninth rise -- which is the one instant the assert
      // is allowed to happen. A producer that raised `byte_done` in a low phase would arm a
      // slot whose assert landed a full phase late, and the acknowledge would miss its
      // pulse entirely.
      input  wire  byte_done,

      // The DECISION, sampled when the slot is armed. 1 = acknowledge (pull SDA low),
      // 0 = do not (leave it released, which the master reads as a NACK).
      input  wire  ack_en,

      // Framing aborts the slot outright. A START or STOP mid-acknowledge means the
      // transaction this acknowledge belonged to no longer exists.
      input  wire  start_pulse,
      input  wire  stop_pulse,

      // DRIVE INTENT, never a level to be driven high. 1 = pull SDA low, 0 = release.
      output reg   sda_drive_low,

      // Exposed so Chapter 18.11 can see the slot and so a bench can check the boundaries.
      output reg   ack_active,      // the slot is in progress
      output reg   ack_armed,       // the byte is done, waiting for the eighth SCL fall
      output reg   [CNT_W-1:0] n_acks,
      output reg   [CNT_W-1:0] n_nacks
   );

      localparam [1:0] A_IDLE  = 2'd0,   // nothing owed
                       A_ARMED = 2'd1,   // byte complete, waiting for SCL to fall
                       A_DRIVE = 2'd2;   // driving through the ninth pulse

      reg [1:0] state;
      reg will_ack;

      always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
            // Reset RELEASES. A slave that came out of reset pulling SDA low would hold the
            // whole bus down and nothing else could lift it.
            sda_drive_low <= 1'b0;
            ack_active    <= 1'b0;
            ack_armed     <= 1'b0;
            will_ack      <= 1'b0;
            state         <= A_IDLE;
            n_acks        <= {CNT_W{1'b0}};
            n_nacks       <= {CNT_W{1'b0}};
         end else if (start_pulse || stop_pulse) begin
            // Framing wins over everything. Release immediately: the framing edge has
            // already happened, so this release cannot itself produce one.
            sda_drive_low <= 1'b0;
            ack_active    <= 1'b0;
            ack_armed     <= 1'b0;
            state         <= A_IDLE;
         end else begin
            case (state)
               A_IDLE: begin
                  if (byte_done) begin
                     // Arm, and latch the decision NOW. The inputs that produced it -- an
                     // address comparison, a register's writability -- are valid at byte
                     // completion and need not still be valid two SCL phases later.
                     will_ack   <= ack_en;
                     ack_armed  <= 1'b1;
                     state      <= A_ARMED;
                  end
               end

               A_ARMED: begin
                  // Wait for SCL to FALL. This is the whole of the "not early" rule: the
                  // assert may only happen in a low phase, because SDA falling while SCL
                  // is high is a START and not an acknowledge.
                  if (scl_fall) begin
                     ack_armed     <= 1'b0;
                     ack_active    <= 1'b1;
                     // A NACK is the ABSENCE of drive, not a driven one. There is no
                     // drive-high anywhere in a conforming I²C interface.
                     sda_drive_low <= will_ack;
                     if (will_ack) n_acks  <= n_acks  + 1'b1;
                     else          n_nacks <= n_nacks + 1'b1;
                     state         <= A_DRIVE;
                  end
               end

               A_DRIVE: begin
                  // The ninth pulse happens here: SCL rises, the master samples, SCL falls.
                  // Release on that fall -- the next low phase belongs to the master, which
                  // will drive the next bit into it.
                  if (scl_fall) begin
                     sda_drive_low <= 1'b0;
                     ack_active    <= 1'b0;
                     state         <= A_IDLE;
                  end
               end

               default: state <= A_IDLE;
            endcase
         end
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_ack.vhd — the same design in VHDL
   -- -----------------------------------------------------------------------------
   -- i2c_slave_ack.vhd
   -- The acknowledge bit: the right slot, and released in time. Same ports, generic, reset
   -- values and window boundaries as the SystemVerilog and Verilog versions.
   -- -----------------------------------------------------------------------------
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_slave_ack is
      generic (
         CNT_W : positive := 16
      );
      port (
         clk   : in  std_logic;
         rst_n : in  std_logic;

         -- From Chapter 18.2. The two falling edges that bound the window.
         scl_fall : in  std_logic;

         -- One cycle, from whichever block just completed a byte.
         --
         -- INTERFACE ASSUMPTION, guaranteed rather than hoped for: this pulse always arrives
         -- while SCL is HIGH, because a byte completes when its eighth bit is SAMPLED and
         -- sampling happens at scl_rise (§3.1.2). So the next scl_fall is always the low
         -- phase immediately before the ninth rise.
         byte_done : in  std_logic;

         -- The DECISION, latched when the slot is armed.
         ack_en : in  std_logic;

         -- Framing aborts the slot outright.
         start_pulse : in  std_logic;
         stop_pulse  : in  std_logic;

         -- DRIVE INTENT, never a level to be driven high.
         sda_drive_low : out std_logic;

         ack_active : out std_logic;
         ack_armed  : out std_logic;
         n_acks     : out unsigned(CNT_W-1 downto 0);
         n_nacks    : out unsigned(CNT_W-1 downto 0)
      );
   end entity i2c_slave_ack;

   architecture rtl of i2c_slave_ack is
      type state_t is (A_IDLE, A_ARMED, A_DRIVE);
      signal state : state_t := A_IDLE;

      signal r_drive, r_active, r_armed, will_ack : std_logic := '0';
      signal c_ack, c_nack : unsigned(CNT_W-1 downto 0) := (others => '0');
   begin

      process (clk, rst_n)
      begin
         if rst_n = '0' then
            -- Reset RELEASES. A slave out of reset pulling SDA low holds the whole bus down.
            r_drive  <= '0';
            r_active <= '0';
            r_armed  <= '0';
            will_ack <= '0';
            state    <= A_IDLE;
            c_ack    <= (others => '0');
            c_nack   <= (others => '0');
         elsif rising_edge(clk) then
            if start_pulse = '1' or stop_pulse = '1' then
               -- Framing wins over everything. Releasing here cannot itself produce a
               -- framing edge: the edge has already happened.
               r_drive  <= '0';
               r_active <= '0';
               r_armed  <= '0';
               state    <= A_IDLE;
            else
               case state is
                  when A_IDLE =>
                     if byte_done = '1' then
                        -- Arm, and latch the decision NOW: the inputs that produced it need
                        -- not still be valid two SCL phases later.
                        will_ack <= ack_en;
                        r_armed  <= '1';
                        state    <= A_ARMED;
                     end if;

                  when A_ARMED =>
                     -- Wait for SCL to FALL. The whole of the "not early" rule: SDA falling
                     -- while SCL is high is a START, not an acknowledge.
                     if scl_fall = '1' then
                        r_armed  <= '0';
                        r_active <= '1';
                        -- A NACK is the ABSENCE of drive, not a driven one.
                        r_drive  <= will_ack;
                        if will_ack = '1' then
                           c_ack <= c_ack + 1;
                        else
                           c_nack <= c_nack + 1;
                        end if;
                        state <= A_DRIVE;
                     end if;

                  when A_DRIVE =>
                     -- Release on the ninth fall: the next low phase belongs to the master.
                     if scl_fall = '1' then
                        r_drive  <= '0';
                        r_active <= '0';
                        state    <= A_IDLE;
                     end if;
               end case;
            end if;
         end if;
      end process;

      sda_drive_low <= r_drive;
      ack_active    <= r_active;
      ack_armed     <= r_armed;
      n_acks        <= c_ack;
      n_nacks       <= c_nack;

   end architecture rtl;

8a. The testbenches

Fifteen checks. The bench resolves the line as the bus does — wired-AND of the controller's and the slave's drive-lows — because the whole subject is when SDA is low, and a bench ignoring the slave's contribution could not see the acknowledge at all.

The critical observer counts SDA changing while SCL is HIGH. Every defect in this block moves SDA in the wrong phase, so they all surface there.

#TestProperty
T1reset releases SDAa target holding it low kills the bus
T2nothing happens without a bytethe false-positive test
T3a byte completing while SCL is HIGH does not assert yet§2's "not early" rule
T4... and it asserts on the falling edgethe first legal instant
T5it holds through the whole ninth high phase§3.1.4's "remains stable LOW"
T6and releases on the ninth fallbefore the master drives the next bit
T7the whole slot, with the master samplingLOW in the ninth pulse and nowhere else
T8a NACK is the absence of drive
T9the decision is latched at arming, not re-read
T10one slot, not a levelsilent through following bits
T11two bytes, two acknowledges
T12reset mid-acknowledge releases
T13a STOP mid-acknowledge abandons the slotadded after M8; see §9
T14and a repeated START does the samethe other half
T15an armed slot is abandoned toonot merely an active one
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_ack_tb.sv — the self-checking testbench
   // -----------------------------------------------------------------------------
   // i2c_slave_ack_tb.sv
   // Independent oracle for i2c_slave_ack.
   //
   // The bench drives the bus as a controller AND resolves the line as the bus does, so
   // the slave's drive-low actually pulls the wire down. That is essential here: the whole
   // subject is when SDA is low, and a bench that ignored the slave's contribution could
   // not see the acknowledge at all.
   //
   // The critical observer is the one that watches for SDA changing while SCL is HIGH.
   // Every defect in this block shows up there, because every one of them moves SDA in the
   // wrong phase. Every wait is bounded.
   // -----------------------------------------------------------------------------
   `timescale 1ns/1ps

   module i2c_slave_ack_tb;

      localparam integer HALF = 8;

      logic clk = 1'b0, rst_n = 1'b0;
      logic m_scl_low = 1'b0, m_sda_low = 1'b0;   // the controller's drive-low intents
      logic byte_done = 1'b0, ack_en = 1'b1;
      // Framing inputs, DRIVEN rather than tied off. Tying them to zero leaves the
      // abort path unexercised -- and a slot that survives framing keeps driving SDA
      // into a transfer that no longer exists.
      logic f_start = 1'b0, f_stop = 1'b0;

      logic s_sda_low;                            // the slave's drive-low intent

      // The bus, resolved exactly as the wired-AND does it: low if anybody pulls low.
      wire scl = ~m_scl_low;
      wire sda = ~(m_sda_low | s_sda_low);

      logic scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall;
      logic ack_active, ack_armed;
      logic [15:0] n_ack, n_nack;

      integer errors = 0;
      integer n, k;

      // ---- the observer that matters ---------------------------------------------
      //
      // SDA must never change while SCL is HIGH -- §3.1.2 -- except for the framing the
      // bench itself generates. The bench counts violations caused by the SLAVE by watching
      // the slave's own intent change while the line is high.
      integer sda_moved_high = 0;
      integer ack_low_cycles = 0;
      logic s_d = 1'b0;
      always @(posedge clk) begin
         if (rst_n) begin
            if ((s_sda_low !== s_d) && scl) sda_moved_high <= sda_moved_high + 1;
            if (s_sda_low) ack_low_cycles <= ack_low_cycles + 1;
         end
         s_d <= s_sda_low;
      end

      // Was SDA low, on the wire, during the high phase of the pulse just finished?
      integer sampled_low = 0, sampled_high = 0;
      always @(posedge clk) if (rst_n && scl_rise) begin
         if (!sda) sampled_low  <= sampled_low  + 1;
         else      sampled_high <= sampled_high + 1;
      end

      i2c_slave_sync #(.SYNC_DEPTH(2)) u_sync (
         .clk(clk), .rst_n(rst_n), .scl_pin(scl), .sda_pin(sda),
         .scl_q(scl_q), .sda_q(sda_q), .scl_rise(scl_rise), .scl_fall(scl_fall),
         .sda_rise(sda_rise), .sda_fall(sda_fall));

      i2c_slave_ack #(.CNT_W(16)) dut (
         .clk(clk), .rst_n(rst_n), .scl_fall(scl_fall),
         .byte_done(byte_done), .ack_en(ack_en),
         .start_pulse(f_start), .stop_pulse(f_stop),
         .sda_drive_low(s_sda_low),
         .ack_active(ack_active), .ack_armed(ack_armed),
         .n_acks(n_ack), .n_nacks(n_nack));

      always #5 clk = ~clk;

      task step;  begin @(posedge clk); @(negedge clk); end endtask
      task phase; begin for (n = 0; n < HALF; n = n + 1) step; end endtask

      task do_reset;
         begin
            @(negedge clk); rst_n = 1'b0;
            m_scl_low = 1'b0; m_sda_low = 1'b0; byte_done = 1'b0; ack_en = 1'b1;
            f_start = 1'b0; f_stop = 1'b0;
            sda_moved_high = 0; ack_low_cycles = 0; sampled_low = 0; sampled_high = 0;
            step; step;
            @(negedge clk); rst_n = 1'b1; phase;
         end
      endtask

      // One SCL pulse, starting and ending in the LOW phase. The master's data bit, if it
      // drives one, is placed in the low phase before the rise.
      task gen_pulse (input drive_low_during);
         begin
            @(negedge clk); m_scl_low = 1'b1; phase;         // low phase
            @(negedge clk); m_sda_low = drive_low_during; phase;
            @(negedge clk); m_scl_low = 1'b0; phase;         // high phase
            @(negedge clk); m_scl_low = 1'b1; phase;         // and back low
         end
      endtask

      task pulse_byte_done;
         begin @(negedge clk); byte_done = 1'b1; step; @(negedge clk); byte_done = 1'b0; end
      endtask

      task ck_bit (input [200*8:1] what, input g, input e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0b expected %0b", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      task ck_int (input [200*8:1] what, input integer g, input integer e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0d expected %0d", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      initial begin
         $display("=== i2c_slave_ack: the right slot, and released in time ===");

         // ----------------------------------------------------------------
         // T1. RESET RELEASES SDA. A slave holding SDA low out of reset takes the whole
         //     bus down and nothing else can lift it.
         // ----------------------------------------------------------------
         do_reset;
         $display("T1  reset releases SDA");
         ck_bit("T1 not driving", s_sda_low, 1'b0);
         ck_bit("T1 the line is high", sda, 1'b1);
         ck_bit("T1 no slot in progress", ack_active, 1'b0);

         // ----------------------------------------------------------------
         // T2. NOTHING HAPPENS WITHOUT A BYTE. Clock all day; the slave owes no
         //     acknowledge and must drive nothing. The false-positive test.
         // ----------------------------------------------------------------
         do_reset;
         for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0);
         $display("T2  with no byte complete, the slave drives nothing");
         ck_int("T2 never drove", ack_low_cycles, 0);
         ck_int("T2 and never moved SDA in a high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T3. THE ASSERT WAITS FOR SCL TO FALL. `byte_done` arrives while SCL is still
         //     HIGH -- which is exactly when the eighth bit is being sampled -- and the
         //     slave must NOT pull SDA down yet. SDA falling while SCL is high is a START
         //     (§3.1.1), so an early acknowledge is not early: it restarts the transaction.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); m_scl_low = 1'b0; phase;      // SCL HIGH
         pulse_byte_done;
         for (k = 0; k < 3; k = k + 1) step;
         $display("T3  a byte completing while SCL is HIGH does not assert the ACK yet");
         ck_bit("T3 the slot is armed", ack_armed, 1'b1);
         ck_bit("T3 but SDA is NOT being driven", s_sda_low, 1'b0);
         ck_int("T3 and SDA has not moved in a high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T4. ... AND ASSERTS ON THE FALL. The first legal instant.
         // ----------------------------------------------------------------
         @(negedge clk); m_scl_low = 1'b1;             // SCL falls
         for (k = 0; k < 6; k = k + 1) step;
         $display("T4  and it asserts on the falling edge, the first legal instant");
         ck_bit("T4 now driving", s_sda_low, 1'b1);
         ck_bit("T4 the slot is active", ack_active, 1'b1);
         ck_bit("T4 and the line is pulled low", sda, 1'b0);

         // ----------------------------------------------------------------
         // T5. IT HOLDS THROUGH THE WHOLE NINTH HIGH PHASE, which is what §3.1.4 requires:
         //     "it remains stable LOW during the HIGH period of this clock pulse".
         // ----------------------------------------------------------------
         @(negedge clk); m_scl_low = 1'b0; phase;      // the ninth HIGH phase
         ck_bit("T5 still driving through the high phase", s_sda_low, 1'b1);
         ck_bit("T5 the line is still low", sda, 1'b0);
         ck_int("T5 and SDA never moved during it", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T6. AND RELEASES ON THE NINTH FALL, before the master can drive the next bit.
         //     A slave still pulling SDA down in the following low phase turns the
         //     master's one into a zero -- which the master is entitled to read as losing
         //     arbitration (§3.1.8), so a late release can knock a master off the bus.
         // ----------------------------------------------------------------
         @(negedge clk); m_scl_low = 1'b1; phase;      // the ninth fall
         $display("T6  and releases on the ninth fall, before the next bit is driven");
         ck_bit("T6 released", s_sda_low, 1'b0);
         ck_bit("T6 the slot is over", ack_active, 1'b0);
         ck_bit("T6 and the line is free", sda, 1'b1);
         ck_int("T6 one acknowledge counted", n_ack, 1);

         // ----------------------------------------------------------------
         // T7. THE WHOLE SLOT, END TO END, with the master sampling it. The bus must read
         //     LOW at the ninth rising edge and at no other.
         // ----------------------------------------------------------------
         do_reset;
         gen_pulse(1'b0);                  // a data bit, master drives a one
         pulse_byte_done;                  // ... and the byte completes
         gen_pulse(1'b0);                  // the ninth pulse: the acknowledge
         gen_pulse(1'b0);                  // and the next bit slot
         $display("T7  the master samples LOW in the ninth pulse and HIGH in the others");
         ck_int("T7 exactly one low sample", sampled_low, 1);
         ck_int("T7 and the rest read high", sampled_high, 2);
         ck_int("T7 no SDA movement in any high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T8. A NACK IS THE ABSENCE OF DRIVE. With `ack_en` low the slave must leave the
         //     line released -- there is no drive-high, so a NACK is what the pull-up does
         //     when nobody objects.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); ack_en = 1'b0;
         pulse_byte_done;
         gen_pulse(1'b0);
         $display("T8  a NACK is the absence of drive, not a driven one");
         ck_int("T8 never pulled the line down", ack_low_cycles, 0);
         ck_int("T8 the master sampled high", sampled_low, 0);
         ck_int("T8 and it was counted as a NACK", n_nack, 1);
         ck_int("T8 with no acknowledge counted", n_ack, 0);

         // ----------------------------------------------------------------
         // T9. THE DECISION IS LATCHED WHEN THE SLOT IS ARMED. The inputs that produced it
         //     -- an address comparison, a register's writability -- are valid at byte
         //     completion and need not still be valid two SCL phases later.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); ack_en = 1'b1;
         pulse_byte_done;
         @(negedge clk); ack_en = 1'b0;         // withdrawn AFTER arming
         gen_pulse(1'b0);
         $display("T9  the decision is latched at arming, not re-read in the slot");
         ck_int("T9 the acknowledge still happened", n_ack, 1);
         ck_int("T9 and the master saw it", sampled_low, 1);

         // ----------------------------------------------------------------
         // T10. ONE SLOT, NOT A LEVEL. After the acknowledge the slave must be silent
         //      through the following data bits -- a design that held ACK as a level until
         //      the next byte would drive through them.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); ack_en = 1'b1;
         pulse_byte_done;
         gen_pulse(1'b0);                  // the acknowledge
         ack_low_cycles = 0;
         for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0);   // four more bit slots
         $display("T10 the acknowledge is one slot, not a level held into the next byte");
         ck_int("T10 silent for every following bit", ack_low_cycles, 0);

         // ----------------------------------------------------------------
         // T11. TWO BYTES IN A ROW each get exactly one acknowledge.
         // ----------------------------------------------------------------
         do_reset;
         for (k = 0; k < 2; k = k + 1) begin
            // `byte_done` must be raised while SCL is HIGH, because that is the only way it
            // can occur in reality: a byte completes when its eighth bit is sampled, and
            // sampling happens at the rising edge. Raising it in a low phase would arm a
            // slot whose assert lands a phase late -- a bench artefact, not a design defect.
            @(negedge clk); m_scl_low = 1'b0; phase;      // SCL high: the eighth bit
            pulse_byte_done;
            gen_pulse(1'b0);                              // the ninth pulse
         end
         $display("T11 two bytes, two acknowledges, no more");
         ck_int("T11 two acknowledges", n_ack, 2);
         ck_int("T11 the master saw two low samples", sampled_low, 2);
         ck_int("T11 and SDA never moved in a high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T12. RESET MID-SLOT RELEASES IMMEDIATELY. A slave reset while acknowledging
         //      must let go, even though letting go while SCL is high would normally be
         //      forbidden -- reset has no other option, and holding is worse.
         // ----------------------------------------------------------------
         do_reset;
         pulse_byte_done;
         @(negedge clk); m_scl_low = 1'b1; phase;         // assert the acknowledge
         ck_bit("T12 driving before reset", s_sda_low, 1'b1);
         @(negedge clk); rst_n = 1'b0; step; step;
         $display("T12 reset mid-acknowledge releases the line");
         ck_bit("T12 released by reset", s_sda_low, 1'b0);
         ck_bit("T12 and the bus is free", sda, 1'b1);

         // ----------------------------------------------------------------
         // T13. FRAMING ABORTS THE SLOT. A STOP arriving while the acknowledge is being
         //      driven means the transaction it belonged to no longer exists -- so the slot
         //      must be abandoned and the line released at once. A slot that survived
         //      framing would keep pulling SDA down through whatever the master does next,
         //      which on a bus the master is trying to release is a stuck line.
         // ----------------------------------------------------------------
         do_reset;
         pulse_byte_done;
         @(negedge clk); m_scl_low = 1'b1; phase;        // assert the acknowledge
         ck_bit("T13 driving before the framing event", s_sda_low, 1'b1);
         @(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
         step;
         $display("T13 a STOP mid-acknowledge abandons the slot and releases the line");
         ck_bit("T13 released by the STOP", s_sda_low, 1'b0);
         ck_bit("T13 the slot is over", ack_active, 1'b0);
         ck_bit("T13 and the bus is free", sda, 1'b1);

         // ----------------------------------------------------------------
         // T14. AND A START DOES THE SAME. The other half: a repeated START mid-acknowledge
         //      is the more likely of the two, because a master that abandons a transfer
         //      usually restarts rather than stopping.
         // ----------------------------------------------------------------
         do_reset;
         pulse_byte_done;
         @(negedge clk); m_scl_low = 1'b1; phase;
         ck_bit("T14 driving before the framing event", s_sda_low, 1'b1);
         @(negedge clk); f_start = 1'b1; step; @(negedge clk); f_start = 1'b0;
         step;
         $display("T14 a repeated START mid-acknowledge does the same");
         ck_bit("T14 released by the START", s_sda_low, 1'b0);
         ck_bit("T14 the slot is over", ack_active, 1'b0);

         // ----------------------------------------------------------------
         // T15. AN ARMED SLOT IS ALSO ABANDONED. Framing between byte completion and the
         //      falling edge must cancel the pending acknowledge, not merely the active one
         //      -- otherwise the slot fires one phase later into a transfer that has moved on.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); m_scl_low = 1'b0; phase;        // SCL HIGH
         pulse_byte_done;
         ck_bit("T15 the slot is armed", ack_armed, 1'b1);
         @(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
         ck_bit("T15 arming was cancelled", ack_armed, 1'b0);
         ack_low_cycles = 0;
         gen_pulse(1'b0);
         $display("T15 an armed slot is abandoned too, not merely an active one");
         ck_int("T15 and nothing was ever driven", ack_low_cycles, 0);

         if (errors == 0) $display("=== i2c_slave_ack: ALL CHECKS PASSED ===");
         else             $display("=== i2c_slave_ack: %0d CHECK(S) FAILED ===", errors);
         $finish;
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_ack_tb.v — the same tests in Verilog-2001
   // -----------------------------------------------------------------------------
   // i2c_slave_ack_tb.v
   // Independent oracle for i2c_slave_ack.
   //
   // The bench drives the bus as a controller AND resolves the line as the bus does, so
   // the slave's drive-low actually pulls the wire down. That is essential here: the whole
   // subject is when SDA is low, and a bench that ignored the slave's contribution could
   // not see the acknowledge at all.
   //
   // The critical observer is the one that watches for SDA changing while SCL is HIGH.
   // Every defect in this block shows up there, because every one of them moves SDA in the
   // wrong phase. Every wait is bounded.
   // -----------------------------------------------------------------------------
   `timescale 1ns/1ps

   module i2c_slave_ack_tb;

      localparam integer HALF = 8;

      reg clk = 1'b0, rst_n = 1'b0;
      reg m_scl_low = 1'b0, m_sda_low = 1'b0;   // the controller's drive-low intents
      reg byte_done = 1'b0, ack_en = 1'b1;
      // Framing inputs, DRIVEN rather than tied off. Tying them to zero leaves the
      // abort path unexercised -- and a slot that survives framing keeps driving SDA
      // into a transfer that no longer exists.
      reg f_start = 1'b0, f_stop = 1'b0;

      wire s_sda_low;                            // the slave's drive-low intent

      // The bus, resolved exactly as the wired-AND does it: low if anybody pulls low.
      wire scl = ~m_scl_low;
      wire sda = ~(m_sda_low | s_sda_low);

      wire scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall;
      wire ack_active, ack_armed;
      wire [15:0] n_ack, n_nack;

      integer errors = 0;
      integer n, k;

      // ---- the observer that matters ---------------------------------------------
      //
      // SDA must never change while SCL is HIGH -- §3.1.2 -- except for the framing the
      // bench itself generates. The bench counts violations caused by the SLAVE by watching
      // the slave's own intent change while the line is high.
      integer sda_moved_high = 0;
      integer ack_low_cycles = 0;
      reg s_d = 1'b0;
      always @(posedge clk) begin
         if (rst_n) begin
            if ((s_sda_low !== s_d) && scl) sda_moved_high <= sda_moved_high + 1;
            if (s_sda_low) ack_low_cycles <= ack_low_cycles + 1;
         end
         s_d <= s_sda_low;
      end

      // Was SDA low, on the wire, during the high phase of the pulse just finished?
      integer sampled_low = 0, sampled_high = 0;
      always @(posedge clk) if (rst_n && scl_rise) begin
         if (!sda) sampled_low  <= sampled_low  + 1;
         else      sampled_high <= sampled_high + 1;
      end

      i2c_slave_sync #(.SYNC_DEPTH(2)) u_sync (
         .clk(clk), .rst_n(rst_n), .scl_pin(scl), .sda_pin(sda),
         .scl_q(scl_q), .sda_q(sda_q), .scl_rise(scl_rise), .scl_fall(scl_fall),
         .sda_rise(sda_rise), .sda_fall(sda_fall));

      i2c_slave_ack #(.CNT_W(16)) dut (
         .clk(clk), .rst_n(rst_n), .scl_fall(scl_fall),
         .byte_done(byte_done), .ack_en(ack_en),
         .start_pulse(f_start), .stop_pulse(f_stop),
         .sda_drive_low(s_sda_low),
         .ack_active(ack_active), .ack_armed(ack_armed),
         .n_acks(n_ack), .n_nacks(n_nack));

      always #5 clk = ~clk;

      task step;  begin @(posedge clk); @(negedge clk); end endtask
      task phase; begin for (n = 0; n < HALF; n = n + 1) step; end endtask

      task do_reset;
         begin
            @(negedge clk); rst_n = 1'b0;
            m_scl_low = 1'b0; m_sda_low = 1'b0; byte_done = 1'b0; ack_en = 1'b1;
            f_start = 1'b0; f_stop = 1'b0;
            sda_moved_high = 0; ack_low_cycles = 0; sampled_low = 0; sampled_high = 0;
            step; step;
            @(negedge clk); rst_n = 1'b1; phase;
         end
      endtask

      // One SCL pulse, starting and ending in the LOW phase. The master's data bit, if it
      // drives one, is placed in the low phase before the rise.
      task gen_pulse (input drive_low_during);
         begin
            @(negedge clk); m_scl_low = 1'b1; phase;         // low phase
            @(negedge clk); m_sda_low = drive_low_during; phase;
            @(negedge clk); m_scl_low = 1'b0; phase;         // high phase
            @(negedge clk); m_scl_low = 1'b1; phase;         // and back low
         end
      endtask

      task pulse_byte_done;
         begin @(negedge clk); byte_done = 1'b1; step; @(negedge clk); byte_done = 1'b0; end
      endtask

      task ck_bit (input [200*8:1] what, input g, input e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0b expected %0b", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      task ck_int (input [200*8:1] what, input integer g, input integer e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0d expected %0d", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      initial begin
         $display("=== i2c_slave_ack: the right slot, and released in time ===");

         // ----------------------------------------------------------------
         // T1. RESET RELEASES SDA. A slave holding SDA low out of reset takes the whole
         //     bus down and nothing else can lift it.
         // ----------------------------------------------------------------
         do_reset;
         $display("T1  reset releases SDA");
         ck_bit("T1 not driving", s_sda_low, 1'b0);
         ck_bit("T1 the line is high", sda, 1'b1);
         ck_bit("T1 no slot in progress", ack_active, 1'b0);

         // ----------------------------------------------------------------
         // T2. NOTHING HAPPENS WITHOUT A BYTE. Clock all day; the slave owes no
         //     acknowledge and must drive nothing. The false-positive test.
         // ----------------------------------------------------------------
         do_reset;
         for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0);
         $display("T2  with no byte complete, the slave drives nothing");
         ck_int("T2 never drove", ack_low_cycles, 0);
         ck_int("T2 and never moved SDA in a high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T3. THE ASSERT WAITS FOR SCL TO FALL. `byte_done` arrives while SCL is still
         //     HIGH -- which is exactly when the eighth bit is being sampled -- and the
         //     slave must NOT pull SDA down yet. SDA falling while SCL is high is a START
         //     (§3.1.1), so an early acknowledge is not early: it restarts the transaction.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); m_scl_low = 1'b0; phase;      // SCL HIGH
         pulse_byte_done;
         for (k = 0; k < 3; k = k + 1) step;
         $display("T3  a byte completing while SCL is HIGH does not assert the ACK yet");
         ck_bit("T3 the slot is armed", ack_armed, 1'b1);
         ck_bit("T3 but SDA is NOT being driven", s_sda_low, 1'b0);
         ck_int("T3 and SDA has not moved in a high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T4. ... AND ASSERTS ON THE FALL. The first legal instant.
         // ----------------------------------------------------------------
         @(negedge clk); m_scl_low = 1'b1;             // SCL falls
         for (k = 0; k < 6; k = k + 1) step;
         $display("T4  and it asserts on the falling edge, the first legal instant");
         ck_bit("T4 now driving", s_sda_low, 1'b1);
         ck_bit("T4 the slot is active", ack_active, 1'b1);
         ck_bit("T4 and the line is pulled low", sda, 1'b0);

         // ----------------------------------------------------------------
         // T5. IT HOLDS THROUGH THE WHOLE NINTH HIGH PHASE, which is what §3.1.4 requires:
         //     "it remains stable LOW during the HIGH period of this clock pulse".
         // ----------------------------------------------------------------
         @(negedge clk); m_scl_low = 1'b0; phase;      // the ninth HIGH phase
         ck_bit("T5 still driving through the high phase", s_sda_low, 1'b1);
         ck_bit("T5 the line is still low", sda, 1'b0);
         ck_int("T5 and SDA never moved during it", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T6. AND RELEASES ON THE NINTH FALL, before the master can drive the next bit.
         //     A slave still pulling SDA down in the following low phase turns the
         //     master's one into a zero -- which the master is entitled to read as losing
         //     arbitration (§3.1.8), so a late release can knock a master off the bus.
         // ----------------------------------------------------------------
         @(negedge clk); m_scl_low = 1'b1; phase;      // the ninth fall
         $display("T6  and releases on the ninth fall, before the next bit is driven");
         ck_bit("T6 released", s_sda_low, 1'b0);
         ck_bit("T6 the slot is over", ack_active, 1'b0);
         ck_bit("T6 and the line is free", sda, 1'b1);
         ck_int("T6 one acknowledge counted", n_ack, 1);

         // ----------------------------------------------------------------
         // T7. THE WHOLE SLOT, END TO END, with the master sampling it. The bus must read
         //     LOW at the ninth rising edge and at no other.
         // ----------------------------------------------------------------
         do_reset;
         gen_pulse(1'b0);                  // a data bit, master drives a one
         pulse_byte_done;                  // ... and the byte completes
         gen_pulse(1'b0);                  // the ninth pulse: the acknowledge
         gen_pulse(1'b0);                  // and the next bit slot
         $display("T7  the master samples LOW in the ninth pulse and HIGH in the others");
         ck_int("T7 exactly one low sample", sampled_low, 1);
         ck_int("T7 and the rest read high", sampled_high, 2);
         ck_int("T7 no SDA movement in any high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T8. A NACK IS THE ABSENCE OF DRIVE. With `ack_en` low the slave must leave the
         //     line released -- there is no drive-high, so a NACK is what the pull-up does
         //     when nobody objects.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); ack_en = 1'b0;
         pulse_byte_done;
         gen_pulse(1'b0);
         $display("T8  a NACK is the absence of drive, not a driven one");
         ck_int("T8 never pulled the line down", ack_low_cycles, 0);
         ck_int("T8 the master sampled high", sampled_low, 0);
         ck_int("T8 and it was counted as a NACK", n_nack, 1);
         ck_int("T8 with no acknowledge counted", n_ack, 0);

         // ----------------------------------------------------------------
         // T9. THE DECISION IS LATCHED WHEN THE SLOT IS ARMED. The inputs that produced it
         //     -- an address comparison, a register's writability -- are valid at byte
         //     completion and need not still be valid two SCL phases later.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); ack_en = 1'b1;
         pulse_byte_done;
         @(negedge clk); ack_en = 1'b0;         // withdrawn AFTER arming
         gen_pulse(1'b0);
         $display("T9  the decision is latched at arming, not re-read in the slot");
         ck_int("T9 the acknowledge still happened", n_ack, 1);
         ck_int("T9 and the master saw it", sampled_low, 1);

         // ----------------------------------------------------------------
         // T10. ONE SLOT, NOT A LEVEL. After the acknowledge the slave must be silent
         //      through the following data bits -- a design that held ACK as a level until
         //      the next byte would drive through them.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); ack_en = 1'b1;
         pulse_byte_done;
         gen_pulse(1'b0);                  // the acknowledge
         ack_low_cycles = 0;
         for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0);   // four more bit slots
         $display("T10 the acknowledge is one slot, not a level held into the next byte");
         ck_int("T10 silent for every following bit", ack_low_cycles, 0);

         // ----------------------------------------------------------------
         // T11. TWO BYTES IN A ROW each get exactly one acknowledge.
         // ----------------------------------------------------------------
         do_reset;
         for (k = 0; k < 2; k = k + 1) begin
            // `byte_done` must be raised while SCL is HIGH, because that is the only way it
            // can occur in reality: a byte completes when its eighth bit is sampled, and
            // sampling happens at the rising edge. Raising it in a low phase would arm a
            // slot whose assert lands a phase late -- a bench artefact, not a design defect.
            @(negedge clk); m_scl_low = 1'b0; phase;      // SCL high: the eighth bit
            pulse_byte_done;
            gen_pulse(1'b0);                              // the ninth pulse
         end
         $display("T11 two bytes, two acknowledges, no more");
         ck_int("T11 two acknowledges", n_ack, 2);
         ck_int("T11 the master saw two low samples", sampled_low, 2);
         ck_int("T11 and SDA never moved in a high phase", sda_moved_high, 0);

         // ----------------------------------------------------------------
         // T12. RESET MID-SLOT RELEASES IMMEDIATELY. A slave reset while acknowledging
         //      must let go, even though letting go while SCL is high would normally be
         //      forbidden -- reset has no other option, and holding is worse.
         // ----------------------------------------------------------------
         do_reset;
         pulse_byte_done;
         @(negedge clk); m_scl_low = 1'b1; phase;         // assert the acknowledge
         ck_bit("T12 driving before reset", s_sda_low, 1'b1);
         @(negedge clk); rst_n = 1'b0; step; step;
         $display("T12 reset mid-acknowledge releases the line");
         ck_bit("T12 released by reset", s_sda_low, 1'b0);
         ck_bit("T12 and the bus is free", sda, 1'b1);

         // ----------------------------------------------------------------
         // T13. FRAMING ABORTS THE SLOT. A STOP arriving while the acknowledge is being
         //      driven means the transaction it belonged to no longer exists -- so the slot
         //      must be abandoned and the line released at once. A slot that survived
         //      framing would keep pulling SDA down through whatever the master does next,
         //      which on a bus the master is trying to release is a stuck line.
         // ----------------------------------------------------------------
         do_reset;
         pulse_byte_done;
         @(negedge clk); m_scl_low = 1'b1; phase;        // assert the acknowledge
         ck_bit("T13 driving before the framing event", s_sda_low, 1'b1);
         @(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
         step;
         $display("T13 a STOP mid-acknowledge abandons the slot and releases the line");
         ck_bit("T13 released by the STOP", s_sda_low, 1'b0);
         ck_bit("T13 the slot is over", ack_active, 1'b0);
         ck_bit("T13 and the bus is free", sda, 1'b1);

         // ----------------------------------------------------------------
         // T14. AND A START DOES THE SAME. The other half: a repeated START mid-acknowledge
         //      is the more likely of the two, because a master that abandons a transfer
         //      usually restarts rather than stopping.
         // ----------------------------------------------------------------
         do_reset;
         pulse_byte_done;
         @(negedge clk); m_scl_low = 1'b1; phase;
         ck_bit("T14 driving before the framing event", s_sda_low, 1'b1);
         @(negedge clk); f_start = 1'b1; step; @(negedge clk); f_start = 1'b0;
         step;
         $display("T14 a repeated START mid-acknowledge does the same");
         ck_bit("T14 released by the START", s_sda_low, 1'b0);
         ck_bit("T14 the slot is over", ack_active, 1'b0);

         // ----------------------------------------------------------------
         // T15. AN ARMED SLOT IS ALSO ABANDONED. Framing between byte completion and the
         //      falling edge must cancel the pending acknowledge, not merely the active one
         //      -- otherwise the slot fires one phase later into a transfer that has moved on.
         // ----------------------------------------------------------------
         do_reset;
         @(negedge clk); m_scl_low = 1'b0; phase;        // SCL HIGH
         pulse_byte_done;
         ck_bit("T15 the slot is armed", ack_armed, 1'b1);
         @(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
         ck_bit("T15 arming was cancelled", ack_armed, 1'b0);
         ack_low_cycles = 0;
         gen_pulse(1'b0);
         $display("T15 an armed slot is abandoned too, not merely an active one");
         ck_int("T15 and nothing was ever driven", ack_low_cycles, 0);

         if (errors == 0) $display("=== i2c_slave_ack: ALL CHECKS PASSED ===");
         else             $display("=== i2c_slave_ack: %0d CHECK(S) FAILED ===", errors);
         $finish;
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_ack_tb.vhd — the same tests in VHDL
   -- -----------------------------------------------------------------------------
   -- i2c_slave_ack_tb.vhd
   -- Independent oracle for i2c_slave_ack. Behavioural twin of the SystemVerilog and
   -- Verilog benches.
   --
   -- The bench drives the bus as a controller AND resolves the line as the bus does, so the
   -- slave's drive-low actually pulls the wire down. Essential here: the whole subject is
   -- when SDA is low. The critical observer watches for SDA changing while SCL is HIGH,
   -- because every defect in this block moves SDA in the wrong phase.
   -- -----------------------------------------------------------------------------
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_slave_ack_tb is
   end entity i2c_slave_ack_tb;

   architecture sim of i2c_slave_ack_tb is

      constant HALF : positive := 8;

      signal clk   : std_logic := '0';
      signal rst_n : std_logic := '0';
      signal m_scl_low, m_sda_low : std_logic := '0';
      signal byte_done : std_logic := '0';
      signal ack_en    : std_logic := '1';
      -- Framing inputs, DRIVEN rather than tied off. Tying them to zero leaves the
      -- abort path unexercised.
      signal f_start, f_stop : std_logic := '0';

      signal s_sda_low : std_logic;

      -- The bus, resolved exactly as the wired-AND does it.
      signal scl, sda : std_logic;

      signal scl_q, sda_q : std_logic;
      signal scl_rise, scl_fall, sda_rise, sda_fall : std_logic;
      signal ack_active, ack_armed : std_logic;
      signal n_ack, n_nack : unsigned(15 downto 0);

      signal halt : boolean := false;

      signal sda_moved_high, ack_low_cycles : integer := 0;
      signal sampled_low, sampled_high : integer := 0;
      signal clr : boolean := false;

   begin

      scl <= not m_scl_low;
      sda <= not (m_sda_low or s_sda_low);

      u_sync : entity work.i2c_slave_sync
         generic map (SYNC_DEPTH => 2)
         port map (clk => clk, rst_n => rst_n, scl_pin => scl, sda_pin => sda,
            scl_q => scl_q, sda_q => sda_q, scl_rise => scl_rise, scl_fall => scl_fall,
            sda_rise => sda_rise, sda_fall => sda_fall);

      dut : entity work.i2c_slave_ack
         generic map (CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_fall => scl_fall,
            byte_done => byte_done, ack_en => ack_en,
            start_pulse => f_start, stop_pulse => f_stop,
            sda_drive_low => s_sda_low,
            ack_active => ack_active, ack_armed => ack_armed,
            n_acks => n_ack, n_nacks => n_nack);

      clkgen : process
      begin
         while not halt loop
            clk <= '0'; wait for 5 ns;
            clk <= '1'; wait for 5 ns;
         end loop;
         wait;
      end process;

      obs : process (clk, clr)
         variable s_d : std_logic := '0';
      begin
         if clr then
            sda_moved_high <= 0; ack_low_cycles <= 0;
            sampled_low <= 0; sampled_high <= 0;
         elsif rising_edge(clk) then
            if rst_n = '1' then
               if s_sda_low /= s_d and scl = '1' then
                  sda_moved_high <= sda_moved_high + 1;
               end if;
               if s_sda_low = '1' then ack_low_cycles <= ack_low_cycles + 1; end if;
               if scl_rise = '1' then
                  if sda = '0' then sampled_low  <= sampled_low  + 1;
                  else              sampled_high <= sampled_high + 1; end if;
               end if;
            end if;
            s_d := s_sda_low;
         end if;
      end process;

      stim : process
         variable err : integer := 0;
         variable k : integer;

         procedure step is
         begin
            wait until rising_edge(clk); wait until falling_edge(clk);
         end procedure;

         procedure phase is
         begin
            for i in 1 to HALF loop step; end loop;
         end procedure;

         procedure do_reset is
         begin
            wait until falling_edge(clk);
            rst_n <= '0'; m_scl_low <= '0'; m_sda_low <= '0';
            byte_done <= '0'; ack_en <= '1'; f_start <= '0'; f_stop <= '0';
            clr <= true; wait for 1 ns; clr <= false;
            step; step;
            wait until falling_edge(clk); rst_n <= '1';
            phase;
         end procedure;

         -- One SCL pulse, starting and ending in the LOW phase.
         procedure gen_pulse (drive_low_during : std_logic) is
         begin
            wait until falling_edge(clk); m_scl_low <= '1'; phase;
            wait until falling_edge(clk); m_sda_low <= drive_low_during; phase;
            wait until falling_edge(clk); m_scl_low <= '0'; phase;
            wait until falling_edge(clk); m_scl_low <= '1'; phase;
         end procedure;

         procedure pulse_byte_done is
         begin
            wait until falling_edge(clk); byte_done <= '1';
            step;
            wait until falling_edge(clk); byte_done <= '0';
         end procedure;

         procedure ck_bit (what : string; g : std_logic; e : std_logic) is
         begin
            if g /= e then
               report "  FAIL " & what severity note;
               err := err + 1;
            end if;
         end procedure;

         procedure ck_int (what : string; g : integer; e : integer) is
         begin
            if g /= e then
               report "  FAIL " & what & ": got " & integer'image(g)
                      & " expected " & integer'image(e) severity note;
               err := err + 1;
            end if;
         end procedure;

      begin
         report "=== i2c_slave_ack: the right slot, and released in time ===" severity note;

         -- T1. Reset releases SDA.
         do_reset;
         report "T1  reset releases SDA" severity note;
         ck_bit("T1 not driving", s_sda_low, '0');
         ck_bit("T1 the line is high", sda, '1');
         ck_bit("T1 no slot in progress", ack_active, '0');

         -- T2. Nothing happens without a byte.
         do_reset;
         for k in 0 to 3 loop gen_pulse('0'); end loop;
         report "T2  with no byte complete, the slave drives nothing" severity note;
         ck_int("T2 never drove", ack_low_cycles, 0);
         ck_int("T2 and never moved SDA in a high phase", sda_moved_high, 0);

         -- T3. The assert waits for SCL to FALL.
         do_reset;
         wait until falling_edge(clk); m_scl_low <= '0'; phase;      -- SCL HIGH
         pulse_byte_done;
         for k in 0 to 2 loop step; end loop;
         report "T3  a byte completing while SCL is HIGH does not assert the ACK yet"
                severity note;
         ck_bit("T3 the slot is armed", ack_armed, '1');
         ck_bit("T3 but SDA is NOT being driven", s_sda_low, '0');
         ck_int("T3 and SDA has not moved in a high phase", sda_moved_high, 0);

         -- T4. ... and asserts on the fall.
         wait until falling_edge(clk); m_scl_low <= '1';
         for k in 0 to 5 loop step; end loop;
         report "T4  and it asserts on the falling edge, the first legal instant"
                severity note;
         ck_bit("T4 now driving", s_sda_low, '1');
         ck_bit("T4 the slot is active", ack_active, '1');
         ck_bit("T4 and the line is pulled low", sda, '0');

         -- T5. It holds through the whole ninth high phase.
         wait until falling_edge(clk); m_scl_low <= '0'; phase;
         ck_bit("T5 still driving through the high phase", s_sda_low, '1');
         ck_bit("T5 the line is still low", sda, '0');
         ck_int("T5 and SDA never moved during it", sda_moved_high, 0);

         -- T6. And releases on the ninth fall.
         wait until falling_edge(clk); m_scl_low <= '1'; phase;
         report "T6  and releases on the ninth fall, before the next bit is driven"
                severity note;
         ck_bit("T6 released", s_sda_low, '0');
         ck_bit("T6 the slot is over", ack_active, '0');
         ck_bit("T6 and the line is free", sda, '1');
         ck_int("T6 one acknowledge counted", to_integer(n_ack), 1);

         -- T7. The whole slot, with the master sampling it.
         do_reset;
         gen_pulse('0');
         pulse_byte_done;
         gen_pulse('0');
         gen_pulse('0');
         report "T7  the master samples LOW in the ninth pulse and HIGH in the others"
                severity note;
         ck_int("T7 exactly one low sample", sampled_low, 1);
         ck_int("T7 and the rest read high", sampled_high, 2);
         ck_int("T7 no SDA movement in any high phase", sda_moved_high, 0);

         -- T8. A NACK is the absence of drive.
         do_reset;
         wait until falling_edge(clk); ack_en <= '0';
         pulse_byte_done;
         gen_pulse('0');
         report "T8  a NACK is the absence of drive, not a driven one" severity note;
         ck_int("T8 never pulled the line down", ack_low_cycles, 0);
         ck_int("T8 the master sampled high", sampled_low, 0);
         ck_int("T8 and it was counted as a NACK", to_integer(n_nack), 1);
         ck_int("T8 with no acknowledge counted", to_integer(n_ack), 0);

         -- T9. The decision is latched when the slot is armed.
         do_reset;
         wait until falling_edge(clk); ack_en <= '1';
         pulse_byte_done;
         wait until falling_edge(clk); ack_en <= '0';      -- withdrawn AFTER arming
         gen_pulse('0');
         report "T9  the decision is latched at arming, not re-read in the slot"
                severity note;
         ck_int("T9 the acknowledge still happened", to_integer(n_ack), 1);
         ck_int("T9 and the master saw it", sampled_low, 1);

         -- T10. One slot, not a level.
         do_reset;
         wait until falling_edge(clk); ack_en <= '1';
         pulse_byte_done;
         gen_pulse('0');
         clr <= true; wait for 1 ns; clr <= false;
         for k in 0 to 3 loop gen_pulse('0'); end loop;
         report "T10 the acknowledge is one slot, not a level held into the next byte"
                severity note;
         ck_int("T10 silent for every following bit", ack_low_cycles, 0);

         -- T11. Two bytes, two acknowledges.
         do_reset;
         for k in 0 to 1 loop
            -- byte_done must be raised while SCL is HIGH, as it can only occur in reality.
            wait until falling_edge(clk); m_scl_low <= '0'; phase;
            pulse_byte_done;
            gen_pulse('0');
         end loop;
         report "T11 two bytes, two acknowledges, no more" severity note;
         ck_int("T11 two acknowledges", to_integer(n_ack), 2);
         ck_int("T11 the master saw two low samples", sampled_low, 2);
         ck_int("T11 and SDA never moved in a high phase", sda_moved_high, 0);

         -- T12. Reset mid-slot releases immediately.
         do_reset;
         pulse_byte_done;
         wait until falling_edge(clk); m_scl_low <= '1'; phase;
         ck_bit("T12 driving before reset", s_sda_low, '1');
         wait until falling_edge(clk); rst_n <= '0'; step; step;
         report "T12 reset mid-acknowledge releases the line" severity note;
         ck_bit("T12 released by reset", s_sda_low, '0');
         ck_bit("T12 and the bus is free", sda, '1');

         -- T13. Framing aborts the slot.
         do_reset;
         pulse_byte_done;
         wait until falling_edge(clk); m_scl_low <= '1'; phase;
         ck_bit("T13 driving before the framing event", s_sda_low, '1');
         wait until falling_edge(clk); f_stop <= '1';
         step;
         wait until falling_edge(clk); f_stop <= '0';
         step;
         report "T13 a STOP mid-acknowledge abandons the slot and releases the line"
                severity note;
         ck_bit("T13 released by the STOP", s_sda_low, '0');
         ck_bit("T13 the slot is over", ack_active, '0');
         ck_bit("T13 and the bus is free", sda, '1');

         -- T14. And a START does the same.
         do_reset;
         pulse_byte_done;
         wait until falling_edge(clk); m_scl_low <= '1'; phase;
         ck_bit("T14 driving before the framing event", s_sda_low, '1');
         wait until falling_edge(clk); f_start <= '1';
         step;
         wait until falling_edge(clk); f_start <= '0';
         step;
         report "T14 a repeated START mid-acknowledge does the same" severity note;
         ck_bit("T14 released by the START", s_sda_low, '0');
         ck_bit("T14 the slot is over", ack_active, '0');

         -- T15. An armed slot is abandoned too.
         do_reset;
         wait until falling_edge(clk); m_scl_low <= '0'; phase;
         pulse_byte_done;
         ck_bit("T15 the slot is armed", ack_armed, '1');
         wait until falling_edge(clk); f_stop <= '1';
         step;
         wait until falling_edge(clk); f_stop <= '0';
         ck_bit("T15 arming was cancelled", ack_armed, '0');
         clr <= true; wait for 1 ns; clr <= false;
         gen_pulse('0');
         report "T15 an armed slot is abandoned too, not merely an active one" severity note;
         ck_int("T15 and nothing was ever driven", ack_low_cycles, 0);

         if err = 0 then
            report "=== i2c_slave_ack: ALL CHECKS PASSED ===" severity note;
         else
            report "=== i2c_slave_ack: " & integer'image(err) & " CHECK(S) FAILED ==="
                   severity note;
         end if;
         halt <= true;
         wait;
      end process;

   end architecture sim;

8b. Execution

DesignSystemVerilogVerilog-2001VHDLFinish
i2c_slave_ackPASS 15/15PASS 15/15PASS 15/158980 ns, all three

9. Mutation Testing

Ten defects. Three survived the first pass, and the three reasons are worth separating because only one was a real coverage gap.

#Injected defectExpected detectionResult
M1assert without waiting for the fall — a START, not an acknowledgeT3KILLED (17)
M2never release: the acknowledge becomes a levelT10KILLED (7)
M3inverted policyT7, T8KILLED (14)
M4a NACK is drivenT8KILLED (3)
M5the decision re-read in the slotT9KILLED (2)
M6release one phase early, mid high phaseT5, T7KILLED (13)
M7reset leaves SDA drivenT1KILLED (10)
M8framing does not abort the slotT13 newKILLED (8)
M9the slot arms on any cycleT2KILLED (10)
M10the acknowledge and NACK counters swappedT8KILLED (6)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
baseline: PASS   (verified before injecting anything)
valid mutants: 10   killed: 10   survived: 0   equivalent: 0   invalid: 0
restored: PASS

Two survivors were invalid mutants, and one was a real gap

M1 and M6, as first written, were inert. Both wrapped a case arm's body in if (1'b1), which changes nothing — the body still contained its own if (scl_fall). Re-injected against the actual wait condition, using the preceding comment line to disambiguate the two identical if (scl_fall) statements, both die immediately: M1 on seventeen checks, M6 on thirteen.

M8 was the real gap: the bench had tied the framing inputs to zero.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
.start_pulse(1'b0), .stop_pulse(1'b0),

So the abort path was never exercised. A target whose acknowledge survives framing keeps pulling SDA down into a transfer that no longer exists — and if the master is trying to issue a STOP, the target's held-low SDA prevents the STOP from happening at all. That is one of the two ways an I²C bus wedges permanently.

The fix was to drive those inputs and add three tests: a STOP mid-acknowledge (T13), a repeated START mid-acknowledge (T14), and — the one that would otherwise still be missing — framing arriving while the slot is merely armed (T15). An armed slot that survives framing fires one phase later into a transfer that has moved on.

10. Verification Connection — Asserting an Ownership Window

Azvya Education Pvt. Ltd.VLSI Mentor
slave_ack_sva.sv — the window as properties, and the one that needs the bus
   // 1. NEVER MOVE SDA WHILE SCL IS HIGH. The single property that catches every timing
   //    defect in this block, because all of them move SDA in the wrong phase. Note it is
   //    stated on the DRIVE INTENT, not on the line -- the line is the wired-AND and the
   //    master may legitimately move it.
   property p_no_sda_change_while_scl_high;
      @(posedge clk) disable iff (!rst_n)
         (scl_q && $past(scl_q)) |-> $stable(sda_drive_low);
   endproperty
   a_phase: assert property (p_no_sda_change_while_scl_high);

   // 2. THE SLOT IS ONE SCL PERIOD. Between assert and release there is exactly one
   //    falling edge -- so `ack_active` may never span two.
   property p_one_period;
      @(posedge clk) disable iff (!rst_n)
         (ack_active && scl_fall) |=> !ack_active;
   endproperty
   a_one: assert property (p_one_period);

   // 3. DRIVE ONLY INSIDE THE SLOT. The drive-low may not be asserted when no slot is
   //    active -- which is mutation M10's "level" defect and M9's "arms anywhere".
   property p_drive_implies_slot;
      @(posedge clk) disable iff (!rst_n) sda_drive_low |-> ack_active;
   endproperty
   a_own: assert property (p_drive_implies_slot);

   // 4. FRAMING RELEASES. Within one cycle of a framing event the drive is gone.
   property p_framing_releases;
      @(posedge clk) disable iff (!rst_n)
         (start_pulse || stop_pulse) |=> !sda_drive_low;
   endproperty
   a_abort: assert property (p_framing_releases);

   // WHAT CANNOT BE ASSERTED FROM INSIDE THIS BLOCK, and it is the interesting one.
   //
   // "the master sampled our acknowledge" is not a property of this block at all. It
   // depends on the master's sampling instant, which is on the other side of the bus. The
   // nearest internal property -- drive asserted before the rise and held through the high
   // phase -- is necessary and not sufficient, because a master sampling outside its own
   // high phase would miss a perfectly legal acknowledge.
   //
   // So the check that the acknowledge WORKED belongs in the bench, as an observation of
   // the WIRE at the controller's sampling instant. That is test T7, and it is why the
   // bench resolves the line rather than watching the DUT's output.
   //
   // COVERAGE:
   //
   //   cover: an ACK  (drive asserted)          every addressed byte
   //   cover: a NACK  (drive withheld)          a refused write, an unmatched address
   //   cover: framing during an ACTIVE slot     T13/T14 -- an aborted transfer
   //   cover: framing during an ARMED slot      T15 -- a narrower window still
   //   illegal_bin: drive asserted while scl_q  the §2 violation, by construction

11. FPGA and ASIC Implications

On an FPGA this is the first block in the module whose output reaches a pad, so it is where the three-signal pin form becomes concrete: sda_drive_low drives the tri-state buffer's output enable with the data input tied low. The readback that 18.2 consumes comes from the buffer's input, never from a copy of the enable — the same requirement Chapter 17.1 §9 stated for the master, and the same defect if it is broken.

The timing constraint of §7 is the FPGA-relevant number: three system clocks inside the SCL low phase with a two-deep synchroniser. Deepening the synchroniser for a faster system clock (18.2 §11) consumes that margin directly, which is the one place where the two chapters' parameters interact.

On an ASIC, the pad's input filter adds to the synchroniser latency and therefore to the same inequality — so a design that is comfortable in simulation can be marginal in silicon if the filter delay was not counted. That is a genuine reason to know the inequality rather than to rely on a healthy ratio.

The other ASIC-specific point is power-up. Before reset deasserts, the pad must be released: a target that holds SDA low while its own logic is undefined prevents every other device on the board from communicating, and there is no recovery from outside because the wired-AND means nobody can lift the line. Test T1 exists for that, and it is the reason sda_drive_low resets low rather than being left to the state machine's initial value.

12. Debugging — The Target That Restarted Every Transaction It Answered

Symptom

A target is addressed and appears to acknowledge. The master then reports that its own transaction never progressed: after the address byte it sees a START condition it did not generate, and its transaction controller abandons the transfer and retries. The retry behaves identically. A logic analyser decoding the bus shows START, address, then a second START -- with no acknowledge anywhere.

Root Cause

The acknowledge was asserted on byte completion rather than on the following falling edge of SCL. Because a byte completes at a rising edge, SCL is still high at that moment -- so the assert pulled SDA low during a high phase, which section 3.1.1 defines as a START condition. The target did not acknowledge late or early in any ordinary sense: it emitted framing. Nothing was wrong with the address comparison, the selection logic or the policy; the byte was correctly identified as one to acknowledge, and the acknowledgement itself was the transaction-destroying event.

Fix
Gate the assert on scl_fall, which is mutation M1 in reverse. Then fix the bench, because that is what let it through: the bench raised byte_done while SCL was LOW, so a falling edge was always already pending and the wait had nothing to wait for. Raising it while SCL is HIGH -- which is the only way it can occur in reality, and is now stated as an interface assumption in the port comment -- makes test T3 meaningful: it asserts that the drive is STILL not asserted while the slot is armed. A bench whose stimulus cannot violate a precondition cannot test the code that enforces it.

Three generalisations.

A target produced framing without a framing generator. Nothing in this block knows what a START is; it pulled a line low at the wrong time and the bus supplied the meaning. Any block with SDA ownership can manufacture framing by accident, which is why Chapter 17.6 §4 needed two different releases and why this one waits for an edge it does not otherwise care about.

The bench's stimulus was impossible, and that made a test vacuous. Pulsing byte_done in a low phase is not merely unrealistic — it removes the condition T3 exists to check. A precondition that the stimulus cannot violate is a precondition that is not being enforced by anything the tests can see.

The symptom named the wrong device. The master reported a spurious START, so the investigation went looking for a second master. The START was real, legal to detect, and produced by the device that was trying to be helpful.

13. Common Misconceptions

"The acknowledge is the ninth data bit." It is an ownership transfer that occupies a clock pulse. Treating it as data means driving it from wherever data comes from, at whatever time data is driven. §1.

"Asserting the acknowledge early is harmless." SDA falling while SCL is high is a START. An early acknowledge does not acknowledge; it restarts the transaction. §2, §12.

"Releasing late is a minor timing slip." It turns the master's next one into a zero, which a master may read as losing arbitration — so a late release can unseat a master. §2.

"Hold the acknowledge until the next byte." Then the target drives through the next byte's data bits. The slot is one SCL period. §3.

"A NACK means driving SDA high." There is no drive-high. A NACK is what the pull-up does when nobody objects. §4.

"The acknowledge decision can be read when the slot fires." Its inputs are valid at byte completion, two SCL phases earlier. Latch it when arming. §4.

"Any block can raise byte_done." It must arrive while SCL is high, because a byte completes at a sampling edge. A low-phase pulse arms a slot whose assert lands a phase late. §5.

"A fast enough system clock makes the timing automatic." It makes it comfortable. The inequality is real — synchroniser latency plus one cycle must fit inside the SCL low phase — and a pad filter eats into it. §7, §11.

"Framing during an acknowledge is a corner case." A target that keeps driving through a STOP prevents the STOP from happening, which wedges the bus. §9.

"A bench that never sets an input is testing the default." It is testing nothing about that input. Tied-off ports are invisible coverage holes. §9.

14. Reason It Through

Why is asserting the acknowledge one cycle after byte completion a protocol violation rather than a timing error?

Because a byte completes at a rising edge, so SCL is still high — and an SDA fall while SCL is high is a START. The target emits framing rather than an acknowledge. §2, §12.

Both ends of the acknowledge window are falling edges. Why not one falling and one rising?

Because §3.1.2 permits SDA to change only while SCL is low, and the two transitions must happen in the two low phases adjacent to the ninth high phase. A rising edge is never a legal instant to change SDA. §2.

A target releases its acknowledge one phase late. What can the master conclude, and why is that worse than a missing acknowledge?

Its next transmitted one comes back as a zero, which §3.1.8 lets it read as losing arbitration — so it abandons a transfer it was winning. A missing acknowledge is a clean, reportable failure; this is a false arbitration loss. §2.

Why latch the acknowledge decision when the slot arms rather than reading it in the slot?

Because the inputs that produced it — an address comparison, a register's writability — are valid at byte completion and need not still be valid two SCL phases later. §4.

State the clock-ratio inequality and say which obligation it threatens first.

Synchroniser latency plus one cycle to assert must fit inside the SCL low phase — three system clocks for a two-deep synchroniser. It threatens the acknowledge first, because that is the only obligation bounded inside a single low phase. §7.

Two mutants survived as inert injections. What is the general tell, and what is its mirror image?

A survivor whose mutation cannot change behaviour. Its mirror is a kill with zero failure lines — a mutant that failed to elaborate. In both the verdict is uninformative and the failure count is the evidence. §9.

Why is a tied-off bench input worse than a missing test?

Because it looks like configuration. A missing test is absent from the list; a tied-off port appears in the instantiation as a decision, and no pass rate or coverage figure distinguishes it from a deliberate one. §9.

15. Understanding Check

16. Summary

Three obligations, not one: pull SDA low before the ninth rise, hold it through the high phase, release before the master drives again. Getting the first right and missing either of the others presents as data corruption.

The window is bounded at both ends by falling edges, because §3.1.2 permits SDA to change only while SCL is low and there are exactly two such phases adjacent to the ninth pulse.

Asserting early is not early — it is a START. A premature acknowledge restarts the transaction instead of acknowledging a byte, and it is the most destructive timing error a target can make.

Releasing late can unseat a master, by turning its next one into a zero that it may read as losing arbitration.

One slot, not a level, or the target drives through the next byte's data.

The decision is policy and arrives as an input, latched when the slot arms — because its own inputs are valid two SCL phases earlier.

A NACK is the absence of drive. There is no drive-high anywhere in a conforming interface.

byte_done arrives while SCL is high, guaranteed, because a byte completes at a sampling edge — and that guarantee is what makes the next falling edge the right instant.

The clock ratio is a real inequality: synchroniser latency plus one cycle must fit inside the SCL low phase. Measured at a two-cycle phase, the acknowledge lands in the high phase and becomes a START.

Ten mutants, ten killed — after three survived: two were inert injections whose mutation could not change behaviour, and one was a genuine hole created by tying the framing inputs to zero in the bench.

Tied-off inputs are invisible coverage holes, because they read as configuration rather than as untested ports.

17. What Comes Next

The target can answer. Chapter 18.6 makes it listen — assembling written bytes and handing them on exactly once.

Its subject is the hand-off rather than the value: a byte delivered twice is as bad as one dropped, and both are invisible to a test that only compares what arrived. It is also where a gating question becomes sharp, because the acknowledge pulse this chapter just built is not a data bit — and a receive path left enabled through it shifts the acknowledge in, displacing every byte after the first by one bit.

Continue learning