I²C · Module 15
Software Reset and Device ID
Two optional reserved-address services and what optional costs a design that wants to rely on them. Works through the seven-step Device ID read, its 12/9/3 bit packing across three bytes, and the two Remarks that name exactly which events destroy it.
Chapter 15.1 established that a bus feature can be optional, and that optional has a precise protocol meaning: not implemented means not acknowledged.
This chapter takes the two reserved-address services a design might actually want to rely on, and the honest answer for both is the same:
You cannot rely on either of them. Both are optional, neither is discoverable, and the one that exists to identify a device is itself only present in devices that chose to implement it.
That sounds like a reason to skip the chapter. It is the opposite: the Device ID read is the most intricate exchange in the base specification, it is the first transaction in this curriculum whose correctness depends on atomicity across a repeated START, and the specification spends two separate Remarks telling you exactly which events destroy it.
1. Software Reset, Already Covered
Software reset needs one paragraph here because Chapter 15.1 §4 already has it.
There is no software-reset address, no dedicated command, and no separate framing. Software reset is general call 06h, and §3.1.14 exists to name it and to say it is optional.
What the separate heading adds is worth extracting, because it changes how a system should use it:
| question | answer |
|---|---|
| Is there a dedicated address? | No. General call 0000 0000, second byte 06h. |
| Is it mandatory? | No. "not all devices respond to this command" |
| Can a master discover whether a device supports it? | No. See §2. |
| Does the acknowledge confirm the reset happened? | No — Chapter 15.1 §2. One ACK masks every refusal. |
| What else does it do besides reset? | Takes in the programmable part of the address, same as 04h. |
So a master issuing a software reset learns nothing about whether it worked, on a bus where some devices may not implement it and none will say so. That is the pattern this whole chapter is about, and the Device ID does not escape it.
2. The Discoverability Problem
Before the mechanism, the thing that makes both features awkward.
The specification provides no way to ask a device what it supports. There is no capability register, no feature bitmap, no negotiation. A master confronting an unknown device can:
- try the general call reset and learn nothing, because of the acknowledge asymmetry;
- try the Device ID read and learn that it is either unsupported or that the device is absent, with no way to separate the two;
- read the datasheet.
The third is the real answer, and it is worth saying plainly rather than implying that the bus is more self-describing than it is. I²C identifies devices by address, which is assigned by the board designer, and the Device ID is a way to confirm an identity you already expected — not a way to enumerate a bus.
The Device ID answers "is the thing at address 0x20 the part I think it is?" It does not answer "what is at address 0x20?", because a device that does not implement the field is silent in exactly the way an empty address is.
That distinction is why §8's responder takes supports_device_id as an input and why test 11 exists: an unsupported Device ID read must look like nothing at all.
3. The Device ID Field
Twelve plus nine plus three is twenty-four, which is three bytes — and that is where the awkwardness starts, because none of the three fields is byte-aligned except the first eight bits of the first.
Twelve bits of manufacturer is a deliberate choice. It is not eight, which would have been convenient, and it is not sixteen. Twelve gives 4096 manufacturers, and Table 4 shows how thinly that space is used:
Fourteen entries, of which four are NXP. That is the honest scale of Device ID adoption: a registry with a dozen participants in a bus used in billions of devices. It is a useful feature where it exists and it is not something a design can assume.
Nine bits of part identification gives 512 parts per manufacturer, and three bits of die revision gives eight. The revision field is the one most likely to matter in practice — it is how a driver distinguishes a silicon erratum from a working part without a different address or a different package.
4. The Seven Steps
Two addressing phases to identify one device. That is the structural oddity, and the reason for it is a counting problem: an I²C address byte has room for one address, and this transaction needs two — the reserved one that says "a Device ID read is happening" and the target's own, which says which device is being asked.
No single address byte can carry both, so the specification spends a write phase on the first and a read phase on the second, with the target's identity held in the slave across the boundary.
Step 3 is the part that surprises people. The master sends an address in a data slot — an 8-bit byte whose upper seven bits are a slave address and whose bottom bit is meaningless. And "only one device must acknowledge this byte", which means every device on the bus has to compare it against its own address and only the match answers. That is the address decoder of Chapter 6.5 being used on a byte that is not in an address position.
5. The Two Remarks
The specification interrupts its own numbered list twice, and both interruptions are about state.
Read that as two separate statements, because they fail differently.
A STOP is fatal. Not "inadvisable" — the read cannot be performed. So the two phases of a Device ID read are not two transactions; they are one transaction with a repeated START in the middle, and the distinction between a repeated START and a STOP-then-START is exactly the distinction Chapter 10.2 was about. Here it is load-bearing rather than merely efficient.
A Re-START followed by someone else is also fatal. The Re-START itself is legal and necessary; what makes it fatal is addressing a different device after it. So the slave is not simply waiting for "any repeated START" — it is waiting for a repeated START followed by the Device ID read address, and anything else releases it.
The Device ID read is the first transaction in this curriculum whose correctness depends on a slave holding state across a framing event. And the specification names both events that clear it.
Which has a practical consequence for software. On an operating system where the I²C driver exposes transactions rather than bus primitives, a Device ID read is only possible if the driver supports a combined transfer — one call that emits a write phase, a repeated START, and a read phase without releasing the bus. A driver that implements "write" and "read" as separate bus transactions cannot perform a Device ID read at all, and the failure looks like the device not supporting the feature.
So the responder is cyclic, not terminating. Three bytes is the length of the identity, not the length of the transfer. A master that acknowledges the third byte gets the first byte again, indefinitely, and the only thing that ends the read is the master's NACK.
That is consistent with Chapter 7.4: in a read, the master decides when to stop, and it says so by not acknowledging. The Device ID follows the same rule; what is unusual is that the data source wraps instead of running out.
6. The Bit Packing
Step 6 describes the packing in prose. Written out:
| byte | bits 7..4 | bits 3..0 |
|---|---|---|
| 0 | manufacturer[11:8] | manufacturer[7:4] |
| 1 | manufacturer[3:0] | part[8:5] |
| 2 | part[4:0] (bits 7..3) | revision[2:0] (bits 2..0) |
Or as three expressions:
byte 0 = manufacturer[11:4]
byte 1 = { manufacturer[3:0], part[8:5] }
byte 2 = { part[4:0], revision[2:0] }
check: 8 + 4 = 12 manufacturer bits
4 + 5 = 9 part identification bits
3 die revision bits
---
24 bits = 3 bytesTwo of the three fields straddle a byte boundary, and that is the whole difficulty. A reader that assumes byte alignment gets:
- the manufacturer as the first byte alone — eight bits of a twelve-bit field, silently truncated, and for every manufacturer in Table 4 the top eight bits are
0000 0000, so the value looks plausible and is wrong; - the part ID from the second byte — four bits of manufacturer mixed with four bits of part, producing a number that belongs to neither field.
Every off-by-one in this packing produces a plausible-looking wrong answer rather than an obvious failure, and for the manufacturer field in particular the truncated value is a valid Table 4 entry.
That is why §8's testbench reconstructs the 24 bits from the three received bytes and compares the three fields, not the three bytes. Mutations X5, X6 and X7 in §9 are three different misalignments, and it is the field-level check that catches all of them.
7. The Exchange, Drawn
Two addressing phases for one device, joined by a repeated START that a STOP would have broken
10 cyclesThe state row is the point. The slave's selection is established in interval 2 and must still be true in interval 5 — across a framing event — and the specification names the two things that would have cleared it.
8. The Device ID Responder in Three Languages
The design is the slave side: it recognises the reserved address, decides whether it is the device being asked, holds that decision across the repeated START, emits the three bytes with the packing of §6, wraps on an acknowledge, and resets on a not-acknowledge.
// -----------------------------------------------------------------------------
// i2c_device_id_responder.sv
// Slave-side Device ID responder (UM10204 3.1.17, Table 3, Table 4).
//
// The Device ID read is the most intricate exchange in the base specification,
// and the reason is that it needs TWO addressing phases to identify ONE device:
// the reserved Device ID address says "a device ID read is happening", and a
// second byte says WHICH device is being asked. Nothing in a single I2C address
// byte can carry both.
//
// The full procedure, from 3.1.17:
//
// 1. START
// 2. master sends 1111 1000 -- the reserved Device ID address, R/W = 0
// 3. master sends the SLAVE ADDRESS of the device it wants, LSB a don't-care.
// "Only one device must acknowledge this byte."
// 4. master sends a Re-START -- and a STOP here KILLS the sequence
// 5. master sends 1111 1001 -- the same reserved address, R/W = 1
// 6. three bytes come back:
// byte 0 = manufacturer[11:4]
// byte 1 = { manufacturer[3:0], part[8:5] }
// byte 2 = { part[4:0], revision[2:0] }
// 7. the master NACKs the last byte, which resets the slave state machine
//
// Four things about that make it easy to get wrong:
//
// THE STATE IS HELD ACROSS A REPEATED START. Between step 3 and step 5 the slave
// must remember that it was the one selected. That is the atomicity property of
// Chapter 10.3, load-bearing: the specification says explicitly that a STOP
// followed by a START "resets the slave state machine and the Device ID Read
// cannot be performed", and so does a Re-START that addresses a different device.
// So this block arms on a repeated START and disarms on a STOP, and the
// difference between those two events is the whole mechanism.
//
// THE LSB OF THE TARGET ADDRESS IS A DON'T-CARE. Step 3 sends a slave address in
// an 8-bit slot, and the direction bit position carries no meaning. Comparing all
// eight bits fails half the time, for reasons that look like an addressing bug.
//
// THE PACKING IS NOT BYTE-ALIGNED. Twelve, nine and three bits into three bytes
// means two of the three fields straddle a byte boundary. Every off-by-one here
// produces a plausible-looking but wrong manufacturer ID.
//
// ACK AFTER THE THIRD BYTE ROLLS BACK. "If the master continues to ACK the bytes
// after the third byte, the slave rolls back to the first byte and keeps sending
// the Device ID sequence until a NACK has been detected." So the responder is
// cyclic, not terminating, and only the master ends it.
// -----------------------------------------------------------------------------
module i2c_device_id_responder #(
// The device's own identity, hard-wired. Defaults describe an NXP part:
// manufacturer 0 is "NXP Semiconductors" in Table 4.
parameter [11:0] MANUFACTURER = 12'h000,
parameter [8:0] PART_ID = 9'h0A5,
parameter [2:0] DIE_REVISION = 3'h3,
parameter [6:0] MY_ADDR = 7'h20,
parameter int CNT_W = 8
) (
input logic clk,
input logic rst_n,
// ---- byte-level bus interface ------------------------------------------
input logic start_seen, // a START condition
input logic restart_seen, // a REPEATED START condition
input logic stop_seen, // a STOP condition
input logic byte_valid, // byte_in is a complete received byte
input logic [7:0] byte_in,
input logic read_byte_done, // the master consumed a transmitted byte
input logic master_acked, // ...and acknowledged it (1) or not (0)
// ---- configuration -----------------------------------------------------
input logic supports_device_id, // the field is OPTIONAL (3.1.17)
// ---- outputs -----------------------------------------------------------
output logic ack, // pull SDA low in the ninth bit
output logic [7:0] tx_byte, // the byte to transmit
output logic tx_valid, // tx_byte is meaningful
output logic selected, // this device is the one being identified
output logic armed, // step 4 completed: a read may follow
output logic [1:0] byte_index, // which of the three bytes is next
output logic rolled_back, // the master ACKed past byte 3
output logic killed_by_stop, // a STOP broke the sequence
output logic [2:0] state,
output logic [CNT_W-1:0] completed_reads,
output logic [CNT_W-1:0] rollback_count
);
// Table 3: the reserved Device ID address is 1111 1XX with R/W. The procedure
// uses 1111 100 as the address field, so the two bytes on the wire are:
localparam [7:0] DEVID_WRITE = 8'b1111_1000; // step 2
localparam [7:0] DEVID_READ = 8'b1111_1001; // step 5
localparam [2:0] S_IDLE = 3'd0, // nothing in progress
S_TARGET = 3'd1, // 1111 1000 accepted; awaiting the target address
S_WAIT_SR = 3'd2, // we are the target; awaiting the Re-START
S_EXPECT = 3'd3, // Re-START seen; awaiting 1111 1001
S_SEND = 3'd4; // transmitting the three bytes
// The three bytes, packed exactly as step 6 describes. Written as one function
// so the packing appears once and can be read against the specification.
function [7:0] id_byte (input [1:0] idx);
case (idx)
2'd0: id_byte = MANUFACTURER[11:4];
2'd1: id_byte = {MANUFACTURER[3:0], PART_ID[8:5]};
default: id_byte = {PART_ID[4:0], DIE_REVISION};
endcase
endfunction
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE;
ack <= 1'b0;
tx_byte <= 8'h00;
tx_valid <= 1'b0;
selected <= 1'b0;
armed <= 1'b0;
byte_index <= 2'd0;
rolled_back <= 1'b0;
killed_by_stop <= 1'b0;
completed_reads <= {CNT_W{1'b0}};
rollback_count <= {CNT_W{1'b0}};
end else begin
ack <= 1'b0;
// -----------------------------------------------------------------
// A STOP always destroys the sequence. This is the single most
// important line in the block: 3.1.17 states that a STOP followed by a
// START "resets the slave state machine and the Device ID Read cannot
// be performed", so the state must not survive one.
// -----------------------------------------------------------------
if (stop_seen) begin
if (state != S_IDLE && state != S_SEND) killed_by_stop <= 1'b1;
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
tx_valid <= 1'b0;
byte_index <= 2'd0;
// A plain START (not a repeated one) is equally fatal mid-sequence:
// it can only have followed a STOP.
end else if (start_seen) begin
if (state == S_WAIT_SR || state == S_EXPECT) killed_by_stop <= 1'b1;
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
tx_valid <= 1'b0;
byte_index <= 2'd0;
// -----------------------------------------------------------------
// Step 4. A repeated START is the ONLY event that carries the sequence
// forward. If we were selected, we are now armed for the read.
// -----------------------------------------------------------------
end else if (restart_seen) begin
if (state == S_WAIT_SR && selected) begin
armed <= 1'b1;
state <= S_EXPECT;
end else begin
// A repeated START anywhere else abandons the sequence, which is
// the "Re-START condition followed by an access to another slave
// device" case.
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
tx_valid <= 1'b0;
end
byte_index <= 2'd0;
end else if (byte_valid) begin
case (state)
// Step 2: the reserved Device ID address with R/W = 0. A device
// that does not implement the optional field must not answer.
S_IDLE: begin
if ((byte_in == DEVID_WRITE) && supports_device_id) begin
ack <= 1'b1;
state <= S_TARGET;
end
end
// Step 3: the target's slave address. The LSB is a DON'T-CARE, so
// only bits 7:1 are compared. "Only one device must acknowledge."
S_TARGET: begin
if (byte_in[7:1] == MY_ADDR) begin
ack <= 1'b1;
selected <= 1'b1;
state <= S_WAIT_SR;
end else begin
// Not us. Stay silent and drop out -- some other device will
// answer, and two devices answering would break step 3.
selected <= 1'b0;
state <= S_IDLE;
end
end
// Step 5: the reserved Device ID address again, now with R/W = 1.
S_EXPECT: begin
if (byte_in == DEVID_READ && armed) begin
ack <= 1'b1;
tx_byte <= id_byte(2'd0);
tx_valid <= 1'b1;
byte_index <= 2'd0;
state <= S_SEND;
end else begin
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
end
end
default: ; // S_WAIT_SR ignores data bytes; S_SEND is driven below
endcase
// -----------------------------------------------------------------
// Step 6 and 7. One byte leaves per read_byte_done, and what happens
// next is decided entirely by the master's acknowledge.
// -----------------------------------------------------------------
end else if (read_byte_done && state == S_SEND) begin
if (!master_acked) begin
// Step 7: a NACK ends the read and resets the state machine. This
// is the only clean termination, and it is the master's to give.
completed_reads <= completed_reads + 1'b1;
tx_valid <= 1'b0;
selected <= 1'b0;
armed <= 1'b0;
byte_index <= 2'd0;
state <= S_IDLE;
end else if (byte_index == 2'd2) begin
// The roll-back. The master ACKed the third byte, so the sequence
// restarts from the first and keeps going until a NACK arrives.
rolled_back <= 1'b1;
rollback_count <= rollback_count + 1'b1;
byte_index <= 2'd0;
tx_byte <= id_byte(2'd0);
end else begin
byte_index <= byte_index + 1'b1;
tx_byte <= id_byte(byte_index + 1'b1);
end
end
end
end
endmodule `timescale 1ns/1ps
// -----------------------------------------------------------------------------
// i2c_device_id_responder_tb.sv
// Independent oracle for i2c_device_id_responder.
//
// The bench holds the expected identity as three SEPARATE fields and repacks them
// itself, so the packing is checked against an independent computation rather than
// against the design's own function. Getting 12/9/3 into three bytes wrong is the
// likeliest defect in this block and it produces plausible output.
//
// The suite also spends four of its twelve tests on ways the sequence must FAIL:
// a STOP in the middle, a plain START in the middle, a Re-START that addresses
// someone else, and a read attempted without the arming Re-START. Those are the
// cases 3.1.17's two Remarks are about, and a responder that holds its state too
// loosely passes every positive test.
// -----------------------------------------------------------------------------
module i2c_device_id_responder_tb;
// The identity under test, held independently of the DUT's parameters.
localparam [11:0] EXP_MANUF = 12'h2A5;
localparam [8:0] EXP_PART = 9'h13C;
localparam [2:0] EXP_REV = 3'h5;
localparam [6:0] EXP_ADDR = 7'h20;
localparam [2:0] S_IDLE = 3'd0, S_TARGET = 3'd1, S_WAIT_SR = 3'd2,
S_EXPECT = 3'd3, S_SEND = 3'd4;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start_seen = 1'b0;
logic restart_seen = 1'b0;
logic stop_seen = 1'b0;
logic byte_valid = 1'b0;
logic [7:0] byte_in = 8'h00;
logic read_byte_done = 1'b0;
logic master_acked = 1'b0;
logic supports_device_id = 1'b1;
logic ack, tx_valid, selected, armed, rolled_back, killed_by_stop;
logic [7:0] tx_byte;
logic [1:0] byte_index;
logic [2:0] state;
logic [7:0] completed_reads, rollback_count;
integer errors = 0;
integer n;
logic [7:0] got0, got1, got2;
i2c_device_id_responder #(
.MANUFACTURER(EXP_MANUF), .PART_ID(EXP_PART),
.DIE_REVISION(EXP_REV), .MY_ADDR(EXP_ADDR), .CNT_W(8)
) dut (
.clk(clk), .rst_n(rst_n),
.start_seen(start_seen), .restart_seen(restart_seen), .stop_seen(stop_seen),
.byte_valid(byte_valid), .byte_in(byte_in),
.read_byte_done(read_byte_done), .master_acked(master_acked),
.supports_device_id(supports_device_id),
.ack(ack), .tx_byte(tx_byte), .tx_valid(tx_valid),
.selected(selected), .armed(armed), .byte_index(byte_index),
.rolled_back(rolled_back), .killed_by_stop(killed_by_stop), .state(state),
.completed_reads(completed_reads), .rollback_count(rollback_count));
always #5 clk = ~clk;
// The bench's OWN packing of step 6, written from the specification text.
function [7:0] exp_byte (input integer idx);
begin
case (idx)
0: exp_byte = EXP_MANUF[11:4]; // first byte
1: exp_byte = {EXP_MANUF[3:0], EXP_PART[8:5]}; // 4 LSBs + 4 MSBs
default: exp_byte = {EXP_PART[4:0], EXP_REV}; // 5 MSBs + 3 LSBs
endcase
end
endfunction
task step; begin @(posedge clk); @(negedge clk); end endtask
task do_reset_dut;
begin
@(negedge clk);
rst_n = 1'b0; start_seen = 1'b0; restart_seen = 1'b0; stop_seen = 1'b0;
byte_valid = 1'b0; read_byte_done = 1'b0; master_acked = 1'b0;
repeat (3) @(posedge clk);
@(negedge clk); rst_n = 1'b1;
step;
end
endtask
task ev_start; begin @(negedge clk); start_seen = 1'b1; @(posedge clk); @(negedge clk); start_seen = 1'b0; end endtask
task ev_restart; begin @(negedge clk); restart_seen = 1'b1; @(posedge clk); @(negedge clk); restart_seen = 1'b0; end endtask
task ev_stop; begin @(negedge clk); stop_seen = 1'b1; @(posedge clk); @(negedge clk); stop_seen = 1'b0; end endtask
task send (input [7:0] b);
begin
@(negedge clk); byte_in = b; byte_valid = 1'b1;
@(posedge clk); @(negedge clk); byte_valid = 1'b0;
end
endtask
// Consume one transmitted byte, acknowledging or not.
task take (input do_ack);
begin
@(negedge clk); read_byte_done = 1'b1; master_acked = do_ack;
@(posedge clk); @(negedge clk); read_byte_done = 1'b0;
end
endtask
task ck_int (input [200*8:1] what, input integer got, input integer exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0d (0x%0h) expected %0d (0x%0h)", what, got, got, exp, exp);
errors = errors + 1;
end
end
endtask
task ck_bit (input [200*8:1] what, input got, input exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0b expected %0b", what, got, exp);
errors = errors + 1;
end
end
endtask
// Steps 1 to 5, the whole arming sequence, done correctly.
task arm_sequence (input [7:0] target);
begin
ev_start;
send(8'b1111_1000); // step 2
send(target); // step 3
ev_restart; // step 4
send(8'b1111_1001); // step 5
end
endtask
initial begin
$display("=== i2c_device_id_responder: a two-phase read held across a repeated START ===");
// ----------------------------------------------------------------
// T1. The whole procedure, and the three bytes checked against the
// bench's own packing of step 6.
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
$display("T1 the complete seven-step procedure");
ck_bit("T1 selected", selected, 1'b1);
ck_bit("T1 armed", armed, 1'b1);
ck_int("T1 in S_SEND", state, S_SEND);
ck_bit("T1 tx_valid", tx_valid, 1'b1);
got0 = tx_byte; take(1'b1);
got1 = tx_byte; take(1'b1);
got2 = tx_byte; take(1'b0); // step 7: NACK the last byte
ck_int("T1 byte 0 = manufacturer[11:4]", got0, exp_byte(0));
ck_int("T1 byte 1 = manufacturer[3:0]+part[8:5]", got1, exp_byte(1));
ck_int("T1 byte 2 = part[4:0]+revision", got2, exp_byte(2));
ck_int("T1 one completed read", completed_reads, 1);
ck_int("T1 back to idle", state, S_IDLE);
ck_bit("T1 no longer selected", selected, 1'b0);
// ----------------------------------------------------------------
// T2. The 24 bits reconstructed. Repacking the three bytes must give
// back exactly the three fields -- which is a stronger check than
// comparing bytes, because it catches a consistent misalignment.
// ----------------------------------------------------------------
$display("T2 the 24 bits reassemble into 12 + 9 + 3");
ck_int("T2 manufacturer", {got0, got1[7:4]}, EXP_MANUF);
ck_int("T2 part id", {got1[3:0], got2[7:3]}, EXP_PART);
ck_int("T2 die revision", got2[2:0], EXP_REV);
// ----------------------------------------------------------------
// T3. THE DON'T-CARE LSB. Step 3's address byte carries a slave address
// in bits 7:1 and the LSB means nothing. Both spellings must select.
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
ck_bit("T3 selected with LSB 0", selected, 1'b1);
take(1'b0);
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b1});
$display("T3 the target address LSB is a don't-care");
ck_bit("T3 selected with LSB 1", selected, 1'b1);
ck_int("T3 still reached S_SEND", state, S_SEND);
take(1'b0);
// ----------------------------------------------------------------
// T4. Addressed to somebody else: stay silent and drop out. Two devices
// answering step 3 would break "only one device must acknowledge".
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
ck_bit("T4 acknowledged the Device ID address", ack, 1'b1);
send({7'h33, 1'b0}); // somebody else's address
$display("T4 a Device ID read aimed at another device is ignored");
ck_bit("T4 did not acknowledge", ack, 1'b0);
ck_bit("T4 not selected", selected, 1'b0);
step;
ck_int("T4 dropped to idle", state, S_IDLE);
// ----------------------------------------------------------------
// T5. A STOP BETWEEN STEP 3 AND STEP 5 KILLS IT. This is 3.1.17's first
// Remark, and it is the reason the sequence needs a Re-START rather
// than a fresh transaction.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
send({EXP_ADDR, 1'b0});
ck_bit("T5 selected before the STOP", selected, 1'b1);
ev_stop; // fatal
$display("T5 a STOP between the phases destroys the sequence");
ck_bit("T5 killed", killed_by_stop, 1'b1);
ck_bit("T5 no longer selected", selected, 1'b0);
ck_int("T5 idle", state, S_IDLE);
// and a START + read now gets nothing, because the selection is gone
ev_start;
send(8'b1111_1001);
ck_bit("T5 a read after the kill sends nothing", tx_valid, 1'b0);
ck_int("T5 still idle", state, S_IDLE);
// ----------------------------------------------------------------
// T6. A PLAIN START mid-sequence is equally fatal. On a real bus it can
// only have followed a STOP, so treating it as a Re-START would let
// the sequence survive an event the specification says ends it.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
send({EXP_ADDR, 1'b0});
ev_start; // NOT a repeated START
$display("T6 a plain START mid-sequence destroys it too");
ck_bit("T6 killed", killed_by_stop, 1'b1);
ck_bit("T6 not armed", armed, 1'b0);
ck_int("T6 idle", state, S_IDLE);
// ----------------------------------------------------------------
// T7. A Re-START that addresses SOMEONE ELSE abandons the sequence.
// This is 3.1.17's second Remark. The Re-START itself is legal; what
// follows it is what decides.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
send({EXP_ADDR, 1'b0});
ev_restart;
ck_bit("T7 armed by the Re-START", armed, 1'b1);
send(8'h42); // an ordinary slave address, not 1111 1001
$display("T7 a Re-START followed by another device abandons the read");
ck_bit("T7 not sending", tx_valid, 1'b0);
ck_bit("T7 selection dropped", selected, 1'b0);
ck_int("T7 idle", state, S_IDLE);
// ----------------------------------------------------------------
// T8. A read attempted with NO arming sequence at all sends nothing.
// The reserved address alone is not authorisation.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1001); // straight to the read
$display("T8 the read address alone does not authorise a read");
ck_bit("T8 nothing to send", tx_valid, 1'b0);
ck_bit("T8 not armed", armed, 1'b0);
ck_int("T8 idle", state, S_IDLE);
// ----------------------------------------------------------------
// T9. THE ROLL-BACK. ACKing the third byte restarts the sequence from
// the first, and it keeps going until a NACK. Nine bytes taken with
// ACK must therefore be three full cycles of the same three bytes.
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
$display("T9 ACKing past the third byte rolls back to the first");
for (n = 0; n < 9; n = n + 1) begin
ck_int("T9 cyclic byte", tx_byte, exp_byte(n % 3));
take(1'b1);
end
ck_bit("T9 rolled back", rolled_back, 1'b1);
ck_int("T9 three roll-backs", rollback_count, 3);
ck_int("T9 no completed read yet", completed_reads, 0);
ck_int("T9 still sending", state, S_SEND);
// Only the NACK ends it.
take(1'b0);
ck_int("T9 now one completed read", completed_reads, 1);
ck_int("T9 now idle", state, S_IDLE);
// ----------------------------------------------------------------
// T10. A NACK anywhere stops it. "The reading of the Device ID can be
// stopped anytime by sending a NACK."
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
take(1'b0); // NACK the very first byte
$display("T10 a NACK on the first byte ends the read immediately");
ck_int("T10 completed", completed_reads, 1);
ck_int("T10 idle", state, S_IDLE);
ck_bit("T10 not sending", tx_valid, 1'b0);
// ----------------------------------------------------------------
// T11. The field is OPTIONAL. A device that does not implement it must
// not acknowledge the reserved address at all.
// ----------------------------------------------------------------
do_reset_dut;
@(negedge clk); supports_device_id = 1'b0;
ev_start;
send(8'b1111_1000);
$display("T11 a device without the optional field stays silent");
ck_bit("T11 did not acknowledge", ack, 1'b0);
ck_int("T11 stayed idle", state, S_IDLE);
send({EXP_ADDR, 1'b0});
ck_bit("T11 still silent", ack, 1'b0);
ck_bit("T11 never selected", selected, 1'b0);
@(negedge clk); supports_device_id = 1'b1;
// ----------------------------------------------------------------
// T12. Two complete reads back to back, to show the state machine is
// genuinely reusable and that nothing leaks between them.
// ----------------------------------------------------------------
do_reset_dut;
for (n = 0; n < 2; n = n + 1) begin
arm_sequence({EXP_ADDR, 1'b0});
ck_int("T12 byte 0", tx_byte, exp_byte(0)); take(1'b1);
ck_int("T12 byte 1", tx_byte, exp_byte(1)); take(1'b1);
ck_int("T12 byte 2", tx_byte, exp_byte(2)); take(1'b0);
ev_stop;
end
$display("T12 two complete reads, nothing carried between them");
ck_int("T12 two completed reads", completed_reads, 2);
ck_int("T12 no roll-backs", rollback_count, 0);
ck_int("T12 idle", state, S_IDLE);
if (errors == 0)
$display("=== i2c_device_id_responder: ALL CHECKS PASSED ===");
else
$display("=== i2c_device_id_responder: %0d CHECK(S) FAILED ===", errors);
$finish;
end
endmodule // -----------------------------------------------------------------------------
// i2c_device_id_responder.sv
// Slave-side Device ID responder (UM10204 3.1.17, Table 3, Table 4).
//
// The Device ID read is the most intricate exchange in the base specification,
// and the reason is that it needs TWO addressing phases to identify ONE device:
// the reserved Device ID address says "a device ID read is happening", and a
// second byte says WHICH device is being asked. Nothing in a single I2C address
// byte can carry both.
//
// The full procedure, from 3.1.17:
//
// 1. START
// 2. master sends 1111 1000 -- the reserved Device ID address, R/W = 0
// 3. master sends the SLAVE ADDRESS of the device it wants, LSB a don't-care.
// "Only one device must acknowledge this byte."
// 4. master sends a Re-START -- and a STOP here KILLS the sequence
// 5. master sends 1111 1001 -- the same reserved address, R/W = 1
// 6. three bytes come back:
// byte 0 = manufacturer[11:4]
// byte 1 = { manufacturer[3:0], part[8:5] }
// byte 2 = { part[4:0], revision[2:0] }
// 7. the master NACKs the last byte, which resets the slave state machine
//
// Four things about that make it easy to get wrong:
//
// THE STATE IS HELD ACROSS A REPEATED START. Between step 3 and step 5 the slave
// must remember that it was the one selected. That is the atomicity property of
// Chapter 10.3, load-bearing: the specification says explicitly that a STOP
// followed by a START "resets the slave state machine and the Device ID Read
// cannot be performed", and so does a Re-START that addresses a different device.
// So this block arms on a repeated START and disarms on a STOP, and the
// difference between those two events is the whole mechanism.
//
// THE LSB OF THE TARGET ADDRESS IS A DON'T-CARE. Step 3 sends a slave address in
// an 8-bit slot, and the direction bit position carries no meaning. Comparing all
// eight bits fails half the time, for reasons that look like an addressing bug.
//
// THE PACKING IS NOT BYTE-ALIGNED. Twelve, nine and three bits into three bytes
// means two of the three fields straddle a byte boundary. Every off-by-one here
// produces a plausible-looking but wrong manufacturer ID.
//
// ACK AFTER THE THIRD BYTE ROLLS BACK. "If the master continues to ACK the bytes
// after the third byte, the slave rolls back to the first byte and keeps sending
// the Device ID sequence until a NACK has been detected." So the responder is
// cyclic, not terminating, and only the master ends it.
// -----------------------------------------------------------------------------
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
module i2c_device_id_responder #(
// The device's own identity, hard-wired. Defaults describe an NXP part:
// manufacturer 0 is "NXP Semiconductors" in Table 4.
parameter [11:0] MANUFACTURER = 12'h000,
parameter [8:0] PART_ID = 9'h0A5,
parameter [2:0] DIE_REVISION = 3'h3,
parameter [6:0] MY_ADDR = 7'h20,
parameter CNT_W = 8
) (
input wire clk,
input wire rst_n,
// ---- byte-level bus interface ------------------------------------------
input wire start_seen, // a START condition
input wire restart_seen, // a REPEATED START condition
input wire stop_seen, // a STOP condition
input wire byte_valid, // byte_in is a complete received byte
input wire [7:0] byte_in,
input wire read_byte_done, // the master consumed a transmitted byte
input wire master_acked, // ...and acknowledged it (1) or not (0)
// ---- configuration -----------------------------------------------------
input wire supports_device_id, // the field is OPTIONAL (3.1.17)
// ---- outputs -----------------------------------------------------------
output reg ack, // pull SDA low in the ninth bit
output reg [7:0] tx_byte, // the byte to transmit
output reg tx_valid, // tx_byte is meaningful
output reg selected, // this device is the one being identified
output reg armed, // step 4 completed: a read may follow
output reg [1:0] byte_index, // which of the three bytes is next
output reg rolled_back, // the master ACKed past byte 3
output reg killed_by_stop, // a STOP broke the sequence
output reg [2:0] state,
output reg [CNT_W-1:0] completed_reads,
output reg [CNT_W-1:0] rollback_count
);
// Table 3: the reserved Device ID address is 1111 1XX with R/W. The procedure
// uses 1111 100 as the address field, so the two bytes on the wire are:
localparam [7:0] DEVID_WRITE = 8'b1111_1000; // step 2
localparam [7:0] DEVID_READ = 8'b1111_1001; // step 5
localparam [2:0] S_IDLE = 3'd0, // nothing in progress
S_TARGET = 3'd1, // 1111 1000 accepted; awaiting the target address
S_WAIT_SR = 3'd2, // we are the target; awaiting the Re-START
S_EXPECT = 3'd3, // Re-START seen; awaiting 1111 1001
S_SEND = 3'd4; // transmitting the three bytes
// The three bytes, packed exactly as step 6 describes. Written as one function
// so the packing appears once and can be read against the specification.
function [7:0] id_byte (input [1:0] idx);
case (idx)
2'd0: id_byte = MANUFACTURER[11:4];
2'd1: id_byte = {MANUFACTURER[3:0], PART_ID[8:5]};
default: id_byte = {PART_ID[4:0], DIE_REVISION};
endcase
endfunction
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE;
ack <= 1'b0;
tx_byte <= 8'h00;
tx_valid <= 1'b0;
selected <= 1'b0;
armed <= 1'b0;
byte_index <= 2'd0;
rolled_back <= 1'b0;
killed_by_stop <= 1'b0;
completed_reads <= {CNT_W{1'b0}};
rollback_count <= {CNT_W{1'b0}};
end else begin
ack <= 1'b0;
// -----------------------------------------------------------------
// A STOP always destroys the sequence. This is the single most
// important line in the block: 3.1.17 states that a STOP followed by a
// START "resets the slave state machine and the Device ID Read cannot
// be performed", so the state must not survive one.
// -----------------------------------------------------------------
if (stop_seen) begin
if (state != S_IDLE && state != S_SEND) killed_by_stop <= 1'b1;
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
tx_valid <= 1'b0;
byte_index <= 2'd0;
// A plain START (not a repeated one) is equally fatal mid-sequence:
// it can only have followed a STOP.
end else if (start_seen) begin
if (state == S_WAIT_SR || state == S_EXPECT) killed_by_stop <= 1'b1;
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
tx_valid <= 1'b0;
byte_index <= 2'd0;
// -----------------------------------------------------------------
// Step 4. A repeated START is the ONLY event that carries the sequence
// forward. If we were selected, we are now armed for the read.
// -----------------------------------------------------------------
end else if (restart_seen) begin
if (state == S_WAIT_SR && selected) begin
armed <= 1'b1;
state <= S_EXPECT;
end else begin
// A repeated START anywhere else abandons the sequence, which is
// the "Re-START condition followed by an access to another slave
// device" case.
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
tx_valid <= 1'b0;
end
byte_index <= 2'd0;
end else if (byte_valid) begin
case (state)
// Step 2: the reserved Device ID address with R/W = 0. A device
// that does not implement the optional field must not answer.
S_IDLE: begin
if ((byte_in == DEVID_WRITE) && supports_device_id) begin
ack <= 1'b1;
state <= S_TARGET;
end
end
// Step 3: the target's slave address. The LSB is a DON'T-CARE, so
// only bits 7:1 are compared. "Only one device must acknowledge."
S_TARGET: begin
if (byte_in[7:1] == MY_ADDR) begin
ack <= 1'b1;
selected <= 1'b1;
state <= S_WAIT_SR;
end else begin
// Not us. Stay silent and drop out -- some other device will
// answer, and two devices answering would break step 3.
selected <= 1'b0;
state <= S_IDLE;
end
end
// Step 5: the reserved Device ID address again, now with R/W = 1.
S_EXPECT: begin
if (byte_in == DEVID_READ && armed) begin
ack <= 1'b1;
tx_byte <= id_byte(2'd0);
tx_valid <= 1'b1;
byte_index <= 2'd0;
state <= S_SEND;
end else begin
state <= S_IDLE;
selected <= 1'b0;
armed <= 1'b0;
end
end
default: ; // S_WAIT_SR ignores data bytes; S_SEND is driven below
endcase
// -----------------------------------------------------------------
// Step 6 and 7. One byte leaves per read_byte_done, and what happens
// next is decided entirely by the master's acknowledge.
// -----------------------------------------------------------------
end else if (read_byte_done && state == S_SEND) begin
if (!master_acked) begin
// Step 7: a NACK ends the read and resets the state machine. This
// is the only clean termination, and it is the master's to give.
completed_reads <= completed_reads + 1'b1;
tx_valid <= 1'b0;
selected <= 1'b0;
armed <= 1'b0;
byte_index <= 2'd0;
state <= S_IDLE;
end else if (byte_index == 2'd2) begin
// The roll-back. The master ACKed the third byte, so the sequence
// restarts from the first and keeps going until a NACK arrives.
rolled_back <= 1'b1;
rollback_count <= rollback_count + 1'b1;
byte_index <= 2'd0;
tx_byte <= id_byte(2'd0);
end else begin
byte_index <= byte_index + 1'b1;
tx_byte <= id_byte(byte_index + 1'b1);
end
end
end
end
endmodule `timescale 1ns/1ps
// -----------------------------------------------------------------------------
// i2c_device_id_responder_tb.sv
// Independent oracle for i2c_device_id_responder.
//
// The bench holds the expected identity as three SEPARATE fields and repacks them
// itself, so the packing is checked against an independent computation rather than
// against the design's own function. Getting 12/9/3 into three bytes wrong is the
// likeliest defect in this block and it produces plausible output.
//
// The suite also spends four of its twelve tests on ways the sequence must FAIL:
// a STOP in the middle, a plain START in the middle, a Re-START that addresses
// someone else, and a read attempted without the arming Re-START. Those are the
// cases 3.1.17's two Remarks are about, and a responder that holds its state too
// loosely passes every positive test.
// -----------------------------------------------------------------------------
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
module i2c_device_id_responder_tb;
// The identity under test, held independently of the DUT's parameters.
localparam [11:0] EXP_MANUF = 12'h2A5;
localparam [8:0] EXP_PART = 9'h13C;
localparam [2:0] EXP_REV = 3'h5;
localparam [6:0] EXP_ADDR = 7'h20;
localparam [2:0] S_IDLE = 3'd0, S_TARGET = 3'd1, S_WAIT_SR = 3'd2,
S_EXPECT = 3'd3, S_SEND = 3'd4;
reg clk = 1'b0;
reg rst_n = 1'b0;
reg start_seen = 1'b0;
reg restart_seen = 1'b0;
reg stop_seen = 1'b0;
reg byte_valid = 1'b0;
reg [7:0] byte_in = 8'h00;
reg read_byte_done = 1'b0;
reg master_acked = 1'b0;
reg supports_device_id = 1'b1;
wire ack, tx_valid, selected, armed, rolled_back, killed_by_stop;
wire [7:0] tx_byte;
wire [1:0] byte_index;
wire [2:0] state;
wire [7:0] completed_reads, rollback_count;
integer errors = 0;
integer n;
reg [7:0] got0, got1, got2;
i2c_device_id_responder #(
.MANUFACTURER(EXP_MANUF), .PART_ID(EXP_PART),
.DIE_REVISION(EXP_REV), .MY_ADDR(EXP_ADDR), .CNT_W(8)
) dut (
.clk(clk), .rst_n(rst_n),
.start_seen(start_seen), .restart_seen(restart_seen), .stop_seen(stop_seen),
.byte_valid(byte_valid), .byte_in(byte_in),
.read_byte_done(read_byte_done), .master_acked(master_acked),
.supports_device_id(supports_device_id),
.ack(ack), .tx_byte(tx_byte), .tx_valid(tx_valid),
.selected(selected), .armed(armed), .byte_index(byte_index),
.rolled_back(rolled_back), .killed_by_stop(killed_by_stop), .state(state),
.completed_reads(completed_reads), .rollback_count(rollback_count));
always #5 clk = ~clk;
// The bench's OWN packing of step 6, written from the specification text.
function [7:0] exp_byte (input integer idx);
begin
case (idx)
0: exp_byte = EXP_MANUF[11:4]; // first byte
1: exp_byte = {EXP_MANUF[3:0], EXP_PART[8:5]}; // 4 LSBs + 4 MSBs
default: exp_byte = {EXP_PART[4:0], EXP_REV}; // 5 MSBs + 3 LSBs
endcase
end
endfunction
task step; begin @(posedge clk); @(negedge clk); end endtask
task do_reset_dut;
begin
@(negedge clk);
rst_n = 1'b0; start_seen = 1'b0; restart_seen = 1'b0; stop_seen = 1'b0;
byte_valid = 1'b0; read_byte_done = 1'b0; master_acked = 1'b0;
repeat (3) @(posedge clk);
@(negedge clk); rst_n = 1'b1;
step;
end
endtask
task ev_start; begin @(negedge clk); start_seen = 1'b1; @(posedge clk); @(negedge clk); start_seen = 1'b0; end endtask
task ev_restart; begin @(negedge clk); restart_seen = 1'b1; @(posedge clk); @(negedge clk); restart_seen = 1'b0; end endtask
task ev_stop; begin @(negedge clk); stop_seen = 1'b1; @(posedge clk); @(negedge clk); stop_seen = 1'b0; end endtask
task send (input [7:0] b);
begin
@(negedge clk); byte_in = b; byte_valid = 1'b1;
@(posedge clk); @(negedge clk); byte_valid = 1'b0;
end
endtask
// Consume one transmitted byte, acknowledging or not.
task take (input do_ack);
begin
@(negedge clk); read_byte_done = 1'b1; master_acked = do_ack;
@(posedge clk); @(negedge clk); read_byte_done = 1'b0;
end
endtask
task ck_int (input [200*8:1] what, input integer got, input integer exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0d (0x%0h) expected %0d (0x%0h)", what, got, got, exp, exp);
errors = errors + 1;
end
end
endtask
task ck_bit (input [200*8:1] what, input got, input exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0b expected %0b", what, got, exp);
errors = errors + 1;
end
end
endtask
// Steps 1 to 5, the whole arming sequence, done correctly.
task arm_sequence (input [7:0] target);
begin
ev_start;
send(8'b1111_1000); // step 2
send(target); // step 3
ev_restart; // step 4
send(8'b1111_1001); // step 5
end
endtask
initial begin
$display("=== i2c_device_id_responder: a two-phase read held across a repeated START ===");
// ----------------------------------------------------------------
// T1. The whole procedure, and the three bytes checked against the
// bench's own packing of step 6.
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
$display("T1 the complete seven-step procedure");
ck_bit("T1 selected", selected, 1'b1);
ck_bit("T1 armed", armed, 1'b1);
ck_int("T1 in S_SEND", state, S_SEND);
ck_bit("T1 tx_valid", tx_valid, 1'b1);
got0 = tx_byte; take(1'b1);
got1 = tx_byte; take(1'b1);
got2 = tx_byte; take(1'b0); // step 7: NACK the last byte
ck_int("T1 byte 0 = manufacturer[11:4]", got0, exp_byte(0));
ck_int("T1 byte 1 = manufacturer[3:0]+part[8:5]", got1, exp_byte(1));
ck_int("T1 byte 2 = part[4:0]+revision", got2, exp_byte(2));
ck_int("T1 one completed read", completed_reads, 1);
ck_int("T1 back to idle", state, S_IDLE);
ck_bit("T1 no longer selected", selected, 1'b0);
// ----------------------------------------------------------------
// T2. The 24 bits reconstructed. Repacking the three bytes must give
// back exactly the three fields -- which is a stronger check than
// comparing bytes, because it catches a consistent misalignment.
// ----------------------------------------------------------------
$display("T2 the 24 bits reassemble into 12 + 9 + 3");
ck_int("T2 manufacturer", {got0, got1[7:4]}, EXP_MANUF);
ck_int("T2 part id", {got1[3:0], got2[7:3]}, EXP_PART);
ck_int("T2 die revision", got2[2:0], EXP_REV);
// ----------------------------------------------------------------
// T3. THE DON'T-CARE LSB. Step 3's address byte carries a slave address
// in bits 7:1 and the LSB means nothing. Both spellings must select.
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
ck_bit("T3 selected with LSB 0", selected, 1'b1);
take(1'b0);
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b1});
$display("T3 the target address LSB is a don't-care");
ck_bit("T3 selected with LSB 1", selected, 1'b1);
ck_int("T3 still reached S_SEND", state, S_SEND);
take(1'b0);
// ----------------------------------------------------------------
// T4. Addressed to somebody else: stay silent and drop out. Two devices
// answering step 3 would break "only one device must acknowledge".
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
ck_bit("T4 acknowledged the Device ID address", ack, 1'b1);
send({7'h33, 1'b0}); // somebody else's address
$display("T4 a Device ID read aimed at another device is ignored");
ck_bit("T4 did not acknowledge", ack, 1'b0);
ck_bit("T4 not selected", selected, 1'b0);
step;
ck_int("T4 dropped to idle", state, S_IDLE);
// ----------------------------------------------------------------
// T5. A STOP BETWEEN STEP 3 AND STEP 5 KILLS IT. This is 3.1.17's first
// Remark, and it is the reason the sequence needs a Re-START rather
// than a fresh transaction.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
send({EXP_ADDR, 1'b0});
ck_bit("T5 selected before the STOP", selected, 1'b1);
ev_stop; // fatal
$display("T5 a STOP between the phases destroys the sequence");
ck_bit("T5 killed", killed_by_stop, 1'b1);
ck_bit("T5 no longer selected", selected, 1'b0);
ck_int("T5 idle", state, S_IDLE);
// and a START + read now gets nothing, because the selection is gone
ev_start;
send(8'b1111_1001);
ck_bit("T5 a read after the kill sends nothing", tx_valid, 1'b0);
ck_int("T5 still idle", state, S_IDLE);
// ----------------------------------------------------------------
// T6. A PLAIN START mid-sequence is equally fatal. On a real bus it can
// only have followed a STOP, so treating it as a Re-START would let
// the sequence survive an event the specification says ends it.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
send({EXP_ADDR, 1'b0});
ev_start; // NOT a repeated START
$display("T6 a plain START mid-sequence destroys it too");
ck_bit("T6 killed", killed_by_stop, 1'b1);
ck_bit("T6 not armed", armed, 1'b0);
ck_int("T6 idle", state, S_IDLE);
// ----------------------------------------------------------------
// T7. A Re-START that addresses SOMEONE ELSE abandons the sequence.
// This is 3.1.17's second Remark. The Re-START itself is legal; what
// follows it is what decides.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1000);
send({EXP_ADDR, 1'b0});
ev_restart;
ck_bit("T7 armed by the Re-START", armed, 1'b1);
send(8'h42); // an ordinary slave address, not 1111 1001
$display("T7 a Re-START followed by another device abandons the read");
ck_bit("T7 not sending", tx_valid, 1'b0);
ck_bit("T7 selection dropped", selected, 1'b0);
ck_int("T7 idle", state, S_IDLE);
// ----------------------------------------------------------------
// T8. A read attempted with NO arming sequence at all sends nothing.
// The reserved address alone is not authorisation.
// ----------------------------------------------------------------
do_reset_dut;
ev_start;
send(8'b1111_1001); // straight to the read
$display("T8 the read address alone does not authorise a read");
ck_bit("T8 nothing to send", tx_valid, 1'b0);
ck_bit("T8 not armed", armed, 1'b0);
ck_int("T8 idle", state, S_IDLE);
// ----------------------------------------------------------------
// T9. THE ROLL-BACK. ACKing the third byte restarts the sequence from
// the first, and it keeps going until a NACK. Nine bytes taken with
// ACK must therefore be three full cycles of the same three bytes.
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
$display("T9 ACKing past the third byte rolls back to the first");
for (n = 0; n < 9; n = n + 1) begin
ck_int("T9 cyclic byte", tx_byte, exp_byte(n % 3));
take(1'b1);
end
ck_bit("T9 rolled back", rolled_back, 1'b1);
ck_int("T9 three roll-backs", rollback_count, 3);
ck_int("T9 no completed read yet", completed_reads, 0);
ck_int("T9 still sending", state, S_SEND);
// Only the NACK ends it.
take(1'b0);
ck_int("T9 now one completed read", completed_reads, 1);
ck_int("T9 now idle", state, S_IDLE);
// ----------------------------------------------------------------
// T10. A NACK anywhere stops it. "The reading of the Device ID can be
// stopped anytime by sending a NACK."
// ----------------------------------------------------------------
do_reset_dut;
arm_sequence({EXP_ADDR, 1'b0});
take(1'b0); // NACK the very first byte
$display("T10 a NACK on the first byte ends the read immediately");
ck_int("T10 completed", completed_reads, 1);
ck_int("T10 idle", state, S_IDLE);
ck_bit("T10 not sending", tx_valid, 1'b0);
// ----------------------------------------------------------------
// T11. The field is OPTIONAL. A device that does not implement it must
// not acknowledge the reserved address at all.
// ----------------------------------------------------------------
do_reset_dut;
@(negedge clk); supports_device_id = 1'b0;
ev_start;
send(8'b1111_1000);
$display("T11 a device without the optional field stays silent");
ck_bit("T11 did not acknowledge", ack, 1'b0);
ck_int("T11 stayed idle", state, S_IDLE);
send({EXP_ADDR, 1'b0});
ck_bit("T11 still silent", ack, 1'b0);
ck_bit("T11 never selected", selected, 1'b0);
@(negedge clk); supports_device_id = 1'b1;
// ----------------------------------------------------------------
// T12. Two complete reads back to back, to show the state machine is
// genuinely reusable and that nothing leaks between them.
// ----------------------------------------------------------------
do_reset_dut;
for (n = 0; n < 2; n = n + 1) begin
arm_sequence({EXP_ADDR, 1'b0});
ck_int("T12 byte 0", tx_byte, exp_byte(0)); take(1'b1);
ck_int("T12 byte 1", tx_byte, exp_byte(1)); take(1'b1);
ck_int("T12 byte 2", tx_byte, exp_byte(2)); take(1'b0);
ev_stop;
end
$display("T12 two complete reads, nothing carried between them");
ck_int("T12 two completed reads", completed_reads, 2);
ck_int("T12 no roll-backs", rollback_count, 0);
ck_int("T12 idle", state, S_IDLE);
if (errors == 0)
$display("=== i2c_device_id_responder: ALL CHECKS PASSED ===");
else
$display("=== i2c_device_id_responder: %0d CHECK(S) FAILED ===", errors);
$finish;
end
endmodule -- ---------------------------------------------------------------------------
-- i2c_device_id_responder.vhd
-- Slave-side Device ID responder (UM10204 3.1.17, Table 3, Table 4).
-- Behavioural twin of i2c_device_id_responder.sv / .v.
--
-- The Device ID read needs TWO addressing phases to identify ONE device: the
-- reserved Device ID address says "a device ID read is happening", and a second
-- byte says WHICH device is being asked. No single I2C address byte carries both.
--
-- 1. START
-- 2. master sends 1111 1000 -- the reserved Device ID address, R/W = 0
-- 3. master sends the SLAVE ADDRESS of the device it wants, LSB a don't-care
-- 4. master sends a Re-START -- and a STOP here KILLS the sequence
-- 5. master sends 1111 1001 -- the same reserved address, R/W = 1
-- 6. three bytes come back:
-- byte 0 = manufacturer(11 downto 4)
-- byte 1 = manufacturer(3 downto 0) & part(8 downto 5)
-- byte 2 = part(4 downto 0) & revision(2 downto 0)
-- 7. the master NACKs the last byte, which resets the slave state machine
--
-- Four things make it easy to get wrong:
--
-- THE STATE IS HELD ACROSS A REPEATED START. 3.1.17 says a STOP followed by a
-- START "resets the slave state machine and the Device ID Read cannot be
-- performed", and so does a Re-START that addresses a different device. So this
-- block arms on a repeated START and disarms on a STOP.
--
-- THE LSB OF THE TARGET ADDRESS IS A DON'T-CARE. Comparing all eight bits fails
-- half the time, for reasons that look like an addressing bug.
--
-- THE PACKING IS NOT BYTE-ALIGNED. Two of the three fields straddle a byte
-- boundary, and every off-by-one produces a plausible but wrong ID.
--
-- ACK AFTER THE THIRD BYTE ROLLS BACK to the first and keeps going until a NACK.
-- ---------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_device_id_responder is
generic (
-- The device's own identity, hard-wired. Manufacturer 0 is "NXP
-- Semiconductors" in Table 4.
MANUFACTURER : std_logic_vector(11 downto 0) := x"000";
PART_ID : std_logic_vector(8 downto 0) := "010100101";
DIE_REVISION : std_logic_vector(2 downto 0) := "011";
MY_ADDR : std_logic_vector(6 downto 0) := "0100000";
CNT_W : integer := 8
);
port (
clk : in std_logic;
rst_n : in std_logic;
-- byte-level bus interface
start_seen : in std_logic; -- a START condition
restart_seen : in std_logic; -- a REPEATED START condition
stop_seen : in std_logic; -- a STOP condition
byte_valid : in std_logic;
byte_in : in std_logic_vector(7 downto 0);
read_byte_done : in std_logic; -- the master consumed a transmitted byte
master_acked : in std_logic; -- ...and acknowledged it
-- configuration
supports_device_id : in std_logic; -- the field is OPTIONAL (3.1.17)
-- outputs
ack : out std_logic;
tx_byte : out std_logic_vector(7 downto 0);
tx_valid : out std_logic;
selected : out std_logic;
armed : out std_logic;
byte_index : out unsigned(1 downto 0);
rolled_back : out std_logic;
killed_by_stop : out std_logic;
state : out unsigned(2 downto 0);
completed_reads : out unsigned(CNT_W-1 downto 0);
rollback_count : out unsigned(CNT_W-1 downto 0)
);
end entity i2c_device_id_responder;
architecture rtl of i2c_device_id_responder is
-- Table 3: the reserved Device ID address is 1111 1XX with R/W. The procedure
-- uses 1111 100 as the address field.
constant DEVID_WRITE : std_logic_vector(7 downto 0) := "11111000"; -- step 2
constant DEVID_READ : std_logic_vector(7 downto 0) := "11111001"; -- step 5
constant ST_IDLE : integer := 0;
constant ST_TARGET : integer := 1; -- 1111 1000 accepted; awaiting the target
constant ST_WAIT_SR : integer := 2; -- we are the target; awaiting the Re-START
constant ST_EXPECT : integer := 3; -- Re-START seen; awaiting 1111 1001
constant ST_SEND : integer := 4; -- transmitting the three bytes
signal st : integer := ST_IDLE;
signal idx : integer := 0;
signal n_done : integer := 0;
signal n_roll : integer := 0;
signal sel_i : std_logic := '0';
signal armed_i : std_logic := '0';
-- The three bytes, packed exactly as step 6 describes. One function so the
-- packing appears once and can be read against the specification.
function id_byte (i : integer) return std_logic_vector is
begin
case i is
when 0 => return MANUFACTURER(11 downto 4);
when 1 => return MANUFACTURER(3 downto 0) & PART_ID(8 downto 5);
when others => return PART_ID(4 downto 0) & DIE_REVISION;
end case;
end function;
begin
state <= to_unsigned(st, 3);
byte_index <= to_unsigned(idx, 2);
selected <= sel_i;
armed <= armed_i;
process (clk, rst_n)
begin
if rst_n = '0' then
st <= ST_IDLE;
ack <= '0';
tx_byte <= (others => '0');
tx_valid <= '0';
sel_i <= '0';
armed_i <= '0';
idx <= 0;
rolled_back <= '0';
killed_by_stop <= '0';
n_done <= 0;
n_roll <= 0;
completed_reads <= (others => '0');
rollback_count <= (others => '0');
elsif rising_edge(clk) then
ack <= '0';
-- A STOP always destroys the sequence. This is the single most important
-- branch in the block: 3.1.17 states that a STOP followed by a START
-- "resets the slave state machine and the Device ID Read cannot be
-- performed", so the state must not survive one.
if stop_seen = '1' then
if st /= ST_IDLE and st /= ST_SEND then
killed_by_stop <= '1';
end if;
st <= ST_IDLE;
sel_i <= '0';
armed_i <= '0';
tx_valid <= '0';
idx <= 0;
-- A plain START mid-sequence is equally fatal: it can only have followed
-- a STOP.
elsif start_seen = '1' then
if st = ST_WAIT_SR or st = ST_EXPECT then
killed_by_stop <= '1';
end if;
st <= ST_IDLE;
sel_i <= '0';
armed_i <= '0';
tx_valid <= '0';
idx <= 0;
-- Step 4. A repeated START is the ONLY event that carries the sequence
-- forward.
elsif restart_seen = '1' then
if st = ST_WAIT_SR and sel_i = '1' then
armed_i <= '1';
st <= ST_EXPECT;
else
-- A repeated START anywhere else abandons the sequence, which is
-- the "Re-START condition followed by an access to another slave
-- device" case.
st <= ST_IDLE;
sel_i <= '0';
armed_i <= '0';
tx_valid <= '0';
end if;
idx <= 0;
elsif byte_valid = '1' then
case st is
-- Step 2: the reserved Device ID address with R/W = 0. A device
-- that does not implement the optional field must not answer.
when ST_IDLE =>
if byte_in = DEVID_WRITE and supports_device_id = '1' then
ack <= '1';
st <= ST_TARGET;
end if;
-- Step 3: the target's slave address. The LSB is a DON'T-CARE, so
-- only bits 7 downto 1 are compared.
when ST_TARGET =>
if byte_in(7 downto 1) = MY_ADDR then
ack <= '1';
sel_i <= '1';
st <= ST_WAIT_SR;
else
-- Not us. Stay silent and drop out; two devices answering
-- would break step 3's "only one device must acknowledge".
sel_i <= '0';
st <= ST_IDLE;
end if;
-- Step 5: the reserved address again, now with R/W = 1.
when ST_EXPECT =>
if byte_in = DEVID_READ and armed_i = '1' then
ack <= '1';
tx_byte <= id_byte(0);
tx_valid <= '1';
idx <= 0;
st <= ST_SEND;
else
st <= ST_IDLE;
sel_i <= '0';
armed_i <= '0';
end if;
when others =>
null; -- ST_WAIT_SR ignores data bytes; ST_SEND is driven below
end case;
-- Steps 6 and 7. One byte leaves per read_byte_done, and what happens
-- next is decided entirely by the master's acknowledge.
elsif read_byte_done = '1' and st = ST_SEND then
if master_acked = '0' then
-- Step 7: a NACK ends the read and resets the state machine. The
-- only clean termination, and it is the master's to give.
n_done <= n_done + 1;
completed_reads <= to_unsigned(n_done + 1, CNT_W);
tx_valid <= '0';
sel_i <= '0';
armed_i <= '0';
idx <= 0;
st <= ST_IDLE;
elsif idx = 2 then
-- The roll-back. The master ACKed the third byte, so the sequence
-- restarts from the first and keeps going until a NACK arrives.
rolled_back <= '1';
n_roll <= n_roll + 1;
rollback_count <= to_unsigned(n_roll + 1, CNT_W);
idx <= 0;
tx_byte <= id_byte(0);
else
idx <= idx + 1;
tx_byte <= id_byte(idx + 1);
end if;
end if;
end if;
end process;
end architecture rtl; -- ---------------------------------------------------------------------------
-- i2c_device_id_responder_tb.vhd
-- Independent oracle for i2c_device_id_responder. Behavioural twin of the
-- SystemVerilog and Verilog benches.
--
-- The bench holds the expected identity as three SEPARATE fields and repacks them
-- itself, so the packing is checked against an independent computation rather than
-- against the design's own function.
--
-- Four of the twelve tests are ways the sequence must FAIL: a STOP in the middle,
-- a plain START in the middle, a Re-START that addresses someone else, and a read
-- attempted without the arming Re-START. Those are 3.1.17's two Remarks, and a
-- responder that holds its state too loosely passes every positive test.
-- ---------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_device_id_responder_tb is
end entity i2c_device_id_responder_tb;
architecture sim of i2c_device_id_responder_tb is
constant TCLK : time := 10 ns;
-- The identity under test, held independently of the DUT's generics.
constant EXP_MANUF : std_logic_vector(11 downto 0) := x"2A5";
constant EXP_PART : std_logic_vector(8 downto 0) := "100111100"; -- 0x13C
constant EXP_REV : std_logic_vector(2 downto 0) := "101"; -- 5
constant EXP_ADDR : std_logic_vector(6 downto 0) := "0100000"; -- 0x20
constant ST_IDLE : integer := 0;
constant ST_TARGET : integer := 1;
constant ST_SEND : integer := 4;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal start_seen : std_logic := '0';
signal restart_seen : std_logic := '0';
signal stop_seen : std_logic := '0';
signal byte_valid : std_logic := '0';
signal byte_in : std_logic_vector(7 downto 0) := (others => '0');
signal read_byte_done : std_logic := '0';
signal master_acked : std_logic := '0';
signal supports_device_id : std_logic := '1';
signal ack, tx_valid, selected, armed, rolled_back, killed_by_stop : std_logic;
signal tx_byte : std_logic_vector(7 downto 0);
signal byte_index : unsigned(1 downto 0);
signal st_o : unsigned(2 downto 0);
signal completed_reads, rollback_count : unsigned(7 downto 0);
signal halt : boolean := false;
begin
dut : entity work.i2c_device_id_responder
generic map (MANUFACTURER => EXP_MANUF, PART_ID => EXP_PART,
DIE_REVISION => EXP_REV, MY_ADDR => EXP_ADDR, CNT_W => 8)
port map (
clk => clk, rst_n => rst_n,
start_seen => start_seen, restart_seen => restart_seen,
stop_seen => stop_seen, byte_valid => byte_valid, byte_in => byte_in,
read_byte_done => read_byte_done, master_acked => master_acked,
supports_device_id => supports_device_id,
ack => ack, tx_byte => tx_byte, tx_valid => tx_valid,
selected => selected, armed => armed, byte_index => byte_index,
rolled_back => rolled_back, killed_by_stop => killed_by_stop,
state => st_o, completed_reads => completed_reads,
rollback_count => rollback_count);
clkgen : process
begin
while not halt loop
clk <= '0'; wait for TCLK/2;
clk <= '1'; wait for TCLK/2;
end loop;
wait;
end process;
stim : process
variable err : integer := 0;
variable got0, got1, got2 : std_logic_vector(7 downto 0);
-- The bench's OWN packing of step 6, written from the specification text.
function exp_byte (i : integer) return std_logic_vector is
begin
case i is
when 0 => return EXP_MANUF(11 downto 4); -- first byte
when 1 => return EXP_MANUF(3 downto 0) & EXP_PART(8 downto 5);
when others => return EXP_PART(4 downto 0) & EXP_REV;
end case;
end function;
procedure ck_int (what : string; got : integer; exp : integer) is
begin
if got /= exp then
report " FAIL " & what & ": got " & integer'image(got)
& " expected " & integer'image(exp) severity note;
err := err + 1;
end if;
end procedure;
procedure ck_bit (what : string; got : std_logic; exp : std_logic) is
begin
if got /= exp then
report " FAIL " & what & ": got " & std_logic'image(got)
& " expected " & std_logic'image(exp) severity note;
err := err + 1;
end if;
end procedure;
procedure step is
begin
wait until rising_edge(clk);
wait until falling_edge(clk);
end procedure;
procedure do_reset_dut is
begin
wait until falling_edge(clk);
rst_n <= '0'; start_seen <= '0'; restart_seen <= '0'; stop_seen <= '0';
byte_valid <= '0'; read_byte_done <= '0'; master_acked <= '0';
for k in 0 to 2 loop wait until rising_edge(clk); end loop;
wait until falling_edge(clk);
rst_n <= '1';
step;
end procedure;
procedure ev_start is
begin
wait until falling_edge(clk); start_seen <= '1';
wait until rising_edge(clk); wait until falling_edge(clk); start_seen <= '0';
end procedure;
procedure ev_restart is
begin
wait until falling_edge(clk); restart_seen <= '1';
wait until rising_edge(clk); wait until falling_edge(clk); restart_seen <= '0';
end procedure;
procedure ev_stop is
begin
wait until falling_edge(clk); stop_seen <= '1';
wait until rising_edge(clk); wait until falling_edge(clk); stop_seen <= '0';
end procedure;
procedure send (b : std_logic_vector(7 downto 0)) is
begin
wait until falling_edge(clk); byte_in <= b; byte_valid <= '1';
wait until rising_edge(clk); wait until falling_edge(clk); byte_valid <= '0';
end procedure;
-- Consume one transmitted byte, acknowledging or not.
procedure take (do_ack : std_logic) is
begin
wait until falling_edge(clk);
read_byte_done <= '1'; master_acked <= do_ack;
wait until rising_edge(clk); wait until falling_edge(clk);
read_byte_done <= '0';
end procedure;
-- Steps 1 to 5, the whole arming sequence, done correctly.
procedure arm_sequence (target : std_logic_vector(7 downto 0)) is
begin
ev_start;
send("11111000"); -- step 2
send(target); -- step 3
ev_restart; -- step 4
send("11111001"); -- step 5
end procedure;
begin
report "=== i2c_device_id_responder: a two-phase read held across a repeated START ==="
severity note;
-- T1. The whole procedure, three bytes against the bench's own packing.
do_reset_dut;
arm_sequence(EXP_ADDR & '0');
report "T1 the complete seven-step procedure" severity note;
ck_bit("T1 selected", selected, '1');
ck_bit("T1 armed", armed, '1');
ck_int("T1 in ST_SEND", to_integer(st_o), ST_SEND);
ck_bit("T1 tx_valid", tx_valid, '1');
got0 := tx_byte; take('1');
got1 := tx_byte; take('1');
got2 := tx_byte; take('0'); -- step 7: NACK the last byte
ck_int("T1 byte 0 = manufacturer(11:4)",
to_integer(unsigned(got0)), to_integer(unsigned(exp_byte(0))));
ck_int("T1 byte 1 = manufacturer(3:0)+part(8:5)",
to_integer(unsigned(got1)), to_integer(unsigned(exp_byte(1))));
ck_int("T1 byte 2 = part(4:0)+revision",
to_integer(unsigned(got2)), to_integer(unsigned(exp_byte(2))));
ck_int("T1 one completed read", to_integer(completed_reads), 1);
ck_int("T1 back to idle", to_integer(st_o), ST_IDLE);
ck_bit("T1 no longer selected", selected, '0');
-- T2. The 24 bits reconstructed: a stronger check than comparing bytes,
-- because it catches a consistent misalignment.
report "T2 the 24 bits reassemble into 12 + 9 + 3" severity note;
ck_int("T2 manufacturer",
to_integer(unsigned(got0 & got1(7 downto 4))),
to_integer(unsigned(EXP_MANUF)));
ck_int("T2 part id",
to_integer(unsigned(got1(3 downto 0) & got2(7 downto 3))),
to_integer(unsigned(EXP_PART)));
ck_int("T2 die revision",
to_integer(unsigned(got2(2 downto 0))),
to_integer(unsigned(EXP_REV)));
-- T3. THE DON'T-CARE LSB. Both spellings of the target address select.
do_reset_dut;
arm_sequence(EXP_ADDR & '0');
ck_bit("T3 selected with LSB 0", selected, '1');
take('0');
do_reset_dut;
arm_sequence(EXP_ADDR & '1');
report "T3 the target address LSB is a don't-care" severity note;
ck_bit("T3 selected with LSB 1", selected, '1');
ck_int("T3 still reached ST_SEND", to_integer(st_o), ST_SEND);
take('0');
-- T4. Addressed to somebody else: stay silent and drop out.
do_reset_dut;
ev_start;
send("11111000");
ck_bit("T4 acknowledged the Device ID address", ack, '1');
send("0110011" & '0'); -- somebody else's address
report "T4 a Device ID read aimed at another device is ignored" severity note;
ck_bit("T4 did not acknowledge", ack, '0');
ck_bit("T4 not selected", selected, '0');
step;
ck_int("T4 dropped to idle", to_integer(st_o), ST_IDLE);
-- T5. A STOP between step 3 and step 5 kills it: 3.1.17's first Remark.
do_reset_dut;
ev_start;
send("11111000");
send(EXP_ADDR & '0');
ck_bit("T5 selected before the STOP", selected, '1');
ev_stop; -- fatal
report "T5 a STOP between the phases destroys the sequence" severity note;
ck_bit("T5 killed", killed_by_stop, '1');
ck_bit("T5 no longer selected", selected, '0');
ck_int("T5 idle", to_integer(st_o), ST_IDLE);
ev_start;
send("11111001");
ck_bit("T5 a read after the kill sends nothing", tx_valid, '0');
ck_int("T5 still idle", to_integer(st_o), ST_IDLE);
-- T6. A plain START mid-sequence is equally fatal.
do_reset_dut;
ev_start;
send("11111000");
send(EXP_ADDR & '0');
ev_start; -- NOT a repeated START
report "T6 a plain START mid-sequence destroys it too" severity note;
ck_bit("T6 killed", killed_by_stop, '1');
ck_bit("T6 not armed", armed, '0');
ck_int("T6 idle", to_integer(st_o), ST_IDLE);
-- T7. A Re-START that addresses SOMEONE ELSE abandons the sequence:
-- 3.1.17's second Remark.
do_reset_dut;
ev_start;
send("11111000");
send(EXP_ADDR & '0');
ev_restart;
ck_bit("T7 armed by the Re-START", armed, '1');
send(x"42"); -- an ordinary slave address
report "T7 a Re-START followed by another device abandons the read"
severity note;
ck_bit("T7 not sending", tx_valid, '0');
ck_bit("T7 selection dropped", selected, '0');
ck_int("T7 idle", to_integer(st_o), ST_IDLE);
-- T8. A read with NO arming sequence sends nothing.
do_reset_dut;
ev_start;
send("11111001");
report "T8 the read address alone does not authorise a read" severity note;
ck_bit("T8 nothing to send", tx_valid, '0');
ck_bit("T8 not armed", armed, '0');
ck_int("T8 idle", to_integer(st_o), ST_IDLE);
-- T9. THE ROLL-BACK. Nine bytes taken with ACK are three full cycles.
do_reset_dut;
arm_sequence(EXP_ADDR & '0');
report "T9 ACKing past the third byte rolls back to the first" severity note;
for k in 0 to 8 loop
ck_int("T9 cyclic byte", to_integer(unsigned(tx_byte)),
to_integer(unsigned(exp_byte(k mod 3))));
take('1');
end loop;
ck_bit("T9 rolled back", rolled_back, '1');
ck_int("T9 three roll-backs", to_integer(rollback_count), 3);
ck_int("T9 no completed read yet", to_integer(completed_reads), 0);
ck_int("T9 still sending", to_integer(st_o), ST_SEND);
take('0');
ck_int("T9 now one completed read", to_integer(completed_reads), 1);
ck_int("T9 now idle", to_integer(st_o), ST_IDLE);
-- T10. A NACK anywhere stops it.
do_reset_dut;
arm_sequence(EXP_ADDR & '0');
take('0'); -- NACK the very first byte
report "T10 a NACK on the first byte ends the read immediately" severity note;
ck_int("T10 completed", to_integer(completed_reads), 1);
ck_int("T10 idle", to_integer(st_o), ST_IDLE);
ck_bit("T10 not sending", tx_valid, '0');
-- T11. The field is OPTIONAL: a device without it stays silent.
do_reset_dut;
wait until falling_edge(clk); supports_device_id <= '0';
ev_start;
send("11111000");
report "T11 a device without the optional field stays silent" severity note;
ck_bit("T11 did not acknowledge", ack, '0');
ck_int("T11 stayed idle", to_integer(st_o), ST_IDLE);
send(EXP_ADDR & '0');
ck_bit("T11 still silent", ack, '0');
ck_bit("T11 never selected", selected, '0');
wait until falling_edge(clk); supports_device_id <= '1';
-- T12. Two complete reads back to back; nothing leaks between them.
do_reset_dut;
for k in 0 to 1 loop
arm_sequence(EXP_ADDR & '0');
ck_int("T12 byte 0", to_integer(unsigned(tx_byte)),
to_integer(unsigned(exp_byte(0)))); take('1');
ck_int("T12 byte 1", to_integer(unsigned(tx_byte)),
to_integer(unsigned(exp_byte(1)))); take('1');
ck_int("T12 byte 2", to_integer(unsigned(tx_byte)),
to_integer(unsigned(exp_byte(2)))); take('0');
ev_stop;
end loop;
report "T12 two complete reads, nothing carried between them" severity note;
ck_int("T12 two completed reads", to_integer(completed_reads), 2);
ck_int("T12 no roll-backs", to_integer(rollback_count), 0);
ck_int("T12 idle", to_integer(st_o), ST_IDLE);
if err = 0 then
report "=== i2c_device_id_responder: ALL CHECKS PASSED ===" severity note;
else
report "=== i2c_device_id_responder: " & integer'image(err)
& " CHECK(S) FAILED ===" severity note;
end if;
halt <= true;
wait;
end process;
end architecture sim;8a. Seven Decisions Worth Defending
A STOP clears the state unconditionally, and the branch is first. §5's first Remark is the most important sentence in the block and it is implemented as the outermost condition, before any byte decode, so no code path can accidentally survive one. killed_by_stop exists to make the destruction observable rather than merely correct.
A plain START is treated as fatal too. On a real bus a plain START mid-sequence can only have followed a STOP, so admitting it would let the sequence survive an event the specification says ends it. Mutation X3 treats it as a repeated START and two checks fail.
Only bits 7:1 of the target address are compared. Step 3 says the LSB is a don't-care. Comparing all eight bits works for exactly half the addresses a master might send and fails for the other half, which presents as an intermittent addressing bug. Mutation X1 is that comparison, and test 3 drives both spellings.
A device that is not the target goes silent and drops out. Step 3 requires that "only one device must acknowledge this byte", so a non-match must not merely decline — it must leave the sequence, or a later repeated START could arm the wrong device. Mutation X4 leaves selected asserted on a mismatch.
The packing is one function, written once. id_byte is the only place the 12/9/3 split appears, so it can be read against §3.1.17 line by line. Mutations X5, X6 and X7 each perturb it differently and all three are killed by the bench's field-level reconstruction rather than by byte comparison.
The roll-back is explicit and counted. rollback_count turns §5's second Remark into a number. A responder that terminated after three bytes would pass any test that reads exactly three, and mutation X8 is that responder — caught only because test 9 reads nine.
armed is redundant with the state, deliberately, and is kept as an output. Mutations X4 and X11 in an earlier run of the suite both survived, and the reason is that armed is written only on the transition into the expect state and read only inside it, so the state implies it. The flag is retained because a bench and a waveform should be able to see the arming without decoding a state encoding — §9 records the proof rather than removing the signal.
8b. Verified Execution
$ iverilog -g2012 -o d i2c_device_id_responder.sv i2c_device_id_responder_tb.sv && ./d
=== i2c_device_id_responder: a two-phase read held across a repeated START ===
T1 the complete seven-step procedure
T2 the 24 bits reassemble into 12 + 9 + 3
T3 the target address LSB is a don't-care
T4 a Device ID read aimed at another device is ignored
T5 a STOP between the phases destroys the sequence
T6 a plain START mid-sequence destroys it too
T7 a Re-START followed by another device abandons the read
T8 the read address alone does not authorise a read
T9 ACKing past the third byte rolls back to the first
T10 a NACK on the first byte ends the read immediately
T11 a device without the optional field stays silent
T12 two complete reads, nothing carried between them
=== i2c_device_id_responder: ALL CHECKS PASSED ===
i2c_device_id_responder_tb.sv:336: $finish called at 2270000 (1ps)
$ iverilog -g2005 -o v i2c_device_id_responder.v i2c_device_id_responder_tb.v && ./v
=== i2c_device_id_responder: a two-phase read held across a repeated START ===
T1 the complete seven-step procedure
T2 the 24 bits reassemble into 12 + 9 + 3
T3 the target address LSB is a don't-care
T4 a Device ID read aimed at another device is ignored
T5 a STOP between the phases destroys the sequence
T6 a plain START mid-sequence destroys it too
T7 a Re-START followed by another device abandons the read
T8 the read address alone does not authorise a read
T9 ACKing past the third byte rolls back to the first
T10 a NACK on the first byte ends the read immediately
T11 a device without the optional field stays silent
T12 two complete reads, nothing carried between them
=== i2c_device_id_responder: ALL CHECKS PASSED ===
i2c_device_id_responder_tb.v:337: $finish called at 2270000 (1ps)
$ nvc --std=2008 -a i2c_device_id_responder.vhd i2c_device_id_responder_tb.vhd
$ nvc --std=2008 -e i2c_device_id_responder_tb && nvc --std=2008 -r i2c_device_id_responder_tb --stop-time=500us
** Note: 0ms+0: === i2c_device_id_responder: a two-phase read held across a repeated START ===
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 150ns+1: T1 the complete seven-step procedure
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 210ns+1: T2 the 24 bits reassemble into 12 + 9 + 3
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 530ns+1: T3 the target address LSB is a don't-care
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 660ns+1: T4 a Device ID read aimed at another device is ignored
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 800ns+1: T5 a STOP between the phases destroys the sequence
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 970ns+1: T6 a plain START mid-sequence destroys it too
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 1120ns+1: T7 a Re-START followed by another device abandons the read
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 1210ns+1: T8 the read address alone does not authorise a read
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 1360ns+1: T9 ACKing past the third byte rolls back to the first
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 1730ns+1: T10 a NACK on the first byte ends the read immediately
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 1830ns+1: T11 a device without the optional field stays silent
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 2270ns+1: T12 two complete reads, nothing carried between them
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82
** Note: 2270ns+1: === i2c_device_id_responder: ALL CHECKS PASSED ===
Process :i2c_device_id_responder_tb:stim at i2c_device_id_responder_tb.vhd:82All three at 2270 ns. Four of the twelve tests are ways the sequence must fail, and they are the reason the run is as long as it is: each one builds the arming sequence correctly and then breaks it at a different point.
8c. What The Testbench Proves
| # | scenario | what it establishes |
|---|---|---|
| 1 | the complete seven steps | the three bytes match the bench's own packing |
| 2 | the three bytes reassembled | 12 + 9 + 3 recovers the fields, not just the bytes |
| 3 | the target address with LSB 0 and LSB 1 | the LSB is genuinely a don't-care |
| 4 | a Device ID read aimed at another device | silent, and drops out |
| 5 | a STOP between the phases | the sequence is destroyed; a later read gets nothing |
| 6 | a plain START between the phases | equally fatal |
| 7 | a Re-START followed by another slave | abandoned — the second Remark |
| 8 | the read address with no arming sequence | nothing is sent |
| 9 | nine bytes taken with ACK | three full cycles; three roll-backs; still sending |
| 10 | a NACK on the first byte | ends immediately |
| 11 | a device without the optional field | never acknowledges the reserved address |
| 12 | two complete reads back to back | reusable, and nothing leaks between them |
Test 2 is stronger than test 1. Test 1 compares three bytes; test 2 reassembles them into manufacturer, part and revision. A packing that is consistently shifted by four bits could pass a byte comparison written from the same wrong assumption — the field reconstruction cannot.
Tests 5, 6, 7 and 8 are the four ways to fail. Each arms the sequence correctly and then breaks it: a STOP, a plain START, a Re-START to the wrong device, and no arming at all. A responder that holds its state too loosely passes tests 1 through 4 and 9 through 12 perfectly.
Test 9 reads nine bytes to prove a three-byte field is cyclic. Three cycles, checked byte by byte against exp_byte(n % 3), with completed_reads still zero throughout — because until the NACK arrives, no read has completed. Then one NACK ends it.
Test 11 is the discoverability problem as a test. A device without the optional field must be indistinguishable from an absent one: no acknowledge on the reserved address, no selection, nothing. §2.
Test 12 exists because a state machine that works once is not the same as one that works twice. The second read must produce identical bytes with no roll-backs, which catches a byte_index or selected that was cleared on the wrong condition.
9. Mutation Testing
Eleven defects injected into the SystemVerilog responder.
| # | injected defect | outcome |
|---|---|---|
| X1 | all eight bits of the target address compared | killed — test 3 |
| X2 | the sequence allowed to survive a STOP | killed — test 5 |
| X3 | a plain START treated as a repeated START | killed — test 6 |
| X4 | selection retained when not addressed | killed — test 4 |
| X5 | the first two identity bytes swapped | killed — 14 checks |
| X6 | the part identification misaligned by one bit | killed — 7 checks |
| X7 | the revision packed into the wrong three bits | killed — 8 checks |
| X8 | terminate after three bytes instead of rolling back | killed — 8 checks |
| X9 | the master NACK ignored | killed — 10 checks |
| X10 | answer even without the optional field | killed — test 11 |
| X11 | the write address accepted at step 5 | killed — test 1 |
Eleven of eleven, and three mutations initially survived and were proved equivalent rather than fixed.
The redundant-flag equivalence. An earlier X4 set armed in the idle state instead of on the repeated START, and an earlier X11 dropped && armed from the step-5 test. Both survived, and the proof is the same from two directions: armed is written only on the transition into S_EXPECT and read only inside it, so S_EXPECT implies armed. The flag is redundant with the state.
A surviving mutation on a redundant signal is not a bench hole. It is the bench telling you the signal carries no independent information.
The resolution was to keep armed — it is an observability output, and test 7 asserts it — record the proof in the suite, and replace both mutations with observable ones: X4 now retains selection on a mismatch, and X11 accepts the write address where the read address belongs. Both are killed.
The packing mutations kill broadly, and that is informative. X5 fires 14 checks, X7 fires 8. A misaligned field corrupts every byte downstream of it and every field-level reconstruction, so the blast radius is large. Contrast X1 and X4, which each fire one or two — a narrow judgement broken narrowly.
X9's ten checks are the NACK being ignored. A responder that keeps sending after a NACK never returns to idle, so completed_reads never increments and every subsequent test in the run inherits a responder stuck mid-transfer. That cascade is why the count is high, and it is worth noting that a high count can mean one defect rather than ten.
10. Verification Connection — A Transaction That Spans A Framing Event
The Device ID read is awkward to verify for a specific reason: a UVM sequence item usually models one addressed transfer, and this is two transfers that are only correct as a pair.
// A Device ID read is NOT two transactions. Section 3.1.17 says a STOP between
// the phases means the read "cannot be performed", so modelling it as two
// sequence items lets a scheduler legally insert a STOP -- or another master's
// traffic -- and the resulting failure looks like a DUT bug.
//
// The whole exchange is therefore ONE item that the driver must emit atomically.
class i2c_devid_item extends uvm_sequence_item;
`uvm_object_utils(i2c_devid_item)
rand bit [6:0] target_addr;
rand bit target_lsb; // step 3: a DON'T-CARE, so randomise it
rand int bytes_to_ack; // how many bytes to ACK before the NACK
// Step 6's fields, as fields. The driver reassembles; it never compares bytes.
bit [11:0] got_manufacturer;
bit [8:0] got_part;
bit [2:0] got_revision;
bit completed;
// The LSB is meaningless, so both values must be exercised.
constraint c_lsb { target_lsb dist { 0 :/ 1, 1 :/ 1 }; }
// Reading 0 bytes is a NACK on the first byte -- legal, per the second
// Remark. Reading more than 3 exercises the roll-back.
constraint c_len { bytes_to_ack inside {[0:9]}; }
function new(string name = "i2c_devid_item");
super.new(name);
endfunction
// The bench's own unpacking of step 6, kept separate from any RTL function.
function void unpack(bit [7:0] b0, bit [7:0] b1, bit [7:0] b2);
got_manufacturer = {b0, b1[7:4]};
got_part = {b1[3:0], b2[7:3]};
got_revision = b2[2:0];
endfunction
function string convert2string();
return $sformatf("devid addr=0x%02h lsb=%0b acked=%0d -> mfr=0x%03h part=0x%03h rev=%0d",
target_addr, target_lsb, bytes_to_ack,
got_manufacturer, got_part, got_revision);
endfunction
endclass
// ------------------------------------------------------------------------
// The driver's obligation, stated in code because it cannot be stated in the
// item: no STOP, and no other traffic, between the phases.
// ------------------------------------------------------------------------
task i2c_devid_driver::drive_item(i2c_devid_item it);
begin
bus.start(); // step 1
bus.write_byte(8'b1111_1000); // step 2
bus.write_byte({it.target_addr, it.target_lsb}); // step 3, LSB don't-care
bus.repeated_start(); // step 4 -- NOT stop_then_start()
bus.write_byte(8'b1111_1001); // step 5
// step 6: read, acknowledging exactly bytes_to_ack of them
...
bus.stop(); // step 7, only after the NACK
end
endtask11. FPGA and ASIC Implications
The identity is hard-wired, so it costs no storage. MANUFACTURER, PART_ID and DIE_REVISION are parameters in §8's design and constants in silicon. Twenty-four bits of tie-off and a 3-to-1 multiplexer is the entire cost of the field, which is why "optional" here is a decision about the state machine rather than about area.
The die revision field is the one with an ongoing cost. Three bits that must change when the die changes means the RTL constant is part of the tape-out checklist. A part that ships two revisions reporting the same value has given up the only field with day-to-day diagnostic use.
Holding state across a repeated START is a design constraint on the whole slave. Most slave state machines are built to reset on any framing event, because that is what Chapter 5.4 makes safe. The Device ID needs the opposite for one specific pair of phases, so the reset condition has to distinguish a repeated START from a STOP — which means the framing detector must report them as two different events, not one "restart" signal. That is a requirement that propagates outward from this feature into the bit-level front end.
A combined-transfer-capable driver is a prerequisite. §5: a host whose I²C API offers only independent reads and writes cannot perform a Device ID read, and the symptom is a device that appears not to implement the feature. Before concluding a part lacks Device ID support, check whether the software can emit a repeated START at all.
Count the roll-backs if you implement the field. A master that never NACKs will loop forever, and rollback_count is the only evidence of it. A responder without that counter presents an infinite read as a hang with no attribution.
12. Debugging — The Device ID That Read As A Different Manufacturer
A driver reads the Device ID from a known NXP part and gets manufacturer 0x000, which is correct, part identification 0x0A5 — and the datasheet says the part ID should be 0x14B. The revision reads 5, which is plausible. Repeated reads give the same wrong part ID every time.
The driver's unpacking is wrong in two places and one of the errors was hiding the other. Manufacturer is twelve bits -- the whole first byte plus the four MSBs of the second -- not eight, so the driver was truncating it; and because every manufacturer in Table 4 has 0x00 in its top eight bits, the truncated value looked correct and confirmed the wrong hypothesis. Part identification is the four LSBs of byte 1 plus the five MSBs of byte 2, not byte 1 shifted with a nibble of byte 2. The bytes on the wire were right all along.
Unpack as the specification states: manufacturer is byte0 with the top nibble of byte1 appended; part is the bottom nibble of byte1 with the top five bits of byte2 appended; revision is the bottom three bits of byte2. For 0x00, 0x0A, 0x5B that gives manufacturer 0x00A, part 0x0AB and revision 3 -- and 0x00A is Fujitsu in Table 4, which is the actual answer and identifies the part as not the one the board was designed for.Three things generalise.
The manufacturer field looked right while being wrong, and that cost the most time. Truncating twelve bits to eight yields 0x00 for every entry in Table 4, so a truncating reader reports "NXP" for a Fujitsu part, an Atmel part and an ON Semiconductor part alike. The field that should identify the vendor is the field most likely to falsely confirm one.
The bytes on the wire were correct throughout. A capture was necessary but not sufficient; the defect was entirely in the unpacking, which is why §8's testbench reconstructs fields rather than comparing bytes.
Two errors partially cancelled. The part ID was wrong in a way that produced a plausible number, and the manufacturer was wrong in a way that produced a reassuring one. Debugging stalled on the reassuring one.
13. Common Misconceptions
"Software reset has its own command." It is general call 0000 0000 followed by 0000 0110. §1.
"The Device ID lets a master enumerate a bus." It confirms an identity you already expected. An unsupported Device ID is indistinguishable from an absent device. §2.
"The Device ID is mandatory." It is optional, and Table 4 lists fourteen manufacturers. §3.
"The manufacturer ID is one byte." Twelve bits: the whole first byte plus the four MSBs of the second. Truncating to eight yields 0x00 for every entry in Table 4 and therefore looks correct. §6 and §12.
"A Device ID read is a write followed by a read." It is one transaction with a repeated START in the middle. A STOP between the phases means the read "cannot be performed". §5.
"Any repeated START carries the sequence forward." A repeated START followed by an access to another device also destroys it. §5.
"The read ends after three bytes." It ends when the master NACKs. Acknowledging the third byte rolls back to the first and the sequence repeats indefinitely. §5.
"The LSB of the target address in step 3 is the direction bit." It is a don't-care. Comparing it fails for half the addresses a master might send. §4.
"A device that does not answer a Device ID read is broken." It may simply not implement the optional field, and the specification provides no way to tell that apart from absence. §2.
14. Reason It Through
A Device ID read returns bytes 0x00, 0x0A, 0x5B. What are the three fields?
Manufacturer is byte 0 plus the top nibble of byte 1: {0x00, 0xA} = 0x00A, which Table 4 gives as Fujitsu Semiconductor. Part identification is the bottom nibble of byte 1 plus the top five bits of byte 2: {0xA, 0b01011} = {4'b1010, 5'b01011} = 0x14B. Revision is the bottom three bits of byte 2: 0b011 = 3.
A master issues step 2, step 3, then a STOP, then a START and step 5. What does it get?
Nothing. The STOP reset the slave's state machine, so the read cannot be performed and no device answers the read address. §5's first Remark, and test 5 in §8.
A master acknowledges all three bytes and then acknowledges three more. What does it receive?
The first three bytes again. The slave rolled back after the third and will keep cycling until a NACK arrives. So it receives byte 0, byte 1, byte 2 a second time. §5's second Remark, and test 9.
Why does step 3 put a slave address in a data slot rather than using a second address byte?
Because an I²C address byte holds one address and this transaction needs two: the reserved address that identifies the kind of transaction, and the target's address that identifies which device. There is no frame with room for both, so the second travels as data — and every device must compare it against its own, with only the match acknowledging.
A host's I²C API offers write(addr, data) and read(addr, len) and nothing else. Can it read a Device ID?
No. Those calls emit a STOP at the end of each, and a STOP between the phases makes the read impossible. It needs a combined transfer — one call emitting write, repeated START, read. A device tested only through such an API will appear not to support Device ID however well it implements it. §11.
Why is a surviving mutation on the armed flag not a testbench hole?
Because armed is written only on the transition into the expect state and read only inside it, so the state already implies it — the flag carries no independent information and no test can distinguish a version that sets it earlier. The correct response is to record the proof and keep the signal for observability, then replace the mutation with one that is observable. §9.
15. Understanding Check
16. Summary
Software reset is general call 06h. No dedicated address, no separate framing, and §3.1.14's contribution is to say the feature is optional.
Neither service is discoverable. There is no capability register. An unsupported Device ID read is indistinguishable from an absent device, so the Device ID confirms an expected identity rather than enumerating a bus.
The Device ID is 24 bits: 12 manufacturer, 9 part, 3 revision. Table 4 lists fourteen manufacturers, four of them NXP — the honest scale of adoption.
It takes two addressing phases to identify one device, because one address byte cannot carry both the reserved address and the target's, so the target's travels as a data byte with a don't-care LSB.
A STOP between the phases makes the read impossible, and so does a repeated START followed by another device. The specification says both, in two Remarks, and the state must survive one framing event and not the other.
Two of the three fields straddle a byte boundary, and every misalignment produces a plausible wrong answer — the manufacturer most of all, because truncating it to eight bits yields 0x00 for every registered vendor.
The read is cyclic. Acknowledging the third byte rolls back to the first; only the master's NACK ends it.
The feature costs almost no silicon — 24 bits of tie-off and a multiplexer — and imposes one real constraint on the rest of the slave: a repeated START and a STOP must be distinguishable events.
And a host that cannot emit a combined transfer cannot read a Device ID at all, which presents as a device not supporting the feature.
17. What Comes Next
Two chapters have covered features the specification calls optional. Chapter 15.3 takes the opposite situation: a set of stricter profiles built on the same wires, where features I²C leaves free become requirements.
SMBus uses, in the specification's own words, "I²C hardware and I²C hardware addressing" and the protocols "are basically the same". Nothing about the framing, the addressing or the acknowledge differs. And yet an I²C-compliant part can fail an SMBus system five separate ways, none of them a protocol difference: SMBus's clock has a minimum where I²C has none, its clock ceiling is lower, its clock stretching is bounded at 35 ms where Chapter 12.4 established that I²C bounds it not at all, its logic thresholds are absolute where I²C's are ratiometric, and its sink current comes in two classes, one of which is 350 µA against I²C's 3 mA.
That last one turns Chapter 14.4's pull-up arithmetic into something sharper. A single 350 µA device on a bus forces a floor nearly nine times I²C's, and Equation 1 then collapses the permitted capacitance by the same factor — to a few tens of picofarads. Which is the quantitative reason a Smart Battery lives on its own short bus, and PMBus, it turns out, inherits exactly that electrical class.
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
Repeated START and Combined Transactions in RTL
A combined transfer is not a write, a STOP and a read — it is one transaction whose direction reverses without the bus ever going free, and the difference is what stops another master moving the pointer in between. Builds the two-phase sequencer, and shows why the case most often got wrong is a failure in phase one, where the bus is still held.
- Related topic
Repeated START — Holding the Bus Between Phases
A repeated START is not a new waveform. It is the START edge again, and what makes it a different event is that the bus was already busy. That single fact is why a classifier needs state and why a monitor that joins late cannot classify what it sees.
- Related topic
10-Bit Addressing
A two-byte addressing mode built entirely out of reserved space, coexisting with seven-bit devices on the same wires. Its first byte is deliberately not unique, and a read has to re-address with only one byte — which is why a 10-bit slave needs memory a 7-bit slave does not.
