I²C · Module 18
ACK Generation and Its Timing Window
Where most slave designs first fail. The acknowledge has three parts, the window is bounded at both ends by falling edges, and asserting early is not early — SDA falling while SCL is high is a START, so a premature acknowledge restarts the transaction instead of acknowledging a byte.
The target knows it is addressed and in which direction (18.4). It now has to answer — and this is the block where most slave designs first fail.
1. Three Obligations, Not One
Read as obligations on the receiver, that is three things:
1. PULL SDA LOW before the ninth SCL rise
2. HOLD IT LOW for the whole of the ninth HIGH period
3. RELEASE IT before the master can change SDA for the next bitA design that gets the first right and misses either of the others is broken in a way that presents as data corruption — which is why the investigation goes to the datapath and the defect is here.
2. The Window Is Bounded at Both Ends by Falling Edges
§3.1.2 permits SDA to change only while SCL is LOW. There are exactly two low phases adjacent to the ninth high phase, and each is the only legal place for one of the two transitions:
assert in the low phase BEFORE the ninth rise (after the eighth fall)
release in the low phase AFTER the ninth fallThat is the single most destructive timing error a slave can make, and it is why the assert is gated on scl_fall rather than on the byte-complete event that precedes it. Mutation M1 removes the gate and fails seventeen checks.
3. One Slot, Not a Level
The output is a drive-low intent true for exactly one SCL period. A design that held the acknowledge as a level until the next byte would be driving through the next byte's data bits — mutation M2, seven failing checks.
4. What This Block Does Not Decide
And a NACK is the absence of drive, not a driven one. There is no drive-high, so declining is what the pull-up does when nobody objects. Mutation M4 drives a NACK and fails three checks.
5. The Interface Assumption, Stated
byte_done always arrives while SCL is HIGH. That is guaranteed rather than hoped for: a byte completes when its eighth bit is sampled, and sampling happens at scl_rise (18.2, 18.4).
So the next scl_fall is always the low phase immediately before the ninth rise — which is the one instant the assert is allowed to happen.
6. The Window, Measured
Armed while SCL is high, asserted in the low phase, held through the high phase
10 cyclesNote interval 2. The byte is complete, the slot is armed, and SDA has not moved. That gap — between knowing an acknowledge is owed and being allowed to drive it — is the whole content of §2.
7. The Clock Ratio Is a Correctness Constraint, and It Is Measurable
Chapter 18.1 §8 claimed the system-to-SCL clock ratio is a design constraint rather than a free choice. This is where it bites, and the constraint is visible in simulation.
Capturing the same block with SCL phases only two system clocks long produced this:
scl_bus 1 1 1 0 0 1 1 1 0 0
ack_armed 0 0 1 1 1 1 0 0 0 0
sda_drive_low 0 0 0 0 0 0 1 1 1 1
^ SCL has ALREADY risen againThe synchroniser reports the falling edge two cycles late (18.2 §7), so by the time the assert happens the low phase is over. The acknowledge lands in the high phase — which is both too late to be sampled and, being an SDA fall while SCL is high, a START.
8. The Acknowledge Generator, in Three Languages
// -----------------------------------------------------------------------------
// i2c_slave_ack.sv
// The acknowledge bit: the right slot, and -- harder -- released in time.
//
// §3.1.4, verbatim: "the transmitter releases the SDA line during the acknowledge clock
// pulse so the receiver can pull the SDA line LOW and it remains stable LOW during the
// HIGH period of this clock pulse."
//
// So the obligation has three parts, and a slave that gets the first and misses either
// of the others is broken in a way that looks like data corruption:
//
// 1. PULL SDA LOW -- before the ninth SCL rise
// 2. HOLD IT LOW -- for the whole of the ninth HIGH period
// 3. RELEASE IT -- before the master can change SDA for the next bit
//
// WHY THE WINDOW IS BOUNDED AT BOTH ENDS BY SCL FALLING EDGES. §3.1.2 permits SDA to
// change only while SCL is LOW. There are exactly two low phases adjacent to the ninth
// high phase, and each one is the only legal place for one of the two transitions:
//
// assert in the low phase BEFORE the ninth rise (after the eighth fall)
// release in the low phase AFTER the ninth fall
//
// ASSERTING EARLY IS NOT "EARLY", IT IS ILLEGAL. If the slave pulls SDA low while SCL is
// still HIGH -- during the eighth bit's high phase, say -- then SDA has fallen while SCL
// is high, and that is a START condition (§3.1.1). Every device on the bus, including
// the master, is entitled to read it as one. The slave has not acknowledged a byte; it
// has restarted the transaction. This is the single most destructive timing error a
// slave can make, and it is why the assert is gated on `scl_fall` rather than on the
// byte-complete event that precedes it.
//
// RELEASING LATE CORRUPTS THE NEXT BIT. The master will drive the next data bit in the
// low phase that follows the ninth fall. If the slave is still pulling SDA down, the
// wired-AND means the master's one becomes a zero -- and on a read the master will see
// its own bit come back wrong, which on a multi-master bus it is entitled to interpret
// as losing arbitration (§3.1.8). A slave that releases late can therefore knock a
// master off a bus it was winning.
//
// ONE SLOT, NOT A LEVEL. The output is a drive-low intent that is true for exactly one
// SCL period. A design that held ACK as a level until the next byte would be driving
// through the next byte's data bits.
//
// WHAT THIS BLOCK DOES NOT DECIDE. Whether to acknowledge at all. That is policy --
// address match (18.4), a register that refuses a write (18.9), a receiver with nowhere
// to put the byte (18.6) -- and it arrives here as `ack_en`. Separating the decision
// from the timing is what lets each be tested on its own.
// -----------------------------------------------------------------------------
module i2c_slave_ack #(
parameter int CNT_W = 16
) (
input logic clk,
input logic rst_n,
// From Chapter 18.2. The two falling edges that bound the window.
input logic scl_fall,
// One cycle, from whichever block just completed a byte: the address block (18.4) or
// the receive datapath (18.6). It means "the eighth bit has been sampled".
//
// INTERFACE ASSUMPTION, and it is guaranteed rather than hoped for: this pulse always
// arrives while SCL is HIGH, because a byte completes when its eighth bit is SAMPLED
// and sampling happens at `scl_rise` (§3.1.2). So the next `scl_fall` is always the
// low phase immediately before the ninth rise -- which is the one instant the assert
// is allowed to happen. A producer that raised `byte_done` in a low phase would arm a
// slot whose assert landed a full phase late, and the acknowledge would miss its
// pulse entirely.
input logic byte_done,
// The DECISION, sampled when the slot is armed. 1 = acknowledge (pull SDA low),
// 0 = do not (leave it released, which the master reads as a NACK).
input logic ack_en,
// Framing aborts the slot outright. A START or STOP mid-acknowledge means the
// transaction this acknowledge belonged to no longer exists.
input logic start_pulse,
input logic stop_pulse,
// DRIVE INTENT, never a level to be driven high. 1 = pull SDA low, 0 = release.
output logic sda_drive_low,
// Exposed so Chapter 18.11 can see the slot and so a bench can check the boundaries.
output logic ack_active, // the slot is in progress
output logic ack_armed, // the byte is done, waiting for the eighth SCL fall
output logic [CNT_W-1:0] n_acks,
output logic [CNT_W-1:0] n_nacks
);
localparam [1:0] A_IDLE = 2'd0, // nothing owed
A_ARMED = 2'd1, // byte complete, waiting for SCL to fall
A_DRIVE = 2'd2; // driving through the ninth pulse
logic [1:0] state;
logic will_ack;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// Reset RELEASES. A slave that came out of reset pulling SDA low would hold the
// whole bus down and nothing else could lift it.
sda_drive_low <= 1'b0;
ack_active <= 1'b0;
ack_armed <= 1'b0;
will_ack <= 1'b0;
state <= A_IDLE;
n_acks <= {CNT_W{1'b0}};
n_nacks <= {CNT_W{1'b0}};
end else if (start_pulse || stop_pulse) begin
// Framing wins over everything. Release immediately: the framing edge has
// already happened, so this release cannot itself produce one.
sda_drive_low <= 1'b0;
ack_active <= 1'b0;
ack_armed <= 1'b0;
state <= A_IDLE;
end else begin
case (state)
A_IDLE: begin
if (byte_done) begin
// Arm, and latch the decision NOW. The inputs that produced it -- an
// address comparison, a register's writability -- are valid at byte
// completion and need not still be valid two SCL phases later.
will_ack <= ack_en;
ack_armed <= 1'b1;
state <= A_ARMED;
end
end
A_ARMED: begin
// Wait for SCL to FALL. This is the whole of the "not early" rule: the
// assert may only happen in a low phase, because SDA falling while SCL
// is high is a START and not an acknowledge.
if (scl_fall) begin
ack_armed <= 1'b0;
ack_active <= 1'b1;
// A NACK is the ABSENCE of drive, not a driven one. There is no
// drive-high anywhere in a conforming I²C interface.
sda_drive_low <= will_ack;
if (will_ack) n_acks <= n_acks + 1'b1;
else n_nacks <= n_nacks + 1'b1;
state <= A_DRIVE;
end
end
A_DRIVE: begin
// The ninth pulse happens here: SCL rises, the master samples, SCL falls.
// Release on that fall -- the next low phase belongs to the master, which
// will drive the next bit into it.
if (scl_fall) begin
sda_drive_low <= 1'b0;
ack_active <= 1'b0;
state <= A_IDLE;
end
end
default: state <= A_IDLE;
endcase
end
end
endmodule // -----------------------------------------------------------------------------
// i2c_slave_ack.v
// The acknowledge bit: the right slot, and -- harder -- released in time.
//
// §3.1.4, verbatim: "the transmitter releases the SDA line during the acknowledge clock
// pulse so the receiver can pull the SDA line LOW and it remains stable LOW during the
// HIGH period of this clock pulse."
//
// So the obligation has three parts, and a slave that gets the first and misses either
// of the others is broken in a way that looks like data corruption:
//
// 1. PULL SDA LOW -- before the ninth SCL rise
// 2. HOLD IT LOW -- for the whole of the ninth HIGH period
// 3. RELEASE IT -- before the master can change SDA for the next bit
//
// WHY THE WINDOW IS BOUNDED AT BOTH ENDS BY SCL FALLING EDGES. §3.1.2 permits SDA to
// change only while SCL is LOW. There are exactly two low phases adjacent to the ninth
// high phase, and each one is the only legal place for one of the two transitions:
//
// assert in the low phase BEFORE the ninth rise (after the eighth fall)
// release in the low phase AFTER the ninth fall
//
// ASSERTING EARLY IS NOT "EARLY", IT IS ILLEGAL. If the slave pulls SDA low while SCL is
// still HIGH -- during the eighth bit's high phase, say -- then SDA has fallen while SCL
// is high, and that is a START condition (§3.1.1). Every device on the bus, including
// the master, is entitled to read it as one. The slave has not acknowledged a byte; it
// has restarted the transaction. This is the single most destructive timing error a
// slave can make, and it is why the assert is gated on `scl_fall` rather than on the
// byte-complete event that precedes it.
//
// RELEASING LATE CORRUPTS THE NEXT BIT. The master will drive the next data bit in the
// low phase that follows the ninth fall. If the slave is still pulling SDA down, the
// wired-AND means the master's one becomes a zero -- and on a read the master will see
// its own bit come back wrong, which on a multi-master bus it is entitled to interpret
// as losing arbitration (§3.1.8). A slave that releases late can therefore knock a
// master off a bus it was winning.
//
// ONE SLOT, NOT A LEVEL. The output is a drive-low intent that is true for exactly one
// SCL period. A design that held ACK as a level until the next byte would be driving
// through the next byte's data bits.
//
// WHAT THIS BLOCK DOES NOT DECIDE. Whether to acknowledge at all. That is policy --
// address match (18.4), a register that refuses a write (18.9), a receiver with nowhere
// to put the byte (18.6) -- and it arrives here as `ack_en`. Separating the decision
// from the timing is what lets each be tested on its own.
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
// -----------------------------------------------------------------------------
module i2c_slave_ack #(
parameter integer CNT_W = 16
) (
input wire clk,
input wire rst_n,
// From Chapter 18.2. The two falling edges that bound the window.
input wire scl_fall,
// One cycle, from whichever block just completed a byte: the address block (18.4) or
// the receive datapath (18.6). It means "the eighth bit has been sampled".
//
// INTERFACE ASSUMPTION, and it is guaranteed rather than hoped for: this pulse always
// arrives while SCL is HIGH, because a byte completes when its eighth bit is SAMPLED
// and sampling happens at `scl_rise` (§3.1.2). So the next `scl_fall` is always the
// low phase immediately before the ninth rise -- which is the one instant the assert
// is allowed to happen. A producer that raised `byte_done` in a low phase would arm a
// slot whose assert landed a full phase late, and the acknowledge would miss its
// pulse entirely.
input wire byte_done,
// The DECISION, sampled when the slot is armed. 1 = acknowledge (pull SDA low),
// 0 = do not (leave it released, which the master reads as a NACK).
input wire ack_en,
// Framing aborts the slot outright. A START or STOP mid-acknowledge means the
// transaction this acknowledge belonged to no longer exists.
input wire start_pulse,
input wire stop_pulse,
// DRIVE INTENT, never a level to be driven high. 1 = pull SDA low, 0 = release.
output reg sda_drive_low,
// Exposed so Chapter 18.11 can see the slot and so a bench can check the boundaries.
output reg ack_active, // the slot is in progress
output reg ack_armed, // the byte is done, waiting for the eighth SCL fall
output reg [CNT_W-1:0] n_acks,
output reg [CNT_W-1:0] n_nacks
);
localparam [1:0] A_IDLE = 2'd0, // nothing owed
A_ARMED = 2'd1, // byte complete, waiting for SCL to fall
A_DRIVE = 2'd2; // driving through the ninth pulse
reg [1:0] state;
reg will_ack;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// Reset RELEASES. A slave that came out of reset pulling SDA low would hold the
// whole bus down and nothing else could lift it.
sda_drive_low <= 1'b0;
ack_active <= 1'b0;
ack_armed <= 1'b0;
will_ack <= 1'b0;
state <= A_IDLE;
n_acks <= {CNT_W{1'b0}};
n_nacks <= {CNT_W{1'b0}};
end else if (start_pulse || stop_pulse) begin
// Framing wins over everything. Release immediately: the framing edge has
// already happened, so this release cannot itself produce one.
sda_drive_low <= 1'b0;
ack_active <= 1'b0;
ack_armed <= 1'b0;
state <= A_IDLE;
end else begin
case (state)
A_IDLE: begin
if (byte_done) begin
// Arm, and latch the decision NOW. The inputs that produced it -- an
// address comparison, a register's writability -- are valid at byte
// completion and need not still be valid two SCL phases later.
will_ack <= ack_en;
ack_armed <= 1'b1;
state <= A_ARMED;
end
end
A_ARMED: begin
// Wait for SCL to FALL. This is the whole of the "not early" rule: the
// assert may only happen in a low phase, because SDA falling while SCL
// is high is a START and not an acknowledge.
if (scl_fall) begin
ack_armed <= 1'b0;
ack_active <= 1'b1;
// A NACK is the ABSENCE of drive, not a driven one. There is no
// drive-high anywhere in a conforming I²C interface.
sda_drive_low <= will_ack;
if (will_ack) n_acks <= n_acks + 1'b1;
else n_nacks <= n_nacks + 1'b1;
state <= A_DRIVE;
end
end
A_DRIVE: begin
// The ninth pulse happens here: SCL rises, the master samples, SCL falls.
// Release on that fall -- the next low phase belongs to the master, which
// will drive the next bit into it.
if (scl_fall) begin
sda_drive_low <= 1'b0;
ack_active <= 1'b0;
state <= A_IDLE;
end
end
default: state <= A_IDLE;
endcase
end
end
endmodule -- -----------------------------------------------------------------------------
-- i2c_slave_ack.vhd
-- The acknowledge bit: the right slot, and released in time. Same ports, generic, reset
-- values and window boundaries as the SystemVerilog and Verilog versions.
-- -----------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_slave_ack is
generic (
CNT_W : positive := 16
);
port (
clk : in std_logic;
rst_n : in std_logic;
-- From Chapter 18.2. The two falling edges that bound the window.
scl_fall : in std_logic;
-- One cycle, from whichever block just completed a byte.
--
-- INTERFACE ASSUMPTION, guaranteed rather than hoped for: this pulse always arrives
-- while SCL is HIGH, because a byte completes when its eighth bit is SAMPLED and
-- sampling happens at scl_rise (§3.1.2). So the next scl_fall is always the low
-- phase immediately before the ninth rise.
byte_done : in std_logic;
-- The DECISION, latched when the slot is armed.
ack_en : in std_logic;
-- Framing aborts the slot outright.
start_pulse : in std_logic;
stop_pulse : in std_logic;
-- DRIVE INTENT, never a level to be driven high.
sda_drive_low : out std_logic;
ack_active : out std_logic;
ack_armed : out std_logic;
n_acks : out unsigned(CNT_W-1 downto 0);
n_nacks : out unsigned(CNT_W-1 downto 0)
);
end entity i2c_slave_ack;
architecture rtl of i2c_slave_ack is
type state_t is (A_IDLE, A_ARMED, A_DRIVE);
signal state : state_t := A_IDLE;
signal r_drive, r_active, r_armed, will_ack : std_logic := '0';
signal c_ack, c_nack : unsigned(CNT_W-1 downto 0) := (others => '0');
begin
process (clk, rst_n)
begin
if rst_n = '0' then
-- Reset RELEASES. A slave out of reset pulling SDA low holds the whole bus down.
r_drive <= '0';
r_active <= '0';
r_armed <= '0';
will_ack <= '0';
state <= A_IDLE;
c_ack <= (others => '0');
c_nack <= (others => '0');
elsif rising_edge(clk) then
if start_pulse = '1' or stop_pulse = '1' then
-- Framing wins over everything. Releasing here cannot itself produce a
-- framing edge: the edge has already happened.
r_drive <= '0';
r_active <= '0';
r_armed <= '0';
state <= A_IDLE;
else
case state is
when A_IDLE =>
if byte_done = '1' then
-- Arm, and latch the decision NOW: the inputs that produced it need
-- not still be valid two SCL phases later.
will_ack <= ack_en;
r_armed <= '1';
state <= A_ARMED;
end if;
when A_ARMED =>
-- Wait for SCL to FALL. The whole of the "not early" rule: SDA falling
-- while SCL is high is a START, not an acknowledge.
if scl_fall = '1' then
r_armed <= '0';
r_active <= '1';
-- A NACK is the ABSENCE of drive, not a driven one.
r_drive <= will_ack;
if will_ack = '1' then
c_ack <= c_ack + 1;
else
c_nack <= c_nack + 1;
end if;
state <= A_DRIVE;
end if;
when A_DRIVE =>
-- Release on the ninth fall: the next low phase belongs to the master.
if scl_fall = '1' then
r_drive <= '0';
r_active <= '0';
state <= A_IDLE;
end if;
end case;
end if;
end if;
end process;
sda_drive_low <= r_drive;
ack_active <= r_active;
ack_armed <= r_armed;
n_acks <= c_ack;
n_nacks <= c_nack;
end architecture rtl;8a. The testbenches
Fifteen checks. The bench resolves the line as the bus does — wired-AND of the controller's and the slave's drive-lows — because the whole subject is when SDA is low, and a bench ignoring the slave's contribution could not see the acknowledge at all.
The critical observer counts SDA changing while SCL is HIGH. Every defect in this block moves SDA in the wrong phase, so they all surface there.
| # | Test | Property |
|---|---|---|
| T1 | reset releases SDA | a target holding it low kills the bus |
| T2 | nothing happens without a byte | the false-positive test |
| T3 | a byte completing while SCL is HIGH does not assert yet | §2's "not early" rule |
| T4 | ... and it asserts on the falling edge | the first legal instant |
| T5 | it holds through the whole ninth high phase | §3.1.4's "remains stable LOW" |
| T6 | and releases on the ninth fall | before the master drives the next bit |
| T7 | the whole slot, with the master sampling | LOW in the ninth pulse and nowhere else |
| T8 | a NACK is the absence of drive | |
| T9 | the decision is latched at arming, not re-read | |
| T10 | one slot, not a level | silent through following bits |
| T11 | two bytes, two acknowledges | |
| T12 | reset mid-acknowledge releases | |
| T13 | a STOP mid-acknowledge abandons the slot | added after M8; see §9 |
| T14 | and a repeated START does the same | the other half |
| T15 | an armed slot is abandoned too | not merely an active one |
// -----------------------------------------------------------------------------
// i2c_slave_ack_tb.sv
// Independent oracle for i2c_slave_ack.
//
// The bench drives the bus as a controller AND resolves the line as the bus does, so
// the slave's drive-low actually pulls the wire down. That is essential here: the whole
// subject is when SDA is low, and a bench that ignored the slave's contribution could
// not see the acknowledge at all.
//
// The critical observer is the one that watches for SDA changing while SCL is HIGH.
// Every defect in this block shows up there, because every one of them moves SDA in the
// wrong phase. Every wait is bounded.
// -----------------------------------------------------------------------------
`timescale 1ns/1ps
module i2c_slave_ack_tb;
localparam integer HALF = 8;
logic clk = 1'b0, rst_n = 1'b0;
logic m_scl_low = 1'b0, m_sda_low = 1'b0; // the controller's drive-low intents
logic byte_done = 1'b0, ack_en = 1'b1;
// Framing inputs, DRIVEN rather than tied off. Tying them to zero leaves the
// abort path unexercised -- and a slot that survives framing keeps driving SDA
// into a transfer that no longer exists.
logic f_start = 1'b0, f_stop = 1'b0;
logic s_sda_low; // the slave's drive-low intent
// The bus, resolved exactly as the wired-AND does it: low if anybody pulls low.
wire scl = ~m_scl_low;
wire sda = ~(m_sda_low | s_sda_low);
logic scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall;
logic ack_active, ack_armed;
logic [15:0] n_ack, n_nack;
integer errors = 0;
integer n, k;
// ---- the observer that matters ---------------------------------------------
//
// SDA must never change while SCL is HIGH -- §3.1.2 -- except for the framing the
// bench itself generates. The bench counts violations caused by the SLAVE by watching
// the slave's own intent change while the line is high.
integer sda_moved_high = 0;
integer ack_low_cycles = 0;
logic s_d = 1'b0;
always @(posedge clk) begin
if (rst_n) begin
if ((s_sda_low !== s_d) && scl) sda_moved_high <= sda_moved_high + 1;
if (s_sda_low) ack_low_cycles <= ack_low_cycles + 1;
end
s_d <= s_sda_low;
end
// Was SDA low, on the wire, during the high phase of the pulse just finished?
integer sampled_low = 0, sampled_high = 0;
always @(posedge clk) if (rst_n && scl_rise) begin
if (!sda) sampled_low <= sampled_low + 1;
else sampled_high <= sampled_high + 1;
end
i2c_slave_sync #(.SYNC_DEPTH(2)) u_sync (
.clk(clk), .rst_n(rst_n), .scl_pin(scl), .sda_pin(sda),
.scl_q(scl_q), .sda_q(sda_q), .scl_rise(scl_rise), .scl_fall(scl_fall),
.sda_rise(sda_rise), .sda_fall(sda_fall));
i2c_slave_ack #(.CNT_W(16)) dut (
.clk(clk), .rst_n(rst_n), .scl_fall(scl_fall),
.byte_done(byte_done), .ack_en(ack_en),
.start_pulse(f_start), .stop_pulse(f_stop),
.sda_drive_low(s_sda_low),
.ack_active(ack_active), .ack_armed(ack_armed),
.n_acks(n_ack), .n_nacks(n_nack));
always #5 clk = ~clk;
task step; begin @(posedge clk); @(negedge clk); end endtask
task phase; begin for (n = 0; n < HALF; n = n + 1) step; end endtask
task do_reset;
begin
@(negedge clk); rst_n = 1'b0;
m_scl_low = 1'b0; m_sda_low = 1'b0; byte_done = 1'b0; ack_en = 1'b1;
f_start = 1'b0; f_stop = 1'b0;
sda_moved_high = 0; ack_low_cycles = 0; sampled_low = 0; sampled_high = 0;
step; step;
@(negedge clk); rst_n = 1'b1; phase;
end
endtask
// One SCL pulse, starting and ending in the LOW phase. The master's data bit, if it
// drives one, is placed in the low phase before the rise.
task gen_pulse (input drive_low_during);
begin
@(negedge clk); m_scl_low = 1'b1; phase; // low phase
@(negedge clk); m_sda_low = drive_low_during; phase;
@(negedge clk); m_scl_low = 1'b0; phase; // high phase
@(negedge clk); m_scl_low = 1'b1; phase; // and back low
end
endtask
task pulse_byte_done;
begin @(negedge clk); byte_done = 1'b1; step; @(negedge clk); byte_done = 1'b0; end
endtask
task ck_bit (input [200*8:1] what, input g, input e);
begin
if (g !== e) begin
$display(" FAIL %0s: got %0b expected %0b", what, g, e);
errors = errors + 1;
end
end
endtask
task ck_int (input [200*8:1] what, input integer g, input integer e);
begin
if (g !== e) begin
$display(" FAIL %0s: got %0d expected %0d", what, g, e);
errors = errors + 1;
end
end
endtask
initial begin
$display("=== i2c_slave_ack: the right slot, and released in time ===");
// ----------------------------------------------------------------
// T1. RESET RELEASES SDA. A slave holding SDA low out of reset takes the whole
// bus down and nothing else can lift it.
// ----------------------------------------------------------------
do_reset;
$display("T1 reset releases SDA");
ck_bit("T1 not driving", s_sda_low, 1'b0);
ck_bit("T1 the line is high", sda, 1'b1);
ck_bit("T1 no slot in progress", ack_active, 1'b0);
// ----------------------------------------------------------------
// T2. NOTHING HAPPENS WITHOUT A BYTE. Clock all day; the slave owes no
// acknowledge and must drive nothing. The false-positive test.
// ----------------------------------------------------------------
do_reset;
for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0);
$display("T2 with no byte complete, the slave drives nothing");
ck_int("T2 never drove", ack_low_cycles, 0);
ck_int("T2 and never moved SDA in a high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T3. THE ASSERT WAITS FOR SCL TO FALL. `byte_done` arrives while SCL is still
// HIGH -- which is exactly when the eighth bit is being sampled -- and the
// slave must NOT pull SDA down yet. SDA falling while SCL is high is a START
// (§3.1.1), so an early acknowledge is not early: it restarts the transaction.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); m_scl_low = 1'b0; phase; // SCL HIGH
pulse_byte_done;
for (k = 0; k < 3; k = k + 1) step;
$display("T3 a byte completing while SCL is HIGH does not assert the ACK yet");
ck_bit("T3 the slot is armed", ack_armed, 1'b1);
ck_bit("T3 but SDA is NOT being driven", s_sda_low, 1'b0);
ck_int("T3 and SDA has not moved in a high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T4. ... AND ASSERTS ON THE FALL. The first legal instant.
// ----------------------------------------------------------------
@(negedge clk); m_scl_low = 1'b1; // SCL falls
for (k = 0; k < 6; k = k + 1) step;
$display("T4 and it asserts on the falling edge, the first legal instant");
ck_bit("T4 now driving", s_sda_low, 1'b1);
ck_bit("T4 the slot is active", ack_active, 1'b1);
ck_bit("T4 and the line is pulled low", sda, 1'b0);
// ----------------------------------------------------------------
// T5. IT HOLDS THROUGH THE WHOLE NINTH HIGH PHASE, which is what §3.1.4 requires:
// "it remains stable LOW during the HIGH period of this clock pulse".
// ----------------------------------------------------------------
@(negedge clk); m_scl_low = 1'b0; phase; // the ninth HIGH phase
ck_bit("T5 still driving through the high phase", s_sda_low, 1'b1);
ck_bit("T5 the line is still low", sda, 1'b0);
ck_int("T5 and SDA never moved during it", sda_moved_high, 0);
// ----------------------------------------------------------------
// T6. AND RELEASES ON THE NINTH FALL, before the master can drive the next bit.
// A slave still pulling SDA down in the following low phase turns the
// master's one into a zero -- which the master is entitled to read as losing
// arbitration (§3.1.8), so a late release can knock a master off the bus.
// ----------------------------------------------------------------
@(negedge clk); m_scl_low = 1'b1; phase; // the ninth fall
$display("T6 and releases on the ninth fall, before the next bit is driven");
ck_bit("T6 released", s_sda_low, 1'b0);
ck_bit("T6 the slot is over", ack_active, 1'b0);
ck_bit("T6 and the line is free", sda, 1'b1);
ck_int("T6 one acknowledge counted", n_ack, 1);
// ----------------------------------------------------------------
// T7. THE WHOLE SLOT, END TO END, with the master sampling it. The bus must read
// LOW at the ninth rising edge and at no other.
// ----------------------------------------------------------------
do_reset;
gen_pulse(1'b0); // a data bit, master drives a one
pulse_byte_done; // ... and the byte completes
gen_pulse(1'b0); // the ninth pulse: the acknowledge
gen_pulse(1'b0); // and the next bit slot
$display("T7 the master samples LOW in the ninth pulse and HIGH in the others");
ck_int("T7 exactly one low sample", sampled_low, 1);
ck_int("T7 and the rest read high", sampled_high, 2);
ck_int("T7 no SDA movement in any high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T8. A NACK IS THE ABSENCE OF DRIVE. With `ack_en` low the slave must leave the
// line released -- there is no drive-high, so a NACK is what the pull-up does
// when nobody objects.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); ack_en = 1'b0;
pulse_byte_done;
gen_pulse(1'b0);
$display("T8 a NACK is the absence of drive, not a driven one");
ck_int("T8 never pulled the line down", ack_low_cycles, 0);
ck_int("T8 the master sampled high", sampled_low, 0);
ck_int("T8 and it was counted as a NACK", n_nack, 1);
ck_int("T8 with no acknowledge counted", n_ack, 0);
// ----------------------------------------------------------------
// T9. THE DECISION IS LATCHED WHEN THE SLOT IS ARMED. The inputs that produced it
// -- an address comparison, a register's writability -- are valid at byte
// completion and need not still be valid two SCL phases later.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); ack_en = 1'b1;
pulse_byte_done;
@(negedge clk); ack_en = 1'b0; // withdrawn AFTER arming
gen_pulse(1'b0);
$display("T9 the decision is latched at arming, not re-read in the slot");
ck_int("T9 the acknowledge still happened", n_ack, 1);
ck_int("T9 and the master saw it", sampled_low, 1);
// ----------------------------------------------------------------
// T10. ONE SLOT, NOT A LEVEL. After the acknowledge the slave must be silent
// through the following data bits -- a design that held ACK as a level until
// the next byte would drive through them.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); ack_en = 1'b1;
pulse_byte_done;
gen_pulse(1'b0); // the acknowledge
ack_low_cycles = 0;
for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0); // four more bit slots
$display("T10 the acknowledge is one slot, not a level held into the next byte");
ck_int("T10 silent for every following bit", ack_low_cycles, 0);
// ----------------------------------------------------------------
// T11. TWO BYTES IN A ROW each get exactly one acknowledge.
// ----------------------------------------------------------------
do_reset;
for (k = 0; k < 2; k = k + 1) begin
// `byte_done` must be raised while SCL is HIGH, because that is the only way it
// can occur in reality: a byte completes when its eighth bit is sampled, and
// sampling happens at the rising edge. Raising it in a low phase would arm a
// slot whose assert lands a phase late -- a bench artefact, not a design defect.
@(negedge clk); m_scl_low = 1'b0; phase; // SCL high: the eighth bit
pulse_byte_done;
gen_pulse(1'b0); // the ninth pulse
end
$display("T11 two bytes, two acknowledges, no more");
ck_int("T11 two acknowledges", n_ack, 2);
ck_int("T11 the master saw two low samples", sampled_low, 2);
ck_int("T11 and SDA never moved in a high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T12. RESET MID-SLOT RELEASES IMMEDIATELY. A slave reset while acknowledging
// must let go, even though letting go while SCL is high would normally be
// forbidden -- reset has no other option, and holding is worse.
// ----------------------------------------------------------------
do_reset;
pulse_byte_done;
@(negedge clk); m_scl_low = 1'b1; phase; // assert the acknowledge
ck_bit("T12 driving before reset", s_sda_low, 1'b1);
@(negedge clk); rst_n = 1'b0; step; step;
$display("T12 reset mid-acknowledge releases the line");
ck_bit("T12 released by reset", s_sda_low, 1'b0);
ck_bit("T12 and the bus is free", sda, 1'b1);
// ----------------------------------------------------------------
// T13. FRAMING ABORTS THE SLOT. A STOP arriving while the acknowledge is being
// driven means the transaction it belonged to no longer exists -- so the slot
// must be abandoned and the line released at once. A slot that survived
// framing would keep pulling SDA down through whatever the master does next,
// which on a bus the master is trying to release is a stuck line.
// ----------------------------------------------------------------
do_reset;
pulse_byte_done;
@(negedge clk); m_scl_low = 1'b1; phase; // assert the acknowledge
ck_bit("T13 driving before the framing event", s_sda_low, 1'b1);
@(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
step;
$display("T13 a STOP mid-acknowledge abandons the slot and releases the line");
ck_bit("T13 released by the STOP", s_sda_low, 1'b0);
ck_bit("T13 the slot is over", ack_active, 1'b0);
ck_bit("T13 and the bus is free", sda, 1'b1);
// ----------------------------------------------------------------
// T14. AND A START DOES THE SAME. The other half: a repeated START mid-acknowledge
// is the more likely of the two, because a master that abandons a transfer
// usually restarts rather than stopping.
// ----------------------------------------------------------------
do_reset;
pulse_byte_done;
@(negedge clk); m_scl_low = 1'b1; phase;
ck_bit("T14 driving before the framing event", s_sda_low, 1'b1);
@(negedge clk); f_start = 1'b1; step; @(negedge clk); f_start = 1'b0;
step;
$display("T14 a repeated START mid-acknowledge does the same");
ck_bit("T14 released by the START", s_sda_low, 1'b0);
ck_bit("T14 the slot is over", ack_active, 1'b0);
// ----------------------------------------------------------------
// T15. AN ARMED SLOT IS ALSO ABANDONED. Framing between byte completion and the
// falling edge must cancel the pending acknowledge, not merely the active one
// -- otherwise the slot fires one phase later into a transfer that has moved on.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); m_scl_low = 1'b0; phase; // SCL HIGH
pulse_byte_done;
ck_bit("T15 the slot is armed", ack_armed, 1'b1);
@(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
ck_bit("T15 arming was cancelled", ack_armed, 1'b0);
ack_low_cycles = 0;
gen_pulse(1'b0);
$display("T15 an armed slot is abandoned too, not merely an active one");
ck_int("T15 and nothing was ever driven", ack_low_cycles, 0);
if (errors == 0) $display("=== i2c_slave_ack: ALL CHECKS PASSED ===");
else $display("=== i2c_slave_ack: %0d CHECK(S) FAILED ===", errors);
$finish;
end
endmodule // -----------------------------------------------------------------------------
// i2c_slave_ack_tb.v
// Independent oracle for i2c_slave_ack.
//
// The bench drives the bus as a controller AND resolves the line as the bus does, so
// the slave's drive-low actually pulls the wire down. That is essential here: the whole
// subject is when SDA is low, and a bench that ignored the slave's contribution could
// not see the acknowledge at all.
//
// The critical observer is the one that watches for SDA changing while SCL is HIGH.
// Every defect in this block shows up there, because every one of them moves SDA in the
// wrong phase. Every wait is bounded.
// -----------------------------------------------------------------------------
`timescale 1ns/1ps
module i2c_slave_ack_tb;
localparam integer HALF = 8;
reg clk = 1'b0, rst_n = 1'b0;
reg m_scl_low = 1'b0, m_sda_low = 1'b0; // the controller's drive-low intents
reg byte_done = 1'b0, ack_en = 1'b1;
// Framing inputs, DRIVEN rather than tied off. Tying them to zero leaves the
// abort path unexercised -- and a slot that survives framing keeps driving SDA
// into a transfer that no longer exists.
reg f_start = 1'b0, f_stop = 1'b0;
wire s_sda_low; // the slave's drive-low intent
// The bus, resolved exactly as the wired-AND does it: low if anybody pulls low.
wire scl = ~m_scl_low;
wire sda = ~(m_sda_low | s_sda_low);
wire scl_q, sda_q, scl_rise, scl_fall, sda_rise, sda_fall;
wire ack_active, ack_armed;
wire [15:0] n_ack, n_nack;
integer errors = 0;
integer n, k;
// ---- the observer that matters ---------------------------------------------
//
// SDA must never change while SCL is HIGH -- §3.1.2 -- except for the framing the
// bench itself generates. The bench counts violations caused by the SLAVE by watching
// the slave's own intent change while the line is high.
integer sda_moved_high = 0;
integer ack_low_cycles = 0;
reg s_d = 1'b0;
always @(posedge clk) begin
if (rst_n) begin
if ((s_sda_low !== s_d) && scl) sda_moved_high <= sda_moved_high + 1;
if (s_sda_low) ack_low_cycles <= ack_low_cycles + 1;
end
s_d <= s_sda_low;
end
// Was SDA low, on the wire, during the high phase of the pulse just finished?
integer sampled_low = 0, sampled_high = 0;
always @(posedge clk) if (rst_n && scl_rise) begin
if (!sda) sampled_low <= sampled_low + 1;
else sampled_high <= sampled_high + 1;
end
i2c_slave_sync #(.SYNC_DEPTH(2)) u_sync (
.clk(clk), .rst_n(rst_n), .scl_pin(scl), .sda_pin(sda),
.scl_q(scl_q), .sda_q(sda_q), .scl_rise(scl_rise), .scl_fall(scl_fall),
.sda_rise(sda_rise), .sda_fall(sda_fall));
i2c_slave_ack #(.CNT_W(16)) dut (
.clk(clk), .rst_n(rst_n), .scl_fall(scl_fall),
.byte_done(byte_done), .ack_en(ack_en),
.start_pulse(f_start), .stop_pulse(f_stop),
.sda_drive_low(s_sda_low),
.ack_active(ack_active), .ack_armed(ack_armed),
.n_acks(n_ack), .n_nacks(n_nack));
always #5 clk = ~clk;
task step; begin @(posedge clk); @(negedge clk); end endtask
task phase; begin for (n = 0; n < HALF; n = n + 1) step; end endtask
task do_reset;
begin
@(negedge clk); rst_n = 1'b0;
m_scl_low = 1'b0; m_sda_low = 1'b0; byte_done = 1'b0; ack_en = 1'b1;
f_start = 1'b0; f_stop = 1'b0;
sda_moved_high = 0; ack_low_cycles = 0; sampled_low = 0; sampled_high = 0;
step; step;
@(negedge clk); rst_n = 1'b1; phase;
end
endtask
// One SCL pulse, starting and ending in the LOW phase. The master's data bit, if it
// drives one, is placed in the low phase before the rise.
task gen_pulse (input drive_low_during);
begin
@(negedge clk); m_scl_low = 1'b1; phase; // low phase
@(negedge clk); m_sda_low = drive_low_during; phase;
@(negedge clk); m_scl_low = 1'b0; phase; // high phase
@(negedge clk); m_scl_low = 1'b1; phase; // and back low
end
endtask
task pulse_byte_done;
begin @(negedge clk); byte_done = 1'b1; step; @(negedge clk); byte_done = 1'b0; end
endtask
task ck_bit (input [200*8:1] what, input g, input e);
begin
if (g !== e) begin
$display(" FAIL %0s: got %0b expected %0b", what, g, e);
errors = errors + 1;
end
end
endtask
task ck_int (input [200*8:1] what, input integer g, input integer e);
begin
if (g !== e) begin
$display(" FAIL %0s: got %0d expected %0d", what, g, e);
errors = errors + 1;
end
end
endtask
initial begin
$display("=== i2c_slave_ack: the right slot, and released in time ===");
// ----------------------------------------------------------------
// T1. RESET RELEASES SDA. A slave holding SDA low out of reset takes the whole
// bus down and nothing else can lift it.
// ----------------------------------------------------------------
do_reset;
$display("T1 reset releases SDA");
ck_bit("T1 not driving", s_sda_low, 1'b0);
ck_bit("T1 the line is high", sda, 1'b1);
ck_bit("T1 no slot in progress", ack_active, 1'b0);
// ----------------------------------------------------------------
// T2. NOTHING HAPPENS WITHOUT A BYTE. Clock all day; the slave owes no
// acknowledge and must drive nothing. The false-positive test.
// ----------------------------------------------------------------
do_reset;
for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0);
$display("T2 with no byte complete, the slave drives nothing");
ck_int("T2 never drove", ack_low_cycles, 0);
ck_int("T2 and never moved SDA in a high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T3. THE ASSERT WAITS FOR SCL TO FALL. `byte_done` arrives while SCL is still
// HIGH -- which is exactly when the eighth bit is being sampled -- and the
// slave must NOT pull SDA down yet. SDA falling while SCL is high is a START
// (§3.1.1), so an early acknowledge is not early: it restarts the transaction.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); m_scl_low = 1'b0; phase; // SCL HIGH
pulse_byte_done;
for (k = 0; k < 3; k = k + 1) step;
$display("T3 a byte completing while SCL is HIGH does not assert the ACK yet");
ck_bit("T3 the slot is armed", ack_armed, 1'b1);
ck_bit("T3 but SDA is NOT being driven", s_sda_low, 1'b0);
ck_int("T3 and SDA has not moved in a high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T4. ... AND ASSERTS ON THE FALL. The first legal instant.
// ----------------------------------------------------------------
@(negedge clk); m_scl_low = 1'b1; // SCL falls
for (k = 0; k < 6; k = k + 1) step;
$display("T4 and it asserts on the falling edge, the first legal instant");
ck_bit("T4 now driving", s_sda_low, 1'b1);
ck_bit("T4 the slot is active", ack_active, 1'b1);
ck_bit("T4 and the line is pulled low", sda, 1'b0);
// ----------------------------------------------------------------
// T5. IT HOLDS THROUGH THE WHOLE NINTH HIGH PHASE, which is what §3.1.4 requires:
// "it remains stable LOW during the HIGH period of this clock pulse".
// ----------------------------------------------------------------
@(negedge clk); m_scl_low = 1'b0; phase; // the ninth HIGH phase
ck_bit("T5 still driving through the high phase", s_sda_low, 1'b1);
ck_bit("T5 the line is still low", sda, 1'b0);
ck_int("T5 and SDA never moved during it", sda_moved_high, 0);
// ----------------------------------------------------------------
// T6. AND RELEASES ON THE NINTH FALL, before the master can drive the next bit.
// A slave still pulling SDA down in the following low phase turns the
// master's one into a zero -- which the master is entitled to read as losing
// arbitration (§3.1.8), so a late release can knock a master off the bus.
// ----------------------------------------------------------------
@(negedge clk); m_scl_low = 1'b1; phase; // the ninth fall
$display("T6 and releases on the ninth fall, before the next bit is driven");
ck_bit("T6 released", s_sda_low, 1'b0);
ck_bit("T6 the slot is over", ack_active, 1'b0);
ck_bit("T6 and the line is free", sda, 1'b1);
ck_int("T6 one acknowledge counted", n_ack, 1);
// ----------------------------------------------------------------
// T7. THE WHOLE SLOT, END TO END, with the master sampling it. The bus must read
// LOW at the ninth rising edge and at no other.
// ----------------------------------------------------------------
do_reset;
gen_pulse(1'b0); // a data bit, master drives a one
pulse_byte_done; // ... and the byte completes
gen_pulse(1'b0); // the ninth pulse: the acknowledge
gen_pulse(1'b0); // and the next bit slot
$display("T7 the master samples LOW in the ninth pulse and HIGH in the others");
ck_int("T7 exactly one low sample", sampled_low, 1);
ck_int("T7 and the rest read high", sampled_high, 2);
ck_int("T7 no SDA movement in any high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T8. A NACK IS THE ABSENCE OF DRIVE. With `ack_en` low the slave must leave the
// line released -- there is no drive-high, so a NACK is what the pull-up does
// when nobody objects.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); ack_en = 1'b0;
pulse_byte_done;
gen_pulse(1'b0);
$display("T8 a NACK is the absence of drive, not a driven one");
ck_int("T8 never pulled the line down", ack_low_cycles, 0);
ck_int("T8 the master sampled high", sampled_low, 0);
ck_int("T8 and it was counted as a NACK", n_nack, 1);
ck_int("T8 with no acknowledge counted", n_ack, 0);
// ----------------------------------------------------------------
// T9. THE DECISION IS LATCHED WHEN THE SLOT IS ARMED. The inputs that produced it
// -- an address comparison, a register's writability -- are valid at byte
// completion and need not still be valid two SCL phases later.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); ack_en = 1'b1;
pulse_byte_done;
@(negedge clk); ack_en = 1'b0; // withdrawn AFTER arming
gen_pulse(1'b0);
$display("T9 the decision is latched at arming, not re-read in the slot");
ck_int("T9 the acknowledge still happened", n_ack, 1);
ck_int("T9 and the master saw it", sampled_low, 1);
// ----------------------------------------------------------------
// T10. ONE SLOT, NOT A LEVEL. After the acknowledge the slave must be silent
// through the following data bits -- a design that held ACK as a level until
// the next byte would drive through them.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); ack_en = 1'b1;
pulse_byte_done;
gen_pulse(1'b0); // the acknowledge
ack_low_cycles = 0;
for (k = 0; k < 4; k = k + 1) gen_pulse(1'b0); // four more bit slots
$display("T10 the acknowledge is one slot, not a level held into the next byte");
ck_int("T10 silent for every following bit", ack_low_cycles, 0);
// ----------------------------------------------------------------
// T11. TWO BYTES IN A ROW each get exactly one acknowledge.
// ----------------------------------------------------------------
do_reset;
for (k = 0; k < 2; k = k + 1) begin
// `byte_done` must be raised while SCL is HIGH, because that is the only way it
// can occur in reality: a byte completes when its eighth bit is sampled, and
// sampling happens at the rising edge. Raising it in a low phase would arm a
// slot whose assert lands a phase late -- a bench artefact, not a design defect.
@(negedge clk); m_scl_low = 1'b0; phase; // SCL high: the eighth bit
pulse_byte_done;
gen_pulse(1'b0); // the ninth pulse
end
$display("T11 two bytes, two acknowledges, no more");
ck_int("T11 two acknowledges", n_ack, 2);
ck_int("T11 the master saw two low samples", sampled_low, 2);
ck_int("T11 and SDA never moved in a high phase", sda_moved_high, 0);
// ----------------------------------------------------------------
// T12. RESET MID-SLOT RELEASES IMMEDIATELY. A slave reset while acknowledging
// must let go, even though letting go while SCL is high would normally be
// forbidden -- reset has no other option, and holding is worse.
// ----------------------------------------------------------------
do_reset;
pulse_byte_done;
@(negedge clk); m_scl_low = 1'b1; phase; // assert the acknowledge
ck_bit("T12 driving before reset", s_sda_low, 1'b1);
@(negedge clk); rst_n = 1'b0; step; step;
$display("T12 reset mid-acknowledge releases the line");
ck_bit("T12 released by reset", s_sda_low, 1'b0);
ck_bit("T12 and the bus is free", sda, 1'b1);
// ----------------------------------------------------------------
// T13. FRAMING ABORTS THE SLOT. A STOP arriving while the acknowledge is being
// driven means the transaction it belonged to no longer exists -- so the slot
// must be abandoned and the line released at once. A slot that survived
// framing would keep pulling SDA down through whatever the master does next,
// which on a bus the master is trying to release is a stuck line.
// ----------------------------------------------------------------
do_reset;
pulse_byte_done;
@(negedge clk); m_scl_low = 1'b1; phase; // assert the acknowledge
ck_bit("T13 driving before the framing event", s_sda_low, 1'b1);
@(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
step;
$display("T13 a STOP mid-acknowledge abandons the slot and releases the line");
ck_bit("T13 released by the STOP", s_sda_low, 1'b0);
ck_bit("T13 the slot is over", ack_active, 1'b0);
ck_bit("T13 and the bus is free", sda, 1'b1);
// ----------------------------------------------------------------
// T14. AND A START DOES THE SAME. The other half: a repeated START mid-acknowledge
// is the more likely of the two, because a master that abandons a transfer
// usually restarts rather than stopping.
// ----------------------------------------------------------------
do_reset;
pulse_byte_done;
@(negedge clk); m_scl_low = 1'b1; phase;
ck_bit("T14 driving before the framing event", s_sda_low, 1'b1);
@(negedge clk); f_start = 1'b1; step; @(negedge clk); f_start = 1'b0;
step;
$display("T14 a repeated START mid-acknowledge does the same");
ck_bit("T14 released by the START", s_sda_low, 1'b0);
ck_bit("T14 the slot is over", ack_active, 1'b0);
// ----------------------------------------------------------------
// T15. AN ARMED SLOT IS ALSO ABANDONED. Framing between byte completion and the
// falling edge must cancel the pending acknowledge, not merely the active one
// -- otherwise the slot fires one phase later into a transfer that has moved on.
// ----------------------------------------------------------------
do_reset;
@(negedge clk); m_scl_low = 1'b0; phase; // SCL HIGH
pulse_byte_done;
ck_bit("T15 the slot is armed", ack_armed, 1'b1);
@(negedge clk); f_stop = 1'b1; step; @(negedge clk); f_stop = 1'b0;
ck_bit("T15 arming was cancelled", ack_armed, 1'b0);
ack_low_cycles = 0;
gen_pulse(1'b0);
$display("T15 an armed slot is abandoned too, not merely an active one");
ck_int("T15 and nothing was ever driven", ack_low_cycles, 0);
if (errors == 0) $display("=== i2c_slave_ack: ALL CHECKS PASSED ===");
else $display("=== i2c_slave_ack: %0d CHECK(S) FAILED ===", errors);
$finish;
end
endmodule -- -----------------------------------------------------------------------------
-- i2c_slave_ack_tb.vhd
-- Independent oracle for i2c_slave_ack. Behavioural twin of the SystemVerilog and
-- Verilog benches.
--
-- The bench drives the bus as a controller AND resolves the line as the bus does, so the
-- slave's drive-low actually pulls the wire down. Essential here: the whole subject is
-- when SDA is low. The critical observer watches for SDA changing while SCL is HIGH,
-- because every defect in this block moves SDA in the wrong phase.
-- -----------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_slave_ack_tb is
end entity i2c_slave_ack_tb;
architecture sim of i2c_slave_ack_tb is
constant HALF : positive := 8;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal m_scl_low, m_sda_low : std_logic := '0';
signal byte_done : std_logic := '0';
signal ack_en : std_logic := '1';
-- Framing inputs, DRIVEN rather than tied off. Tying them to zero leaves the
-- abort path unexercised.
signal f_start, f_stop : std_logic := '0';
signal s_sda_low : std_logic;
-- The bus, resolved exactly as the wired-AND does it.
signal scl, sda : std_logic;
signal scl_q, sda_q : std_logic;
signal scl_rise, scl_fall, sda_rise, sda_fall : std_logic;
signal ack_active, ack_armed : std_logic;
signal n_ack, n_nack : unsigned(15 downto 0);
signal halt : boolean := false;
signal sda_moved_high, ack_low_cycles : integer := 0;
signal sampled_low, sampled_high : integer := 0;
signal clr : boolean := false;
begin
scl <= not m_scl_low;
sda <= not (m_sda_low or s_sda_low);
u_sync : entity work.i2c_slave_sync
generic map (SYNC_DEPTH => 2)
port map (clk => clk, rst_n => rst_n, scl_pin => scl, sda_pin => sda,
scl_q => scl_q, sda_q => sda_q, scl_rise => scl_rise, scl_fall => scl_fall,
sda_rise => sda_rise, sda_fall => sda_fall);
dut : entity work.i2c_slave_ack
generic map (CNT_W => 16)
port map (clk => clk, rst_n => rst_n, scl_fall => scl_fall,
byte_done => byte_done, ack_en => ack_en,
start_pulse => f_start, stop_pulse => f_stop,
sda_drive_low => s_sda_low,
ack_active => ack_active, ack_armed => ack_armed,
n_acks => n_ack, n_nacks => n_nack);
clkgen : process
begin
while not halt loop
clk <= '0'; wait for 5 ns;
clk <= '1'; wait for 5 ns;
end loop;
wait;
end process;
obs : process (clk, clr)
variable s_d : std_logic := '0';
begin
if clr then
sda_moved_high <= 0; ack_low_cycles <= 0;
sampled_low <= 0; sampled_high <= 0;
elsif rising_edge(clk) then
if rst_n = '1' then
if s_sda_low /= s_d and scl = '1' then
sda_moved_high <= sda_moved_high + 1;
end if;
if s_sda_low = '1' then ack_low_cycles <= ack_low_cycles + 1; end if;
if scl_rise = '1' then
if sda = '0' then sampled_low <= sampled_low + 1;
else sampled_high <= sampled_high + 1; end if;
end if;
end if;
s_d := s_sda_low;
end if;
end process;
stim : process
variable err : integer := 0;
variable k : integer;
procedure step is
begin
wait until rising_edge(clk); wait until falling_edge(clk);
end procedure;
procedure phase is
begin
for i in 1 to HALF loop step; end loop;
end procedure;
procedure do_reset is
begin
wait until falling_edge(clk);
rst_n <= '0'; m_scl_low <= '0'; m_sda_low <= '0';
byte_done <= '0'; ack_en <= '1'; f_start <= '0'; f_stop <= '0';
clr <= true; wait for 1 ns; clr <= false;
step; step;
wait until falling_edge(clk); rst_n <= '1';
phase;
end procedure;
-- One SCL pulse, starting and ending in the LOW phase.
procedure gen_pulse (drive_low_during : std_logic) is
begin
wait until falling_edge(clk); m_scl_low <= '1'; phase;
wait until falling_edge(clk); m_sda_low <= drive_low_during; phase;
wait until falling_edge(clk); m_scl_low <= '0'; phase;
wait until falling_edge(clk); m_scl_low <= '1'; phase;
end procedure;
procedure pulse_byte_done is
begin
wait until falling_edge(clk); byte_done <= '1';
step;
wait until falling_edge(clk); byte_done <= '0';
end procedure;
procedure ck_bit (what : string; g : std_logic; e : std_logic) is
begin
if g /= e then
report " FAIL " & what severity note;
err := err + 1;
end if;
end procedure;
procedure ck_int (what : string; g : integer; e : integer) is
begin
if g /= e then
report " FAIL " & what & ": got " & integer'image(g)
& " expected " & integer'image(e) severity note;
err := err + 1;
end if;
end procedure;
begin
report "=== i2c_slave_ack: the right slot, and released in time ===" severity note;
-- T1. Reset releases SDA.
do_reset;
report "T1 reset releases SDA" severity note;
ck_bit("T1 not driving", s_sda_low, '0');
ck_bit("T1 the line is high", sda, '1');
ck_bit("T1 no slot in progress", ack_active, '0');
-- T2. Nothing happens without a byte.
do_reset;
for k in 0 to 3 loop gen_pulse('0'); end loop;
report "T2 with no byte complete, the slave drives nothing" severity note;
ck_int("T2 never drove", ack_low_cycles, 0);
ck_int("T2 and never moved SDA in a high phase", sda_moved_high, 0);
-- T3. The assert waits for SCL to FALL.
do_reset;
wait until falling_edge(clk); m_scl_low <= '0'; phase; -- SCL HIGH
pulse_byte_done;
for k in 0 to 2 loop step; end loop;
report "T3 a byte completing while SCL is HIGH does not assert the ACK yet"
severity note;
ck_bit("T3 the slot is armed", ack_armed, '1');
ck_bit("T3 but SDA is NOT being driven", s_sda_low, '0');
ck_int("T3 and SDA has not moved in a high phase", sda_moved_high, 0);
-- T4. ... and asserts on the fall.
wait until falling_edge(clk); m_scl_low <= '1';
for k in 0 to 5 loop step; end loop;
report "T4 and it asserts on the falling edge, the first legal instant"
severity note;
ck_bit("T4 now driving", s_sda_low, '1');
ck_bit("T4 the slot is active", ack_active, '1');
ck_bit("T4 and the line is pulled low", sda, '0');
-- T5. It holds through the whole ninth high phase.
wait until falling_edge(clk); m_scl_low <= '0'; phase;
ck_bit("T5 still driving through the high phase", s_sda_low, '1');
ck_bit("T5 the line is still low", sda, '0');
ck_int("T5 and SDA never moved during it", sda_moved_high, 0);
-- T6. And releases on the ninth fall.
wait until falling_edge(clk); m_scl_low <= '1'; phase;
report "T6 and releases on the ninth fall, before the next bit is driven"
severity note;
ck_bit("T6 released", s_sda_low, '0');
ck_bit("T6 the slot is over", ack_active, '0');
ck_bit("T6 and the line is free", sda, '1');
ck_int("T6 one acknowledge counted", to_integer(n_ack), 1);
-- T7. The whole slot, with the master sampling it.
do_reset;
gen_pulse('0');
pulse_byte_done;
gen_pulse('0');
gen_pulse('0');
report "T7 the master samples LOW in the ninth pulse and HIGH in the others"
severity note;
ck_int("T7 exactly one low sample", sampled_low, 1);
ck_int("T7 and the rest read high", sampled_high, 2);
ck_int("T7 no SDA movement in any high phase", sda_moved_high, 0);
-- T8. A NACK is the absence of drive.
do_reset;
wait until falling_edge(clk); ack_en <= '0';
pulse_byte_done;
gen_pulse('0');
report "T8 a NACK is the absence of drive, not a driven one" severity note;
ck_int("T8 never pulled the line down", ack_low_cycles, 0);
ck_int("T8 the master sampled high", sampled_low, 0);
ck_int("T8 and it was counted as a NACK", to_integer(n_nack), 1);
ck_int("T8 with no acknowledge counted", to_integer(n_ack), 0);
-- T9. The decision is latched when the slot is armed.
do_reset;
wait until falling_edge(clk); ack_en <= '1';
pulse_byte_done;
wait until falling_edge(clk); ack_en <= '0'; -- withdrawn AFTER arming
gen_pulse('0');
report "T9 the decision is latched at arming, not re-read in the slot"
severity note;
ck_int("T9 the acknowledge still happened", to_integer(n_ack), 1);
ck_int("T9 and the master saw it", sampled_low, 1);
-- T10. One slot, not a level.
do_reset;
wait until falling_edge(clk); ack_en <= '1';
pulse_byte_done;
gen_pulse('0');
clr <= true; wait for 1 ns; clr <= false;
for k in 0 to 3 loop gen_pulse('0'); end loop;
report "T10 the acknowledge is one slot, not a level held into the next byte"
severity note;
ck_int("T10 silent for every following bit", ack_low_cycles, 0);
-- T11. Two bytes, two acknowledges.
do_reset;
for k in 0 to 1 loop
-- byte_done must be raised while SCL is HIGH, as it can only occur in reality.
wait until falling_edge(clk); m_scl_low <= '0'; phase;
pulse_byte_done;
gen_pulse('0');
end loop;
report "T11 two bytes, two acknowledges, no more" severity note;
ck_int("T11 two acknowledges", to_integer(n_ack), 2);
ck_int("T11 the master saw two low samples", sampled_low, 2);
ck_int("T11 and SDA never moved in a high phase", sda_moved_high, 0);
-- T12. Reset mid-slot releases immediately.
do_reset;
pulse_byte_done;
wait until falling_edge(clk); m_scl_low <= '1'; phase;
ck_bit("T12 driving before reset", s_sda_low, '1');
wait until falling_edge(clk); rst_n <= '0'; step; step;
report "T12 reset mid-acknowledge releases the line" severity note;
ck_bit("T12 released by reset", s_sda_low, '0');
ck_bit("T12 and the bus is free", sda, '1');
-- T13. Framing aborts the slot.
do_reset;
pulse_byte_done;
wait until falling_edge(clk); m_scl_low <= '1'; phase;
ck_bit("T13 driving before the framing event", s_sda_low, '1');
wait until falling_edge(clk); f_stop <= '1';
step;
wait until falling_edge(clk); f_stop <= '0';
step;
report "T13 a STOP mid-acknowledge abandons the slot and releases the line"
severity note;
ck_bit("T13 released by the STOP", s_sda_low, '0');
ck_bit("T13 the slot is over", ack_active, '0');
ck_bit("T13 and the bus is free", sda, '1');
-- T14. And a START does the same.
do_reset;
pulse_byte_done;
wait until falling_edge(clk); m_scl_low <= '1'; phase;
ck_bit("T14 driving before the framing event", s_sda_low, '1');
wait until falling_edge(clk); f_start <= '1';
step;
wait until falling_edge(clk); f_start <= '0';
step;
report "T14 a repeated START mid-acknowledge does the same" severity note;
ck_bit("T14 released by the START", s_sda_low, '0');
ck_bit("T14 the slot is over", ack_active, '0');
-- T15. An armed slot is abandoned too.
do_reset;
wait until falling_edge(clk); m_scl_low <= '0'; phase;
pulse_byte_done;
ck_bit("T15 the slot is armed", ack_armed, '1');
wait until falling_edge(clk); f_stop <= '1';
step;
wait until falling_edge(clk); f_stop <= '0';
ck_bit("T15 arming was cancelled", ack_armed, '0');
clr <= true; wait for 1 ns; clr <= false;
gen_pulse('0');
report "T15 an armed slot is abandoned too, not merely an active one" severity note;
ck_int("T15 and nothing was ever driven", ack_low_cycles, 0);
if err = 0 then
report "=== i2c_slave_ack: ALL CHECKS PASSED ===" severity note;
else
report "=== i2c_slave_ack: " & integer'image(err) & " CHECK(S) FAILED ==="
severity note;
end if;
halt <= true;
wait;
end process;
end architecture sim;8b. Execution
| Design | SystemVerilog | Verilog-2001 | VHDL | Finish |
|---|---|---|---|---|
i2c_slave_ack | PASS 15/15 | PASS 15/15 | PASS 15/15 | 8980 ns, all three |
9. Mutation Testing
Ten defects. Three survived the first pass, and the three reasons are worth separating because only one was a real coverage gap.
| # | Injected defect | Expected detection | Result |
|---|---|---|---|
| M1 | assert without waiting for the fall — a START, not an acknowledge | T3 | KILLED (17) |
| M2 | never release: the acknowledge becomes a level | T10 | KILLED (7) |
| M3 | inverted policy | T7, T8 | KILLED (14) |
| M4 | a NACK is driven | T8 | KILLED (3) |
| M5 | the decision re-read in the slot | T9 | KILLED (2) |
| M6 | release one phase early, mid high phase | T5, T7 | KILLED (13) |
| M7 | reset leaves SDA driven | T1 | KILLED (10) |
| M8 | framing does not abort the slot | T13 new | KILLED (8) |
| M9 | the slot arms on any cycle | T2 | KILLED (10) |
| M10 | the acknowledge and NACK counters swapped | T8 | KILLED (6) |
baseline: PASS (verified before injecting anything)
valid mutants: 10 killed: 10 survived: 0 equivalent: 0 invalid: 0
restored: PASSTwo survivors were invalid mutants, and one was a real gap
M1 and M6, as first written, were inert. Both wrapped a case arm's body in if (1'b1), which changes nothing — the body still contained its own if (scl_fall). Re-injected against the actual wait condition, using the preceding comment line to disambiguate the two identical if (scl_fall) statements, both die immediately: M1 on seventeen checks, M6 on thirteen.
M8 was the real gap: the bench had tied the framing inputs to zero.
.start_pulse(1'b0), .stop_pulse(1'b0),So the abort path was never exercised. A target whose acknowledge survives framing keeps pulling SDA down into a transfer that no longer exists — and if the master is trying to issue a STOP, the target's held-low SDA prevents the STOP from happening at all. That is one of the two ways an I²C bus wedges permanently.
The fix was to drive those inputs and add three tests: a STOP mid-acknowledge (T13), a repeated START mid-acknowledge (T14), and — the one that would otherwise still be missing — framing arriving while the slot is merely armed (T15). An armed slot that survives framing fires one phase later into a transfer that has moved on.
10. Verification Connection — Asserting an Ownership Window
// 1. NEVER MOVE SDA WHILE SCL IS HIGH. The single property that catches every timing
// defect in this block, because all of them move SDA in the wrong phase. Note it is
// stated on the DRIVE INTENT, not on the line -- the line is the wired-AND and the
// master may legitimately move it.
property p_no_sda_change_while_scl_high;
@(posedge clk) disable iff (!rst_n)
(scl_q && $past(scl_q)) |-> $stable(sda_drive_low);
endproperty
a_phase: assert property (p_no_sda_change_while_scl_high);
// 2. THE SLOT IS ONE SCL PERIOD. Between assert and release there is exactly one
// falling edge -- so `ack_active` may never span two.
property p_one_period;
@(posedge clk) disable iff (!rst_n)
(ack_active && scl_fall) |=> !ack_active;
endproperty
a_one: assert property (p_one_period);
// 3. DRIVE ONLY INSIDE THE SLOT. The drive-low may not be asserted when no slot is
// active -- which is mutation M10's "level" defect and M9's "arms anywhere".
property p_drive_implies_slot;
@(posedge clk) disable iff (!rst_n) sda_drive_low |-> ack_active;
endproperty
a_own: assert property (p_drive_implies_slot);
// 4. FRAMING RELEASES. Within one cycle of a framing event the drive is gone.
property p_framing_releases;
@(posedge clk) disable iff (!rst_n)
(start_pulse || stop_pulse) |=> !sda_drive_low;
endproperty
a_abort: assert property (p_framing_releases);
// WHAT CANNOT BE ASSERTED FROM INSIDE THIS BLOCK, and it is the interesting one.
//
// "the master sampled our acknowledge" is not a property of this block at all. It
// depends on the master's sampling instant, which is on the other side of the bus. The
// nearest internal property -- drive asserted before the rise and held through the high
// phase -- is necessary and not sufficient, because a master sampling outside its own
// high phase would miss a perfectly legal acknowledge.
//
// So the check that the acknowledge WORKED belongs in the bench, as an observation of
// the WIRE at the controller's sampling instant. That is test T7, and it is why the
// bench resolves the line rather than watching the DUT's output.
//
// COVERAGE:
//
// cover: an ACK (drive asserted) every addressed byte
// cover: a NACK (drive withheld) a refused write, an unmatched address
// cover: framing during an ACTIVE slot T13/T14 -- an aborted transfer
// cover: framing during an ARMED slot T15 -- a narrower window still
// illegal_bin: drive asserted while scl_q the §2 violation, by construction11. FPGA and ASIC Implications
On an FPGA this is the first block in the module whose output reaches a pad, so it is where the three-signal pin form becomes concrete: sda_drive_low drives the tri-state buffer's output enable with the data input tied low. The readback that 18.2 consumes comes from the buffer's input, never from a copy of the enable — the same requirement Chapter 17.1 §9 stated for the master, and the same defect if it is broken.
The timing constraint of §7 is the FPGA-relevant number: three system clocks inside the SCL low phase with a two-deep synchroniser. Deepening the synchroniser for a faster system clock (18.2 §11) consumes that margin directly, which is the one place where the two chapters' parameters interact.
On an ASIC, the pad's input filter adds to the synchroniser latency and therefore to the same inequality — so a design that is comfortable in simulation can be marginal in silicon if the filter delay was not counted. That is a genuine reason to know the inequality rather than to rely on a healthy ratio.
The other ASIC-specific point is power-up. Before reset deasserts, the pad must be released: a target that holds SDA low while its own logic is undefined prevents every other device on the board from communicating, and there is no recovery from outside because the wired-AND means nobody can lift the line. Test T1 exists for that, and it is the reason sda_drive_low resets low rather than being left to the state machine's initial value.
12. Debugging — The Target That Restarted Every Transaction It Answered
A target is addressed and appears to acknowledge. The master then reports that its own transaction never progressed: after the address byte it sees a START condition it did not generate, and its transaction controller abandons the transfer and retries. The retry behaves identically. A logic analyser decoding the bus shows START, address, then a second START -- with no acknowledge anywhere.
The acknowledge was asserted on byte completion rather than on the following falling edge of SCL. Because a byte completes at a rising edge, SCL is still high at that moment -- so the assert pulled SDA low during a high phase, which section 3.1.1 defines as a START condition. The target did not acknowledge late or early in any ordinary sense: it emitted framing. Nothing was wrong with the address comparison, the selection logic or the policy; the byte was correctly identified as one to acknowledge, and the acknowledgement itself was the transaction-destroying event.
Gate the assert on scl_fall, which is mutation M1 in reverse. Then fix the bench, because that is what let it through: the bench raised byte_done while SCL was LOW, so a falling edge was always already pending and the wait had nothing to wait for. Raising it while SCL is HIGH -- which is the only way it can occur in reality, and is now stated as an interface assumption in the port comment -- makes test T3 meaningful: it asserts that the drive is STILL not asserted while the slot is armed. A bench whose stimulus cannot violate a precondition cannot test the code that enforces it.Three generalisations.
A target produced framing without a framing generator. Nothing in this block knows what a START is; it pulled a line low at the wrong time and the bus supplied the meaning. Any block with SDA ownership can manufacture framing by accident, which is why Chapter 17.6 §4 needed two different releases and why this one waits for an edge it does not otherwise care about.
The bench's stimulus was impossible, and that made a test vacuous. Pulsing byte_done in a low phase is not merely unrealistic — it removes the condition T3 exists to check. A precondition that the stimulus cannot violate is a precondition that is not being enforced by anything the tests can see.
The symptom named the wrong device. The master reported a spurious START, so the investigation went looking for a second master. The START was real, legal to detect, and produced by the device that was trying to be helpful.
13. Common Misconceptions
"The acknowledge is the ninth data bit." It is an ownership transfer that occupies a clock pulse. Treating it as data means driving it from wherever data comes from, at whatever time data is driven. §1.
"Asserting the acknowledge early is harmless." SDA falling while SCL is high is a START. An early acknowledge does not acknowledge; it restarts the transaction. §2, §12.
"Releasing late is a minor timing slip." It turns the master's next one into a zero, which a master may read as losing arbitration — so a late release can unseat a master. §2.
"Hold the acknowledge until the next byte." Then the target drives through the next byte's data bits. The slot is one SCL period. §3.
"A NACK means driving SDA high." There is no drive-high. A NACK is what the pull-up does when nobody objects. §4.
"The acknowledge decision can be read when the slot fires." Its inputs are valid at byte completion, two SCL phases earlier. Latch it when arming. §4.
"Any block can raise byte_done." It must arrive while SCL is high, because a byte completes at a sampling edge. A low-phase pulse arms a slot whose assert lands a phase late. §5.
"A fast enough system clock makes the timing automatic." It makes it comfortable. The inequality is real — synchroniser latency plus one cycle must fit inside the SCL low phase — and a pad filter eats into it. §7, §11.
"Framing during an acknowledge is a corner case." A target that keeps driving through a STOP prevents the STOP from happening, which wedges the bus. §9.
"A bench that never sets an input is testing the default." It is testing nothing about that input. Tied-off ports are invisible coverage holes. §9.
14. Reason It Through
Why is asserting the acknowledge one cycle after byte completion a protocol violation rather than a timing error?
Because a byte completes at a rising edge, so SCL is still high — and an SDA fall while SCL is high is a START. The target emits framing rather than an acknowledge. §2, §12.
Both ends of the acknowledge window are falling edges. Why not one falling and one rising?
Because §3.1.2 permits SDA to change only while SCL is low, and the two transitions must happen in the two low phases adjacent to the ninth high phase. A rising edge is never a legal instant to change SDA. §2.
A target releases its acknowledge one phase late. What can the master conclude, and why is that worse than a missing acknowledge?
Its next transmitted one comes back as a zero, which §3.1.8 lets it read as losing arbitration — so it abandons a transfer it was winning. A missing acknowledge is a clean, reportable failure; this is a false arbitration loss. §2.
Why latch the acknowledge decision when the slot arms rather than reading it in the slot?
Because the inputs that produced it — an address comparison, a register's writability — are valid at byte completion and need not still be valid two SCL phases later. §4.
State the clock-ratio inequality and say which obligation it threatens first.
Synchroniser latency plus one cycle to assert must fit inside the SCL low phase — three system clocks for a two-deep synchroniser. It threatens the acknowledge first, because that is the only obligation bounded inside a single low phase. §7.
Two mutants survived as inert injections. What is the general tell, and what is its mirror image?
A survivor whose mutation cannot change behaviour. Its mirror is a kill with zero failure lines — a mutant that failed to elaborate. In both the verdict is uninformative and the failure count is the evidence. §9.
Why is a tied-off bench input worse than a missing test?
Because it looks like configuration. A missing test is absent from the list; a tied-off port appears in the instantiation as a decision, and no pass rate or coverage figure distinguishes it from a deliberate one. §9.
15. Understanding Check
16. Summary
Three obligations, not one: pull SDA low before the ninth rise, hold it through the high phase, release before the master drives again. Getting the first right and missing either of the others presents as data corruption.
The window is bounded at both ends by falling edges, because §3.1.2 permits SDA to change only while SCL is low and there are exactly two such phases adjacent to the ninth pulse.
Asserting early is not early — it is a START. A premature acknowledge restarts the transaction instead of acknowledging a byte, and it is the most destructive timing error a target can make.
Releasing late can unseat a master, by turning its next one into a zero that it may read as losing arbitration.
One slot, not a level, or the target drives through the next byte's data.
The decision is policy and arrives as an input, latched when the slot arms — because its own inputs are valid two SCL phases earlier.
A NACK is the absence of drive. There is no drive-high anywhere in a conforming interface.
byte_done arrives while SCL is high, guaranteed, because a byte completes at a sampling edge — and that guarantee is what makes the next falling edge the right instant.
The clock ratio is a real inequality: synchroniser latency plus one cycle must fit inside the SCL low phase. Measured at a two-cycle phase, the acknowledge lands in the high phase and becomes a START.
Ten mutants, ten killed — after three survived: two were inert injections whose mutation could not change behaviour, and one was a genuine hole created by tying the framing inputs to zero in the bench.
Tied-off inputs are invisible coverage holes, because they read as configuration rather than as untested ports.
17. What Comes Next
The target can answer. Chapter 18.6 makes it listen — assembling written bytes and handing them on exactly once.
Its subject is the hand-off rather than the value: a byte delivered twice is as bad as one dropped, and both are invisible to a test that only compares what arrived. It is also where a gating question becomes sharp, because the acknowledge pulse this chapter just built is not a data bit — and a receive path left enabled through it shifts the acknowledge in, displacing every byte after the first by one bit.
Continue learning
Related tutorials
- Related topic
Sampling SDA and SCL — Edge Detection Inside a Slave
The block every other one in a target is written against: two asynchronous wires turned into five synchronous facts. Three of its decisions produce defects nothing later can recover from — what the samplers reset to, which signals the edge pulses come from, and why the synchroniser belongs here and nowhere else.
- Related topic
START and STOP Detection Inside a Slave
Detection is two AND gates. The substance is what a START resets, and the distinction that is not on the wire at all — a first START and a repeated START are the identical edge, separated only by state the target keeps itself.
- Related topic
The Transmit Datapath — Sourcing Read Data in Time
The hardest datapath in a target, because the master decides when the next bit is wanted and the slave must have decided what it is one phase earlier. Builds the pre-fetch, shows why a transmitted one is a release, and why the ninth slot must be given back.
- Related topic
The SCL Timing Generator — Phases, Strobes and the Readback Rule
Where Table 10's microseconds become counts of system-clock cycles. Derives the period budget that must include rise and fall time, shows why rounding down is always illegal and rounding up always legal, and builds a generator that leaves its low phase only when the line actually reads back high — which implements clock stretching and clock synchronization with no extra logic.
