I²C · Module 7
Who Acknowledges When — Slave ACK and Master ACK
The acknowledge belongs to the receiver of a byte, so the direction of that byte decides who answers — not whether a device is the master or the slave. One sentence, four table rows, and the confusion it resolves is the most common one on this bus.
Chapter 7.2 built a block that drives the acknowledge slot and took we_acknowledge as an input it never computed. This chapter computes it, and the whole thing reduces to one sentence.
1. The Sentence
UM10204 §3.1.6, first paragraph:
The acknowledge bit allows the receiver to signal the transmitter that the byte was successfully received and another byte may be sent.
The receiver of a byte acknowledges it. Not the slave. Not the device that did not send it. The receiver.
That sounds too obvious to need a chapter, and it is the source of more confusion than any other rule on this bus — because of what it implies when the transfer is a read.
2. Why "the Slave Acknowledges" Is Wrong Half the Time
The mental model most engineers acquire first is the master talks and the slave answers, and it is correct for a write. It is exactly backwards for the data phase of a read.
Walk it through. On a read, after the addressing is done, the slave is transmitting — it is putting the register contents on SDA. The master is receiving them. So by §1's sentence, the master acknowledges each byte, and the slave is the one waiting to hear whether to send another.
The specification says so directly, in §3.1.10:
At the moment of the first acknowledge, the master-transmitter becomes a master-receiver and the slave-receiver becomes a slave-transmitter. This first acknowledge is still generated by the slave. The master generates subsequent acknowledges.
Three facts in three clauses, and each one matters:
The roles swap mid-transfer. A master-transmitter becomes a master-receiver at the acknowledge following the address byte. That is one instant at which a device changes from driving SDA to reading it.
The address byte's acknowledge is the slave's, always. Even on a read. The address byte is transmitted by the master and received by the slaves, so the slave is the receiver of that byte regardless of what the direction bit says about the bytes that follow.
Every acknowledge after that is the master's, on a read. "The master generates subsequent acknowledges."
3. The Four Rows
Every byte on this bus is one of four cases. This table is the chapter.
| byte | direction | who transmits the 8 bits | who acknowledges the 9th |
|---|---|---|---|
| address | write (R/W = 0) | master | slave (if addressed) |
| address | read (R/W = 1) | master | slave (if addressed) |
| data | write | master | slave |
| data | read | slave | master |
Read the columns rather than the rows and two patterns appear.
The transmit and acknowledge columns are always opposites. No device is ever both the transmitter and the acknowledger of the same byte — it would be answering itself, and the wired-AND would make that indistinguishable from a real answer. §6's testbench asserts this as an invariant over all sixteen input combinations, and §7 injects a fault that violates it.
Only one row has the master acknowledging. Data on a read. That is the only case, and it is the one that Chapter 7.4 is entirely about — because the master acknowledging means the master gets to decide when to stop.
Write data byte — master transmits, slave acknowledges
9 cyclesRead data byte — slave transmits, master acknowledges
9 cycles4. The Handover, and What It Costs
The role swap happens at one specific instant: the acknowledge following the address byte of a read.
Before it, the master is transmitting and the slave receiving. After it, the slave transmits and the master receives. And the acknowledge at that instant belongs to the slave — it is the last thing the slave does as a receiver, and it doubles as confirmation that it is ready to become a transmitter.
Two consequences for a design.
Both devices change direction on the same edge, and neither can afford to be late. The slave must stop driving the acknowledge and start driving data; the master must stop driving and start sampling. Both transitions happen in the SCL low phase following the acknowledge — the only interval in which SDA may legally move.
A device that is slow to turn around has exactly one legal option, and it is the same one as always: hold SCL low. Chapter 7.1 §8 quoted the specification's provision for it, and the handover is one of the places a real slave most often needs it, because it has to fetch the first data byte from somewhere.
5. The Resolver in Three Languages
Three lines of logic. The value is entirely in getting the three lines right, which is why the verification is exhaustive.
// Who drives the eight data bits, and who drives the ninth slot, for ONE byte.
// The rule is one sentence: the RECEIVER of a byte acknowledges it. Everything
// below is that sentence applied to the three kinds of byte on this bus.
module i2c_ack_role_resolver (
input logic is_master, // 1 = this device is the controller
input logic addressed, // slave only: is this transfer for me?
input logic byte_is_address, // 1 = the address byte, 0 = a data byte
input logic transfer_is_read, // the latched R/W of this transfer
output logic we_transmit, // do we drive the eight data bits?
output logic we_acknowledge // do we drive the ninth slot?
);
// The address byte is ALWAYS transmitted by the master and received by the
// slaves -- which is why the direction bit does not change who acknowledges it.
// The specification says so directly: "This first acknowledge is still
// generated by the slave. The master generates subsequent acknowledges."
logic slave_is_receiver;
assign slave_is_receiver = byte_is_address ? 1'b1 : ~transfer_is_read;
// The transmitter is whoever is NOT the receiver.
assign we_transmit = is_master ? slave_is_receiver
: (~slave_is_receiver && addressed);
// And the receiver is the one that acknowledges.
assign we_acknowledge = is_master ? ~slave_is_receiver
: ( slave_is_receiver && addressed);
endmodule module i2c_ack_role_resolver_tb;
logic is_master, addressed, byte_is_address, transfer_is_read;
logic we_transmit, we_acknowledge;
int errors = 0;
int both_count = 0;
i2c_ack_role_resolver dut (.*);
initial begin #20000; $display("FAIL: watchdog expired"); $finish; end
// INDEPENDENT reference model, written as the SPECIFICATION'S ENGLISH rather
// than as boolean algebra: name the receiver first, then derive the two roles.
// A model written as the same algebra as the design would agree with it even
// when both were wrong about who receives an address byte.
function automatic bit master_receives(input bit is_addr, input bit rd);
if (is_addr) return 1'b0; // the master SENDS the address byte
else return rd; // on a read, data flows to the master
endfunction
function automatic bit ref_tx(input bit m, input bit ad, input bit is_addr, input bit rd);
// The transmitter is whoever is not the receiver.
if (m) return !master_receives(is_addr, rd);
else return master_receives(is_addr, rd) && ad;
endfunction
function automatic bit ref_ack(input bit m, input bit ad, input bit is_addr, input bit rd);
// "The receiver acknowledges" -- so the acknowledger IS the receiver.
if (m) return master_receives(is_addr, rd);
else return !master_receives(is_addr, rd) && ad;
endfunction
bit e_tx, e_ack;
initial begin
// Exhaustive: four one-bit inputs is sixteen combinations. Walk all of them.
for (int v = 0; v < 16; v++) begin
is_master = v[3];
addressed = v[2];
byte_is_address = v[1];
transfer_is_read = v[0];
#1;
e_tx = ref_tx (v[3], v[2], v[1], v[0]);
e_ack = ref_ack(v[3], v[2], v[1], v[0]);
if (we_transmit !== e_tx) begin
$display("FAIL: m=%0b ad=%0b addr=%0b rd=%0b -- we_transmit %0b, expected %0b",
v[3], v[2], v[1], v[0], we_transmit, e_tx);
errors++;
end
if (we_acknowledge !== e_ack) begin
$display("FAIL: m=%0b ad=%0b addr=%0b rd=%0b -- we_acknowledge %0b, expected %0b",
v[3], v[2], v[1], v[0], we_acknowledge, e_ack);
errors++;
end
// THE INVARIANT: no device is ever both the transmitter and the
// acknowledger of the SAME byte. A design that allowed it would have a
// device answering its own byte, which the wired-AND would hide.
if (we_transmit && we_acknowledge) begin
$display("FAIL: m=%0b ad=%0b addr=%0b rd=%0b -- transmit AND acknowledge",
v[3], v[2], v[1], v[0]);
both_count++;
errors++;
end
end
// The four rows of the table every engineer should be able to recite.
// Address byte, master: the master sends, the slave answers.
is_master=1; addressed=0; byte_is_address=1; transfer_is_read=0; #1;
if (we_transmit !== 1'b1 || we_acknowledge !== 1'b0) begin
$display("FAIL: master must SEND the address byte and not acknowledge it"); errors++; end
// Address byte on a READ transfer -- still the slave that answers.
transfer_is_read=1; #1;
if (we_transmit !== 1'b1 || we_acknowledge !== 1'b0) begin
$display("FAIL: the address byte's acknowledge must not depend on R/W"); errors++; end
// Data byte on a WRITE: the slave receives, so the slave answers.
is_master=0; addressed=1; byte_is_address=0; transfer_is_read=0; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b1) begin
$display("FAIL: an addressed slave must acknowledge a written data byte"); errors++; end
// Data byte on a READ: the MASTER receives, so the MASTER answers.
is_master=1; addressed=0; byte_is_address=0; transfer_is_read=1; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b1) begin
$display("FAIL: the master must acknowledge a data byte it read"); errors++; end
// ... and the slave transmits it.
is_master=0; addressed=1; #1;
if (we_transmit !== 1'b1 || we_acknowledge !== 1'b0) begin
$display("FAIL: an addressed slave must transmit on a read and not acknowledge"); errors++; end
// An UNADDRESSED slave does nothing at all, in either direction.
addressed=0; byte_is_address=0; transfer_is_read=0; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b0) begin
$display("FAIL: an unaddressed slave must neither transmit nor acknowledge"); errors++; end
transfer_is_read=1; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b0) begin
$display("FAIL: an unaddressed slave must stay silent on a read too"); errors++; end
if (errors == 0)
$display("PASS: all 16 combinations match an independent model; transmit and acknowledge never coincide");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule // Who drives the eight data bits, and who drives the ninth slot, for ONE byte.
// The rule is one sentence: the RECEIVER of a byte acknowledges it.
module i2c_ack_role_resolver (
input wire is_master, // 1 = this device is the controller
input wire addressed, // slave only: is this transfer for me?
input wire byte_is_address, // 1 = the address byte, 0 = a data byte
input wire transfer_is_read, // the latched R/W of this transfer
output wire we_transmit, // do we drive the eight data bits?
output wire we_acknowledge // do we drive the ninth slot?
);
// The address byte is ALWAYS transmitted by the master and received by the
// slaves -- which is why the direction bit does not change who acknowledges it.
// "This first acknowledge is still generated by the slave. The master generates
// subsequent acknowledges."
wire slave_is_receiver = byte_is_address ? 1'b1 : ~transfer_is_read;
// The transmitter is whoever is NOT the receiver.
assign we_transmit = is_master ? slave_is_receiver
: (~slave_is_receiver & addressed);
// And the receiver is the one that acknowledges.
assign we_acknowledge = is_master ? ~slave_is_receiver
: ( slave_is_receiver & addressed);
endmodule module i2c_ack_role_resolver_tb;
reg is_master, addressed, byte_is_address, transfer_is_read;
wire we_transmit, we_acknowledge;
integer errors, both_count, v;
i2c_ack_role_resolver dut (.is_master(is_master), .addressed(addressed),
.byte_is_address(byte_is_address), .transfer_is_read(transfer_is_read),
.we_transmit(we_transmit), .we_acknowledge(we_acknowledge));
initial begin #20000; $display("FAIL: watchdog expired"); $finish; end
// INDEPENDENT reference model, written as the SPECIFICATION'S ENGLISH rather
// than as boolean algebra: name the receiver first, then derive the two roles.
// A model written as the same algebra as the design would agree with it even
// when both were wrong about who receives an address byte.
function master_receives; input is_addr; input rd; begin
if (is_addr) master_receives = 1'b0; // the master SENDS the address byte
else master_receives = rd; // on a read, data flows to the master
end endfunction
function ref_tx; input m; input ad; input is_addr; input rd; begin
// The transmitter is whoever is not the receiver.
if (m) ref_tx = !master_receives(is_addr, rd);
else ref_tx = master_receives(is_addr, rd) && ad;
end endfunction
function ref_ack; input m; input ad; input is_addr; input rd; begin
// "The receiver acknowledges" -- so the acknowledger IS the receiver.
if (m) ref_ack = master_receives(is_addr, rd);
else ref_ack = !master_receives(is_addr, rd) && ad;
end endfunction
reg e_tx, e_ack;
initial begin
errors = 0; both_count = 0;
// Exhaustive: four one-bit inputs is sixteen combinations. Walk all of them.
for (v = 0; v < 16; v = v + 1) begin
is_master = v[3];
addressed = v[2];
byte_is_address = v[1];
transfer_is_read = v[0];
#1;
e_tx = ref_tx (v[3], v[2], v[1], v[0]);
e_ack = ref_ack(v[3], v[2], v[1], v[0]);
if (we_transmit !== e_tx) begin
$display("FAIL: m=%0b ad=%0b addr=%0b rd=%0b -- we_transmit %0b, expected %0b",
v[3], v[2], v[1], v[0], we_transmit, e_tx);
errors = errors + 1;
end
if (we_acknowledge !== e_ack) begin
$display("FAIL: m=%0b ad=%0b addr=%0b rd=%0b -- we_acknowledge %0b, expected %0b",
v[3], v[2], v[1], v[0], we_acknowledge, e_ack);
errors = errors + 1;
end
// THE INVARIANT: no device is ever both the transmitter and the
// acknowledger of the SAME byte. A design that allowed it would have a
// device answering its own byte, which the wired-AND would hide.
if (we_transmit && we_acknowledge) begin
$display("FAIL: m=%0b ad=%0b addr=%0b rd=%0b -- transmit AND acknowledge",
v[3], v[2], v[1], v[0]);
both_count = both_count + 1;
errors = errors + 1;
end
end
// The four rows of the table every engineer should be able to recite.
// Address byte, master: the master sends, the slave answers.
is_master=1; addressed=0; byte_is_address=1; transfer_is_read=0; #1;
if (we_transmit !== 1'b1 || we_acknowledge !== 1'b0) begin
$display("FAIL: master must SEND the address byte and not acknowledge it"); errors = errors + 1; end
// Address byte on a READ transfer -- still the slave that answers.
transfer_is_read=1; #1;
if (we_transmit !== 1'b1 || we_acknowledge !== 1'b0) begin
$display("FAIL: the address byte's acknowledge must not depend on R/W"); errors = errors + 1; end
// Data byte on a WRITE: the slave receives, so the slave answers.
is_master=0; addressed=1; byte_is_address=0; transfer_is_read=0; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b1) begin
$display("FAIL: an addressed slave must acknowledge a written data byte"); errors = errors + 1; end
// Data byte on a READ: the MASTER receives, so the MASTER answers.
is_master=1; addressed=0; byte_is_address=0; transfer_is_read=1; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b1) begin
$display("FAIL: the master must acknowledge a data byte it read"); errors = errors + 1; end
// ... and the slave transmits it.
is_master=0; addressed=1; #1;
if (we_transmit !== 1'b1 || we_acknowledge !== 1'b0) begin
$display("FAIL: an addressed slave must transmit on a read and not acknowledge"); errors = errors + 1; end
// An UNADDRESSED slave does nothing at all, in either direction.
addressed=0; byte_is_address=0; transfer_is_read=0; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b0) begin
$display("FAIL: an unaddressed slave must neither transmit nor acknowledge"); errors = errors + 1; end
transfer_is_read=1; #1;
if (we_transmit !== 1'b0 || we_acknowledge !== 1'b0) begin
$display("FAIL: an unaddressed slave must stay silent on a read too"); errors = errors + 1; end
if (errors == 0)
$display("PASS: all 16 combinations match an independent model; transmit and acknowledge never coincide");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
-- Who drives the eight data bits, and who drives the ninth slot, for ONE byte.
-- The rule is one sentence: the RECEIVER of a byte acknowledges it.
entity i2c_ack_role_resolver is
port (
is_master : in std_logic; -- 1 = this device is the controller
addressed : in std_logic; -- slave only: is this transfer for me?
byte_is_address : in std_logic; -- 1 = the address byte, 0 = a data byte
transfer_is_read : in std_logic; -- the latched R/W of this transfer
we_transmit : out std_logic; -- do we drive the eight data bits?
we_acknowledge : out std_logic -- do we drive the ninth slot?
);
end entity;
architecture rtl of i2c_ack_role_resolver is
-- The address byte is ALWAYS transmitted by the master and received by the
-- slaves -- which is why the direction bit does not change who acknowledges it.
-- "This first acknowledge is still generated by the slave. The master generates
-- subsequent acknowledges."
signal slave_is_receiver : std_logic;
begin
slave_is_receiver <= '1' when byte_is_address = '1' else (not transfer_is_read);
-- The transmitter is whoever is NOT the receiver.
we_transmit <= slave_is_receiver when is_master = '1'
else ((not slave_is_receiver) and addressed);
-- And the receiver is the one that acknowledges.
we_acknowledge <= (not slave_is_receiver) when is_master = '1'
else (slave_is_receiver and addressed);
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_ack_role_resolver_tb is
end entity;
architecture sim of i2c_ack_role_resolver_tb is
signal is_master, addressed, byte_is_address, transfer_is_read : std_logic := '0';
signal we_transmit, we_acknowledge : std_logic;
signal test_done : std_logic := '0';
begin
dut : entity work.i2c_ack_role_resolver
port map (is_master => is_master, addressed => addressed,
byte_is_address => byte_is_address, transfer_is_read => transfer_is_read,
we_transmit => we_transmit, we_acknowledge => we_acknowledge);
watchdog : process
begin
wait for 20 us;
if test_done = '0' then
report "watchdog expired -- the sweep never completed" severity failure;
end if;
wait;
end process;
stim : process
variable errs : natural := 0;
-- INDEPENDENT reference model, written as the specification's English rather
-- than as boolean algebra: name the receiver first, then derive both roles.
function master_receives (is_addr, rd : std_logic) return boolean is
begin
if is_addr = '1' then return false; -- the master SENDS the address
else return rd = '1'; -- on a read, data flows to it
end if;
end function;
function ref_tx (m, ad, is_addr, rd : std_logic) return std_logic is
begin
if m = '1' then
if master_receives(is_addr, rd) then return '0'; else return '1'; end if;
else
if master_receives(is_addr, rd) and ad = '1' then return '1';
else return '0'; end if;
end if;
end function;
function ref_ack (m, ad, is_addr, rd : std_logic) return std_logic is
begin
if m = '1' then
if master_receives(is_addr, rd) then return '1'; else return '0'; end if;
else
if (not master_receives(is_addr, rd)) and ad = '1' then return '1';
else return '0'; end if;
end if;
end function;
variable bits : std_logic_vector(3 downto 0);
begin
-- Exhaustive: four one-bit inputs is sixteen combinations.
for v in 0 to 15 loop
bits := std_logic_vector(to_unsigned(v, 4));
is_master <= bits(3);
addressed <= bits(2);
byte_is_address <= bits(1);
transfer_is_read <= bits(0);
wait for 1 ns;
if we_transmit /= ref_tx(bits(3), bits(2), bits(1), bits(0)) then
report "combination " & integer'image(v) & ": we_transmit disagrees"
severity error; errs := errs + 1;
end if;
if we_acknowledge /= ref_ack(bits(3), bits(2), bits(1), bits(0)) then
report "combination " & integer'image(v) & ": we_acknowledge disagrees"
severity error; errs := errs + 1;
end if;
-- THE INVARIANT: never both the transmitter and the acknowledger of the
-- same byte.
if we_transmit = '1' and we_acknowledge = '1' then
report "combination " & integer'image(v) & ": transmit AND acknowledge"
severity error; errs := errs + 1;
end if;
end loop;
-- The four rows every engineer should be able to recite.
is_master <= '1'; addressed <= '0'; byte_is_address <= '1'; transfer_is_read <= '0';
wait for 1 ns;
if we_transmit /= '1' or we_acknowledge /= '0' then
report "master must SEND the address byte and not acknowledge it" severity error;
errs := errs + 1; end if;
transfer_is_read <= '1'; wait for 1 ns;
if we_transmit /= '1' or we_acknowledge /= '0' then
report "the address byte's acknowledge must not depend on R/W" severity error;
errs := errs + 1; end if;
is_master <= '0'; addressed <= '1'; byte_is_address <= '0'; transfer_is_read <= '0';
wait for 1 ns;
if we_transmit /= '0' or we_acknowledge /= '1' then
report "an addressed slave must acknowledge a written data byte" severity error;
errs := errs + 1; end if;
is_master <= '1'; addressed <= '0'; transfer_is_read <= '1'; wait for 1 ns;
if we_transmit /= '0' or we_acknowledge /= '1' then
report "the master must acknowledge a data byte it read" severity error;
errs := errs + 1; end if;
is_master <= '0'; addressed <= '1'; wait for 1 ns;
if we_transmit /= '1' or we_acknowledge /= '0' then
report "an addressed slave must transmit on a read and not acknowledge"
severity error; errs := errs + 1; end if;
addressed <= '0'; transfer_is_read <= '0'; wait for 1 ns;
if we_transmit /= '0' or we_acknowledge /= '0' then
report "an unaddressed slave must neither transmit nor acknowledge"
severity error; errs := errs + 1; end if;
transfer_is_read <= '1'; wait for 1 ns;
if we_transmit /= '0' or we_acknowledge /= '0' then
report "an unaddressed slave must stay silent on a read too" severity error;
errs := errs + 1; end if;
if errs = 0 then
report "i2c_ack_role_resolver self-check complete: all 16 combinations match an "
& "independent model; transmit and acknowledge never coincide" severity note;
else
report "i2c_ack_role_resolver self-check FAILED" severity error;
end if;
test_done <= '1';
wait;
end process;
end architecture;5a. The One Decision Worth Explaining
The design computes an intermediate called slave_is_receiver and derives both outputs from it:
slave_is_receiver = byte_is_address ? 1 : !transfer_is_read
address byte -> 1, ALWAYS. The master addresses; the slaves listen. The
direction bit describes the DATA, not this byte.
data, write -> 1. Data flows master -> slave.
data, read -> 0. Data flows slave -> master.
then, because the receiver acknowledges and the other one transmits:
we_transmit = is_master ? slave_is_receiver : (!slave_is_receiver && addressed)
we_acknowledge = is_master ? !slave_is_receiver : ( slave_is_receiver && addressed)
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
exact complements -- which is what guarantees no
device is ever both, for any input combination
The `addressed` term appears only in the slave branch, because an unaddressed
slave does nothing at all: it neither transmits nor answers. A master has no
equivalent term because a master is always a participant in its own transfer.Naming the receiver first and deriving the roles from it is what makes the three lines reviewable against §1's sentence. A version that wrote out the four cases as boolean expressions would be the same logic and would be checkable only by truth table — and the truth table is exactly what a reader would then have to reconstruct to believe it.
5b. Verified Execution and Cross-Language Parity
| language | simulator | result | completes at |
|---|---|---|---|
| SystemVerilog | Icarus Verilog, -g2012 | PASS | 23 ns |
| Verilog-2005 | Icarus Verilog, -g2005 | PASS | 23 ns |
| VHDL | nvc 1.23.0 | PASS | 23 ns |
| SystemVerilog | Verilog-2005 | VHDL | |
|---|---|---|---|
| implementation | three assigns | three assigns | three conditional signal assignments |
| reference model | two returning functions | two returning functions | two returning functions |
| exhaustive loop | for (int v = 0; v < 16; v++) | for (v = 0; ...) | for v in 0 to 15 loop |
The reference model is split into two functions returning one bit each rather than one function with output arguments, because Verilog functions cannot have outputs and Icarus does not accept SystemVerilog's output arguments either. The split is a portability requirement that happens to read better.
6. Verifying a Truth Table Exhaustively
Four one-bit inputs is sixteen combinations. The suite walks all of them, and the design of the reference model is the part that matters.
The model is written from the specification's English, not from the design's algebra. The RTL computes slave_is_receiver and derives two complements. The model asks "does the master receive this byte?" and then says the transmitter is whoever does not receive and the acknowledger is whoever does. Same table, two different routes.
That distinction is not cosmetic. If the model were written as the same boolean expressions, it would agree with the design on every input including a shared misunderstanding about who receives an address byte — which is precisely the error §7's first mutation injects. Writing the model as the sentence rather than as the algebra is what gives the sweep any power at all.
The invariant is checked separately from the table. On every one of the sixteen combinations the suite asserts that we_transmit and we_acknowledge are not both set. That is not implied by matching the model — a model with the same fault would also match — so it is a genuinely independent property, and it is the one that catches a design where the two outputs have drifted to the same expression.
Six named rows are checked explicitly on top of the sweep: the address byte in both directions, a write data byte from the slave's side, a read data byte from both sides, and an unaddressed slave in both directions. The sweep already covers them; naming them means a failure reports which rule broke rather than which of sixteen numbered combinations.
7. Mutation Testing
Four faults injected into the verified RTL. All four caught.
| mutation | what it breaks | result |
|---|---|---|
| the address byte's acknowledge depends on R/W | merges the address byte with the data bytes | FAIL — slave, addressed, address byte, read |
we_transmit and we_acknowledge share one expression | a device answers its own byte | FAIL — the invariant, and the table |
| an unaddressed slave still participates | every slave on the bus answers every byte | FAIL — unaddressed slave, write |
| master and slave transmit roles swapped | data flows the wrong way | FAIL — slave, addressed, write |
The first is the one this chapter exists to prevent, and it is worth seeing exactly where it fails.
Removing the byte_is_address term makes slave_is_receiver = !transfer_is_read — which is correct for every data byte and wrong only for the address byte of a read. So the mutant works perfectly for all writes, and for the address phase of a read it concludes that the master receives the address byte and should therefore acknowledge it.
The consequence on a real bus is that nobody acknowledges the address of a read: the slave thinks it is the transmitter of that byte and stays out of the ninth slot, and the master thinks it is the receiver and pulls the slot low — so the master forges an acknowledge to itself, exactly as in Chapter 7.1 §11, and then reads garbage from a slave that never became a transmitter.
It is caught by the sweep at the combination is_master=0, addressed=1, byte_is_address=1, transfer_is_read=1. A suite that only tested writes would miss it entirely — the same lesson as Chapter 6.2 §12, where a write-only suite could not reach the read path's state at all.
8. Verification Connection — Role Is Configuration, Not Code
An agent that models a device on this bus needs to know which side it is, and the useful property is that the acknowledge policy is derivable rather than configurable.
// A reusable agent should not have separate master and slave acknowledge logic.
// The rule is symmetric: the receiver answers. So the SAME function serves both
// agents, with is_master as configuration -- and a disagreement between the two
// agents about who should answer becomes impossible by construction.
typedef enum { BYTE_ADDRESS, BYTE_DATA } i2c_byte_kind_e;
function bit i2c_agent::should_i_acknowledge(i2c_byte_kind_e kind, bit transfer_is_read);
bit slave_receives;
// The address byte is always master -> slave, whatever the direction bit says.
slave_receives = (kind == BYTE_ADDRESS) ? 1'b1 : !transfer_is_read;
if (cfg.is_master) return !slave_receives;
else return slave_receives && this.addressed;
endfunction
// The invariant, as an assertion rather than as a comment. It is the one property
// that no amount of role confusion can satisfy.
property never_transmitter_and_acknowledger;
@(posedge clk) disable iff (!rst_n)
!(we_transmit && we_acknowledge);
endproperty
assert property (never_transmitter_and_acknowledger)
else $error("this device is both transmitting and acknowledging the same byte");Two observations.
One function, two agents. The temptation is to write a master agent that acknowledges reads and a slave agent that acknowledges writes, which duplicates the rule and lets the two copies disagree. Deriving both from slave_receives means the rule exists once; if it is wrong, both agents are wrong in the same way and the mismatch shows up against the DUT rather than as an internal inconsistency.
The invariant is the assertion worth having. "Never both" is a property of the protocol, not of a particular device, and it holds for every byte of every test. A directed test can confirm the four table rows; the assertion confirms that no fifth case was ever invented.
9. FPGA and ASIC Implications
The resolver is combinational and free, and it must be fed the right context. Three gates. The engineering content is that it needs byte_is_address, which means something upstream has to count bytes within a transfer — the frame sequencing of Chapter 7.5. A design that tries to resolve roles without that context is the §7 mutation.
The handover is where a slave needs its data ready. §4's role swap gives the slave one SCL low phase to stop acknowledging and start transmitting. A slave that has to fetch the byte from a register file, or worse from a sensor conversion, will not make it — and its only legal recourse is to stretch the clock. Designing the fetch to start at the address acknowledge rather than at the handover is what avoids needing to.
On a master, the acknowledge path and the transmit path are both needed, and they are used in different phases. A master-only controller still needs the ability to pull SDA low for an acknowledge, because reads require it. An implementation that treated the master's SDA as output-only would work for every write and fail for every read — and the failure would be that the slave, waiting for an acknowledge that never comes, stops transmitting after one byte.
addressed gating is what keeps an unaddressed slave silent. It costs one AND gate per output and it is the difference between a bus with one responder and a bus where every device answers every byte. §7's third mutation is that gate removed.
10. Debugging — The Read That Returned One Good Byte and Then Stopped
A sensor read that worked for the first byte and returned 0xFF forever after
Pitfall — a master that transmits but never acknowledges
// A small bit-banged master, written write-first because writes were all that was
// needed at the time. Its SDA handling is output-only:
//
// static void sda_out(int level) {
// if (level) gpio_set_input(SDA); // release -- open-drain high
// else gpio_set_output_low(SDA);
// }
//
// uint8_t i2c_read_byte(void) {
// uint8_t v = 0;
// for (int i = 0; i < 8; i++) {
// scl_high(); v = (v << 1) | gpio_read(SDA); scl_low();
// }
// scl_high(); // the ninth clock
// scl_low(); // ... and that is all
// return v;
// }
//
// The ninth clock is generated -- the loop is even correct that a byte is nine
// pulses. What the function never does is DRIVE the ninth slot. It clocks it and
// reads nothing into it.Writes are flawless. Reads return one plausible byte and then 0xFF for every subsequent byte, on every device, forever.
0xFF is the tell that nobody recognises at first, because it looks like a plausible register value and like a classic "floating bus" reading -- so the investigation goes to pull-ups, to capacitance, and to whether the sensor is actually producing data.
The first byte being CORRECT is what makes it so confusing. A pull-up problem would not deliver one good byte. A dead sensor would not deliver one good byte. A wiring fault would not deliver one good byte. Everything that explains 0xFF fails to explain the byte before it.
A capture shows the slave driving the first data byte correctly, then the ninth clock pulse with SDA high, and then the slave not driving anything at all.
On a read, the MASTER acknowledges -- and this master never did.
Section 3's table has exactly one row where the master acknowledges: a data byte during a read. This master was built for writes, where the SLAVE always acknowledges, so the ninth slot had never needed to be driven and the code never learned to.
What the slave sees is a NACK. Section 7.2's five conditions include condition 5 -- a master-receiver signalling the end of the transfer -- so the slave draws the only conclusion available to it: the master wants no more bytes. It stops transmitting and releases SDA, correctly and permanently.
After that the master keeps clocking, nobody is driving, and the pull-up delivers 0xFF for every subsequent byte. The 0xFF is not corrupted data; it is an idle bus being read. And the first byte was correct because the slave had not yet been told to stop -- the handover from section 4 had completed properly and the slave transmitted exactly one byte before the master's NACK ended it.
Note how completely the write-first mental model explains the bug. "The slave acknowledges" is true for the address byte and true for every write data byte -- which is every byte this master had ever transferred. The rule only breaks on the one row the code had never exercised.
// Drive the ninth slot when receiving. Acknowledge every byte except the last,
// which is Chapter 7.4's sequencer:
//
// uint8_t i2c_read_byte(int send_ack) {
// uint8_t v = 0;
// sda_out(1); // release: the SLAVE drives the data
// for (int i = 0; i < 8; i++) {
// scl_high(); v = (v << 1) | gpio_read(SDA); scl_low();
// }
// sda_out(send_ack ? 0 : 1); // ACK is a LOW; NACK is a release
// scl_high(); scl_low(); // the ninth pulse
// sda_out(1); // release before the next byte
// return v;
// }
//
// The lessons, in order of how far they generalise.
//
// 1. "THE SLAVE ACKNOWLEDGES" IS TRUE FOR THREE OF THE FOUR ROWS. That is what
// makes it such a durable misconception -- it is right often enough to feel
// like a rule. Section 3's table has one row where the master answers, and it
// is the row a write-only implementation never reaches.
//
// 2. A MASTER'S SDA IS BIDIRECTIONAL, INCLUDING ITS DRIVE PATH. A controller that
// can only read SDA cannot perform a read at all. This is a structural gap,
// not a missing feature -- and it is invisible until the first read.
//
// 3. 0xFF AFTER ONE GOOD BYTE MEANS THE SLAVE WAS TOLD TO STOP. An idle bus reads
// as all ones, so a stream of 0xFF is usually "nobody is driving" rather than
// "the data is wrong". Ask what told the transmitter to stop; on a read, the
// answer is almost always a missing or mistimed acknowledge from the master.
//
// 4. TEST BOTH DIRECTIONS BEFORE BELIEVING AN ACKNOWLEDGE PATH WORKS. Section 7's
// first mutation makes the same point from the RTL side: a fault in the address
// byte's acknowledge rule is correct for every write and wrong for every read,
// so a write-only suite reports a clean pass.
//
// The design habit: write down section 3's four rows before implementing anything
// that touches the ninth slot, and check off which of the four your code has
// actually been run against. Three of four passing is the normal state of a
// write-first implementation, and it feels identical to four of four.11. Common Misconceptions
"The slave acknowledges." True for three of §3's four rows and false for the fourth — a data byte on a read is acknowledged by the master. That is what makes the misconception so durable.
"The R/W bit tells you who acknowledges the address byte." It does not. The address byte is always master-to-slave, so the slave always acknowledges it — the direction bit describes the data bytes that follow.
"The master only transmits, and the slave only receives." Both devices transmit and receive, and they swap at the acknowledge following the address byte of a read. A master whose SDA is output-only cannot perform a read at all.
"A device might need to both transmit and acknowledge a byte." Never. The two are exact complements for every input combination — §6 asserts it as an invariant and §7 injects a fault that breaks it.
"An unaddressed slave can ignore the acknowledge logic." It has to actively stay out of the slot. Remove the addressed gating and every slave on the bus answers every byte, which the wired-AND makes indistinguishable from one correct responder.
"The role swap is a protocol feature that needs handling." It is one instant — the acknowledge after a read's address byte — and both devices change direction in the SCL low phase that follows. What needs handling is having the first data byte ready, and the legal recourse if it is not is clock stretching.
"0xFF from a read means corrupted data." It usually means nobody is driving. On a read that most often means the master failed to acknowledge, so the slave concluded the transfer was over and stopped.
12. Reason It Through
A master reads three bytes from a sensor. How many acknowledges occur, and who produces each?
Four. The slave acknowledges the address byte — that byte is master-to-slave regardless of the read direction. Then the master acknowledges the first and second data bytes, and NACKs the third to end the transfer. So one slave acknowledge and two master acknowledges, plus one master NACK.
Why can a resolver not compute who acknowledges from the direction bit alone?
Because the direction bit describes the data bytes, not the byte that carries it. The address byte is always received by the slave, so a resolver without a byte_is_address input gets that byte wrong on every read — correct for all writes, which is why the fault survives a write-only test suite.
A master's SDA pin is configured as an output only. Which transfers work?
All writes. No reads — because a read requires the master to pull SDA low for the acknowledge of each data byte it keeps, and to release it so the slave can drive the data. §10 is that failure: the first byte arrives, the missing acknowledge reads as a NACK, the slave correctly stops, and every subsequent byte is an idle bus read as 0xFF.
Why can no device be both the transmitter and the acknowledger of one byte?
Because it would be answering itself, and the wired-AND would make that indistinguishable from a genuine answer — so the transmitter would receive confirmation it manufactured. The two roles are exact complements of slave_is_receiver, which makes the overlap structurally impossible rather than merely avoided.
A read returns one correct byte and then 0xFF forever. What single mechanism explains both halves of that, and why is the first byte the important clue?
The master is not acknowledging. The first byte is correct because the handover completed and the slave transmitted before it had been told anything; the missing acknowledge is then read by the slave as condition 5 — a master-receiver ending the transfer — so it stops driving, and subsequent clocks read an idle bus. The first good byte is the clue precisely because every electrical explanation of 0xFF fails to produce it.
Your acknowledge logic passes every write test. What have you not established?
That the address byte's rule is right on a read, and that the master can acknowledge at all. Those are the two things only a read exercises, and they are exactly where §7's first mutation and §10's bug live. Three of §3's four rows passing feels identical to four of four.
13. Understanding Check
14. Summary
The receiver of a byte acknowledges it. One sentence, and the rest of the chapter is applying it.
"The slave acknowledges" is right three times out of four, which is what makes it such a durable misconception. On a data byte during a read, the master answers.
The direction bit does not describe the byte that carries it. The address byte is always master-to-slave, so the slave always acknowledges it — and a resolver without that context is correct for every write and wrong for every read.
Transmit and acknowledge are exact complements. No device is ever both for one byte, and asserting that invariant catches faults a truth-table comparison cannot.
The roles swap once, at the acknowledge following a read's address byte, and both devices turn around in the SCL low phase that follows. A slave that cannot have its first byte ready by then must stretch the clock.
An unaddressed slave must actively stay silent, which is one AND gate per output and the difference between one responder and all of them.
A master's SDA must be bidirectional including its drive path, or reads are structurally impossible — and the failure is one good byte followed by an idle bus.
Write-only verification cannot reach the rule that matters. Both §7's first mutation and §10's production bug are correct for every write.
15. What Comes Next
The one row of §3's table where the master acknowledges turns out to carry a responsibility the other three do not. If the master decides each acknowledge on a read, then the master also decides when the slave should stop sending — and the mechanism it uses is to withhold one.
Chapter 7.4 is that mechanism: why a master deliberately NACKs the final byte of every read, why that is the fifth NACK condition rather than an error, and what breaks in a slave when a master forgets.
Browse the full path on the I²C tutorials index. For the slot whose owner this chapter computes, see The ACK/NACK Cycle; for the byte the address rule applies to, The Address Byte.
Continue learning
Related tutorials
- Related topic
Reserved Addresses and the I²C Address Map
Sixteen of the 128 seven-bit addresses are spoken for, two carry outright prohibitions on responding, and one address means two opposite things depending on a single direction bit. Lay out the map, decode it in hardware, and verify it exhaustively against an independent model.
- Related topic
The ACK/NACK Cycle — What the Ninth Clock Proves
An acknowledge is one device pulling SDA low for one clock pulse. A not-acknowledge is nobody doing anything. That asymmetry decides how the bus behaves when a device is absent, and it is why an ACK proves far less than engineers assume.
- Related topic
The START Condition
START is SDA falling while SCL is high, it is generated only by the controller, and it makes the bus busy. Derive what every device must do in response, then build a detector in three languages and find out why its two guard terms and its reset value are all load-bearing.
- Related topic
The STOP Condition and Releasing the Bus
STOP is SDA rising while SCL is high — the mirror edge of START, with obligations that are not mirrored at all. Releasing the bus is not the same as making it available, and the interval between the two is where a whole class of intermittent failure lives.
