Skip to content
VLSI Mentor

I²C · Module 18

Repeated START, Transaction State and Clock Stretching

The layer that knows where in a transaction the slave is. Separates protocol state from application state, starts a read inside the acknowledge slot, and builds the wait state — the target's only legitimate way to say not yet.

Every block built so far knows how to do one thing and has no idea when to do it. 18.6 can receive a byte but cannot know whether that byte is a register pointer or data. 18.7 can send a byte but cannot know whether a read has begun or ended. This chapter builds the layer that knows.

1. The State Split

§3.1.10 note 4 requires a device to reset its bus logic on a START. 18.9 §2 argued that this cannot mean all its state, because a device that cleared the register pointer on a START could not implement the combined transfer the specification itself describes.

So there are two kinds of state, and the boundary between them is a module boundary:

cleared by a STARTwhere it lives
protocol stateyes — note 4here: the phase, the direction, the byte index
application stateno18.9: the pointer, the register contents
A block diagram. Framing events from chapter 18.3 and an address match from chapter 18.4 feed a phase tracker holding the phase, the direction and a byte index. The phase tracker produces a receive enable for chapter 18.6, a pointer-or-data flag for chapter 18.9, and a transmit start for chapter 18.7. The master's acknowledge from chapter 18.8 produces a transmit continue. A stall request from the application feeds a wait-state block that pulls SCL low. The register file is drawn below the boundary, outside the cleared region.Framing18.3 — clears everythingaddr_done + match18.4 — opens a phasePhase / dir / indexprotocol staterx_enableto 18.6rx_is_pointerto 18.9tx_start / continueto 18.7mack_valid + ack18.8Wait statepull SCL lowstall_reqthe application12
Figure 1 — the transaction layer. It consumes framing and address events and produces permissions. Everything it holds is cleared by a START; the register file below it is not.

2. A Repeated START Restarts the Framing, Not the Application

This is the property the whole module has been building towards, and it is now one test. The bench writes a register through a real bus, issues a repeated START, and asserts both halves:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
before the restart    pointer = 6, register 5 = 0x7C
after the restart     phase gone, direction gone, byte index gone
                      pointer STILL 6, register 5 STILL 0x7C

3. Starting a Read Inside the Acknowledge Slot

Here is a defect that a unit testbench cannot find, and the reason this chapter's bench is an integration bench.

On a read, the slave must have its first bit on SDA before the master's first rising edge after the address acknowledge. 18.7 places that bit on the first falling edge after it is started — and the fall that must carry it is the one terminating the ninth pulse.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tx_start at the END of the slot   ->  next fall is a bit time later
                                      the master clocks a bit nobody drove
tx_start at the BEGINNING         ->  the transmitter is loaded and waiting
                                      the terminating fall carries bit 7

This is also why ack_active is an input rather than a one-cycle ack_done: this block needs the slot's leading edge, and reconstructing an edge from a level it already receives is cheaper than asking 18.5 for a new output.

4. Clock Stretching — The Only Way to Say "Not Yet"

§3.1.9: the slave may hold the SCL line LOW to force the master into a wait state. It is the only mechanism a target has for needing more time, and it has exactly two rules.

A stretch is a pull-down, never a drive-high

There is no drive-high of SCL anywhere in this design. The slave adds its own low to a line the master has already taken low, and when it is ready it releases — the master's next rising edge then happens on the master's own schedule.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
scl_drive_low = stretching      the only SCL output this block has

A stretch may only begin in the low phase

stretching has exactly one assignment that can set it, and its condition contains scl_fall:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (ack_last_fall && stall_req) stretching <= 1;

The master releases SCL and nothing happens, because the slave has not finished

10 cycles
Ten cycles. The master's SCL pull-down is asserted for cycles zero and one, then released from cycle two onward. The slave's SCL pull-down asserts at cycle two and stays asserted through cycle six, releasing at cycle seven. The resolved SCL line is low for cycles zero through six and rises at cycle seven. An acknowledge-slot flag is high for cycles zero and one. A stretching flag is high for cycles two through six.the acknowledge slotthe acknowledge slotthe wait statethe wait statethe slot's closing fallthe slot's closing fallengage — SCL already lowengage — SCL already lowmaster released; line stays lowmaster released; line stayslowreleased: the master's risereleased: the master's risemaster pullslave pullscl_busack_activestretchingt0t1t2t3t4t5t6t7t8t9
Figure 2 — a wait state. The acknowledge slot's terminating fall engages the stretch; the master releases SCL but the line stays low until the slave lets go. Conceptual figure: one interval per half-phase.

Where the stretch goes

At the end of the acknowledge slot, which is the one place in a byte where both directions have slack: the master has finished the ninth pulse and has not yet started the next byte. Stretching mid-byte would be legal and pointless — the slave already has a whole bit time of notice for every bit.

5. Three Things This Design Does Not Contain

A mutation pass on the first draft produced six survivors. Three were missing checks, and three were code defending against cases that cannot happen — so the fix was to delete the code, not to test it.

removedwhy it was unreachable
stretching <= 0 on framinga stretch holds SCL low; framing is an SDA edge qualified by SCL high. They cannot coexist.
a !framing guard on the stretch logicscl_fall means scl_q is already low this cycle, and a framing pulse requires it high. Mutually exclusive.
!stretching in rx_enable18.6 shifts on scl_rise, and while stretching there is no rising edge to shift on.
!read_done && data_index == 0 on tx_start18.5's slot is armed only by a byte the slave must answer, and a read phase has exactly one — the address. ack_begin occurs once by construction.

Removing the fourth left read_done with no reader at all, so that register went too.

6. The Transaction Layer, in Three Languages

One clocked block, four combinational outputs, and a four-way priority: reset, framing, address match, then the per-byte bookkeeping.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_txn.sv — transaction state, the byte index and the wait state
   // -----------------------------------------------------------------------------
   // i2c_slave_txn.sv
   // The transaction layer: which byte is this, whose turn is it, and may the master have
   // the next bit yet?
   //
   // WHAT THIS BLOCK IS FOR. Every block built so far knows how to do one thing and has no
   // idea when to do it. Chapter 18.6 can receive a byte but cannot know whether that byte
   // is a register pointer or data. Chapter 18.7 can send a byte but cannot know whether a
   // read has begun or ended. This block is the layer that knows WHERE IN A TRANSACTION the
   // slave is, and it exists because that knowledge is genuinely separate from every
   // datapath that consumes it.
   //
   // THE STATE SPLIT, which is the reason Chapter 18.9 has no framing ports:
   //
   //   PROTOCOL STATE   lives here      cleared by a START -- §3.1.10 note 4
   //                                    the phase, the direction, the byte index
   //   APPLICATION STATE lives in 18.9  survives a START
   //                                    the register pointer, the register contents
   //
   // A repeated START therefore RESTARTS THE FRAMING WITHOUT DESTROYING THE APPLICATION
   // STATE, and that is not a subtlety -- it is the only reason the combined transfer works.
   // Everything this block holds is deliberately disposable.
   //
   // CLOCK STRETCHING IS THE TARGET'S ONLY WAY TO SAY "NOT YET". §3.1.9: "the slave may hold
   // the SCL line LOW to force the master into a wait state". Two rules follow, and both are
   // structural here rather than remembered:
   //
   //   1. A STRETCH IS ONLY EVER A PULL-DOWN. There is no drive-high anywhere in this file.
   //      The slave adds its own low to a line the master has already taken low; it never
   //      releases SCL to make it rise, because the master owns the rise.
   //
   //   2. A STRETCH MAY ONLY BEGIN IN THE LOW PHASE. `stretching` can be set at exactly one
   //      instant -- `scl_fall` -- so asserting a pull-down while SCL is high is not a bug
   //      this block can have. Doing it at the rising edge instead would shorten the master's
   //      HIGH time below tHIGH and corrupt the bit every other device on the bus is sampling.
   //
   // WHERE THE STRETCH GOES. At the end of the acknowledge slot, which is the one place in a
   // byte where both directions have slack: the master has finished the ninth pulse and has
   // not yet started the next byte. Stretching anywhere inside a byte would be legal but
   // pointless -- the slave already has a whole bit time of notice.
   // -----------------------------------------------------------------------------

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

      // From Chapter 18.2.
      input  logic scl_q,
      input  logic scl_fall,

      // From Chapter 18.3.
      input  logic start_pulse,
      input  logic restart_pulse,
      input  logic stop_pulse,

      // From Chapter 18.4.
      input  logic addr_done,
      input  logic match,
      input  logic dir_read,

      // From Chapter 18.5 -- the acknowledge slot, as a level. Its END is the stretch point,
      // so this block detects the fall itself rather than asking 18.5 for a new output.
      input  logic ack_active,

      // From Chapter 18.6 and Chapter 18.7.
      input  logic rx_valid,
      input  logic byte_sent,

      // From Chapter 18.8 -- the master's answer to a read byte.
      input  logic mack_valid,
      input  logic mack_ack,

      // From the application: a level meaning "I need more time".
      input  logic stall_req,

      // ---- what the datapaths need to know ------------------------------------
      output logic in_phase,       // we are the addressed device in the current phase
      output logic dir_q,          // latched direction for this phase
      output logic rx_is_pointer,  // this received byte is the register pointer -- 18.9
      output logic rx_enable,      // 18.6 may shift
      output logic tx_start,       // one cycle: begin a read -- 18.7
      output logic tx_continue,    // one cycle: the master acknowledged, send another
      output logic [3:0] data_index, // data bytes completed in this phase, saturating

      // ---- the wait state -----------------------------------------------------
      output logic scl_drive_low,  // pull SCL down. NEVER a drive-high.
      output logic stretching,

      output logic [CNT_W-1:0] n_phases,
      output logic [CNT_W-1:0] n_restarts,
      output logic [CNT_W-1:0] n_stretch
   );

      // The acknowledge slot's two edges. Both matter, and they mean different things.
      //
      //   ack_begin  the slot has just started. The slave is acknowledging, and it has a
      //              whole bit time before it must produce anything else. THIS is where a
      //              read is started -- see below.
      //   ack_end    the slot is over. Used only to notice that a byte boundary passed.
      logic ack_q;
      wire  ack_begin = ack_active && !ack_q;
      wire  ack_end   = ack_q && !ack_active;

      // The falling edge that TERMINATES the acknowledge slot. It is the master's ninth pulse
      // ending, and it is the only falling edge available to the transmitter for its first
      // bit -- so it is also the one instant at which a wait state may be engaged.
      wire  ack_last_fall = ack_active && scl_fall;

      // NOTE what is not here: a `read_done` flag. It existed while `tx_start` was guarded by
      // it, and removing that guard left it with no reader -- so it went too. A NACK ends a
      // read because no `tx_continue` is issued, not because a flag says so.

      // Any framing event ends the phase. A repeated START and a plain START are the same
      // thing to this block: both mean an address byte is coming and everything below is
      // stale. `restart_pulse` is taken separately only to count it.
      wire framing = start_pulse || restart_pulse || stop_pulse;

      assign rx_is_pointer = in_phase && !dir_q && (data_index == 4'd0);
      // 18.6 must not shift during the acknowledge slot -- the ninth bit is not a data bit.
      //
      // AND THERE IS NO `!stretching` TERM, which an earlier draft had. 18.6 shifts on
      // `scl_rise`, and while this block is stretching it is holding SCL LOW -- so there is no
      // rising edge to shift on, and the term could never change an outcome. A mutation
      // removing it survived a full pass, which is what a redundant term looks like from the
      // outside: not a bug, but a line claiming to prevent something that cannot happen.
      assign rx_enable     = in_phase && !dir_q && !ack_active;
      assign scl_drive_low = stretching;

      always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
            ack_q         <= 1'b0;
            in_phase      <= 1'b0;
            dir_q         <= 1'b0;
            data_index    <= 4'd0;
            tx_start      <= 1'b0;
            tx_continue   <= 1'b0;
            stretching    <= 1'b0;
            n_phases      <= {CNT_W{1'b0}};
            n_restarts    <= {CNT_W{1'b0}};
            n_stretch     <= {CNT_W{1'b0}};
         end else begin
            ack_q       <= ack_active;
            tx_start    <= 1'b0;
            tx_continue <= 1'b0;

            // ---- the phase ----------------------------------------------------
            if (framing) begin
               // EVERYTHING HERE IS DISPOSABLE. This is §3.1.10 note 4's "reset its bus
               // logic", and it is the whole of what a repeated START destroys.
               in_phase      <= 1'b0;
               dir_q         <= 1'b0;
               data_index    <= 4'd0;
               // NO `stretching <= 0` HERE, and that is a deliberate removal rather than an
               // omission. A stretch and a framing event CANNOT COEXIST:
               //
               //   `stretching` is only ever set at `scl_fall`, and while it is set this block
               //   holds SCL low -- so `scl_q` is low for the whole of it.
               //   A START or a STOP is an SDA edge qualified by `scl_q` HIGH (Chapter 18.3).
               //
               // So no framing pulse can arrive while `stretching` is set, and a clear here
               // would be code for a case that does not exist. The bench asserts the property
               // over every cycle of every test rather than trusting the argument.
               if (restart_pulse) n_restarts <= n_restarts + 1'b1;
            end else if (addr_done && match) begin
               in_phase   <= 1'b1;
               dir_q      <= dir_read;
               data_index <= 4'd0;
               n_phases   <= n_phases + 1'b1;
            end else begin
               // ---- the byte index --------------------------------------------
               // Saturating at two, because the only distinction that matters is
               // "first data byte" versus "not the first". Wrapping would make byte
               // sixteen a pointer again, which is the kind of defect that ships.
               if (in_phase && (rx_valid || byte_sent) && data_index != 4'd2)
                  data_index <= data_index + 4'd1;

               // ---- the read handshake ----------------------------------------
               if (in_phase && dir_q && mack_valid) begin
                  // A NACK issues nothing. That is the whole mechanism: the transmitter is
                  // only ever continued by an explicit acknowledgement, so silence ends the
                  // read. Chapter 18.8 §4 is why that matters.
                  if (mack_ack) tx_continue <= 1'b1;
               end
            end

            // ---- starting a read ----------------------------------------------
            // AT THE BEGINNING OF THE ADDRESS ACKNOWLEDGE SLOT, and the reason is worth
            // stating because the obvious alternative is wrong.
            //
            // Chapter 18.7 loads its first bit on the FIRST FALLING EDGE AFTER being started,
            // and the fall that must carry that bit is the one terminating the ninth pulse.
            // So the transmitter has to be started BEFORE that fall -- which means during the
            // acknowledge slot, not at its end. Starting at `ack_end` leaves the transmitter
            // one bit late, and the master clocks a bit the slave never drove.
            //
            // Not at `addr_done` either: the ninth bit is still 18.5's acknowledge of the
            // address, and this only says "be ready", it does not drive anything.
            //
            // THREE CONDITIONS, NOT FIVE. An earlier draft also required `data_index == 0` and
            // a not-yet-ended read, and both were unreachable: 18.5's slot is armed only by a
            // byte THE SLAVE must answer, and in a read phase there is exactly one of those --
            // the address. So `ack_begin` occurs once per read phase by construction, and the
            // extra guards were defending against a second occurrence that cannot exist. Two
            // mutants proved it by surviving.
            if (ack_begin && in_phase && dir_q)
               tx_start <= 1'b1;

            // ---- the wait state -----------------------------------------------
            // ENGAGE ONLY ON THE FALL THAT ENDS THE ACKNOWLEDGE SLOT. This is the one
            // assignment that can set `stretching`, and its condition contains `scl_fall` --
            // which is why "never pull SCL down while it is high" is a property of the
            // structure rather than of the author's memory.
            //
            // The instant is also the right one on protocol grounds: the master has finished
            // the ninth pulse and has not begun the next byte, so this is the one byte
            // boundary where a wait costs nothing and interrupts nothing.
            //
            // There is no `!framing` guard, for the reason given above: `scl_fall` means
            // `scl_q` is already low this cycle, and a framing pulse requires it high. The two
            // conditions are mutually exclusive, so a guard would be unreachable.
            if (ack_last_fall && stall_req) begin
               stretching <= 1'b1;
               n_stretch  <= n_stretch + 1'b1;
            end

            // RELEASE WHEN THE APPLICATION IS READY. The master's next rising edge then
            // happens on its own schedule -- the slave does not produce it, it only stops
            // preventing it.
            if (stretching && !stall_req) stretching <= 1'b0;
         end
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_txn.v — the same design in Verilog-2001
   // -----------------------------------------------------------------------------
   // i2c_slave_txn.v
   // The transaction layer: which byte is this, whose turn is it, and may the master have
   // the next bit yet?
   //
   // WHAT THIS BLOCK IS FOR. Every block built so far knows how to do one thing and has no
   // idea when to do it. Chapter 18.6 can receive a byte but cannot know whether that byte
   // is a register pointer or data. Chapter 18.7 can send a byte but cannot know whether a
   // read has begun or ended. This block is the layer that knows WHERE IN A TRANSACTION the
   // slave is, and it exists because that knowledge is genuinely separate from every
   // datapath that consumes it.
   //
   // THE STATE SPLIT, which is the reason Chapter 18.9 has no framing ports:
   //
   //   PROTOCOL STATE   lives here      cleared by a START -- §3.1.10 note 4
   //                                    the phase, the direction, the byte index
   //   APPLICATION STATE lives in 18.9  survives a START
   //                                    the register pointer, the register contents
   //
   // A repeated START therefore RESTARTS THE FRAMING WITHOUT DESTROYING THE APPLICATION
   // STATE, and that is not a subtlety -- it is the only reason the combined transfer works.
   // Everything this block holds is deliberately disposable.
   //
   // CLOCK STRETCHING IS THE TARGET'S ONLY WAY TO SAY "NOT YET". §3.1.9: "the slave may hold
   // the SCL line LOW to force the master into a wait state". Two rules follow, and both are
   // structural here rather than remembered:
   //
   //   1. A STRETCH IS ONLY EVER A PULL-DOWN. There is no drive-high anywhere in this file.
   //      The slave adds its own low to a line the master has already taken low; it never
   //      releases SCL to make it rise, because the master owns the rise.
   //
   //   2. A STRETCH MAY ONLY BEGIN IN THE LOW PHASE. `stretching` can be set at exactly one
   //      instant -- `scl_fall` -- so asserting a pull-down while SCL is high is not a bug
   //      this block can have. Doing it at the rising edge instead would shorten the master's
   //      HIGH time below tHIGH and corrupt the bit every other device on the bus is sampling.
   //
   // WHERE THE STRETCH GOES. At the end of the acknowledge slot, which is the one place in a
   // byte where both directions have slack: the master has finished the ninth pulse and has
   // not yet started the next byte. Stretching anywhere inside a byte would be legal but
   // pointless -- the slave already has a whole bit time of notice.
   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   // -----------------------------------------------------------------------------

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

      // From Chapter 18.2.
      input  wire  scl_q,
      input  wire  scl_fall,

      // From Chapter 18.3.
      input  wire  start_pulse,
      input  wire  restart_pulse,
      input  wire  stop_pulse,

      // From Chapter 18.4.
      input  wire  addr_done,
      input  wire  match,
      input  wire  dir_read,

      // From Chapter 18.5 -- the acknowledge slot, as a level. Its END is the stretch point,
      // so this block detects the fall itself rather than asking 18.5 for a new output.
      input  wire  ack_active,

      // From Chapter 18.6 and Chapter 18.7.
      input  wire  rx_valid,
      input  wire  byte_sent,

      // From Chapter 18.8 -- the master's answer to a read byte.
      input  wire  mack_valid,
      input  wire  mack_ack,

      // From the application: a level meaning "I need more time".
      input  wire  stall_req,

      // ---- what the datapaths need to know ------------------------------------
      output reg   in_phase,       // we are the addressed device in the current phase
      output reg   dir_q,          // latched direction for this phase
      output wire rx_is_pointer,  // this received byte is the register pointer -- 18.9
      output wire rx_enable,      // 18.6 may shift
      output reg   tx_start,       // one cycle: begin a read -- 18.7
      output reg   tx_continue,    // one cycle: the master acknowledged, send another
      output reg   [3:0] data_index, // data bytes completed in this phase, saturating

      // ---- the wait state -----------------------------------------------------
      output wire scl_drive_low,  // pull SCL down. NEVER a drive-high.
      output reg   stretching,

      output reg   [CNT_W-1:0] n_phases,
      output reg   [CNT_W-1:0] n_restarts,
      output reg   [CNT_W-1:0] n_stretch
   );

      // The acknowledge slot's two edges. Both matter, and they mean different things.
      //
      //   ack_begin  the slot has just started. The slave is acknowledging, and it has a
      //              whole bit time before it must produce anything else. THIS is where a
      //              read is started -- see below.
      //   ack_end    the slot is over. Used only to notice that a byte boundary passed.
      reg ack_q;
      wire  ack_begin = ack_active && !ack_q;
      wire  ack_end   = ack_q && !ack_active;

      // The falling edge that TERMINATES the acknowledge slot. It is the master's ninth pulse
      // ending, and it is the only falling edge available to the transmitter for its first
      // bit -- so it is also the one instant at which a wait state may be engaged.
      wire  ack_last_fall = ack_active && scl_fall;

      // NOTE what is not here: a `read_done` flag. It existed while `tx_start` was guarded by
      // it, and removing that guard left it with no reader -- so it went too. A NACK ends a
      // read because no `tx_continue` is issued, not because a flag says so.

      // Any framing event ends the phase. A repeated START and a plain START are the same
      // thing to this block: both mean an address byte is coming and everything below is
      // stale. `restart_pulse` is taken separately only to count it.
      wire framing = start_pulse || restart_pulse || stop_pulse;

      assign rx_is_pointer = in_phase && !dir_q && (data_index == 4'd0);
      // 18.6 must not shift during the acknowledge slot -- the ninth bit is not a data bit.
      //
      // AND THERE IS NO `!stretching` TERM, which an earlier draft had. 18.6 shifts on
      // `scl_rise`, and while this block is stretching it is holding SCL LOW -- so there is no
      // rising edge to shift on, and the term could never change an outcome. A mutation
      // removing it survived a full pass, which is what a redundant term looks like from the
      // outside: not a bug, but a line claiming to prevent something that cannot happen.
      assign rx_enable     = in_phase && !dir_q && !ack_active;
      assign scl_drive_low = stretching;

      always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
            ack_q         <= 1'b0;
            in_phase      <= 1'b0;
            dir_q         <= 1'b0;
            data_index    <= 4'd0;
            tx_start      <= 1'b0;
            tx_continue   <= 1'b0;
            stretching    <= 1'b0;
            n_phases      <= {CNT_W{1'b0}};
            n_restarts    <= {CNT_W{1'b0}};
            n_stretch     <= {CNT_W{1'b0}};
         end else begin
            ack_q       <= ack_active;
            tx_start    <= 1'b0;
            tx_continue <= 1'b0;

            // ---- the phase ----------------------------------------------------
            if (framing) begin
               // EVERYTHING HERE IS DISPOSABLE. This is §3.1.10 note 4's "reset its bus
               // logic", and it is the whole of what a repeated START destroys.
               in_phase      <= 1'b0;
               dir_q         <= 1'b0;
               data_index    <= 4'd0;
               // NO `stretching <= 0` HERE, and that is a deliberate removal rather than an
               // omission. A stretch and a framing event CANNOT COEXIST:
               //
               //   `stretching` is only ever set at `scl_fall`, and while it is set this block
               //   holds SCL low -- so `scl_q` is low for the whole of it.
               //   A START or a STOP is an SDA edge qualified by `scl_q` HIGH (Chapter 18.3).
               //
               // So no framing pulse can arrive while `stretching` is set, and a clear here
               // would be code for a case that does not exist. The bench asserts the property
               // over every cycle of every test rather than trusting the argument.
               if (restart_pulse) n_restarts <= n_restarts + 1'b1;
            end else if (addr_done && match) begin
               in_phase   <= 1'b1;
               dir_q      <= dir_read;
               data_index <= 4'd0;
               n_phases   <= n_phases + 1'b1;
            end else begin
               // ---- the byte index --------------------------------------------
               // Saturating at two, because the only distinction that matters is
               // "first data byte" versus "not the first". Wrapping would make byte
               // sixteen a pointer again, which is the kind of defect that ships.
               if (in_phase && (rx_valid || byte_sent) && data_index != 4'd2)
                  data_index <= data_index + 4'd1;

               // ---- the read handshake ----------------------------------------
               if (in_phase && dir_q && mack_valid) begin
                  // A NACK issues nothing. That is the whole mechanism: the transmitter is
                  // only ever continued by an explicit acknowledgement, so silence ends the
                  // read. Chapter 18.8 §4 is why that matters.
                  if (mack_ack) tx_continue <= 1'b1;
               end
            end

            // ---- starting a read ----------------------------------------------
            // AT THE BEGINNING OF THE ADDRESS ACKNOWLEDGE SLOT, and the reason is worth
            // stating because the obvious alternative is wrong.
            //
            // Chapter 18.7 loads its first bit on the FIRST FALLING EDGE AFTER being started,
            // and the fall that must carry that bit is the one terminating the ninth pulse.
            // So the transmitter has to be started BEFORE that fall -- which means during the
            // acknowledge slot, not at its end. Starting at `ack_end` leaves the transmitter
            // one bit late, and the master clocks a bit the slave never drove.
            //
            // Not at `addr_done` either: the ninth bit is still 18.5's acknowledge of the
            // address, and this only says "be ready", it does not drive anything.
            //
            // THREE CONDITIONS, NOT FIVE. An earlier draft also required `data_index == 0` and
            // a not-yet-ended read, and both were unreachable: 18.5's slot is armed only by a
            // byte THE SLAVE must answer, and in a read phase there is exactly one of those --
            // the address. So `ack_begin` occurs once per read phase by construction, and the
            // extra guards were defending against a second occurrence that cannot exist. Two
            // mutants proved it by surviving.
            if (ack_begin && in_phase && dir_q)
               tx_start <= 1'b1;

            // ---- the wait state -----------------------------------------------
            // ENGAGE ONLY ON THE FALL THAT ENDS THE ACKNOWLEDGE SLOT. This is the one
            // assignment that can set `stretching`, and its condition contains `scl_fall` --
            // which is why "never pull SCL down while it is high" is a property of the
            // structure rather than of the author's memory.
            //
            // The instant is also the right one on protocol grounds: the master has finished
            // the ninth pulse and has not begun the next byte, so this is the one byte
            // boundary where a wait costs nothing and interrupts nothing.
            //
            // There is no `!framing` guard, for the reason given above: `scl_fall` means
            // `scl_q` is already low this cycle, and a framing pulse requires it high. The two
            // conditions are mutually exclusive, so a guard would be unreachable.
            if (ack_last_fall && stall_req) begin
               stretching <= 1'b1;
               n_stretch  <= n_stretch + 1'b1;
            end

            // RELEASE WHEN THE APPLICATION IS READY. The master's next rising edge then
            // happens on its own schedule -- the slave does not produce it, it only stops
            // preventing it.
            if (stretching && !stall_req) stretching <= 1'b0;
         end
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_txn.vhd — the same design in VHDL
   -- -----------------------------------------------------------------------------
   -- i2c_slave_txn.vhd
   -- The transaction layer -- the same design in VHDL.
   --
   -- The state split is the whole point, and it is unchanged across the three languages:
   --
   --   PROTOCOL STATE    lives here        cleared by a START -- §3.1.10 note 4
   --   APPLICATION STATE lives in 18.9     survives a START
   --
   -- A repeated START therefore restarts the framing without destroying the application
   -- state, which is the only reason the combined transfer works.
   --
   -- Clock stretching is a PULL-DOWN ONLY, and it may only begin on a falling edge. There is
   -- no drive-high of SCL anywhere in this file, and `stretching` has exactly one assignment
   -- that can set it -- so both rules are structural rather than remembered.
   -- -----------------------------------------------------------------------------
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

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

         -- from 18.2
         scl_q         : in  std_logic;
         scl_fall      : in  std_logic;

         -- from 18.3
         start_pulse   : in  std_logic;
         restart_pulse : in  std_logic;
         stop_pulse    : in  std_logic;

         -- from 18.4
         addr_done     : in  std_logic;
         match         : in  std_logic;
         dir_read      : in  std_logic;

         -- from 18.5 -- the acknowledge slot as a level; this block finds its edges
         ack_active    : in  std_logic;

         -- from 18.6 and 18.7
         rx_valid      : in  std_logic;
         byte_sent     : in  std_logic;

         -- from 18.8
         mack_valid    : in  std_logic;
         mack_ack      : in  std_logic;

         -- from the application
         stall_req     : in  std_logic;

         in_phase      : out std_logic;
         dir_q         : out std_logic;
         rx_is_pointer : out std_logic;
         rx_enable     : out std_logic;
         tx_start      : out std_logic;
         tx_continue   : out std_logic;
         data_index    : out unsigned(3 downto 0);

         scl_drive_low : out std_logic;
         stretching    : out std_logic;

         n_phases      : out unsigned(CNT_W-1 downto 0);
         n_restarts    : out unsigned(CNT_W-1 downto 0);
         n_stretch     : out unsigned(CNT_W-1 downto 0)
      );
   end entity i2c_slave_txn;

   architecture rtl of i2c_slave_txn is

      -- `out` ports are not readable in VHDL-93, so the state lives in internal signals.
      signal ack_q     : std_logic := '0';
      signal phase_r   : std_logic := '0';
      signal dir_r     : std_logic := '0';
      signal idx_r     : unsigned(3 downto 0) := (others => '0');
      signal txs_r     : std_logic := '0';
      signal txc_r     : std_logic := '0';
      signal str_r     : std_logic := '0';
      signal nph       : unsigned(CNT_W-1 downto 0) := (others => '0');
      signal nrs       : unsigned(CNT_W-1 downto 0) := (others => '0');
      signal nst       : unsigned(CNT_W-1 downto 0) := (others => '0');

      signal ack_begin     : std_logic;
      signal ack_end       : std_logic;
      signal ack_last_fall : std_logic;
      signal framing       : std_logic;

   begin

      ack_begin     <= ack_active and not ack_q;
      ack_end       <= ack_q and not ack_active;
      ack_last_fall <= ack_active and scl_fall;
      framing       <= start_pulse or restart_pulse or stop_pulse;

      in_phase   <= phase_r;
      dir_q      <= dir_r;
      data_index <= idx_r;
      tx_start   <= txs_r;
      tx_continue <= txc_r;
      stretching <= str_r;
      n_phases   <= nph;
      n_restarts <= nrs;
      n_stretch  <= nst;

      rx_is_pointer <= '1' when (phase_r = '1' and dir_r = '0' and idx_r = 0) else '0';
      -- No `str_r = '0'` term: 18.6 shifts on scl_rise, and while this block stretches it holds
      -- SCL LOW, so there is no rising edge to shift on. A mutation removing the term survived
      -- a full pass, which is what a redundant condition looks like from the outside.
      rx_enable     <= '1' when (phase_r = '1' and dir_r = '0' and
                                 ack_active = '0') else '0';
      scl_drive_low <= str_r;

      process (clk, rst_n)
      begin
         if rst_n = '0' then
            ack_q     <= '0';
            phase_r   <= '0';
            dir_r     <= '0';
            idx_r     <= (others => '0');
            txs_r     <= '0';
            txc_r     <= '0';
            str_r     <= '0';
            nph       <= (others => '0');
            nrs       <= (others => '0');
            nst       <= (others => '0');
         elsif rising_edge(clk) then
            ack_q <= ack_active;
            txs_r <= '0';
            txc_r <= '0';

            -- the phase
            if framing = '1' then
               -- Everything here is disposable: this is note 4's "reset its bus logic".
               phase_r   <= '0';
               dir_r     <= '0';
               idx_r     <= (others => '0');
               -- NO `str_r <= '0'` HERE, and that is a deliberate removal. A stretch and a
               -- framing event cannot coexist: `str_r` is only ever set at `scl_fall` and holds
               -- SCL low for the whole of its life, while a START or a STOP is an SDA edge
               -- qualified by `scl_q` HIGH. A clear here would be code for a case that does not
               -- exist, and the bench asserts the impossibility on every cycle instead.
               if restart_pulse = '1' then nrs <= nrs + 1; end if;
            elsif addr_done = '1' and match = '1' then
               phase_r   <= '1';
               dir_r     <= dir_read;
               idx_r     <= (others => '0');
               nph       <= nph + 1;
            else
               -- The byte index, saturating at two: the only distinction that matters is
               -- "first data byte" versus "not the first".
               if phase_r = '1' and (rx_valid = '1' or byte_sent = '1') and idx_r /= 2 then
                  idx_r <= idx_r + 1;
               end if;

               -- A NACK issues nothing. That is the whole mechanism: the transmitter is only
               -- ever continued by an explicit acknowledgement, so silence ends the read.
               if phase_r = '1' and dir_r = '1' and mack_valid = '1'
                  and mack_ack = '1' then
                  txc_r <= '1';
               end if;
            end if;

            -- Starting a read AT THE BEGINNING of the address acknowledge slot: 18.7 loads on
            -- the first falling edge after being started, and the fall that must carry the
            -- first bit is the one terminating the ninth pulse.
            --
            -- Three conditions, not five: 18.5's slot is armed only by a byte the slave must
            -- answer, and a read phase has exactly one of those -- the address. So `ack_begin`
            -- occurs once per read phase by construction, and the guards an earlier draft had
            -- were defending against a second occurrence that cannot exist.
            if ack_begin = '1' and phase_r = '1' and dir_r = '1' then
               txs_r <= '1';
            end if;

            -- the wait state. The one assignment that can set `stretching`, and its condition
            -- contains scl_fall -- so a pull-down while SCL is high is not a bug this block can
            -- have. There is no `framing = '0'` guard for the reason above: `scl_fall` means
            -- `scl_q` is already low, and a framing pulse requires it high.
            if ack_last_fall = '1' and stall_req = '1' then
               str_r <= '1';
               nst   <= nst + 1;
            end if;

            if str_r = '1' and stall_req = '0' then
               str_r <= '0';
            end if;
         end if;
      end process;

   end architecture rtl;

7. The Testbench Is an Integration Testbench

Every output of this block is an instruction to another block, and an instruction with no recipient cannot be checked. "An output with no consumer instantiated" is the shape that let two mutants survive in 18.7, so this bench instantiates the whole slave — 18.2 through 18.9 — and drives it from a master model across 17.1's wired-AND bus.

Fifteen tests:

testwhat it establishes
T1a target held in reset does not hold the bus down — and a reset target is in no phase
T2our address with the write bit opens a write phase
T3the first data byte is the pointer and the second is not
T4another device's address opens no phase and enables no shifting
T5rx_enable drops inside the acknowledge slot
T6a repeated START clears the phase and keeps the pointer
T7a read is started inside the address acknowledge slot, so the first bit is on time
T8an acknowledged read byte is followed by another; a NACK ends it
T9the application asks for time and the master blocks until it is given
T10across every test, SCL was never pulled down while it was high
T11a stretch and a framing event never coexist
T12the byte index saturates: a later byte never becomes a pointer again
T13a STOP ends the phase
T14reset clears the phase, the index and every counter
T15a NACKed read ends even against a master that keeps clocking

Three of those need their reasoning stated.

T9's stall is a countdown, not a level. A test that raises stall_req and lowers it by hand can only ever stretch while nothing is happening, so the master never has to wait:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
raise a level, do nothing, lower it     the stretch is invisible to the master
raise a countdown, then clock           the master BLOCKS inside its own bit task

The second form is the only one that makes the wait an observable fact about the bus rather than a flag inside the DUT. T9 asserts stretch_waits == 1 — the master waited exactly once, at exactly the point the slave asked it to.

T15 is 18.8 §4 at the transaction level. After a NACK the master keeps clocking eight more bits, and the slave must drive nothing. The register it would have sourced holds 0xFF, so every bit of an unwanted byte would be a pull-down — which is exactly what prevents the master's STOP. The test then issues the STOP and asserts it happened.

T1 checks the bus while reset is asserted, not after it. A mutant whose reset value was stretching cleared itself on the first clock after release, so every check taken after reset passed — and the defect, a slave that pulls SCL low for the whole of its reset, was invisible.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_txn_tb.sv — the self-checking testbench
   // -----------------------------------------------------------------------------
   // i2c_slave_txn_tb.sv
   // Independent oracle for i2c_slave_txn -- and the first bench in this module that is an
   // INTEGRATION bench rather than a unit bench.
   //
   // WHY IT HAS TO BE. Every output of this block is an instruction to another block: 18.6
   // may shift, 18.7 may start, 18.9 may treat this byte as a pointer. An instruction with no
   // recipient cannot be checked, and "an output with no consumer instantiated" is the shape
   // that let two mutants survive earlier in this module. So the bench builds the real stack
   // -- 18.2, 18.3, 18.4, 18.5, 18.6, 18.7, 18.8, 18.9 -- and drives it from a master model
   // across the wired-AND bus of Chapter 17.1.
   //
   // AND THE MASTER MODEL MUST OBEY A STRETCH. A master that clocks on a fixed schedule cannot
   // test clock stretching at all: it would simply drive SCL high while the slave held it low,
   // which is electrically impossible and would make the stretch invisible. So this master
   // RELEASES SCL and then waits for the line to actually rise, counting how many times it had
   // to wait. That count is the only direct evidence a stretch had an effect on anybody.
   // -----------------------------------------------------------------------------
   `timescale 1ns/1ps

   module i2c_slave_txn_tb;

      localparam int HALF    = 8;        // clocks per SCL half-phase
      localparam [6:0] ADDR  = 7'h50;
      localparam int N_REG   = 8;
      localparam int RO_MASK = 8'h04;

      logic clk = 1'b0;
      logic rst_n = 1'b0;

      // ---- the bus: device 0 is the master model, device 1 is the slave --------
      logic [1:0] scl_dl, sda_dl;
      logic scl, sda;
      logic [1:0] scl_in, sda_in, scl_rbl, sda_rbl;
      logic [7:0] scl_holders, sda_holders;

      logic m_scl_low = 1'b0, m_sda_low = 1'b0;

      i2c_line_model #(.N_DEV(2)) bus (
         .scl_drive_low(scl_dl), .sda_drive_low(sda_dl),
         .scl(scl), .sda(sda), .scl_in(scl_in), .sda_in(sda_in),
         .scl_released_but_low(scl_rbl), .sda_released_but_low(sda_rbl),
         .scl_holders(scl_holders), .sda_holders(sda_holders)
      );

      // ---- the slave stack ----------------------------------------------------
      logic scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall;

      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)
      );

      logic start_pulse, restart_pulse, stop_pulse, bus_active, framing_midbyte;
      logic [15:0] n_sta, n_rs, n_sto;
      logic acquiring, addr_done, match, selected, dir_read;
      logic [3:0] a_bit_index;
      logic [7:0] addr_byte;
      logic [15:0] n_match, n_miss;
      logic receiving, rx_valid, rx_byte_done;
      logic [3:0] r_bit_index;
      logic [7:0] rx_byte;
      logic [15:0] n_bytes_rx, n_partial;
      wire  mid_byte = acquiring || receiving;

      i2c_slave_framing #(.CNT_W(16)) u_frm (
         .clk(clk), .rst_n(rst_n), .scl_q(scl_q), .sda_rise(sda_rise), .sda_fall(sda_fall),
         .start_pulse(start_pulse), .restart_pulse(restart_pulse), .stop_pulse(stop_pulse),
         .bus_active(bus_active), .mid_byte(mid_byte), .framing_midbyte(framing_midbyte),
         .n_starts(n_sta), .n_restarts(n_rs), .n_stops(n_sto)
      );

      i2c_slave_addr #(.MY_ADDR(ADDR), .CNT_W(16)) u_addr (
         .clk(clk), .rst_n(rst_n), .scl_rise(scl_rise), .sda_q(sda_q),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse),
         .acquiring(acquiring), .bit_index(a_bit_index), .addr_done(addr_done),
         .addr_byte(addr_byte), .match(match), .selected(selected), .dir_read(dir_read),
         .n_match(n_match), .n_miss(n_miss)
      );

      // ---- the DUT ------------------------------------------------------------
      logic in_phase, dir_q, rx_is_pointer, rx_enable, tx_start, tx_continue;
      logic [3:0] data_index;
      logic txn_scl_low, stretching;
      logic [15:0] n_phases, n_restarts_txn, n_stretch;

      // THE APPLICATION'S STALL, AS A COUNTDOWN. A test that raises a level and lowers it by
      // hand can only ever stretch while nothing is happening -- so the master never has to
      // wait, and the stretch is invisible to it. A countdown that expires on its own lets the
      // master BLOCK inside its own bit task, which is the only way the wait becomes an
      // observable fact about the bus rather than a flag inside the DUT.
      localparam int STALL_N = 40;
      logic stall_load = 1'b0;
      int   stall_hold = 0;
      wire  stall_req  = (stall_hold > 0);

      always @(posedge clk) begin
         if (!rst_n)               stall_hold <= 0;
         else if (stall_load)      stall_hold <= STALL_N;
         else if (stall_hold > 0)  stall_hold <= stall_hold - 1;
      end

      logic ack_active, ack_armed, ack_sda_low;
      logic [15:0] n_acks, n_nacks;

      logic tx_req, tx_sda_low, driving, byte_sent;
      logic [3:0] t_bit_index;
      logic [15:0] n_bytes_tx, n_bits_tx;
      logic [7:0] rd_data;

      logic awaiting, mack_valid, mack_ack, keep_sourcing;
      logic [15:0] n_m_ack, n_m_nack;

      logic wr_accept;
      logic [8*N_REG-1:0] reg_flat;
      logic [7:0] pointer;
      logic [15:0] n_writes, n_refused, n_reads;

      i2c_slave_txn #(.CNT_W(16)) dut (
         .clk(clk), .rst_n(rst_n), .scl_q(scl_q), .scl_fall(scl_fall),
         .start_pulse(start_pulse), .restart_pulse(restart_pulse), .stop_pulse(stop_pulse),
         .addr_done(addr_done), .match(match), .dir_read(dir_read),
         .ack_active(ack_active), .rx_valid(rx_valid), .byte_sent(byte_sent),
         .mack_valid(mack_valid), .mack_ack(mack_ack), .stall_req(stall_req),
         .in_phase(in_phase), .dir_q(dir_q), .rx_is_pointer(rx_is_pointer),
         .rx_enable(rx_enable), .tx_start(tx_start), .tx_continue(tx_continue),
         .data_index(data_index),
         .scl_drive_low(txn_scl_low), .stretching(stretching),
         .n_phases(n_phases), .n_restarts(n_restarts_txn), .n_stretch(n_stretch)
      );

      // The acknowledge slot: armed by any completed byte we owe an answer for. The address
      // byte is always acknowledged when it matched; a data byte is acknowledged only if the
      // register file will take it, which is 18.9's wr_accept arriving as an ack policy.
      wire byte_done_any = rx_byte_done || (addr_done && match);
      wire ack_en        = (addr_done && match) ? 1'b1 : wr_accept;

      i2c_slave_ack #(.CNT_W(16)) u_ack (
         .clk(clk), .rst_n(rst_n), .scl_fall(scl_fall), .byte_done(byte_done_any),
         .ack_en(ack_en), .start_pulse(start_pulse), .stop_pulse(stop_pulse),
         .sda_drive_low(ack_sda_low), .ack_active(ack_active), .ack_armed(ack_armed),
         .n_acks(n_acks), .n_nacks(n_nacks)
      );

      i2c_slave_rx #(.CNT_W(16)) u_rx (
         .clk(clk), .rst_n(rst_n), .scl_rise(scl_rise), .sda_q(sda_q),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse), .rx_enable(rx_enable),
         .receiving(receiving), .bit_index(r_bit_index), .rx_byte(rx_byte),
         .rx_valid(rx_valid), .byte_done(rx_byte_done),
         .n_bytes(n_bytes_rx), .n_partial(n_partial)
      );

      // ---- the transmit side, so tx_start and tx_continue have a consumer -----
      i2c_slave_tx #(.CNT_W(16)) u_tx (
         .clk(clk), .rst_n(rst_n), .scl_fall(scl_fall),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse),
         .tx_start(tx_start), .tx_continue(tx_continue),
         .tx_req(tx_req), .tx_byte(rd_data), .sda_drive_low(tx_sda_low),
         .driving(driving), .bit_index(t_bit_index), .byte_sent(byte_sent),
         .n_bytes(n_bytes_tx), .n_bits(n_bits_tx)
      );

      i2c_slave_mack #(.CNT_W(16)) u_mack (
         .clk(clk), .rst_n(rst_n), .scl_rise(scl_rise), .sda_q(sda_q),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse), .byte_sent(byte_sent),
         .awaiting(awaiting), .mack_valid(mack_valid), .mack_ack(mack_ack),
         .keep_sourcing(keep_sourcing), .n_ack(n_m_ack), .n_nack(n_m_nack)
      );

      // ---- the register file, whose pointer must survive a repeated START ----
      i2c_slave_regs #(.N_REG(N_REG), .RO_MASK(RO_MASK), .CNT_W(16)) u_regs (
         .clk(clk), .rst_n(rst_n),
         .rx_valid(rx_valid), .rx_byte(rx_byte), .rx_is_pointer(rx_is_pointer),
         .wr_accept(wr_accept), .rd_data(rd_data), .rd_taken(tx_req),
         .reg_flat(reg_flat), .pointer(pointer),
         .n_writes(n_writes), .n_refused(n_refused), .n_reads(n_reads)
      );

      assign scl_dl = {txn_scl_low, m_scl_low};
      assign sda_dl = {ack_sda_low | tx_sda_low, m_sda_low};

      always #5 clk = ~clk;

      int errors = 0;
      // `k` belongs to the bus tasks -- m_byte drives it -- so a test that loops must use its
      // own variable. Sharing one cost an afternoon: the outer loop never terminated, because
      // m_byte left k at -1 every time round.
      int k;
      int j;

      // ---- observers ---------------------------------------------------------
      // The rule of §3.1.9 that cannot be checked from the DUT's ports alone: a stretch is a
      // pull-down that begins in the LOW phase. So watch for the drive ASSERTING while the
      // resolved line is still high -- which would shorten the master's high time below tHIGH
      // and corrupt the bit every device on the bus is sampling.
      int   drive_while_high = 0;
      int   stretch_waits    = 0;      // how many times the master had to wait for the line
      int   framing_in_stretch = 0;    // must stay zero: see T11
      int   ptr_out_of_phase   = 0;    // must stay zero: see T15
      int   scl_held_in_reset  = 0;    // must stay zero: see T1
      logic scl_low_d = 1'b0;

      // NOT GUARDED BY rst_n, and that is the point: a device held in reset must not be holding
      // the bus down. A mutant whose reset value was "stretching" cleared itself on the first
      // clock after release, so every check taken after reset passed -- and the defect, a slave
      // that pulls SCL low for the whole of its reset, was invisible.
      always @(posedge clk) if (!rst_n && txn_scl_low) scl_held_in_reset++;

      // AN OUTPUT MUST BE CORRECT IN ITSELF. `rx_is_pointer` is only consumed together with
      // rx_valid, so a version that asserted it outside any phase was harmless in this
      // integration -- and a mutant removing the phase gate survived a full pass. Downstream
      // masking is not correctness: the next integration may not mask it.
      always @(posedge clk) if (rst_n && !in_phase && rx_is_pointer) ptr_out_of_phase++;

      always @(posedge clk) if (rst_n) begin
         if (txn_scl_low && !scl_low_d && scl === 1'b1) drive_while_high++;
         if (stretching && (start_pulse || restart_pulse || stop_pulse))
            framing_in_stretch++;
         scl_low_d <= txn_scl_low;
      end

      initial begin
         repeat (400000) @(posedge clk);
         $display("  FAIL watchdog: the bench did not finish");
         $fatal(1);
      end

      // ---- the master model --------------------------------------------------
      task automatic hp;                     // one half-phase of idling
         begin repeat (HALF) @(posedge clk); end
      endtask

      // Release SCL and WAIT FOR IT TO RISE. A master that skipped the wait could not observe
      // a stretch, because it would be driving against the slave's pull-down.
      task automatic m_scl_release;
         int guard;
         begin
            @(negedge clk); m_scl_low = 1'b0;
            // SETTLE BEFORE READING. Reading `scl` in the same delta as releasing it returns
            // the stale value, so the wait would be counted whether or not anybody was
            // holding the line -- a check that passes for the wrong reason. The VHDL port of
            // this bench is what found it.
            #1;
            guard = 0;
            if (scl !== 1'b1) begin
               stretch_waits++;
               while (scl !== 1'b1 && guard < 20000) begin @(posedge clk); guard++; end
               if (guard >= 20000) begin
                  $display("  FAIL the slave never released SCL"); errors++;
               end
            end
            hp();
         end
      endtask

      task automatic m_scl_pull;
         begin @(negedge clk); m_scl_low = 1'b1; hp(); end
      endtask

      task automatic m_start;
         begin
            @(negedge clk); m_scl_low = 1'b0; m_sda_low = 1'b0; hp();
            @(negedge clk); m_sda_low = 1'b1; hp();     // SDA falls while SCL is high
            m_scl_pull();
         end
      endtask

      task automatic m_restart;
         begin
            @(negedge clk); m_sda_low = 1'b0; hp();     // release SDA in the low phase
            m_scl_release();                            // SCL rises
            @(negedge clk); m_sda_low = 1'b1; hp();     // SDA falls while SCL is high
            m_scl_pull();
         end
      endtask

      task automatic m_stop;
         begin
            @(negedge clk); m_sda_low = 1'b1; hp();     // SDA low in the low phase
            m_scl_release();
            @(negedge clk); m_sda_low = 1'b0; hp();     // SDA rises while SCL is high
         end
      endtask

      // One bit clocked by the master. `sda_low` is what the MASTER drives; pass 0 to release
      // so the slave can own the line.
      task automatic m_bit (input bit sda_low);
         begin
            @(negedge clk); m_sda_low = sda_low; hp();
            m_scl_release();
            m_scl_pull();
         end
      endtask

      task automatic m_byte (input [7:0] d);
         begin
            for (k = 7; k >= 0; k--) m_bit(~d[k]);      // a zero is a pull-down
         end
      endtask

      // The ninth slot with the master releasing: the slave answers.
      task automatic m_ack_slot_listen;
         begin m_bit(1'b0); end
      endtask

      // The ninth slot with the master answering: a pull-down means ACK.
      task automatic m_ack_slot_drive (input bit ack);
         begin m_bit(ack); end
      endtask

      task automatic do_reset;
         begin
            @(negedge clk);
            rst_n = 1'b0; m_scl_low = 1'b0; m_sda_low = 1'b0;
            drive_while_high = 0; stretch_waits = 0;
            framing_in_stretch = 0; stall_load = 1'b0;
            ptr_out_of_phase = 0; scl_held_in_reset = 0;
            repeat (4) @(posedge clk);
            @(negedge clk); rst_n = 1'b1;
            hp(); #1;
         end
      endtask

      task automatic ck (input string what, input int got, input int exp);
         begin
            if (got !== exp) begin
               $display("  FAIL %0s: got %0d expected %0d", what, got, exp);
               errors++;
            end
         end
      endtask

      // ---- tests --------------------------------------------------------------
      initial begin
         $display("=== i2c_slave_txn: transaction state, a surviving pointer, and a wait state ===");

         // ---- T1. Reset, including what the slave does WHILE held in reset.
         @(negedge clk); rst_n = 1'b0; m_scl_low = 1'b0; m_sda_low = 1'b0;
         scl_held_in_reset = 0;
         repeat (20) @(posedge clk);
         $display("T1  a target held in reset does not hold the bus down");
         ck("T1 SCL untouched throughout reset", scl_held_in_reset, 0);
         ck("T1 and the line is idle high", scl, 1);
         do_reset();
         $display("T1  a reset target is in no phase, stretching nothing, driving nothing");
         ck("T1 no phase", in_phase, 0);
         ck("T1 not stretching", stretching, 0);
         ck("T1 SCL not pulled", txn_scl_low, 0);
         ck("T1 rx disabled", rx_enable, 0);

         // ---- T2. An addressed write opens a phase.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         m_ack_slot_listen();
         $display("T2  our address with the write bit opens a write phase");
         ck("T2 in a phase", in_phase, 1);
         ck("T2 direction is write", dir_q, 0);
         ck("T2 the next byte is the pointer", rx_is_pointer, 1);
         ck("T2 one phase counted", n_phases, 1);

         // ---- T3. The first data byte is the pointer, the rest are not.
         m_byte(8'h03); m_ack_slot_listen();
         $display("T3  the first data byte is the pointer and the second is not");
         ck("T3 pointer loaded", pointer, 3);
         ck("T3 index advanced", data_index, 1);
         ck("T3 no longer the pointer byte", rx_is_pointer, 0);
         m_byte(8'h9E); m_ack_slot_listen();
         ck("T3 the second byte is data", reg_flat[8*3 +: 8], 8'h9E);
         ck("T3 index saturated", data_index, 2);

         // ---- T4. Somebody else's address opens nothing.
         do_reset();
         m_start();
         m_byte({7'h21, 1'b0});
         m_ack_slot_listen();
         $display("T4  another device's address opens no phase and enables no shifting");
         ck("T4 no phase", in_phase, 0);
         ck("T4 rx disabled", rx_enable, 0);
         ck("T4 no phase counted", n_phases, 0);

         // ---- T5. The acknowledge slot is not a data bit.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         @(negedge clk); m_sda_low = 1'b0; hp();
         m_scl_release();
         $display("T5  rx_enable drops inside the acknowledge slot: the ninth bit is not data");
         ck("T5 the slot is active", ack_active, 1);
         ck("T5 so shifting is disabled", rx_enable, 0);
         m_scl_pull();

         // ---- T6. THE HEADLINE: a repeated START keeps the pointer.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h05);        m_ack_slot_listen();     // pointer = 5
         m_byte(8'h7C);        m_ack_slot_listen();     // reg 5 = 0x7C, pointer = 6
         ck("T6 written before the restart", reg_flat[8*5 +: 8], 8'h7C);
         ck("T6 pointer before the restart", pointer, 6);
         m_restart();
         $display("T6  a repeated START clears the phase and KEEPS the pointer");
         ck("T6 the phase is gone", in_phase, 0);
         ck("T6 the direction is gone", dir_q, 0);
         ck("T6 the byte index is gone", data_index, 0);
         ck("T6 the pointer SURVIVED", pointer, 6);
         ck("T6 and so did the data", reg_flat[8*5 +: 8], 8'h7C);
         ck("T6 one restart counted", n_restarts_txn, 1);

         // ---- T7. A read is started INSIDE the address acknowledge slot, so the first bit
         // lands on the fall that terminates it. Starting at the slot's end leaves the
         // transmitter one bit late, which this check is what found.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b1});
         ck("T7 no bit has been driven at the eighth bit", n_bits_tx, 0);
         m_ack_slot_listen();
         $display("T7  a read is started inside the address acknowledge slot, so the first bit is on time");
         ck("T7 in a read phase", in_phase, 1);
         ck("T7 direction is read", dir_q, 1);
         ck("T7 the first bit is already driven", n_bits_tx, 1);
         ck("T7 and the transmitter owns SDA", driving, 1);

         // ---- T8. The master's answer decides whether another byte follows.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h00);        m_ack_slot_listen();
         m_byte(8'hC3);        m_ack_slot_listen();     // reg 0 = 0xC3
         m_restart();
         m_byte({ADDR, 1'b1}); m_ack_slot_listen();
         for (j = 0; j < 8; j++) m_bit(1'b0);           // the slave sources a byte
         m_ack_slot_drive(1'b1);                        // the master ACKs: send another
         $display("T8  an acknowledged read byte is followed by another; a NACK ends it");
         ck("T8 the master acknowledged", n_m_ack, 1);
         ck("T8 so sourcing continues", keep_sourcing, 1);
         for (j = 0; j < 8; j++) m_bit(1'b0);
         m_ack_slot_drive(1'b0);                        // the master NACKs
         ck("T8 the master NACKed", n_m_nack, 1);
         ck("T8 so sourcing stopped", keep_sourcing, 0);
         ck("T8 two bytes were served", n_bytes_tx, 2);
         m_stop();

         // ---- T9. Clock stretching, observed by a master that had to wait.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         @(negedge clk); stall_load = 1'b1;             // the application needs time
         @(posedge clk);
         @(negedge clk); stall_load = 1'b0;
         m_ack_slot_listen();
         $display("T9  the application asks for time, and the master BLOCKS until it is given");
         ck("T9 the slave is stretching", stretching, 1);
         ck("T9 and is pulling SCL down", txn_scl_low, 1);
         ck("T9 SCL is low", scl, 0);
         ck("T9 the master has not waited yet", stretch_waits, 0);
         // The next byte cannot begin until the slave lets go, so this call BLOCKS.
         m_byte(8'h01);
         ck("T9 the master had to wait for the line", stretch_waits, 1);
         ck("T9 and the stretch is over", stretching, 0);
         m_ack_slot_listen();
         ck("T9 the transfer continued afterwards", pointer, 1);
         ck("T9 one stretch counted", n_stretch, 1);

         // ---- T10. A stretch is never a pull-down in the high phase.
         $display("T10 across every test so far, SCL was never pulled down while it was high");
         ck("T10 no drive in a high phase", drive_while_high, 0);

         // ---- T11. A stretch and a framing event cannot coexist, and the design therefore
         // carries no code for the combination. This is the check that justifies the removal:
         // a stretch holds SCL low, and framing is an SDA edge qualified by SCL HIGH.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         @(negedge clk); stall_load = 1'b1;
         @(posedge clk);
         @(negedge clk); stall_load = 1'b0;
         m_ack_slot_listen();
         ck("T11 stretching", stretching, 1);
         m_byte(8'h02);                                 // blocks, then completes
         m_ack_slot_listen();
         m_stop();
         $display("T11 a stretch and a framing event never coexist: SCL cannot be low and high");
         ck("T11 no framing arrived during a stretch", framing_in_stretch, 0);
         ck("T11 the phase closed on the STOP", in_phase, 0);
         ck("T11 not stretching", stretching, 0);
         ck("T11 SCL released", txn_scl_low, 0);

         // ---- T12. The byte index saturates.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h00);        m_ack_slot_listen();
         for (j = 0; j < 4; j++) begin m_byte(8'h20); m_ack_slot_listen(); end
         $display("T12 the byte index saturates: a later byte never becomes a pointer again");
         ck("T12 index pinned at two", data_index, 2);
         ck("T12 not a pointer byte", rx_is_pointer, 0);

         // ---- T13. A STOP ends the phase.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         ck("T13 in a phase", in_phase, 1);
         m_stop();
         $display("T13 a STOP ends the phase");
         ck("T13 phase closed", in_phase, 0);
         ck("T13 index cleared", data_index, 0);

         // ---- T14. Reset clears the transaction state and every counter.
         do_reset();
         $display("T14 reset clears the phase, the index and every counter");
         ck("T14 no phase", in_phase, 0);
         ck("T14 index zero", data_index, 0);
         ck("T14 phases zero", n_phases, 0);
         ck("T14 restarts zero", n_restarts_txn, 0);
         ck("T14 stretches zero", n_stretch, 0);

         // ---- T15. A NACKed read must END, even if the master keeps clocking. This is
         // Chapter 18.8 §4 at the transaction level: a slave that sources another byte after a
         // NACK holds SDA low for every zero in it and prevents the master's STOP.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h00);        m_ack_slot_listen();
         m_byte(8'hFF);        m_ack_slot_listen();     // reg 0 = 0xFF: every bit a pull-down
         m_restart();
         m_byte({ADDR, 1'b1}); m_ack_slot_listen();
         for (j = 0; j < 8; j++) m_bit(1'b0);
         m_ack_slot_drive(1'b0);                        // NACK: that was the last byte
         ck("T15 one byte was served", n_bytes_tx, 1);
         // The master now keeps clocking anyway. Nothing may come back.
         for (j = 0; j < 8; j++) begin
            m_bit(1'b0);
            if (tx_sda_low) begin
               $display("  FAIL T15 the slave drove SDA after a NACK"); errors++;
            end
         end
         $display("T15 a NACKed read ends even against a master that keeps clocking");
         ck("T15 still one byte", n_bytes_tx, 1);
         ck("T15 the transmitter is idle", driving, 0);
         ck("T15 and SDA was left to the master", tx_sda_low, 0);
         m_stop();
         ck("T15 so the STOP happened", n_sto > 0, 1);
         ck("T15 rx_is_pointer never asserted outside a phase", ptr_out_of_phase, 0);

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

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_txn_tb.v — the same tests in Verilog-2001
   // -----------------------------------------------------------------------------
   // i2c_slave_txn_tb.v
   // Independent oracle for i2c_slave_txn -- and the first bench in this module that is an
   // INTEGRATION bench rather than a unit bench.
   //
   // WHY IT HAS TO BE. Every output of this block is an instruction to another block: 18.6
   // may shift, 18.7 may start, 18.9 may treat this byte as a pointer. An instruction with no
   // recipient cannot be checked, and "an output with no consumer instantiated" is the shape
   // that let two mutants survive earlier in this module. So the bench builds the real stack
   // -- 18.2, 18.3, 18.4, 18.5, 18.6, 18.7, 18.8, 18.9 -- and drives it from a master model
   // across the wired-AND bus of Chapter 17.1.
   //
   // AND THE MASTER MODEL MUST OBEY A STRETCH. A master that clocks on a fixed schedule cannot
   // test clock stretching at all: it would simply drive SCL high while the slave held it low,
   // which is electrically impossible and would make the stretch invisible. So this master
   // RELEASES SCL and then waits for the line to actually rise, counting how many times it had
   // to wait. That count is the only direct evidence a stretch had an effect on anybody.
   // -----------------------------------------------------------------------------
   `timescale 1ns/1ps

   module i2c_slave_txn_tb;

      localparam integer HALF    = 8;        // clocks per SCL half-phase
      localparam [6:0] ADDR  = 7'h50;
      localparam integer N_REG   = 8;
      localparam integer RO_MASK = 8'h04;

      reg  clk = 1'b0;
      reg  rst_n = 1'b0;

      // ---- the bus: device 0 is the master model, device 1 is the slave --------
      wire [1:0] scl_dl, sda_dl;
      wire scl, sda;
      wire [1:0] scl_in, sda_in, scl_rbl, sda_rbl;
      wire [7:0] scl_holders, sda_holders;

      reg  m_scl_low = 1'b0, m_sda_low = 1'b0;

      i2c_line_model #(.N_DEV(2)) bus (
         .scl_drive_low(scl_dl), .sda_drive_low(sda_dl),
         .scl(scl), .sda(sda), .scl_in(scl_in), .sda_in(sda_in),
         .scl_released_but_low(scl_rbl), .sda_released_but_low(sda_rbl),
         .scl_holders(scl_holders), .sda_holders(sda_holders)
      );

      // ---- the slave stack ----------------------------------------------------
      wire scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall;

      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)
      );

      wire start_pulse, restart_pulse, stop_pulse, bus_active, framing_midbyte;
      wire [15:0] n_sta, n_rs, n_sto;
      wire acquiring, addr_done, match, selected, dir_read;
      wire [3:0] a_bit_index;
      wire [7:0] addr_byte;
      wire [15:0] n_match, n_miss;
      wire receiving, rx_valid, rx_byte_done;
      wire [3:0] r_bit_index;
      wire [7:0] rx_byte;
      wire [15:0] n_bytes_rx, n_partial;
      wire  mid_byte = acquiring || receiving;

      i2c_slave_framing #(.CNT_W(16)) u_frm (
         .clk(clk), .rst_n(rst_n), .scl_q(scl_q), .sda_rise(sda_rise), .sda_fall(sda_fall),
         .start_pulse(start_pulse), .restart_pulse(restart_pulse), .stop_pulse(stop_pulse),
         .bus_active(bus_active), .mid_byte(mid_byte), .framing_midbyte(framing_midbyte),
         .n_starts(n_sta), .n_restarts(n_rs), .n_stops(n_sto)
      );

      i2c_slave_addr #(.MY_ADDR(ADDR), .CNT_W(16)) u_addr (
         .clk(clk), .rst_n(rst_n), .scl_rise(scl_rise), .sda_q(sda_q),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse),
         .acquiring(acquiring), .bit_index(a_bit_index), .addr_done(addr_done),
         .addr_byte(addr_byte), .match(match), .selected(selected), .dir_read(dir_read),
         .n_match(n_match), .n_miss(n_miss)
      );

      // ---- the DUT ------------------------------------------------------------
      wire in_phase, dir_q, rx_is_pointer, rx_enable, tx_start, tx_continue;
      wire [3:0] data_index;
      wire txn_scl_low, stretching;
      wire [15:0] n_phases, n_restarts_txn, n_stretch;

      // THE APPLICATION'S STALL, AS A COUNTDOWN. A test that raises a level and lowers it by
      // hand can only ever stretch while nothing is happening -- so the master never has to
      // wait, and the stretch is invisible to it. A countdown that expires on its own lets the
      // master BLOCK inside its own bit task, which is the only way the wait becomes an
      // observable fact about the bus rather than a flag inside the DUT.
      localparam integer STALL_N = 40;
      reg  stall_load = 1'b0;
      integer stall_hold = 0;
      wire  stall_req  = (stall_hold > 0);

      always @(posedge clk) begin
         if (!rst_n)               stall_hold <= 0;
         else if (stall_load)      stall_hold <= STALL_N;
         else if (stall_hold > 0)  stall_hold <= stall_hold - 1;
      end

      wire ack_active, ack_armed, ack_sda_low;
      wire [15:0] n_acks, n_nacks;

      wire tx_req, tx_sda_low, driving, byte_sent;
      wire [3:0] t_bit_index;
      wire [15:0] n_bytes_tx, n_bits_tx;
      wire [7:0] rd_data;

      wire awaiting, mack_valid, mack_ack, keep_sourcing;
      wire [15:0] n_m_ack, n_m_nack;

      wire wr_accept;
      wire [8*N_REG-1:0] reg_flat;
      wire [7:0] pointer;
      wire [15:0] n_writes, n_refused, n_reads;

      i2c_slave_txn #(.CNT_W(16)) dut (
         .clk(clk), .rst_n(rst_n), .scl_q(scl_q), .scl_fall(scl_fall),
         .start_pulse(start_pulse), .restart_pulse(restart_pulse), .stop_pulse(stop_pulse),
         .addr_done(addr_done), .match(match), .dir_read(dir_read),
         .ack_active(ack_active), .rx_valid(rx_valid), .byte_sent(byte_sent),
         .mack_valid(mack_valid), .mack_ack(mack_ack), .stall_req(stall_req),
         .in_phase(in_phase), .dir_q(dir_q), .rx_is_pointer(rx_is_pointer),
         .rx_enable(rx_enable), .tx_start(tx_start), .tx_continue(tx_continue),
         .data_index(data_index),
         .scl_drive_low(txn_scl_low), .stretching(stretching),
         .n_phases(n_phases), .n_restarts(n_restarts_txn), .n_stretch(n_stretch)
      );

      // The acknowledge slot: armed by any completed byte we owe an answer for. The address
      // byte is always acknowledged when it matched; a data byte is acknowledged only if the
      // register file will take it, which is 18.9's wr_accept arriving as an ack policy.
      wire byte_done_any = rx_byte_done || (addr_done && match);
      wire ack_en        = (addr_done && match) ? 1'b1 : wr_accept;

      i2c_slave_ack #(.CNT_W(16)) u_ack (
         .clk(clk), .rst_n(rst_n), .scl_fall(scl_fall), .byte_done(byte_done_any),
         .ack_en(ack_en), .start_pulse(start_pulse), .stop_pulse(stop_pulse),
         .sda_drive_low(ack_sda_low), .ack_active(ack_active), .ack_armed(ack_armed),
         .n_acks(n_acks), .n_nacks(n_nacks)
      );

      i2c_slave_rx #(.CNT_W(16)) u_rx (
         .clk(clk), .rst_n(rst_n), .scl_rise(scl_rise), .sda_q(sda_q),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse), .rx_enable(rx_enable),
         .receiving(receiving), .bit_index(r_bit_index), .rx_byte(rx_byte),
         .rx_valid(rx_valid), .byte_done(rx_byte_done),
         .n_bytes(n_bytes_rx), .n_partial(n_partial)
      );

      // ---- the transmit side, so tx_start and tx_continue have a consumer -----
      i2c_slave_tx #(.CNT_W(16)) u_tx (
         .clk(clk), .rst_n(rst_n), .scl_fall(scl_fall),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse),
         .tx_start(tx_start), .tx_continue(tx_continue),
         .tx_req(tx_req), .tx_byte(rd_data), .sda_drive_low(tx_sda_low),
         .driving(driving), .bit_index(t_bit_index), .byte_sent(byte_sent),
         .n_bytes(n_bytes_tx), .n_bits(n_bits_tx)
      );

      i2c_slave_mack #(.CNT_W(16)) u_mack (
         .clk(clk), .rst_n(rst_n), .scl_rise(scl_rise), .sda_q(sda_q),
         .start_pulse(start_pulse), .stop_pulse(stop_pulse), .byte_sent(byte_sent),
         .awaiting(awaiting), .mack_valid(mack_valid), .mack_ack(mack_ack),
         .keep_sourcing(keep_sourcing), .n_ack(n_m_ack), .n_nack(n_m_nack)
      );

      // ---- the register file, whose pointer must survive a repeated START ----
      i2c_slave_regs #(.N_REG(N_REG), .RO_MASK(RO_MASK), .CNT_W(16)) u_regs (
         .clk(clk), .rst_n(rst_n),
         .rx_valid(rx_valid), .rx_byte(rx_byte), .rx_is_pointer(rx_is_pointer),
         .wr_accept(wr_accept), .rd_data(rd_data), .rd_taken(tx_req),
         .reg_flat(reg_flat), .pointer(pointer),
         .n_writes(n_writes), .n_refused(n_refused), .n_reads(n_reads)
      );

      assign scl_dl = {txn_scl_low, m_scl_low};
      assign sda_dl = {ack_sda_low | tx_sda_low, m_sda_low};

      always #5 clk = ~clk;

      integer errors = 0;
      // `k` belongs to the bus tasks -- m_byte drives it -- so a test that loops must use its
      // own variable. Sharing one cost an afternoon: the outer loop never terminated, because
      // m_byte left k at -1 every time round.
      integer k;
      integer j;

      // ---- observers ---------------------------------------------------------
      // The rule of §3.1.9 that cannot be checked from the DUT's ports alone: a stretch is a
      // pull-down that begins in the LOW phase. So watch for the drive ASSERTING while the
      // resolved line is still high -- which would shorten the master's high time below tHIGH
      // and corrupt the bit every device on the bus is sampling.
      integer drive_while_high = 0;
      integer stretch_waits = 0;      // how many times the master had to wait for the line
      integer framing_in_stretch = 0;    // must stay zero: see T11
      integer ptr_out_of_phase = 0;    // must stay zero: see T15
      integer scl_held_in_reset = 0;    // must stay zero: see T1
      reg  scl_low_d = 1'b0;

      // NOT GUARDED BY rst_n, and that is the point: a device held in reset must not be holding
      // the bus down. A mutant whose reset value was "stretching" cleared itself on the first
      // clock after release, so every check taken after reset passed -- and the defect, a slave
      // that pulls SCL low for the whole of its reset, was invisible.
      always @(posedge clk) if (!rst_n && txn_scl_low) scl_held_in_reset = scl_held_in_reset + 1;

      // AN OUTPUT MUST BE CORRECT IN ITSELF. `rx_is_pointer` is only consumed together with
      // rx_valid, so a version that asserted it outside any phase was harmless in this
      // integration -- and a mutant removing the phase gate survived a full pass. Downstream
      // masking is not correctness: the next integration may not mask it.
      always @(posedge clk) if (rst_n && !in_phase && rx_is_pointer) ptr_out_of_phase = ptr_out_of_phase + 1;

      always @(posedge clk) if (rst_n) begin
         if (txn_scl_low && !scl_low_d && scl === 1'b1) drive_while_high = drive_while_high + 1;
         if (stretching && (start_pulse || restart_pulse || stop_pulse))
            framing_in_stretch = framing_in_stretch + 1;
         scl_low_d <= txn_scl_low;
      end

      initial begin
         repeat (400000) @(posedge clk);
         $display("  FAIL watchdog: the bench did not finish");
         $fatal(1);
      end

      // ---- the master model --------------------------------------------------
      task hp;                     // one half-phase of idling
         begin repeat (HALF) @(posedge clk); end
      endtask

      // Release SCL and WAIT FOR IT TO RISE. A master that skipped the wait could not observe
      // a stretch, because it would be driving against the slave's pull-down.
      task m_scl_release;
         integer guard;
         begin
            @(negedge clk); m_scl_low = 1'b0;
            // SETTLE BEFORE READING. Reading `scl` in the same delta as releasing it returns
            // the stale value, so the wait would be counted whether or not anybody was
            // holding the line -- a check that passes for the wrong reason. The VHDL port of
            // this bench is what found it.
            #1;
            guard = 0;
            if (scl !== 1'b1) begin
               stretch_waits = stretch_waits + 1;
               while (scl !== 1'b1 && guard < 20000) begin @(posedge clk); guard = guard + 1; end
               if (guard >= 20000) begin
                  $display("  FAIL the slave never released SCL"); errors = errors + 1;
               end
            end
            hp();
         end
      endtask

      task m_scl_pull;
         begin @(negedge clk); m_scl_low = 1'b1; hp(); end
      endtask

      task m_start;
         begin
            @(negedge clk); m_scl_low = 1'b0; m_sda_low = 1'b0; hp();
            @(negedge clk); m_sda_low = 1'b1; hp();     // SDA falls while SCL is high
            m_scl_pull();
         end
      endtask

      task m_restart;
         begin
            @(negedge clk); m_sda_low = 1'b0; hp();     // release SDA in the low phase
            m_scl_release();                            // SCL rises
            @(negedge clk); m_sda_low = 1'b1; hp();     // SDA falls while SCL is high
            m_scl_pull();
         end
      endtask

      task m_stop;
         begin
            @(negedge clk); m_sda_low = 1'b1; hp();     // SDA low in the low phase
            m_scl_release();
            @(negedge clk); m_sda_low = 1'b0; hp();     // SDA rises while SCL is high
         end
      endtask

      // One bit clocked by the master. `sda_low` is what the MASTER drives; pass 0 to release
      // so the slave can own the line.
      task m_bit (input sda_low);
         begin
            @(negedge clk); m_sda_low = sda_low; hp();
            m_scl_release();
            m_scl_pull();
         end
      endtask

      task m_byte (input [7:0] d);
         begin
            for (k = 7; k >= 0; k = k - 1) m_bit(~d[k]);      // a zero is a pull-down
         end
      endtask

      // The ninth slot with the master releasing: the slave answers.
      task m_ack_slot_listen;
         begin m_bit(1'b0); end
      endtask

      // The ninth slot with the master answering: a pull-down means ACK.
      task m_ack_slot_drive (input ack);
         begin m_bit(ack); end
      endtask

      task do_reset;
         begin
            @(negedge clk);
            rst_n = 1'b0; m_scl_low = 1'b0; m_sda_low = 1'b0;
            drive_while_high = 0; stretch_waits = 0;
            framing_in_stretch = 0; stall_load = 1'b0;
            ptr_out_of_phase = 0; scl_held_in_reset = 0;
            repeat (4) @(posedge clk);
            @(negedge clk); rst_n = 1'b1;
            hp(); #1;
         end
      endtask

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

      // ---- tests --------------------------------------------------------------
      initial begin
         $display("=== i2c_slave_txn: transaction state, a surviving pointer, and a wait state ===");

         // ---- T1. Reset, including what the slave does WHILE held in reset.
         @(negedge clk); rst_n = 1'b0; m_scl_low = 1'b0; m_sda_low = 1'b0;
         scl_held_in_reset = 0;
         repeat (20) @(posedge clk);
         $display("T1  a target held in reset does not hold the bus down");
         ck("T1 SCL untouched throughout reset", scl_held_in_reset, 0);
         ck("T1 and the line is idle high", scl, 1);
         do_reset();
         $display("T1  a reset target is in no phase, stretching nothing, driving nothing");
         ck("T1 no phase", in_phase, 0);
         ck("T1 not stretching", stretching, 0);
         ck("T1 SCL not pulled", txn_scl_low, 0);
         ck("T1 rx disabled", rx_enable, 0);

         // ---- T2. An addressed write opens a phase.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         m_ack_slot_listen();
         $display("T2  our address with the write bit opens a write phase");
         ck("T2 in a phase", in_phase, 1);
         ck("T2 direction is write", dir_q, 0);
         ck("T2 the next byte is the pointer", rx_is_pointer, 1);
         ck("T2 one phase counted", n_phases, 1);

         // ---- T3. The first data byte is the pointer, the rest are not.
         m_byte(8'h03); m_ack_slot_listen();
         $display("T3  the first data byte is the pointer and the second is not");
         ck("T3 pointer loaded", pointer, 3);
         ck("T3 index advanced", data_index, 1);
         ck("T3 no longer the pointer byte", rx_is_pointer, 0);
         m_byte(8'h9E); m_ack_slot_listen();
         ck("T3 the second byte is data", reg_flat[8*3 +: 8], 8'h9E);
         ck("T3 index saturated", data_index, 2);

         // ---- T4. Somebody else's address opens nothing.
         do_reset();
         m_start();
         m_byte({7'h21, 1'b0});
         m_ack_slot_listen();
         $display("T4  another device's address opens no phase and enables no shifting");
         ck("T4 no phase", in_phase, 0);
         ck("T4 rx disabled", rx_enable, 0);
         ck("T4 no phase counted", n_phases, 0);

         // ---- T5. The acknowledge slot is not a data bit.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         @(negedge clk); m_sda_low = 1'b0; hp();
         m_scl_release();
         $display("T5  rx_enable drops inside the acknowledge slot: the ninth bit is not data");
         ck("T5 the slot is active", ack_active, 1);
         ck("T5 so shifting is disabled", rx_enable, 0);
         m_scl_pull();

         // ---- T6. THE HEADLINE: a repeated START keeps the pointer.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h05);        m_ack_slot_listen();     // pointer = 5
         m_byte(8'h7C);        m_ack_slot_listen();     // reg 5 = 0x7C, pointer = 6
         ck("T6 written before the restart", reg_flat[8*5 +: 8], 8'h7C);
         ck("T6 pointer before the restart", pointer, 6);
         m_restart();
         $display("T6  a repeated START clears the phase and KEEPS the pointer");
         ck("T6 the phase is gone", in_phase, 0);
         ck("T6 the direction is gone", dir_q, 0);
         ck("T6 the byte index is gone", data_index, 0);
         ck("T6 the pointer SURVIVED", pointer, 6);
         ck("T6 and so did the data", reg_flat[8*5 +: 8], 8'h7C);
         ck("T6 one restart counted", n_restarts_txn, 1);

         // ---- T7. A read is started INSIDE the address acknowledge slot, so the first bit
         // lands on the fall that terminates it. Starting at the slot's end leaves the
         // transmitter one bit late, which this check is what found.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b1});
         ck("T7 no bit has been driven at the eighth bit", n_bits_tx, 0);
         m_ack_slot_listen();
         $display("T7  a read is started inside the address acknowledge slot, so the first bit is on time");
         ck("T7 in a read phase", in_phase, 1);
         ck("T7 direction is read", dir_q, 1);
         ck("T7 the first bit is already driven", n_bits_tx, 1);
         ck("T7 and the transmitter owns SDA", driving, 1);

         // ---- T8. The master's answer decides whether another byte follows.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h00);        m_ack_slot_listen();
         m_byte(8'hC3);        m_ack_slot_listen();     // reg 0 = 0xC3
         m_restart();
         m_byte({ADDR, 1'b1}); m_ack_slot_listen();
         for (j = 0; j < 8; j = j + 1) m_bit(1'b0);           // the slave sources a byte
         m_ack_slot_drive(1'b1);                        // the master ACKs: send another
         $display("T8  an acknowledged read byte is followed by another; a NACK ends it");
         ck("T8 the master acknowledged", n_m_ack, 1);
         ck("T8 so sourcing continues", keep_sourcing, 1);
         for (j = 0; j < 8; j = j + 1) m_bit(1'b0);
         m_ack_slot_drive(1'b0);                        // the master NACKs
         ck("T8 the master NACKed", n_m_nack, 1);
         ck("T8 so sourcing stopped", keep_sourcing, 0);
         ck("T8 two bytes were served", n_bytes_tx, 2);
         m_stop();

         // ---- T9. Clock stretching, observed by a master that had to wait.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         @(negedge clk); stall_load = 1'b1;             // the application needs time
         @(posedge clk);
         @(negedge clk); stall_load = 1'b0;
         m_ack_slot_listen();
         $display("T9  the application asks for time, and the master BLOCKS until it is given");
         ck("T9 the slave is stretching", stretching, 1);
         ck("T9 and is pulling SCL down", txn_scl_low, 1);
         ck("T9 SCL is low", scl, 0);
         ck("T9 the master has not waited yet", stretch_waits, 0);
         // The next byte cannot begin until the slave lets go, so this call BLOCKS.
         m_byte(8'h01);
         ck("T9 the master had to wait for the line", stretch_waits, 1);
         ck("T9 and the stretch is over", stretching, 0);
         m_ack_slot_listen();
         ck("T9 the transfer continued afterwards", pointer, 1);
         ck("T9 one stretch counted", n_stretch, 1);

         // ---- T10. A stretch is never a pull-down in the high phase.
         $display("T10 across every test so far, SCL was never pulled down while it was high");
         ck("T10 no drive in a high phase", drive_while_high, 0);

         // ---- T11. A stretch and a framing event cannot coexist, and the design therefore
         // carries no code for the combination. This is the check that justifies the removal:
         // a stretch holds SCL low, and framing is an SDA edge qualified by SCL HIGH.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0});
         @(negedge clk); stall_load = 1'b1;
         @(posedge clk);
         @(negedge clk); stall_load = 1'b0;
         m_ack_slot_listen();
         ck("T11 stretching", stretching, 1);
         m_byte(8'h02);                                 // blocks, then completes
         m_ack_slot_listen();
         m_stop();
         $display("T11 a stretch and a framing event never coexist: SCL cannot be low and high");
         ck("T11 no framing arrived during a stretch", framing_in_stretch, 0);
         ck("T11 the phase closed on the STOP", in_phase, 0);
         ck("T11 not stretching", stretching, 0);
         ck("T11 SCL released", txn_scl_low, 0);

         // ---- T12. The byte index saturates.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h00);        m_ack_slot_listen();
         for (j = 0; j < 4; j = j + 1) begin m_byte(8'h20); m_ack_slot_listen(); end
         $display("T12 the byte index saturates: a later byte never becomes a pointer again");
         ck("T12 index pinned at two", data_index, 2);
         ck("T12 not a pointer byte", rx_is_pointer, 0);

         // ---- T13. A STOP ends the phase.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         ck("T13 in a phase", in_phase, 1);
         m_stop();
         $display("T13 a STOP ends the phase");
         ck("T13 phase closed", in_phase, 0);
         ck("T13 index cleared", data_index, 0);

         // ---- T14. Reset clears the transaction state and every counter.
         do_reset();
         $display("T14 reset clears the phase, the index and every counter");
         ck("T14 no phase", in_phase, 0);
         ck("T14 index zero", data_index, 0);
         ck("T14 phases zero", n_phases, 0);
         ck("T14 restarts zero", n_restarts_txn, 0);
         ck("T14 stretches zero", n_stretch, 0);

         // ---- T15. A NACKed read must END, even if the master keeps clocking. This is
         // Chapter 18.8 §4 at the transaction level: a slave that sources another byte after a
         // NACK holds SDA low for every zero in it and prevents the master's STOP.
         do_reset();
         m_start();
         m_byte({ADDR, 1'b0}); m_ack_slot_listen();
         m_byte(8'h00);        m_ack_slot_listen();
         m_byte(8'hFF);        m_ack_slot_listen();     // reg 0 = 0xFF: every bit a pull-down
         m_restart();
         m_byte({ADDR, 1'b1}); m_ack_slot_listen();
         for (j = 0; j < 8; j = j + 1) m_bit(1'b0);
         m_ack_slot_drive(1'b0);                        // NACK: that was the last byte
         ck("T15 one byte was served", n_bytes_tx, 1);
         // The master now keeps clocking anyway. Nothing may come back.
         for (j = 0; j < 8; j = j + 1) begin
            m_bit(1'b0);
            if (tx_sda_low) begin
               $display("  FAIL T15 the slave drove SDA after a NACK"); errors = errors + 1;
            end
         end
         $display("T15 a NACKed read ends even against a master that keeps clocking");
         ck("T15 still one byte", n_bytes_tx, 1);
         ck("T15 the transmitter is idle", driving, 0);
         ck("T15 and SDA was left to the master", tx_sda_low, 0);
         m_stop();
         ck("T15 so the STOP happened", n_sto > 0, 1);
         ck("T15 rx_is_pointer never asserted outside a phase", ptr_out_of_phase, 0);

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

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_slave_txn_tb.vhd — the same tests in VHDL
   -- -----------------------------------------------------------------------------
   -- i2c_slave_txn_tb.vhd
   -- Independent oracle for i2c_slave_txn -- the integration bench, in VHDL.
   --
   -- The whole slave stack is instantiated (18.2 through 18.9) and driven from a master model
   -- across the wired-AND bus, because every output of this block is an instruction to another
   -- block and an instruction with no recipient cannot be checked.
   --
   -- The master RELEASES SCL and waits for the line to rise. A master that clocked on a fixed
   -- schedule could not test a stretch at all.
   -- -----------------------------------------------------------------------------
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_slave_txn_tb is
   end entity i2c_slave_txn_tb;

   architecture sim of i2c_slave_txn_tb is

      constant HALF    : positive := 8;
      constant ADDR    : std_logic_vector(6 downto 0) := "1010000";   -- 0x50
      constant N_REG   : positive := 8;
      constant RO_MASK : natural  := 4;                               -- register 2 read-only

      signal clk   : std_logic := '0';
      signal rst_n : std_logic := '0';

      signal m_scl_low, m_sda_low : std_logic := '0';

      signal scl_dl, sda_dl : std_logic_vector(1 downto 0);
      signal scl, sda : std_logic;
      signal scl_in, sda_in, scl_rbl, sda_rbl : std_logic_vector(1 downto 0);
      signal scl_holders, sda_holders : unsigned(7 downto 0);

      signal scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall : std_logic;

      signal start_pulse, restart_pulse, stop_pulse, bus_active, framing_midbyte : std_logic;
      signal n_sta, n_rs, n_sto : unsigned(15 downto 0);

      signal acquiring, addr_done, match, selected, dir_read : std_logic;
      signal a_bit_index : unsigned(3 downto 0);
      signal addr_byte : std_logic_vector(7 downto 0);
      signal n_match, n_miss : unsigned(15 downto 0);

      signal receiving, rx_valid, rx_byte_done : std_logic;
      signal r_bit_index : unsigned(3 downto 0);
      signal rx_byte : std_logic_vector(7 downto 0);
      signal n_bytes_rx, n_partial : unsigned(15 downto 0);

      signal in_phase, dir_q, rx_is_pointer, rx_enable, tx_start, tx_continue : std_logic;
      signal data_index : unsigned(3 downto 0);
      signal txn_scl_low, stretching : std_logic;
      signal n_phases, n_restarts_txn, n_stretch : unsigned(15 downto 0);

      -- THE APPLICATION'S STALL, AS A COUNTDOWN. A test that raises a level and lowers it by
      -- hand can only stretch while nothing is happening, so the master never has to wait and
      -- the stretch is invisible to it. A countdown that expires on its own lets the master
      -- BLOCK inside its own bit procedure, which is the only way the wait becomes an
      -- observable fact about the bus.
      constant STALL_N : positive := 40;
      signal stall_load : std_logic := '0';
      signal stall_hold : integer := 0;
      signal stall_req  : std_logic;

      signal ack_active, ack_armed, ack_sda_low : std_logic;
      signal n_acks, n_nacks : unsigned(15 downto 0);

      signal tx_req, tx_sda_low, driving, byte_sent : std_logic;
      signal t_bit_index : unsigned(3 downto 0);
      signal n_bytes_tx, n_bits_tx : unsigned(15 downto 0);
      signal rd_data : std_logic_vector(7 downto 0);

      signal awaiting, mack_valid, mack_ack, keep_sourcing : std_logic;
      signal n_m_ack, n_m_nack : unsigned(15 downto 0);

      signal wr_accept : std_logic;
      signal reg_flat : std_logic_vector(8*N_REG-1 downto 0);
      signal pointer : std_logic_vector(7 downto 0);
      signal n_writes, n_refused, n_reads : unsigned(15 downto 0);

      signal mid_byte      : std_logic;
      signal byte_done_any : std_logic;
      signal ack_en        : std_logic;

      signal halt : boolean := false;

      -- observers
      signal drive_while_high   : integer := 0;
      signal stretch_waits      : integer := 0;
      signal framing_in_stretch : integer := 0;
      signal ptr_out_of_phase   : integer := 0;
      signal scl_held_in_reset  : integer := 0;
      signal clr_obs            : boolean := false;

   begin

      stall_req <= '1' when stall_hold > 0 else '0';

      stall_ctr : process (clk)
      begin
         if rising_edge(clk) then
            if rst_n = '0' then
               stall_hold <= 0;
            elsif stall_load = '1' then
               stall_hold <= STALL_N;
            elsif stall_hold > 0 then
               stall_hold <= stall_hold - 1;
            end if;
         end if;
      end process;

      mid_byte      <= acquiring or receiving;
      byte_done_any <= rx_byte_done or (addr_done and match);
      ack_en        <= '1' when (addr_done = '1' and match = '1') else wr_accept;

      scl_dl <= txn_scl_low & m_scl_low;
      sda_dl <= (ack_sda_low or tx_sda_low) & m_sda_low;

      bus_model : entity work.i2c_line_model
         generic map (N_DEV => 2)
         port map (scl_drive_low => scl_dl, sda_drive_low => sda_dl,
            scl => scl, sda => sda, scl_in => scl_in, sda_in => sda_in,
            scl_released_but_low => scl_rbl, sda_released_but_low => sda_rbl,
            scl_holders => scl_holders, sda_holders => sda_holders);

      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);

      u_frm : entity work.i2c_slave_framing
         generic map (CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_q => scl_q,
            sda_rise => sda_rise, sda_fall => sda_fall,
            start_pulse => start_pulse, restart_pulse => restart_pulse,
            stop_pulse => stop_pulse, bus_active => bus_active,
            mid_byte => mid_byte, framing_midbyte => framing_midbyte,
            n_starts => n_sta, n_restarts => n_rs, n_stops => n_sto);

      u_addr : entity work.i2c_slave_addr
         generic map (MY_ADDR => ADDR, CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_rise => scl_rise, sda_q => sda_q,
            start_pulse => start_pulse, stop_pulse => stop_pulse,
            acquiring => acquiring, bit_index => a_bit_index, addr_done => addr_done,
            addr_byte => addr_byte, match => match, selected => selected,
            dir_read => dir_read, n_match => n_match, n_miss => n_miss);

      dut : entity work.i2c_slave_txn
         generic map (CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_q => scl_q, scl_fall => scl_fall,
            start_pulse => start_pulse, restart_pulse => restart_pulse,
            stop_pulse => stop_pulse, addr_done => addr_done, match => match,
            dir_read => dir_read, ack_active => ack_active, rx_valid => rx_valid,
            byte_sent => byte_sent, mack_valid => mack_valid, mack_ack => mack_ack,
            stall_req => stall_req,
            in_phase => in_phase, dir_q => dir_q, rx_is_pointer => rx_is_pointer,
            rx_enable => rx_enable, tx_start => tx_start, tx_continue => tx_continue,
            data_index => data_index, scl_drive_low => txn_scl_low,
            stretching => stretching, n_phases => n_phases,
            n_restarts => n_restarts_txn, n_stretch => n_stretch);

      u_ack : 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_any, ack_en => ack_en,
            start_pulse => start_pulse, stop_pulse => stop_pulse,
            sda_drive_low => ack_sda_low, ack_active => ack_active,
            ack_armed => ack_armed, n_acks => n_acks, n_nacks => n_nacks);

      u_rx : entity work.i2c_slave_rx
         generic map (CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_rise => scl_rise, sda_q => sda_q,
            start_pulse => start_pulse, stop_pulse => stop_pulse, rx_enable => rx_enable,
            receiving => receiving, bit_index => r_bit_index, rx_byte => rx_byte,
            rx_valid => rx_valid, byte_done => rx_byte_done,
            n_bytes => n_bytes_rx, n_partial => n_partial);

      u_tx : entity work.i2c_slave_tx
         generic map (CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_fall => scl_fall,
            start_pulse => start_pulse, stop_pulse => stop_pulse,
            tx_start => tx_start, tx_continue => tx_continue,
            tx_req => tx_req, tx_byte => rd_data, sda_drive_low => tx_sda_low,
            driving => driving, bit_index => t_bit_index, byte_sent => byte_sent,
            n_bytes => n_bytes_tx, n_bits => n_bits_tx);

      u_mack : entity work.i2c_slave_mack
         generic map (CNT_W => 16)
         port map (clk => clk, rst_n => rst_n, scl_rise => scl_rise, sda_q => sda_q,
            start_pulse => start_pulse, stop_pulse => stop_pulse, byte_sent => byte_sent,
            awaiting => awaiting, mack_valid => mack_valid, mack_ack => mack_ack,
            keep_sourcing => keep_sourcing, n_ack => n_m_ack, n_nack => n_m_nack);

      u_regs : entity work.i2c_slave_regs
         generic map (N_REG => N_REG, RO_MASK => RO_MASK, CNT_W => 16)
         port map (clk => clk, rst_n => rst_n,
            rx_valid => rx_valid, rx_byte => rx_byte, rx_is_pointer => rx_is_pointer,
            wr_accept => wr_accept, rd_data => rd_data, rd_taken => tx_req,
            reg_flat => reg_flat, pointer => pointer,
            n_writes => n_writes, n_refused => n_refused, n_reads => n_reads);

      -- NOT guarded by rst_n: a device held in reset must not be holding the bus down. A mutant
      -- whose reset value was "stretching" cleared itself on the first clock after release, so
      -- every check taken after reset passed and the defect was invisible.
      obs_reset : process (clk, clr_obs)
      begin
         if clr_obs then
            scl_held_in_reset <= 0;
         elsif rising_edge(clk) then
            if rst_n = '0' and txn_scl_low = '1' then
               scl_held_in_reset <= scl_held_in_reset + 1;
            end if;
         end if;
      end process;

      -- AN OUTPUT MUST BE CORRECT IN ITSELF. rx_is_pointer is only consumed with rx_valid, so a
      -- version asserting it outside any phase was harmless HERE -- and a mutant removing the
      -- phase gate survived a full pass. Downstream masking is not correctness.
      obs_ptr : process (clk, clr_obs)
      begin
         if clr_obs then
            ptr_out_of_phase <= 0;
         elsif rising_edge(clk) then
            if rst_n = '1' and in_phase = '0' and rx_is_pointer = '1' then
               ptr_out_of_phase <= ptr_out_of_phase + 1;
            end if;
         end if;
      end process;

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

      -- The rule of §3.1.9 that cannot be checked from the DUT's ports alone: a stretch is a
      -- pull-down that begins in the LOW phase.
      obs : process (clk, clr_obs)
         variable d : std_logic := '0';
      begin
         if clr_obs then
            drive_while_high <= 0; framing_in_stretch <= 0;
         elsif rising_edge(clk) then
            if rst_n = '1' then
               if txn_scl_low = '1' and d = '0' and scl = '1' then
                  drive_while_high <= drive_while_high + 1;
               end if;
               if stretching = '1' and
                  (start_pulse = '1' or restart_pulse = '1' or stop_pulse = '1') then
                  framing_in_stretch <= framing_in_stretch + 1;
               end if;
            end if;
            d := txn_scl_low;
         end if;
      end process;

      stim : process
         variable err : integer := 0;

         procedure hp is
         begin
            for i in 1 to HALF loop wait until rising_edge(clk); end loop;
         end procedure;

         procedure ck (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;

         function b2i (b : std_logic) return integer is
         begin
            if b = '1' then return 1; else return 0; end if;
         end function;

         function slice8 (f : std_logic_vector; idx : integer) return integer is
         begin
            return to_integer(unsigned(f(8*idx+7 downto 8*idx)));
         end function;

         -- Release SCL and WAIT FOR IT TO RISE.
         procedure m_scl_release is
            variable guard : integer := 0;
         begin
            wait until falling_edge(clk);
            m_scl_low <= '0';
            wait for 1 ns;
            if scl /= '1' then
               stretch_waits <= stretch_waits + 1;
               while scl /= '1' and guard < 20000 loop
                  wait until rising_edge(clk);
                  guard := guard + 1;
               end loop;
               if guard >= 20000 then
                  report "  FAIL the slave never released SCL" severity note;
                  err := err + 1;
               end if;
            end if;
            hp;
         end procedure;

         procedure m_scl_pull is
         begin
            wait until falling_edge(clk); m_scl_low <= '1'; hp;
         end procedure;

         procedure m_start is
         begin
            wait until falling_edge(clk); m_scl_low <= '0'; m_sda_low <= '0'; hp;
            wait until falling_edge(clk); m_sda_low <= '1'; hp;
            m_scl_pull;
         end procedure;

         procedure m_restart is
         begin
            wait until falling_edge(clk); m_sda_low <= '0'; hp;
            m_scl_release;
            wait until falling_edge(clk); m_sda_low <= '1'; hp;
            m_scl_pull;
         end procedure;

         procedure m_stop is
         begin
            wait until falling_edge(clk); m_sda_low <= '1'; hp;
            m_scl_release;
            wait until falling_edge(clk); m_sda_low <= '0'; hp;
         end procedure;

         procedure m_bit (sda_low : std_logic) is
         begin
            wait until falling_edge(clk); m_sda_low <= sda_low; hp;
            m_scl_release;
            m_scl_pull;
         end procedure;

         procedure m_byte (d : std_logic_vector(7 downto 0)) is
         begin
            for i in 7 downto 0 loop m_bit(not d(i)); end loop;
         end procedure;

         procedure m_ack_slot_listen is
         begin
            m_bit('0');
         end procedure;

         procedure m_ack_slot_drive (ack : std_logic) is
         begin
            m_bit(ack);
         end procedure;

         procedure do_reset is
         begin
            wait until falling_edge(clk);
            rst_n <= '0'; m_scl_low <= '0'; m_sda_low <= '0'; stall_load <= '0';
            clr_obs <= true; stretch_waits <= 0; wait for 1 ns; clr_obs <= false;
            for i in 1 to 4 loop wait until rising_edge(clk); end loop;
            wait until falling_edge(clk); rst_n <= '1';
            hp; wait for 1 ns;
         end procedure;

      begin
         report "=== i2c_slave_txn: transaction state, a surviving pointer, and a wait state ==="
                severity note;

         -- T1. Reset, including what the slave does WHILE held in reset.
         wait until falling_edge(clk);
         rst_n <= '0'; m_scl_low <= '0'; m_sda_low <= '0';
         clr_obs <= true; wait for 1 ns; clr_obs <= false;
         for i in 1 to 20 loop wait until rising_edge(clk); end loop;
         report "T1  a target held in reset does not hold the bus down" severity note;
         ck("T1 SCL untouched throughout reset", scl_held_in_reset, 0);
         ck("T1 and the line is idle high", b2i(scl), 1);
         do_reset;
         report "T1  a reset target is in no phase, stretching nothing, driving nothing"
                severity note;
         ck("T1 no phase", b2i(in_phase), 0);
         ck("T1 not stretching", b2i(stretching), 0);
         ck("T1 SCL not pulled", b2i(txn_scl_low), 0);
         ck("T1 rx disabled", b2i(rx_enable), 0);

         -- T2.
         do_reset;
         m_start;
         m_byte(ADDR & '0');
         m_ack_slot_listen;
         report "T2  our address with the write bit opens a write phase" severity note;
         ck("T2 in a phase", b2i(in_phase), 1);
         ck("T2 direction is write", b2i(dir_q), 0);
         ck("T2 the next byte is the pointer", b2i(rx_is_pointer), 1);
         ck("T2 one phase counted", to_integer(n_phases), 1);

         -- T3.
         m_byte(x"03"); m_ack_slot_listen;
         report "T3  the first data byte is the pointer and the second is not" severity note;
         ck("T3 pointer loaded", to_integer(unsigned(pointer)), 3);
         ck("T3 index advanced", to_integer(data_index), 1);
         ck("T3 no longer the pointer byte", b2i(rx_is_pointer), 0);
         m_byte(x"9E"); m_ack_slot_listen;
         ck("T3 the second byte is data", slice8(reg_flat, 3), 16#9E#);
         ck("T3 index saturated", to_integer(data_index), 2);

         -- T4.
         do_reset;
         m_start;
         m_byte("0100001" & '0');
         m_ack_slot_listen;
         report "T4  another device's address opens no phase and enables no shifting"
                severity note;
         ck("T4 no phase", b2i(in_phase), 0);
         ck("T4 rx disabled", b2i(rx_enable), 0);
         ck("T4 no phase counted", to_integer(n_phases), 0);

         -- T5.
         do_reset;
         m_start;
         m_byte(ADDR & '0');
         wait until falling_edge(clk); m_sda_low <= '0'; hp;
         m_scl_release;
         report "T5  rx_enable drops inside the acknowledge slot: the ninth bit is not data"
                severity note;
         ck("T5 the slot is active", b2i(ack_active), 1);
         ck("T5 so shifting is disabled", b2i(rx_enable), 0);
         m_scl_pull;

         -- T6. THE HEADLINE.
         do_reset;
         m_start;
         m_byte(ADDR & '0'); m_ack_slot_listen;
         m_byte(x"05");      m_ack_slot_listen;
         m_byte(x"7C");      m_ack_slot_listen;
         ck("T6 written before the restart", slice8(reg_flat, 5), 16#7C#);
         ck("T6 pointer before the restart", to_integer(unsigned(pointer)), 6);
         m_restart;
         report "T6  a repeated START clears the phase and KEEPS the pointer" severity note;
         ck("T6 the phase is gone", b2i(in_phase), 0);
         ck("T6 the direction is gone", b2i(dir_q), 0);
         ck("T6 the byte index is gone", to_integer(data_index), 0);
         ck("T6 the pointer SURVIVED", to_integer(unsigned(pointer)), 6);
         ck("T6 and so did the data", slice8(reg_flat, 5), 16#7C#);
         ck("T6 one restart counted", to_integer(n_restarts_txn), 1);

         -- T7.
         do_reset;
         m_start;
         m_byte(ADDR & '1');
         ck("T7 no bit has been driven at the eighth bit", to_integer(n_bits_tx), 0);
         m_ack_slot_listen;
         report "T7  a read is started inside the address acknowledge slot, so the first bit is on time"
                severity note;
         ck("T7 in a read phase", b2i(in_phase), 1);
         ck("T7 direction is read", b2i(dir_q), 1);
         ck("T7 the first bit is already driven", to_integer(n_bits_tx), 1);
         ck("T7 and the transmitter owns SDA", b2i(driving), 1);

         -- T8.
         do_reset;
         m_start;
         m_byte(ADDR & '0'); m_ack_slot_listen;
         m_byte(x"00");      m_ack_slot_listen;
         m_byte(x"C3");      m_ack_slot_listen;
         m_restart;
         m_byte(ADDR & '1'); m_ack_slot_listen;
         for i in 0 to 7 loop m_bit('0'); end loop;
         m_ack_slot_drive('1');
         report "T8  an acknowledged read byte is followed by another; a NACK ends it"
                severity note;
         ck("T8 the master acknowledged", to_integer(n_m_ack), 1);
         ck("T8 so sourcing continues", b2i(keep_sourcing), 1);
         for i in 0 to 7 loop m_bit('0'); end loop;
         m_ack_slot_drive('0');
         ck("T8 the master NACKed", to_integer(n_m_nack), 1);
         ck("T8 so sourcing stopped", b2i(keep_sourcing), 0);
         ck("T8 two bytes were served", to_integer(n_bytes_tx), 2);
         m_stop;

         -- T9.
         do_reset;
         m_start;
         m_byte(ADDR & '0');
         wait until falling_edge(clk); stall_load <= '1';
         wait until rising_edge(clk);
         wait until falling_edge(clk); stall_load <= '0';
         m_ack_slot_listen;
         report "T9  the application asks for time, and the master BLOCKS until it is given"
                severity note;
         ck("T9 the slave is stretching", b2i(stretching), 1);
         ck("T9 and is pulling SCL down", b2i(txn_scl_low), 1);
         ck("T9 SCL is low", b2i(scl), 0);
         ck("T9 the master has not waited yet", stretch_waits, 0);
         m_byte(x"01");
         ck("T9 the master had to wait for the line", stretch_waits, 1);
         ck("T9 and the stretch is over", b2i(stretching), 0);
         m_ack_slot_listen;
         ck("T9 the transfer continued afterwards", to_integer(unsigned(pointer)), 1);
         ck("T9 one stretch counted", to_integer(n_stretch), 1);

         -- T10.
         report "T10 across every test so far, SCL was never pulled down while it was high"
                severity note;
         ck("T10 no drive in a high phase", drive_while_high, 0);

         -- T11.
         do_reset;
         m_start;
         m_byte(ADDR & '0');
         wait until falling_edge(clk); stall_load <= '1';
         wait until rising_edge(clk);
         wait until falling_edge(clk); stall_load <= '0';
         m_ack_slot_listen;
         ck("T11 stretching", b2i(stretching), 1);
         m_byte(x"02");
         m_ack_slot_listen;
         m_stop;
         report "T11 a stretch and a framing event never coexist: SCL cannot be low and high"
                severity note;
         ck("T11 no framing arrived during a stretch", framing_in_stretch, 0);
         ck("T11 the phase closed on the STOP", b2i(in_phase), 0);
         ck("T11 not stretching", b2i(stretching), 0);
         ck("T11 SCL released", b2i(txn_scl_low), 0);

         -- T12.
         do_reset;
         m_start;
         m_byte(ADDR & '0'); m_ack_slot_listen;
         m_byte(x"00");      m_ack_slot_listen;
         for i in 0 to 3 loop m_byte(x"20"); m_ack_slot_listen; end loop;
         report "T12 the byte index saturates: a later byte never becomes a pointer again"
                severity note;
         ck("T12 index pinned at two", to_integer(data_index), 2);
         ck("T12 not a pointer byte", b2i(rx_is_pointer), 0);

         -- T13.
         do_reset;
         m_start;
         m_byte(ADDR & '0'); m_ack_slot_listen;
         ck("T13 in a phase", b2i(in_phase), 1);
         m_stop;
         report "T13 a STOP ends the phase" severity note;
         ck("T13 phase closed", b2i(in_phase), 0);
         ck("T13 index cleared", to_integer(data_index), 0);

         -- T14.
         do_reset;
         report "T14 reset clears the phase, the index and every counter" severity note;
         ck("T14 no phase", b2i(in_phase), 0);
         ck("T14 index zero", to_integer(data_index), 0);
         ck("T14 phases zero", to_integer(n_phases), 0);
         ck("T14 restarts zero", to_integer(n_restarts_txn), 0);
         ck("T14 stretches zero", to_integer(n_stretch), 0);

         -- T15. A NACKed read must END, even if the master keeps clocking.
         do_reset;
         m_start;
         m_byte(ADDR & '0'); m_ack_slot_listen;
         m_byte(x"00");      m_ack_slot_listen;
         m_byte(x"FF");      m_ack_slot_listen;
         m_restart;
         m_byte(ADDR & '1'); m_ack_slot_listen;
         for i in 0 to 7 loop m_bit('0'); end loop;
         m_ack_slot_drive('0');
         ck("T15 one byte was served", to_integer(n_bytes_tx), 1);
         for i in 0 to 7 loop
            m_bit('0');
            if tx_sda_low = '1' then
               report "  FAIL T15 the slave drove SDA after a NACK" severity note;
               err := err + 1;
            end if;
         end loop;
         report "T15 a NACKed read ends even against a master that keeps clocking"
                severity note;
         ck("T15 still one byte", to_integer(n_bytes_tx), 1);
         ck("T15 the transmitter is idle", b2i(driving), 0);
         ck("T15 and SDA was left to the master", b2i(tx_sda_low), 0);
         m_stop;
         if n_sto > 0 then ck("T15 so the STOP happened", 1, 1);
         else              ck("T15 so the STOP happened", 0, 1); end if;
         ck("T15 rx_is_pointer never asserted outside a phase", ptr_out_of_phase, 0);

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

   end architecture sim;

All three languages finish at the same instant:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
i2c_slave_txn_tb.sv    ALL CHECKS PASSED    $finish at 75425000
i2c_slave_txn_tb.v     ALL CHECKS PASSED    $finish at 75425000
i2c_slave_txn_tb.vhd   ALL CHECKS PASSED    stopped at 75425 ns

8. Mutation Testing

Two passes. The first produced six survivors, and they split three-and-three in a way that is worth more than the final score.

Pass one: six survivors, two different diagnoses

#Injected defectVerdictDiagnosis
—stretch engaged at the rising edgeINVALID — did not elaboratereferenced a signal this block has no port for
M9receive path shifts while stretchingSURVIVEDredundant code — no rising edge exists to shift on
M10a NACK continues the read anywaySURVIVEDmissing test — nothing clocked after the NACK
M11direction taken live rather than latchedSURVIVEDequivalent — see below
M13pointer byte announced outside any phaseSURVIVEDmissing check — the output was masked downstream
M14reset comes up stretchingSURVIVEDmissing check — cleared before anyone looked
M15a read started on every acknowledge slotSURVIVEDredundant code — the slot occurs once per read

Three of the six were the testbench's fault and three were the design's — and in the design's case the fix was deletion, not a new test. §5 has the four removals and their proofs.

M11 is a genuine equivalent. dir_read is 18.4's level and dir_q is this block's latch of it, taken at the same instant from the same source. They can only differ if dir_read changes while in_phase is set — and dir_read changes only at addr_done, which requires eight bits after a START, which clears in_phase. So while the substitution matters, it cannot be reached. Discarded, not counted.

Pass two: sixteen valid mutants, sixteen killed

#Injected defectExpected detectionResult
M1a read started at the end of the acknowledge slotT7KILLED (6)
M2stretch engaged while SCL is still highT10KILLED (2)
M3stretch engaged on any cycle of the slotT9, T10KILLED (6)
M4the stretch is never releasedT9KILLED (26)
M5framing does not close the phaseT6, T13KILLED (6)
M6a repeated START leaves the byte index behindT6KILLED (2)
M7the byte index wraps instead of saturatingT12KILLED (2)
M8the receive path shifts during the acknowledge slotT5KILLED (9)
M9a NACK continues the read anywayT15 newKILLED (9)
M10the pointer byte is announced outside any phaseobserver newKILLED (2)
M11reset comes up stretchingT1 newKILLED (3)
M12a phase opens on any address byte, matched or notT4KILLED (4)
M13the direction is not latched at allT2, T5KILLED (11)
M14the byte index never advancesT3KILLED (11)
M15a stretch is announced but SCL is never pulledT9KILLED (3)
M16the transmitter is never continuedT8KILLED (4)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
baseline: PASS   (verified before injecting anything)
valid mutants: 16   killed: 16   survived: 0   equivalent: 1   invalid: 1
design lines deleted as unreachable: 4 (one register among them)
restored: PASS

M4's twenty-six failures are the loudest number in this module, and they are not a compliment to the bench — a stretch that never releases hangs the master, so every check after it fails. M2's two failures are the informative ones: exactly the two an observer raised, at exactly the instant the rule forbids.

9. Verification Connection — Properties of a Wait State

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Not synthesisable. Icarus rejects SVA, so these document the intent the bench
// checks procedurally, and each one is a rule from §3.1.9 or §3.1.10.

// A stretch never begins while SCL is high.
property stretch_begins_low;
   @(posedge clk) disable iff (!rst_n)
      $rose(scl_drive_low) |-> !scl_q;
endproperty

// A stretch and a framing event never coexist -- the property that replaced
// four lines of unreachable RTL.
property no_framing_during_stretch;
   @(posedge clk) disable iff (!rst_n)
      stretching |-> !(start_pulse || restart_pulse || stop_pulse);
endproperty

// A device held in reset holds nothing.
property reset_releases_the_bus;
   @(posedge clk) !rst_n |-> !scl_drive_low;
endproperty

// The pointer byte is only ever announced inside a phase.
property pointer_only_in_phase;
   @(posedge clk) disable iff (!rst_n)
      rx_is_pointer |-> in_phase;
endproperty

The last two exist because mutants survived. That is the honest way to build an assertion set: each property is a defect somebody actually injected and the bench could not see.

10. FPGA and ASIC Implications

The wait state is the only part of this block with a bus-level consequence, and it is a single flop driving a pad's pull-down enable. It must reach the pad without combinational gating that could glitch, because a glitch on SCL is a clock pulse to every device on the bus. Register it at the boundary and drive the pad from that register — Module 19 owns the pad itself.

Everything else is bookkeeping, and there is no arithmetic wider than a four-bit saturating index. The design's cost is the three diagnostic counters, which CNT_W exists to remove.

stall_req is a level from the application, and it crosses no clock domain here — this block and the application share clk. If the application genuinely runs on another clock, the crossing needs a synchroniser on stall_req, and getting it wrong makes the slave stretch for one clock or not at all. That is Module 19's territory, and it is the one place in a slave where a missing synchroniser produces a bus-level failure rather than a local one.

11. Debugging — The Read Whose First Bit Is Always Wrong

Symptom. Writes work. Reads return a byte that is the expected value shifted left by one, with a one in the least significant bit — or, on a different master, the expected value with its top bit replaced by a one. Single-byte and multi-byte reads are equally affected. A logic analyser shows nine data pulses where there should be eight.

What it is not. Not the register file, whose contents are provably right from the write side. Not the shift order, which would produce a reversal rather than a shift. Not the address decode.

What it is. The transmitter was started at the end of the acknowledge slot instead of its beginning, so it missed the terminating fall and placed bit 7 one bit-time late. The master's first sample therefore reads the released line — a one — and every subsequent sample reads the bit before the one it wanted.

Why the two symptoms. It depends on whether the master counts nine pulses or eight. A master that clocks exactly eight and then reads the ninth as its own acknowledge slot sees the value shifted; one that resynchronises sees the top bit replaced.

12. Common Misconceptions

"A repeated START resets the slave, so the register pointer is lost." It resets the bus logic — this block — and nothing else. The pointer is application state and survives, which is the only reason a combined transfer works.

"Clock stretching means the slave drives SCL." It means the slave pulls SCL low. There is no drive-high; the master owns every rising edge.

"A slave can start stretching whenever it needs to." Only in a low phase. Pulling SCL down while it is high truncates the sampling window for every device on the bus.

"The slave should begin its read data after the address acknowledge finishes." It must be loaded before that, so the acknowledge slot's own terminating fall can carry the first bit. §3.

"A NACK ends a read because the slave sets a flag." It ends because no continue is issued. This design has no read-ended flag at all — one was written, found to be unreachable, and deleted.

"A surviving mutant always means a missing test." Three of six survivors here meant the opposite: a line of RTL that no test could distinguish from its absence, because the case it defended against cannot occur. §5.

13. Reason It Through

Of each register this block holds, ask: would a combined transfer still work if a START cleared it? What does the answer tell you?

All three answer yes, which is why all three belong here. A register that answered no would be application state in the wrong module. §1.

Why must the transmitter be started inside the acknowledge slot rather than at its end?

Because 18.7 loads on the first fall after being started, and the fall that must carry bit 7 is the one terminating the ninth pulse. Starting at the end leaves it one bit-time late. §3.

A stretch engages one cycle after the fall that ends the acknowledge slot. Why is that safe, and what would make it unsafe?

SCL is low for the whole low phase, so the pull-down lands inside it. It would be unsafe if the condition were the slot's level rather than its closing fall, which could assert during the high phase — mutation M3. §4.

Four lines were deleted rather than tested. What is the obligation that justifies a deletion?

A proof that the condition under which the line changes an outcome is unsatisfiable. Each of the four has a one-sentence proof resting on a property established elsewhere. §5.

Two mutants were wrong and invisible because a consumer masked them. What does that say about how to judge an output?

That an output must be correct in itself, not merely harmless in the current integration. Both were caught by per-cycle observers on the output, not by new scenarios. §8.

Why can a master model with a fixed clock schedule not test clock stretching?

Because it would drive SCL high against the slave's pull-down, which is electrically impossible and makes the stretch unobservable. The master must release and wait. §7.

14. Understanding Check

15. Summary

Protocol state is cleared by a START; application state is not — and the boundary between them is a module boundary, which is what makes the rule enforceable rather than remembered.

Everything this block holds is deliberately disposable. The test of whether a register belongs here is whether a combined transfer would still work if a START cleared it.

A repeated START clears the phase and keeps the pointer, and a bench must assert both halves: one without the other passes a design that is broken in the opposite direction.

A read is started inside the address acknowledge slot, not at its end, because 18.7 loads on the next falling edge and the only one available is the slot's own. That defect lives between two correct modules, which is why the bench had to be an integration bench.

Clock stretching is a pull-down that begins in a low phase. One assignment can set it, and scl_fall is in that assignment's condition — so the rule is structural, not remembered.

The master model must be able to lose. A master that clocks on a fixed schedule cannot observe a stretch at all, so the bench's master releases SCL and counts the times it had to wait.

Three mutation survivors meant the testbench was incomplete; three meant the design contained code for cases that cannot occur. The second diagnosis demands deletion, and discharging it is a proof obligation rather than a judgement.

Four lines went, one of them a whole register — and the arguments that justified their removal became assertions, which is cheaper to maintain than the code and says what the code only implied.

Two mutants were wrong and invisible because a consumer masked them. An output that is only right because something downstream ignores it is not right, and 18.11 is the integration that will stop ignoring it.

Sixteen valid mutants, sixteen killed, with one equivalent and one non-elaborating injection removed from the denominator — and the informative failure counts are the small ones.

16. What Comes Next

Every block exists and every block is verified in three languages. What does not exist is one module you can instantiate.

Chapter 18.11 builds it: the wiring made explicit as RTL rather than as a testbench, the arbitration between the two blocks that can drive SDA, what reset must mean for a device attached to a bus it does not own, and what a target does when the bus does something the protocol does not allow.

It is also where the integration tests stop proving connectivity and start proving behaviour — because a wiring diagram that compiles is not a slave.

Continue learning