Skip to content
VLSI Mentor

I²C · Module 6

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.

Chapter 6.1 established seven address bits and a direction bit in one byte. Seven bits reach 128 devices, and a later chapter will show that sixteen of those are spoken for — so a board has 112 addresses to allocate, shared across every part any vendor ever shipped.

That is not many. Two devices from different vendors wanting the same address is an everyday problem, and Chapter 6.4 is about surviving it. 10-bit addressing is the other answer: expand the space instead of managing the shortage.

The specification is candid about how that went. UM10204 §3.1.11, in its second sentence:

Currently, 10-bit addressing is not being widely used.

That sentence has been in the document for years and remains accurate. So this chapter has two jobs: teach the mechanism properly, because you will meet it in a specification and occasionally in silicon, and explain why a mode that solves a real problem was declined — because the reasons are instructive about protocol design generally.

1. Built Out of Reserved Space

The trick that makes 10-bit addressing possible without breaking anything is that it does not add a new byte to the protocol. It claims part of the seven-bit space and uses it as an escape hatch.

UM10204 §3.1.11:

The 10-bit slave address is formed from the first two bytes following a START condition (S) or a repeated START condition (Sr). The first seven bits of the first byte are the combination 11110XX of which the last two bits (XX) are the two Most-Significant Bits (MSB) of the 10-bit address; the eighth bit of the first byte is the R/W bit that determines the direction of the message.

So the first byte is an ordinary address byte — seven bits plus direction, exactly as Chapter 6.1 described — whose seven bits happen to start with 11110. A seven-bit device sees an address that is not its own and ignores it. A 10-bit device recognises the prefix and knows a second byte is coming.

And one more constraint, easy to miss and worth stating because it is a real trap:

Although there are eight possible combinations of the reserved address bits 1111XXX, only the four combinations 11110XX are used for 10-bit addressing. The remaining four combinations 11111XX are reserved for future I²C-bus enhancements.

11110XX is the prefix; 11111XX is not. Four combinations, giving two bits of address in the first byte. Those two bits plus the eight bits of the second byte are the ten.

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — where the ten bits live
   10-bit address A[9:0], example 0x1A3 = 01 1010 0011

   first byte:   1 1 1 1 0 | A9 A8 | R/W
                 ^^^^^^^^^   ^^^^^   ^^^
                 prefix      the two the direction, exactly as in a
                 (5 bits)    MSBs    seven-bit address byte

                 = 11110 01 0  =  1111_0010  =  0xF2     (write)

   second byte:  A7 A6 A5 A4 A3 A2 A1 A0
                 ^^^^^^^^^^^^^^^^^^^^^^^^
                 all EIGHT bits are address -- there is NO second direction bit

                 = 1010_0011  =  0xA3

   address space:  2 bits (first byte) + 8 bits (second byte) = 10 bits = 1024

   cost:  the four prefixes 11110XX are consumed from the seven-bit space, so a
          bus using 10-bit addressing has four fewer seven-bit addresses -- which
          were already reserved, so nothing usable is actually lost.

The second byte having no direction bit is the first thing to internalise. Direction is decided once, in the first byte, and the second byte is pure address.

First byte — prefix, two address bits, direction

8 cycles
Eight clock periods, one per bit. SDA carries one one one one zero for the reserved prefix, then zero and one as address bits nine and eight, then a final zero which is the write direction bit.reserved 10-bit prefixreserved 10-bit prefixaddress bits 9 and 8address bits 9 and 8R/WR/W11110 — the reserved prefix11110 — the reserved prefixA9 and A8A9 and A8R/W = 0: writeR/W = 0: writesclsda11110010t0t1t2t3t4t5t6t7
Figure 1 — the first address byte of a 10-bit write. One SCL pulse per bit, eight bits. Five bits of reserved prefix, then the two most significant address bits, then the direction bit in the same position it occupies in any address byte. This byte is 0xF2, which is the prefix plus address bits 01 plus a write.

Second byte — eight address bits, no direction bit

8 cycles
Eight clock periods, one per bit. SDA carries one zero one zero zero zero one one, which are address bits seven down to zero. Every bit in this byte is an address bit.address bits 7 down to 0address bits 7 down to 0A7 — MSB of the low byteA7 — MSB of the low byteA0A0sclsda10100011t0t1t2t3t4t5t6t7
Figure 2 — the second address byte. All eight bits are address; there is no second direction bit, because direction was settled in the first byte. This byte is 0xA3, the low eight bits of address 0x1A3. Only one device on the bus matches both bytes.

2. The First Byte Is Deliberately Not Unique

This is the part that makes 10-bit addressing genuinely different from seven-bit addressing, rather than just longer.

UM10204 §3.1.11, on the write flow:

each slave compares the first seven bits of the first byte of the slave address (11110XX) with its own address and tests if the eighth bit (R/W direction bit) is 0. It is possible that more than one device finds a match and generate an acknowledge (A1). All slaves that found a match compare the eight bits of the second byte of the slave address (XXXXXXXX) with their own addresses, but only one slave finds a match and generates an acknowledge (A2).

Read that twice. The first acknowledge can come from several devices at once, and that is not an error — it is the design.

It follows arithmetically. The first byte carries only two of the ten address bits, so every device whose address shares those two bits recognises the prefix as its own. With ten-bit addresses spread over four prefix combinations, up to 256 devices could share a first byte. All of them acknowledge.

Two consequences, and both are counter-intuitive if you arrived from seven-bit addressing:

The first ACK proves almost nothing. In a seven-bit transfer, an acknowledge on the address byte means the device you wanted is present and listening. In a 10-bit transfer, the first acknowledge means at least one device on this bus has an address whose top two bits match — which may include the device you wanted, and may not. Only the second acknowledge is diagnostic.

Multiple devices driving the acknowledge simultaneously is electrically fine. This is the wired-AND of Chapter 2.5 doing exactly what it was built for: an acknowledge is a device pulling SDA low, and several devices pulling the same line low produce the same level as one. The bus cannot count how many, and does not need to.

That second point connects to a general-call property Chapter 6.3 returns to, and which the specification states directly for the general call: the master does not know how many devices acknowledged. The same electrical fact, in a different context.

3. The Read That Re-Addresses With One Byte

Now the mechanism that forces a 10-bit slave to contain something a seven-bit slave does not: memory.

Consider what a read has to look like. Direction lives in the first byte, so a read would have to set R/W = 1 there — but then the second byte, which carries eight of the ten address bits, would arrive after the bus had already turned around, with the slave transmitting. The address would have to be sent by the device being addressed. That cannot work.

So the specification does something else. UM10204 §3.1.11:

Master-receiver reads slave-transmitter with a 10-bit slave address. The transfer direction is changed after the second R/W bit. Up to and including acknowledge bit A2, the procedure is the same as that described for a master-transmitter addressing a slave-receiver. After the repeated START condition (Sr), a matching slave remembers that it was addressed before. This slave then checks if the first seven bits of the first byte of the slave address following Sr are the same as they were after the START condition (S), and tests if the eighth (R/W) bit is 1.

The sequence, spelled out:

Azvya Education Pvt. Ltd.VLSI Mentor
ARCHITECTURAL PSEUDOCODE — the two 10-bit transfer shapes
   WRITE to a 10-bit slave:

     S  ->  11110 A9A8 0  ->  A1  ->  A7..A0  ->  A2  ->  data ...  ->  P
            ^^^^^^^^^^^^      ^^      ^^^^^^      ^^
            first byte        many    second      exactly ONE device
                              devices byte        acknowledges
                              may ACK

   READ from a 10-bit slave:

     S  ->  11110 A9A8 0  ->  A1  ->  A7..A0  ->  A2  ->   <-- full WRITE-style
            (note: R/W = 0)                                    addressing first

     Sr ->  11110 A9A8 1  ->  A3  ->  data ...  ->  P
            ^^^^^^^^^^^^      ^^
            FIRST BYTE ONLY.  only the device that
            R/W is now 1.     REMEMBERS being selected
                              may acknowledge here

   The read re-address carries only TWO of the ten address bits. It is not a
   complete address and cannot be. What makes it unambiguous is that exactly one
   device on the bus is carrying the memory of having matched all ten.

So a 10-bit read is: address fully, as a write, then repeated START and re-address with the first byte alone. The device that answers is the one that remembers.

This is why Module 5 had to come first. The repeated START is not an optimisation here — it is load-bearing. A STOP between the two phases would release the bus, and Chapter 5.3 established that a STOP releases every device unconditionally, so the memory would be gone and the re-address would match nobody. 10-bit reads are only possible because Sr keeps the bus and the state.

The read re-address — one byte, direction reversed

8 cycles
Eight clock periods, one per bit. SDA carries one one one one zero for the reserved prefix, then zero and one as address bits nine and eight, then a final one which is the read direction bit. The pattern is identical to the first byte of the write except for the last bit.identical to the write byteidentical to the write byteR/W = 1R/W = 1the same prefix as the writethe same prefix as thewritethe same A9 and A8the same A9 and A8R/W = 1: now a readR/W = 1: now a readsclsda11110011t0t1t2t3t4t5t6t7
Figure 3 — the read re-address, after the repeated START. The same five prefix bits and the same two address bits as Figure 1, with the direction bit now set. This single byte carries only two of the ten address bits; what makes it unambiguous is that exactly one device remembers having matched all ten during the write phase that preceded it.

4. How Long Does the Selection Last?

The specification is precise, and the precision matters for the RTL:

The matching slave remains addressed by the master until it receives a STOP condition (P) or a repeated START condition (Sr) followed by a different slave address.

Two release conditions, and the second is conditional. A STOP always releases. A repeated START releases only if the address that follows it belongs to somebody else — which means a device cannot decide what to do at the Sr itself. It has to wait and see.

That is a genuinely awkward requirement to implement, and it is worth noticing why: the device must hold its selection through the beginning of a new address phase, and drop it only when the new address resolves to a different device. A design that released at the Sr would break the read re-address of §3, because the re-address is itself preceded by an Sr.

The matcher in §6 handles this by keeping the memory (was_selected) across a framing START while dropping the selection (selected), and then letting the new address decide. Two pieces of state that are easy to conflate and must not be:

statemeaningcleared by
selectedthis device is the active target right nowa framing START, or a STOP
was_selectedthis device matched all ten bits earlier in this transactiona STOP, or a second-byte mismatch

was_selected deliberately survives a repeated START. That is the entire point of it. §8 injects a fault that clears it there, and the read re-address stops working.

5. Coexistence With Seven-Bit Devices

10-bit and seven-bit devices share a bus without negotiation, and the mechanism is simply that the prefix is an address no seven-bit device has.

Devices with 7-bit and 10-bit addresses can be connected to the same I²C-bus, and both 7-bit and 10-bit addressing can be used in all bus speed modes.

The coexistence works in both directions:

  • A seven-bit device sees 11110XX and compares it against its own address. It does not match, so the device ignores the transfer — including the second byte, because a device that did not match the address byte stops paying attention until the next framing event.
  • A 10-bit device sees an ordinary seven-bit address, finds it is not 11110XX, and ignores it.

There is one nuance the specification calls out, and it is a small mercy:

Slave devices with 10-bit addressing react to a 'general call' in the same way as slave devices with 7-bit addressing.

So the broadcast mechanism does not fork. Chapter 6.3 covers the general call properly.

6. The Matcher in Three Languages

A five-state machine, one memory bit, and two comparators. It consumes captured bytes from Chapter 6.1's capture block and framing events from Module 5.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_10bit_address_matcher.sv — SYNTHESIZABLE RTL. Two-phase match, with the memory a read re-address depends on.
   module i2c_10bit_address_matcher #(
       // The device's own 10-bit address. Bits [9:8] ride in the first byte, bits
       // [7:0] occupy the whole second byte.
       parameter logic [9:0] OWN_ADDR_10 = 10'h123
   )(
       input  logic clk,
       input  logic rst_n,
       input  logic frame_start,        // pulse: S or Sr observed (Module 5)
       input  logic frame_stop,         // pulse: P observed (Module 5)
       input  logic byte_valid,         // pulse: a byte has been captured
       input  logic [7:0] byte_in,      // that byte, as it appeared on the wire
       output logic phase1_match,       // the 11110XX prefix matched -- NOT unique
       output logic selected,           // full 10-bit match -- unique to one device
       output logic rw_latched,         // direction of the transfer that selected us
       output logic ack_request         // this device wants to ACK the byte just seen
   );
       // The reserved prefix for 10-bit addressing. Only 11110XX is used for it;
       // 11111XX is reserved for future enhancements and is NOT a 10-bit prefix.
       localparam logic [4:0] TENBIT_PREFIX = 5'b11110;

       typedef enum logic [2:0] {
           ST_IDLE,     // not addressed, and no address byte expected
           ST_ADDR1,    // a framing START just happened: expecting the first byte
           ST_ADDR2,    // the prefix matched: expecting the second address byte
           ST_SEL_W,    // fully selected, master writing
           ST_SEL_R     // fully selected, master reading
       } state_e;

       state_e state;

       // "After the repeated START condition, a matching slave REMEMBERS that it was
       // addressed before." That memory is this bit, and it is what makes a 10-bit
       // READ possible at all -- a read re-addresses with only the FIRST byte.
       logic was_selected;

       // Continuous assignments rather than always_comb: these are pure decodes of
       // the incoming byte, and a part-select inside always_comb makes some tools
       // complain about sensitivity without changing the behaviour.
       logic prefix_ok, second_ok;
       assign prefix_ok = (byte_in[7:1] == {TENBIT_PREFIX, OWN_ADDR_10[9:8]});
       assign second_ok = (byte_in      == OWN_ADDR_10[7:0]);

       always_ff @(posedge clk) begin
           if (!rst_n) begin
               state        <= ST_IDLE;
               was_selected <= 1'b0;
               rw_latched   <= 1'b0;
               phase1_match <= 1'b0;
               ack_request  <= 1'b0;
           end else begin
               ack_request <= 1'b0;

               if (frame_stop) begin
                   // A STOP ends the addressing entirely, including the memory that
                   // a read re-address depends on.
                   state        <= ST_IDLE;
                   was_selected <= 1'b0;
                   phase1_match <= 1'b0;
               end else if (frame_start) begin
                   // S or Sr: an address byte is coming. was_selected is deliberately
                   // NOT cleared -- surviving the Sr is the whole point of it.
                   state        <= ST_ADDR1;
                   phase1_match <= 1'b0;
               end else if (byte_valid) begin
                   case (state)
                       ST_ADDR1: begin
                           if (prefix_ok && byte_in[0] == 1'b0) begin
                               // Write: several devices can reach this point, because
                               // the prefix carries only two of the ten address bits.
                               phase1_match <= 1'b1;
                               ack_request  <= 1'b1;
                               rw_latched   <= 1'b0;
                               state        <= ST_ADDR2;
                           end else if (prefix_ok && byte_in[0] == 1'b1 && was_selected) begin
                               // Read: legal ONLY as a re-address of a device that the
                               // master already selected with a full two-byte write
                               // phase. Without that memory this must be ignored.
                               phase1_match <= 1'b1;
                               ack_request  <= 1'b1;
                               rw_latched   <= 1'b1;
                               state        <= ST_SEL_R;
                           end else begin
                               phase1_match <= 1'b0;
                               state        <= ST_IDLE;
                           end
                       end

                       ST_ADDR2: begin
                           if (second_ok) begin
                               // Only one device on the bus reaches this point.
                               ack_request  <= 1'b1;
                               was_selected <= 1'b1;
                               state        <= ST_SEL_W;
                           end else begin
                               // Matched the prefix but not the device: step aside, and
                               // forget -- this device was never selected.
                               was_selected <= 1'b0;
                               state        <= ST_IDLE;
                           end
                       end

                       // Once selected, further bytes are DATA. Acknowledging them is
                       // the byte-transfer layer's job (Module 7), not the decoder's.
                       ST_SEL_W, ST_SEL_R: ;

                       default: state <= ST_IDLE;
                   endcase
               end
           end
       end

       assign selected = (state == ST_SEL_W) || (state == ST_SEL_R);
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_10bit_address_matcher_tb.sv — SELF-CHECKING TESTBENCH, SIMULATION ONLY. Two devices sharing a prefix; both ACK the first byte, one ACKs the second.
   module i2c_10bit_address_matcher_tb;
       // TWO devices sharing the upper two address bits and differing in the lower
       // eight. This is the configuration the specification describes: both answer
       // the first byte, only one answers the second.
       localparam logic [9:0] ADDR_A = 10'b01_1010_0011;   // 0x1A3
       localparam logic [9:0] ADDR_B = 10'b01_0101_1100;   // 0x15C
       localparam logic [4:0] PFX    = 5'b11110;

       logic clk = 1'b0, rst_n, frame_start, frame_stop, byte_valid;
       logic [7:0] byte_in;
       logic p1A, selA, rwA, ackA;
       logic p1B, selB, rwB, ackB;
       int errors = 0;

       i2c_10bit_address_matcher #(.OWN_ADDR_10(ADDR_A)) dutA
           (.clk(clk), .rst_n(rst_n), .frame_start(frame_start), .frame_stop(frame_stop),
            .byte_valid(byte_valid), .byte_in(byte_in),
            .phase1_match(p1A), .selected(selA), .rw_latched(rwA), .ack_request(ackA));
       i2c_10bit_address_matcher #(.OWN_ADDR_10(ADDR_B)) dutB
           (.clk(clk), .rst_n(rst_n), .frame_start(frame_start), .frame_stop(frame_stop),
            .byte_valid(byte_valid), .byte_in(byte_in),
            .phase1_match(p1B), .selected(selB), .rw_latched(rwB), .ack_request(ackB));

       always #5 clk = ~clk;
       initial begin #40000; $display("FAIL: watchdog expired"); $finish; end

       int nackA = 0, nackB = 0;
       always @(posedge clk) if (rst_n) begin
           if (ackA) nackA++;
           if (ackB) nackB++;
       end

       task automatic send_byte(input logic [7:0] b);
           byte_in = b; byte_valid = 1'b1; @(negedge clk);
           byte_valid = 1'b0;              @(negedge clk);
       endtask
       task automatic pulse_start(); frame_start = 1'b1; @(negedge clk); frame_start = 1'b0; @(negedge clk); endtask
       task automatic pulse_stop();  frame_stop  = 1'b1; @(negedge clk); frame_stop  = 1'b0; @(negedge clk); endtask

       // First address byte: 11110 then the two MSBs of the address, then R/W.
       function automatic logic [7:0] first_byte(input logic [9:0] a, input logic dir);
           return {PFX, a[9:8], dir};
       endfunction

       task automatic chk(input logic got, input logic want, input string what, input int step);
           if (got !== want) begin
               $display("FAIL: step %0d -- %s was %0b, expected %0b", step, what, got, want);
               errors++;
           end
       endtask

       initial begin
           rst_n = 1'b0; frame_start = 1'b0; frame_stop = 1'b0; byte_valid = 1'b0; byte_in = 8'h00;
           repeat (3) @(negedge clk);
           chk(selA, 1'b0, "selA", 0); chk(selB, 1'b0, "selB", 0);
           rst_n = 1'b1; @(negedge clk);

           // 1 -- an address byte with NO framing START is not an address at all.
           send_byte(first_byte(ADDR_A, 1'b0));
           chk(p1A, 1'b0, "p1A without a START", 1);
           if (nackA != 0) begin $display("FAIL: acked a byte with no preceding START"); errors++; end

           // 2 -- a 7-bit address must not look like a 10-bit prefix.
           pulse_start();
           send_byte(8'b1010010_0);
           chk(p1A, 1'b0, "p1A on a 7-bit address", 2);
           chk(selA, 1'b0, "selA on a 7-bit address", 2);

           // 3 -- 11111XX is NOT the 10-bit prefix. Only 11110XX is; the other four
           //      combinations are reserved for future enhancements.
           pulse_start();
           send_byte({5'b11111, ADDR_A[9:8], 1'b0});
           chk(p1A, 1'b0, "p1A on the 11111XX prefix", 3);

           // 4 -- THE SPECIFICATION'S CENTRAL POINT: both devices answer the FIRST
           //      byte, because it carries only two of the ten address bits.
           nackA = 0; nackB = 0;
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b0));
           chk(p1A, 1'b1, "p1A on the shared prefix", 4);
           chk(p1B, 1'b1, "p1B on the shared prefix", 4);
           if (nackA != 1 || nackB != 1) begin
               $display("FAIL: both devices must ACK the first byte (A=%0d B=%0d)", nackA, nackB); errors++; end
           chk(selA, 1'b0, "selA before the second byte", 4);
           chk(selB, 1'b0, "selB before the second byte", 4);

           // 5 -- and only ONE answers the second byte.
           nackA = 0; nackB = 0;
           send_byte(ADDR_A[7:0]);
           chk(selA, 1'b1, "selA after its own second byte", 5);
           chk(selB, 1'b0, "selB after another device's second byte", 5);
           if (nackA != 1 || nackB != 0) begin
               $display("FAIL: only the matching device may ACK the second byte (A=%0d B=%0d)", nackA, nackB); errors++; end
           chk(rwA, 1'b0, "rwA (write)", 5);

           // 6 -- data bytes after selection are not addresses and must not be acked
           //      by the decoder; that is the byte-transfer layer's job.
           nackA = 0;
           send_byte(8'hFF); send_byte(8'h00);
           if (nackA != 0) begin $display("FAIL: decoder acked data bytes"); errors++; end
           chk(selA, 1'b1, "selA still selected across data", 6);

           // 7 -- THE READ RE-ADDRESS. After Sr, the first byte alone with R/W=1
           //      re-selects the device that was already selected. Note only ONE byte.
           nackA = 0; nackB = 0;
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b1));
           chk(selA, 1'b1, "selA re-addressed for read", 7);
           chk(rwA, 1'b1, "rwA (read)", 7);
           chk(selB, 1'b0, "selB on the read re-address", 7);
           if (nackA != 1 || nackB != 0) begin
               $display("FAIL: only the remembered device may ACK the read re-address (A=%0d B=%0d)", nackA, nackB); errors++; end

           // 8 -- a STOP forgets everything, so the same read re-address now fails.
           //      Without this, a device would answer reads it was never selected for.
           pulse_stop();
           chk(selA, 1'b0, "selA after STOP", 8);
           nackA = 0;
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b1));
           chk(selA, 1'b0, "selA on a read with no prior write phase", 8);
           if (nackA != 0) begin
               $display("FAIL: acked a read re-address without ever being selected"); errors++; end

           // 9 -- Sr followed by a DIFFERENT device's address deselects this one.
           //      "remains addressed until P or Sr followed by a different address".
           pulse_stop();
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b0));
           send_byte(ADDR_A[7:0]);
           chk(selA, 1'b1, "selA selected before the handover", 9);
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b0));
           send_byte(ADDR_B[7:0]);                       // the OTHER device's low byte
           chk(selA, 1'b0, "selA after Sr to a different address", 9);
           chk(selB, 1'b1, "selB after Sr to its own address", 9);

           if (errors == 0)
               $display("PASS: prefix shared and acked by both, second byte unique to one, read re-address requires memory");
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_10bit_address_matcher.v — SYNTHESIZABLE RTL. The same matcher in Verilog-2005.
   module i2c_10bit_address_matcher #(
       // The device's own 10-bit address. Bits [9:8] ride in the first byte, bits
       // [7:0] occupy the whole second byte.
       parameter [9:0] OWN_ADDR_10 = 10'h123
   )(
       input  wire clk,
       input  wire rst_n,
       input  wire frame_start,        // pulse: S or Sr observed (Module 5)
       input  wire frame_stop,         // pulse: P observed (Module 5)
       input  wire byte_valid,         // pulse: a byte has been captured
       input  wire [7:0] byte_in,      // that byte, as it appeared on the wire
       output reg  phase1_match,       // the 11110XX prefix matched -- NOT unique
       output wire selected,           // full 10-bit match -- unique to one device
       output reg  rw_latched,         // direction of the transfer that selected us
       output reg  ack_request         // this device wants to ACK the byte just seen
   );
       // Only 11110XX is the 10-bit prefix; 11111XX is reserved for future use.
       localparam [4:0] TENBIT_PREFIX = 5'b11110;

       localparam ST_IDLE  = 3'd0;   // not addressed, no address byte expected
       localparam ST_ADDR1 = 3'd1;   // framing START seen: expecting the first byte
       localparam ST_ADDR2 = 3'd2;   // prefix matched: expecting the second byte
       localparam ST_SEL_W = 3'd3;   // fully selected, master writing
       localparam ST_SEL_R = 3'd4;   // fully selected, master reading

       reg [2:0] state;

       // "After the repeated START condition, a matching slave REMEMBERS that it was
       // addressed before." That memory is this bit, and it is what makes a 10-bit
       // READ possible at all.
       reg was_selected;

       wire prefix_ok = (byte_in[7:1] == {TENBIT_PREFIX, OWN_ADDR_10[9:8]});
       wire second_ok = (byte_in      == OWN_ADDR_10[7:0]);

       always @(posedge clk) begin
           if (!rst_n) begin
               state        <= ST_IDLE;
               was_selected <= 1'b0;
               rw_latched   <= 1'b0;
               phase1_match <= 1'b0;
               ack_request  <= 1'b0;
           end else begin
               ack_request <= 1'b0;

               if (frame_stop) begin
                   state        <= ST_IDLE;
                   was_selected <= 1'b0;
                   phase1_match <= 1'b0;
               end else if (frame_start) begin
                   // S or Sr: was_selected is deliberately NOT cleared -- surviving
                   // the Sr is the whole point of it.
                   state        <= ST_ADDR1;
                   phase1_match <= 1'b0;
               end else if (byte_valid) begin
                   case (state)
                       ST_ADDR1: begin
                           if (prefix_ok && byte_in[0] == 1'b0) begin
                               // Write: several devices reach here, because the prefix
                               // carries only two of the ten address bits.
                               phase1_match <= 1'b1;
                               ack_request  <= 1'b1;
                               rw_latched   <= 1'b0;
                               state        <= ST_ADDR2;
                           end else if (prefix_ok && byte_in[0] == 1'b1 && was_selected) begin
                               // Read: legal ONLY as a re-address of a device already
                               // selected by a full two-byte write phase.
                               phase1_match <= 1'b1;
                               ack_request  <= 1'b1;
                               rw_latched   <= 1'b1;
                               state        <= ST_SEL_R;
                           end else begin
                               phase1_match <= 1'b0;
                               state        <= ST_IDLE;
                           end
                       end

                       ST_ADDR2: begin
                           if (second_ok) begin
                               // Only one device on the bus reaches this point.
                               ack_request  <= 1'b1;
                               was_selected <= 1'b1;
                               state        <= ST_SEL_W;
                           end else begin
                               was_selected <= 1'b0;
                               state        <= ST_IDLE;
                           end
                       end

                       // Once selected, further bytes are DATA -- Module 7's job.
                       ST_SEL_W, ST_SEL_R: ;

                       default: state <= ST_IDLE;
                   endcase
               end
           end
       end

       assign selected = (state == ST_SEL_W) || (state == ST_SEL_R);
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_10bit_address_matcher_tb.v — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same cases in Verilog idiom.
   module i2c_10bit_address_matcher_tb;
       // TWO devices sharing the upper two address bits and differing in the lower
       // eight -- the configuration the specification describes.
       localparam [9:0] ADDR_A = 10'b01_1010_0011;   // 0x1A3
       localparam [9:0] ADDR_B = 10'b01_0101_1100;   // 0x15C
       localparam [4:0] PFX    = 5'b11110;

       reg clk, rst_n, frame_start, frame_stop, byte_valid;
       reg [7:0] byte_in;
       wire p1A, selA, rwA, ackA;
       wire p1B, selB, rwB, ackB;
       integer errors, nackA, nackB;

       i2c_10bit_address_matcher #(.OWN_ADDR_10(ADDR_A)) dutA
           (.clk(clk), .rst_n(rst_n), .frame_start(frame_start), .frame_stop(frame_stop),
            .byte_valid(byte_valid), .byte_in(byte_in),
            .phase1_match(p1A), .selected(selA), .rw_latched(rwA), .ack_request(ackA));
       i2c_10bit_address_matcher #(.OWN_ADDR_10(ADDR_B)) dutB
           (.clk(clk), .rst_n(rst_n), .frame_start(frame_start), .frame_stop(frame_stop),
            .byte_valid(byte_valid), .byte_in(byte_in),
            .phase1_match(p1B), .selected(selB), .rw_latched(rwB), .ack_request(ackB));

       initial clk = 1'b0;
       always #5 clk = ~clk;
       initial begin #40000; $display("FAIL: watchdog expired"); $finish; end

       always @(posedge clk) if (rst_n) begin
           if (ackA) nackA = nackA + 1;
           if (ackB) nackB = nackB + 1;
       end

       task send_byte; input [7:0] b; begin
           byte_in = b; byte_valid = 1'b1; @(negedge clk);
           byte_valid = 1'b0;              @(negedge clk);
       end endtask
       task pulse_start; begin frame_start = 1'b1; @(negedge clk); frame_start = 1'b0; @(negedge clk); end endtask
       task pulse_stop;  begin frame_stop  = 1'b1; @(negedge clk); frame_stop  = 1'b0; @(negedge clk); end endtask

       function [7:0] first_byte; input [9:0] a; input dir; begin
           first_byte = {PFX, a[9:8], dir};
       end endfunction

       task chk; input got; input want; input integer step; begin
           if (got !== want) begin
               $display("FAIL: step %0d -- signal was %0b, expected %0b", step, got, want);
               errors = errors + 1;
           end
       end endtask

       initial begin
           errors = 0; nackA = 0; nackB = 0;
           rst_n = 1'b0; frame_start = 1'b0; frame_stop = 1'b0; byte_valid = 1'b0; byte_in = 8'h00;
           repeat (3) @(negedge clk);
           chk(selA, 1'b0, 0); chk(selB, 1'b0, 0);
           rst_n = 1'b1; @(negedge clk);

           send_byte(first_byte(ADDR_A, 1'b0));
           chk(p1A, 1'b0, 1);
           if (nackA != 0) begin $display("FAIL: acked a byte with no preceding START"); errors = errors + 1; end

           pulse_start();
           send_byte(8'b1010010_0);
           chk(p1A, 1'b0, 2); chk(selA, 1'b0, 2);

           // 11111XX is NOT the 10-bit prefix.
           pulse_start();
           send_byte({5'b11111, ADDR_A[9:8], 1'b0});
           chk(p1A, 1'b0, 3);

           // Both devices answer the FIRST byte.
           nackA = 0; nackB = 0;
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b0));
           chk(p1A, 1'b1, 4); chk(p1B, 1'b1, 4);
           if (nackA != 1 || nackB != 1) begin
               $display("FAIL: both devices must ACK the first byte (A=%0d B=%0d)", nackA, nackB); errors = errors + 1; end
           chk(selA, 1'b0, 4); chk(selB, 1'b0, 4);

           // Only ONE answers the second.
           nackA = 0; nackB = 0;
           send_byte(ADDR_A[7:0]);
           chk(selA, 1'b1, 5); chk(selB, 1'b0, 5);
           if (nackA != 1 || nackB != 0) begin
               $display("FAIL: only the matching device may ACK the second byte (A=%0d B=%0d)", nackA, nackB); errors = errors + 1; end
           chk(rwA, 1'b0, 5);

           // Data bytes are not addresses.
           nackA = 0;
           send_byte(8'hFF); send_byte(8'h00);
           if (nackA != 0) begin $display("FAIL: decoder acked data bytes"); errors = errors + 1; end
           chk(selA, 1'b1, 6);

           // The READ re-address: first byte alone, R/W=1.
           nackA = 0; nackB = 0;
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b1));
           chk(selA, 1'b1, 7); chk(rwA, 1'b1, 7); chk(selB, 1'b0, 7);
           if (nackA != 1 || nackB != 0) begin
               $display("FAIL: only the remembered device may ACK the read re-address (A=%0d B=%0d)", nackA, nackB); errors = errors + 1; end

           // A STOP forgets everything.
           pulse_stop();
           chk(selA, 1'b0, 8);
           nackA = 0;
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b1));
           chk(selA, 1'b0, 8);
           if (nackA != 0) begin
               $display("FAIL: acked a read re-address without ever being selected"); errors = errors + 1; end

           // Sr to a DIFFERENT address deselects this device.
           pulse_stop();
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b0));
           send_byte(ADDR_A[7:0]);
           chk(selA, 1'b1, 9);
           pulse_start();
           send_byte(first_byte(ADDR_A, 1'b0));
           send_byte(ADDR_B[7:0]);
           chk(selA, 1'b0, 9); chk(selB, 1'b1, 9);

           if (errors == 0)
               $display("PASS: prefix shared and acked by both, second byte unique to one, read re-address requires memory");
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_10bit_address_matcher.vhd — SYNTHESIZABLE RTL. The same matcher in VHDL.
   library ieee;
   use ieee.std_logic_1164.all;

   entity i2c_10bit_address_matcher is
       generic (
           -- The device's own 10-bit address. Bits 9..8 ride in the first byte,
           -- bits 7..0 occupy the whole second byte.
           OWN_ADDR_10 : std_logic_vector(9 downto 0) := "0100100011"
       );
       port (
           clk          : in  std_logic;
           rst_n        : in  std_logic;
           frame_start  : in  std_logic;                    -- pulse: S or Sr (Module 5)
           frame_stop   : in  std_logic;                    -- pulse: P (Module 5)
           byte_valid   : in  std_logic;                    -- pulse: a byte captured
           byte_in      : in  std_logic_vector(7 downto 0); -- that byte, as on the wire
           phase1_match : out std_logic;                    -- 11110XX matched -- NOT unique
           selected     : out std_logic;                    -- full match -- unique
           rw_latched   : out std_logic;
           ack_request  : out std_logic
       );
   end entity;

   architecture rtl of i2c_10bit_address_matcher is
       -- Only 11110XX is the 10-bit prefix; 11111XX is reserved for future use.
       constant TENBIT_PREFIX : std_logic_vector(4 downto 0) := "11110";

       type state_t is (
           ST_IDLE,     -- not addressed, no address byte expected
           ST_ADDR1,    -- framing START seen: expecting the first byte
           ST_ADDR2,    -- prefix matched: expecting the second address byte
           ST_SEL_W,    -- fully selected, master writing
           ST_SEL_R     -- fully selected, master reading
       );
       signal state : state_t := ST_IDLE;

       -- "After the repeated START condition, a matching slave REMEMBERS that it was
       -- addressed before." That memory is this bit.
       signal was_selected : std_logic := '0';

       signal prefix_ok, second_ok : std_logic;
   begin
       prefix_ok <= '1' when byte_in(7 downto 1) = (TENBIT_PREFIX & OWN_ADDR_10(9 downto 8))
                    else '0';
       second_ok <= '1' when byte_in = OWN_ADDR_10(7 downto 0) else '0';

       process (clk)
       begin
           if rising_edge(clk) then
               if rst_n = '0' then
                   state        <= ST_IDLE;
                   was_selected <= '0';
                   rw_latched   <= '0';
                   phase1_match <= '0';
                   ack_request  <= '0';
               else
                   ack_request <= '0';

                   if frame_stop = '1' then
                       state        <= ST_IDLE;
                       was_selected <= '0';
                       phase1_match <= '0';
                   elsif frame_start = '1' then
                       -- S or Sr: was_selected is deliberately NOT cleared.
                       state        <= ST_ADDR1;
                       phase1_match <= '0';
                   elsif byte_valid = '1' then
                       case state is
                           when ST_ADDR1 =>
                               if prefix_ok = '1' and byte_in(0) = '0' then
                                   -- Write: several devices reach here, because the
                                   -- prefix carries only two of the ten address bits.
                                   phase1_match <= '1';
                                   ack_request  <= '1';
                                   rw_latched   <= '0';
                                   state        <= ST_ADDR2;
                               elsif prefix_ok = '1' and byte_in(0) = '1'
                                     and was_selected = '1' then
                                   -- Read: legal ONLY as a re-address of a device
                                   -- already selected by a full write phase.
                                   phase1_match <= '1';
                                   ack_request  <= '1';
                                   rw_latched   <= '1';
                                   state        <= ST_SEL_R;
                               else
                                   phase1_match <= '0';
                                   state        <= ST_IDLE;
                               end if;

                           when ST_ADDR2 =>
                               if second_ok = '1' then
                                   -- Only one device on the bus reaches this point.
                                   ack_request  <= '1';
                                   was_selected <= '1';
                                   state        <= ST_SEL_W;
                               else
                                   was_selected <= '0';
                                   state        <= ST_IDLE;
                               end if;

                           -- Once selected, further bytes are DATA -- Module 7's job.
                           when ST_SEL_W | ST_SEL_R =>
                               null;

                           when others =>
                               state <= ST_IDLE;
                       end case;
                   end if;
               end if;
           end if;
       end process;

       selected <= '1' when (state = ST_SEL_W or state = ST_SEL_R) else '0';
   end architecture;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_10bit_address_matcher_tb.vhd — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same cases with assert report severity.
   library ieee;
   use ieee.std_logic_1164.all;

   entity i2c_10bit_address_matcher_tb is
   end entity;

   architecture sim of i2c_10bit_address_matcher_tb is
       -- TWO devices sharing the upper two address bits and differing in the lower
       -- eight -- the configuration the specification describes.
       constant ADDR_A : std_logic_vector(9 downto 0) := "0110100011";   -- 0x1A3
       constant ADDR_B : std_logic_vector(9 downto 0) := "0101011100";   -- 0x15C
       constant PFX    : std_logic_vector(4 downto 0) := "11110";

       signal clk         : std_logic := '0';
       signal rst_n       : std_logic := '0';
       signal frame_start : std_logic := '0';
       signal frame_stop  : std_logic := '0';
       signal byte_valid  : std_logic := '0';
       signal byte_in     : std_logic_vector(7 downto 0) := (others => '0');
       signal p1A, selA, rwA, ackA : std_logic;
       signal p1B, selB, rwB, ackB : std_logic;

       -- Driven by the counting process, read by the checker.
       signal nackA, nackB : natural := 0;
       signal test_done    : std_logic := '0';
   begin
       dutA : entity work.i2c_10bit_address_matcher
           generic map (OWN_ADDR_10 => ADDR_A)
           port map (clk => clk, rst_n => rst_n, frame_start => frame_start,
                     frame_stop => frame_stop, byte_valid => byte_valid, byte_in => byte_in,
                     phase1_match => p1A, selected => selA, rw_latched => rwA, ack_request => ackA);
       dutB : entity work.i2c_10bit_address_matcher
           generic map (OWN_ADDR_10 => ADDR_B)
           port map (clk => clk, rst_n => rst_n, frame_start => frame_start,
                     frame_stop => frame_stop, byte_valid => byte_valid, byte_in => byte_in,
                     phase1_match => p1B, selected => selB, rw_latched => rwB, ack_request => ackB);

       clk <= not clk after 5 ns;

       watchdog : process
       begin
           wait for 40 us;
           if test_done = '0' then
               report "watchdog expired -- the design never reached the expected state"
                   severity failure;
           end if;
           wait;
       end process;

       count : process (clk)
       begin
           if rising_edge(clk) then
               if rst_n = '1' then
                   if ackA = '1' then nackA <= nackA + 1; end if;
                   if ackB = '1' then nackB <= nackB + 1; end if;
               end if;
           end if;
       end process;

       stim : process
           variable errs : natural := 0;
           variable bA, bB : natural := 0;

           procedure waitn (n : in positive) is
           begin
               for i in 1 to n loop wait until falling_edge(clk); end loop;
           end procedure;

           procedure send_byte (b : in std_logic_vector(7 downto 0)) is
           begin
               byte_in <= b; byte_valid <= '1'; waitn(1);
               byte_valid <= '0';               waitn(1);
           end procedure;

           procedure pulse_start is
           begin
               frame_start <= '1'; waitn(1); frame_start <= '0'; waitn(1);
           end procedure;

           procedure pulse_stop is
           begin
               frame_stop <= '1'; waitn(1); frame_stop <= '0'; waitn(1);
           end procedure;

           function first_byte (a : std_logic_vector(9 downto 0); dir : std_logic)
               return std_logic_vector is
           begin
               return PFX & a(9 downto 8) & dir;
           end function;

           procedure chk (got, want : in std_logic; step : in natural) is
           begin
               if got /= want then
                   report "step " & integer'image(step) & ": signal mismatch" severity error;
                   errs := errs + 1;
               end if;
           end procedure;
       begin
           waitn(3);
           chk(selA, '0', 0); chk(selB, '0', 0);
           rst_n <= '1'; waitn(1);

           send_byte(first_byte(ADDR_A, '0'));
           chk(p1A, '0', 1);
           if nackA /= 0 then
               report "acked a byte with no preceding START" severity error; errs := errs + 1; end if;

           pulse_start;
           send_byte("10100100");
           chk(p1A, '0', 2); chk(selA, '0', 2);

           -- 11111XX is NOT the 10-bit prefix.
           pulse_start;
           send_byte("11111" & ADDR_A(9 downto 8) & '0');
           chk(p1A, '0', 3);

           -- Both devices answer the FIRST byte.
           bA := nackA; bB := nackB;
           pulse_start;
           send_byte(first_byte(ADDR_A, '0'));
           chk(p1A, '1', 4); chk(p1B, '1', 4);
           if (nackA - bA) /= 1 or (nackB - bB) /= 1 then
               report "both devices must ACK the first byte" severity error; errs := errs + 1; end if;
           chk(selA, '0', 4); chk(selB, '0', 4);

           -- Only ONE answers the second.
           bA := nackA; bB := nackB;
           send_byte(ADDR_A(7 downto 0));
           chk(selA, '1', 5); chk(selB, '0', 5);
           if (nackA - bA) /= 1 or (nackB - bB) /= 0 then
               report "only the matching device may ACK the second byte" severity error; errs := errs + 1; end if;
           chk(rwA, '0', 5);

           -- Data bytes are not addresses.
           bA := nackA;
           send_byte("11111111"); send_byte("00000000");
           if (nackA - bA) /= 0 then
               report "decoder acked data bytes" severity error; errs := errs + 1; end if;
           chk(selA, '1', 6);

           -- The READ re-address: first byte alone, R/W = 1.
           bA := nackA; bB := nackB;
           pulse_start;
           send_byte(first_byte(ADDR_A, '1'));
           chk(selA, '1', 7); chk(rwA, '1', 7); chk(selB, '0', 7);
           if (nackA - bA) /= 1 or (nackB - bB) /= 0 then
               report "only the remembered device may ACK the read re-address" severity error;
               errs := errs + 1; end if;

           -- A STOP forgets everything.
           pulse_stop;
           chk(selA, '0', 8);
           bA := nackA;
           pulse_start;
           send_byte(first_byte(ADDR_A, '1'));
           chk(selA, '0', 8);
           if (nackA - bA) /= 0 then
               report "acked a read re-address without ever being selected" severity error;
               errs := errs + 1; end if;

           -- Sr to a DIFFERENT address deselects this device.
           pulse_stop;
           pulse_start;
           send_byte(first_byte(ADDR_A, '0'));
           send_byte(ADDR_A(7 downto 0));
           chk(selA, '1', 9);
           pulse_start;
           send_byte(first_byte(ADDR_A, '0'));
           send_byte(ADDR_B(7 downto 0));
           chk(selA, '0', 9); chk(selB, '1', 9);

           if errs = 0 then
               report "i2c_10bit_address_matcher self-check complete: prefix shared and acked by "
                    & "both, second byte unique to one, read re-address requires memory" severity note;
           else
               report "i2c_10bit_address_matcher self-check FAILED" severity error;
           end if;
           test_done <= '1';
           wait;
       end process;
   end architecture;

6a. Verified Execution and Cross-Language Parity

languagesimulatorresultcompletes at
SystemVerilogIcarus Verilog, -g2012PASS480 ns
Verilog-2005Icarus Verilog, -g2005PASS480 ns
VHDLnvc 1.23.0PASS480 ns
SystemVerilogVerilog-2005VHDL
own addressparameter logic [9:0]parameter [9:0]std_logic_vector(9 downto 0) generic
statestypedef enum logic [2:0]localparam constantstype state_t is (...)
prefix compareassign on a part-selectwire on a part-selectconditional signal assignment
memory bitlogic was_selectedreg was_selectedsignal was_selected
no-op statesST_SEL_W, ST_SEL_R: ;ST_SEL_W, ST_SEL_R: ;when ST_SEL_W | ST_SEL_R => null;

The comparisons are written as continuous assignments rather than inside a combinational process in all three, which is both idiomatic and avoids a sensitivity warning some tools emit for a part-select inside always_comb.

i2c_10bit_address_matcher — prefix, then unique match

10 cycles
Ten internal clock cycles. A framing start pulse occurs in the first cycle. A byte valid pulse in the second cycle carries 0xF2 and phase one match rises in the following cycle together with an acknowledge request. A second byte valid pulse carries 0xA3 and the selected output rises with a second acknowledge request.first byte: prefixfirst byte: prefixprefix matched — ACKprefix matched — ACKsecond bytesecond byteunique match — selectedunique match — selectedclkframe_startbyte_validbyte_in--F2F2F2F2A3A3A3A3A3phase1_matchselectedack_requestt0t1t2t3t4t5t6t7t8t9
Figure 4 — the two-phase match completing, from the simulation. A framing START arms the address phase. The first byte 0xF2 matches the prefix, so phase1_match asserts and the device acknowledges — an acknowledge several devices may be driving simultaneously. The second byte 0xA3 matches this device uniquely, so selected asserts and it acknowledges again. SIMULATION-DERIVED figure, on the internal clock.

7. What the Testbench Proves, and How

The suite instantiates two matchers whose addresses share the upper two bits and differ in the lower eight — 0x1A3 and 0x15C. That configuration is the specification's own scenario, and it is what makes §2's central claim testable rather than merely stated.

stepstimulusrequired result
1a valid first byte with no framing STARTneither device acknowledges
2an ordinary seven-bit addressno prefix match — coexistence
3prefix 11111XXno match — only 11110XX is the prefix
4the shared first byteboth devices match and acknowledge
5device A's second byteonly A selected, only A acknowledges
6data bytes after selectionneither device acknowledges — not addresses
7Sr, then the first byte with R/W = 1only A re-selected, for read
8STOP, then the same read re-addressnobody answers — the memory is gone
9Sr, then a first byte matching both, then B's second byteA deselected, B selected

Step 4 and step 5 are the pair that matters. Counting acknowledges from two devices separately is what turns "more than one device may acknowledge" from a sentence in a specification into a checked property. A single-device testbench cannot express it at all.

Step 8 is the one that proves the memory is scoped correctly. A device that kept was_selected across a STOP would answer a read re-address it was never selected for — and on a shared bus, would answer a read intended for a completely different device that happens to share its prefix. The failure is a device transmitting when it should be silent, which is the worst class of bus failure because it corrupts another transfer rather than merely failing its own.

Step 9 is the conditional release from §4. A repeated START followed by a different address must release this device, and the only way to test it is to send an address that matches the prefix but not the second byte.

8. Mutation Testing

Five faults injected into the verified RTL, testbench run against each. All five caught.

mutationwhat it breaksresult
accept 11111XX as the prefixclaims space reserved for future enhancementsFAIL — step 3
allow a read re-address with no prior selectiona device answers reads it was never selected forFAIL — step 7, device B answered
a repeated START forgets the earlier selectionthe read re-address stops working entirelyFAIL — step 7, device A did not answer
a STOP does not forget the selectionselection leaks across transactionsFAIL — step 8
acknowledge the second byte regardless of matchevery prefix-sharing device claims to be the targetFAIL — step 5, device B selected

The third and fourth are worth putting side by side, because they are the same state bit cleared in the wrong place, and they fail in opposite directions.

Clearing was_selected on a repeated START breaks reads: the device forgets between the write phase and the re-address, so nothing answers and the read times out. Not clearing it on a STOP breaks isolation: the device remembers across transaction boundaries, so it answers a re-address belonging to a different transaction entirely.

One is a device that goes silent; the other is a device that speaks out of turn. A suite that tested only reads would catch the first and miss the second, and the one it missed is the one that corrupts other devices' transfers.

9. Why It Was Not Adopted

The specification says 10-bit addressing is not widely used and does not say why. The reasons are worth reasoning through, because they generalise.

It costs a byte on every single transfer. A 10-bit write spends two address bytes where a seven-bit write spends one. On a bus whose whole value proposition is low cost and adequate speed for control traffic, adding 30% overhead to short transfers to solve a problem that is usually solvable another way is a poor trade.

Reads cost far more than that. §3's read requires a full write-style two-byte addressing phase, a repeated START, and a third address byte — four address bytes and an extra framing event to read one register. For the register-pointer pattern that dominates real sensor access (Chapter 10.1), that is a substantial penalty on the most common operation there is.

It needs slave-side state that seven-bit addressing does not. The was_selected bit is not free: it is state that must be reset correctly, that interacts with framing in a way §8 shows is easy to get wrong in two directions, and that has to be right in every device. Seven-bit matching is a comparator. 10-bit matching is a state machine.

The problem it solves had cheaper answers. Chapter 6.4 covers them: strap pins that let one part take several addresses, bus multiplexers that let the same address appear on two segments, and simply choosing parts whose addresses do not collide. All of those work with unmodified seven-bit devices and cost no protocol overhead. A mux costs a component; 10-bit addressing costs every transfer forever.

And the escape hatch undercut it. Chapter 6.3 quotes the specification's own note that a reserved address may be used as a slave address when its reserved purpose will never be needed. A board that knows it has no CBUS devices and no Hs-mode can reclaim addresses from the reserved groups — which relieves the pressure that 10-bit addressing existed to relieve.

10. Verification Connection — Covering a Two-Phase Address

A 10-bit address phase has states a seven-bit one does not, and the interesting coverage is over those states rather than over address values.

Azvya Education Pvt. Ltd.VLSI Mentor
UVM CONCEPT — VERIFICATION ONLY. Covering the 10-bit address phase, not the address space.
   // Randomising 1024 address values proves almost nothing: every value exercises
   // the same three states. What matters is WHICH PHASE TRANSITIONS occurred, and
   // whether the multi-device cases were ever built.
   covergroup tenbit_phase_cg @(posedge clk);
       option.per_instance = 1;

       // How far into the two-phase match did this transfer get?
       cp_depth : coverpoint match_depth iff (phase_event) {
           bins no_prefix     = { DEPTH_NONE };      // not a 10-bit transfer
           bins prefix_only   = { DEPTH_PREFIX };    // matched byte 1, not byte 2
           bins fully_matched = { DEPTH_FULL };      // matched both
       }

       // The direction of the transfer that selected the device, crossed with
       // depth -- because a read can only reach DEPTH_FULL via a re-address, and
       // that path has its own bug (section 8).
       cp_dir_x_depth : cross cp_depth, cp_dir;

       // The release condition actually exercised. Step 9 of section 7 is the
       // 'sr_other_addr' bin, and it is the one directed tests forget.
       cp_release : coverpoint release_reason iff (release_event) {
           bins stop            = { REL_STOP };
           bins sr_same_addr    = { REL_SR_SAME };     // the read re-address
           bins sr_other_addr   = { REL_SR_OTHER };    // handover to another device
           bins second_mismatch = { REL_BYTE2_MISS };  // prefix matched, device did not
       }
   endgroup

   // And the property that a single-device environment cannot express at all.
   // It needs TWO agents sharing a prefix, which is a TOPOLOGY requirement on the
   // testbench rather than a stimulus requirement.
   property first_ack_may_be_multiple;
       @(posedge clk) disable iff (!rst_n)
       (byte_valid && prefix_matches) |=> (num_acking_devices >= 1);
   endproperty

Two observations that are the point.

prefix_only is a coverage bin, not an error. A transfer where a device matched the first byte and not the second is the specification's normal behaviour for every device that shares a prefix with the target. A coverage model that treated it as an error case would misclassify the majority of what happens on a working 10-bit bus.

The multi-device property is a testbench topology requirement. No amount of stimulus on a single-agent environment can exercise "more than one device acknowledges", because there is only one device. This is worth naming because stimulus coverage is the usual lever and here it is the wrong one — the gap is in how the environment is built, not in what it sends. §7's suite solves it by instantiating two matchers, which is the block-level equivalent of the same decision.

11. FPGA and ASIC Implications

The cost over seven-bit matching is one state machine and one flip-flop. The comparators are the same width in total; what 10-bit addressing adds is sequencing. On an FPGA this is negligible; on a small ASIC peripheral it is still negligible. The reason 10-bit support is uncommon in silicon is not area, it is that nobody asks for it.

The memory bit's reset domain matters, for the same reason it did in Chapter 5.4. was_selected must be cleared by the same reset that clears the framing state. Splitting them means a peripheral reset mid-transaction can leave a device remembering a selection whose transaction no longer exists — and §8's fourth mutation is what that looks like.

A 10-bit slave must still decode the general call and the reserved map. The prefix is reserved space, and so is everything else in the two reserved groups; a 10-bit device is not exempt from Chapter 6.3's prohibitions. In practice this means the classifier and the 10-bit matcher sit side by side, and the classifier's verdict has priority — a point Chapter 6.5 makes concrete.

If you are designing a master, supporting 10-bit addressing is mostly a software question. The hardware already sends bytes; what changes is the driver's addressing sequence and the peripheral's ability to emit a repeated START at the right point. A controller that cannot emit an Sr cannot read from a 10-bit device at all, which ties this chapter directly to Chapter 5.5's sequencer.

12. Debugging — The 10-Bit Device That Could Be Written But Never Read

A device that accepted every write and answered no read

Pitfall — releasing the 10-bit selection at the repeated START
Buggy Code
// A slave implements 10-bit matching, and implements the release rule from the
// specification by reading it slightly too eagerly. The specification says the
// device remains addressed "until it receives a STOP condition (P) or a repeated
// START condition (Sr) followed by a different slave address" -- and the design
// treats the Sr itself as the release:
//
//   if (frame_stop) begin
//       state        <= ST_IDLE;
//       was_selected <= 1'b0;
//   end else if (frame_start) begin
//       state        <= ST_ADDR1;
//       was_selected <= 1'b0;        // <-- "a new address phase, so forget"
//   end
//
// It reads as tidy symmetry: a framing event resets the addressing state, so
// reset all of it. And for WRITES it is completely correct -- a write never needs
// the memory, because both address bytes arrive in the same phase.
Symptom

Writes work perfectly. Every register write to the device is acknowledged twice, lands correctly, and reads back correctly THROUGH A DIFFERENT PATH -- for example via a debug interface or a subsequent power-cycle dump.

Reads over I2C never work. The master addresses the device with the two-byte write-style phase, gets both acknowledges, issues the repeated START, sends the first byte with R/W = 1 -- and gets a NACK. Every time, deterministically.

Deterministic failures are usually easy, and this one is not, because the capture looks correct. Both acknowledges in the write phase are present. The repeated START is well formed. The re-address byte is bit-for-bit identical to the first byte of the write phase except for the direction bit, which is exactly what the specification shows in its own figure. Nothing on the wire is wrong.

And the master is not wrong either, which sends the investigation in circles: the master's sequence matches the specification figure step for step.

Root Cause

The device forgot, one framing event too early.

The read re-address carries only TWO of the ten address bits. It is not a complete address and cannot be -- section 3 derives why. What makes it unambiguous is that exactly one device on the bus remembers having matched all ten during the write phase. That memory is the only thing distinguishing the intended device from every other device sharing its prefix.

Clearing the memory at the Sr destroys precisely that. When the re-address arrives, the device sees a prefix match with R/W = 1 and no record of having been selected -- and correctly refuses, because a device that answered a read re-address without that record would be answering reads intended for other devices. The refusal is the CORRECT response to the state the device is in. The bug is that the state is wrong.

The reason it reads as tidy is that the symmetry is false. A framing START must clear the SELECTION -- the device is no longer the active target until the new address says so -- but must NOT clear the MEMORY, because the memory is what the new address will be interpreted against. Section 4 separates the two deliberately, and they have different clearing conditions:

selected cleared by a framing START, or a STOP was_selected cleared by a STOP, or a second-byte mismatch

Note also why writes hid it completely. A write needs no memory at all: both address bytes arrive within one address phase, so the state machine walks ST_ADDR1 -> ST_ADDR2 -> ST_SEL_W without ever consulting was_selected. The bit is write-only during a write. Any test suite built from writes -- which is what you write first, because writes are simpler -- exercises the buggy line zero times.

Fix
// Keep the memory across a framing START. One line removed:
//
//   end else if (frame_start) begin
//       state        <= ST_ADDR1;
//       // was_selected deliberately NOT cleared: surviving the Sr is the whole
//       // point of it, and the read re-address is interpreted against it.
//       phase1_match <= 1'b0;
//   end
//
// The verification lesson is the transferable part, and there are two halves.
//
//   1. TEST THE READ PATH SEPARATELY FROM THE WRITE PATH. They share a state
//      machine and exercise disjoint parts of it. Section 7 step 7 is the read
//      re-address and it fails immediately against this RTL; section 8 confirms
//      that mutation is caught by that step and nothing else.
//
//   2. TEST THE MEMORY'S SCOPE IN BOTH DIRECTIONS. Forgetting too early breaks
//      reads (this bug). Forgetting too late breaks isolation -- a device that
//      keeps was_selected across a STOP answers a re-address from a different
//      transaction, which corrupts another device's read rather than failing its
//      own. Section 7 step 8 covers that direction. A suite with only one of the
//      two checks passes one of the two bugs, and the one it passes is the one
//      that damages other devices.
//
// The reading habit: when a specification says X is released by "A, or B followed
// by C", the release is conditional and the device cannot decide at B. It has to
// hold state through B and resolve at C. Implementing it as "released by A or B"
// is simpler, passes the obvious tests, and is a different protocol.
//
// The debugging habit for this symptom shape: "writes work, reads do not, and the
// capture is correct" points at SLAVE-SIDE STATE, not at the wire. When both
// sides match the specification figure and the transfer still fails, the
// disagreement is about something neither side transmits.

13. Common Misconceptions

"10-bit addressing sends a 10-bit address." It sends two bytes: five reserved prefix bits plus two address bits plus direction, then eight address bits. The second byte has no direction bit.

"1111XXX is the 10-bit prefix." Only 11110XX is. The other four combinations, 11111XX, are reserved for future enhancements, and a matcher that accepts them claims space that is not its own.

"An acknowledge on the first address byte means the target is present." It means at least one device shares the top two address bits. Several devices may be acknowledging simultaneously, and the wired-AND makes that indistinguishable from one. Only the second acknowledge identifies the target.

"Several devices driving the acknowledge is a bus conflict." It is the wired-AND working as designed: several devices pulling the same line low produce the same level as one. The specification explicitly permits it here.

"A 10-bit read re-addresses with both bytes." With the first byte only. The full two-byte address is sent once, as a write-style phase, and the re-address after the Sr carries just the prefix byte with R/W = 1.

"A repeated START releases a selected 10-bit device." Only if the address following it belongs to somebody else. The device must hold its selection through the start of the new address phase and resolve when the address arrives — and §12 is what releasing early costs.

"A device that passes write tests has a working address matcher." A write never consults the read-path memory, so a write-only suite exercises none of it. §12's bug is invisible to every write test that could be written.

"10-bit addressing is unused because it is badly designed." It is well specified and backwards compatible. It is unused because it costs a byte on every transfer and four on every read, needs slave-side state, and competes against multiplexers and strap pins that cost nothing per transfer — and because the specification marks it optional while seven-bit is mandatory.

14. Reason It Through

A 10-bit write is acknowledged on the first byte but NACKed on the second. What does that tell you, and what does it not?

It tells you that at least one device on the bus has a 10-bit address whose top two bits match the ones you sent, and that no device matched the low eight. So the prefix is right and the low byte is wrong — or the intended device is absent while a prefix-sharing neighbour is present. What it does not tell you is which, and it does not tell you anything about the intended device at all: the first acknowledge may have come entirely from other devices.

Why can a 10-bit read not simply set R/W = 1 in the first byte and send both address bytes?

Because the bus turns around at the acknowledge following the address byte that carries the direction. With R/W = 1 in the first byte, the slave becomes the transmitter before the second address byte is sent — so the remaining eight address bits would have to be transmitted by the device being addressed, which cannot know them yet. The specification's answer is to address fully as a write, then re-address after a repeated START.

Why is was_selected cleared by a STOP but not by a repeated START?

Because the read re-address is preceded by a repeated START, so clearing there destroys the memory the re-address is interpreted against. A STOP, by contrast, ends the transaction entirely — Chapter 5.3 established that it releases every device unconditionally — so keeping the memory past a STOP would let a device answer a re-address belonging to a different transaction. The two clearing conditions are different because they mean different things.

A testbench has one 10-bit slave agent. Which specification behaviour can it not verify, and is that a stimulus problem?

That more than one device may acknowledge the first address byte. It is not a stimulus problem — no sequence of bytes can make one agent behave like two — it is a topology problem in how the environment is built. The fix is a second agent sharing the prefix, which is why §7's suite instantiates two matchers rather than sending more addresses to one.

Your team is short of addresses on a board. What would you compare 10-bit addressing against before choosing it?

Strap pins on the colliding parts, a bus multiplexer or switch to put the same address on two segments, selecting an alternative part with a different fixed address, and — if the board provably has no CBUS, Hs-mode or device-ID traffic — reclaiming addresses from the reserved groups, which the specification explicitly permits. All of those work with unmodified seven-bit parts and cost nothing per transfer, whereas 10-bit addressing costs a byte on every write and four address bytes on every read, and requires every participating device to support it.

15. Understanding Check

16. Summary

10-bit addressing is built out of reserved space. The first byte is an ordinary address byte whose seven bits are 11110 plus the top two address bits, followed by the direction bit; the second byte is eight bits of pure address.

Only 11110XX is the prefix. 11111XX is reserved for future enhancements.

The first byte is deliberately not unique. It carries two of ten address bits, so several devices may match it and acknowledge simultaneously — permitted by the specification and invisible on the wire thanks to the wired-AND. Only the second acknowledge identifies the target.

A read re-addresses with the first byte alone, after a full write-style addressing phase and a repeated START. What disambiguates it is that exactly one device remembers matching all ten bits.

That memory makes the repeated START load-bearing. A STOP between the phases would erase it, so 10-bit reads exist only because Chapter 5.4's mechanism keeps both the bus and the state.

Selection and memory are two pieces of state with different clearing conditions, and conflating them breaks reads in one direction and isolation in the other — §8 injects both.

Writes exercise none of the read-path state, which is why a write-only suite can pass a device that can never be read.

It went unused for economic rather than technical reasons: an extra byte per transfer, four per read, slave-side state, and cheaper alternatives — with the specification's own optional-versus-mandatory table settling it.

17. What Comes Next

This chapter used the reserved prefix 11110XX without ever establishing what else is reserved, why, or how much address space a board actually has. It also referred twice to the specification's escape hatch for reclaiming reserved addresses without quoting it.

Chapter 6.3 lays out the whole map: both reserved groups, every purpose assigned to them, the two footnotes that forbid a device from responding, the general call and what its second byte means, and the arithmetic that turns 128 addresses into the 112 a board can actually allocate — verified by exhaustive simulation rather than asserted.

Browse the full path on the I²C tutorials index. For the single-byte form this extends, see The Address Byte; for the framing event its read path depends on, Repeated START.

Continue learning