Skip to content
VLSI Mentor

I²C · Module 6

Address Allocation, Strapping, Conflicts and Bus Switches

112 addresses are available and boards still collide constantly, because almost no part lets you choose freely. How real devices move their address, what a strap pin is in silicon, how strapping can land a device in reserved space, and how muxes make one address appear twice.

Chapter 6.3 established that a board has 112 seven-bit addresses to allocate. That sounds comfortable. In practice, two parts colliding is one of the most common problems in I²C board design, and it is worth understanding why before looking at the fixes.

This chapter is the most board-oriented in the module. The RTL in it is small, and the reason it exists is that the most expensive addressing mistake in the curriculum so far — a device that silently occupies a reserved address — is a configuration failure that hardware can and should refuse.

1. Why 112 Addresses Are Not Enough

Three compounding reasons, none of which is about the size of the space.

Almost no part lets you choose freely. The specification's own example describes the ideal case — "a device with seven user-assignable address pins allows all 128 addresses to be assigned" — and that part essentially does not exist. Real devices offer zero, one, two or three strap pins, which is one, two, four or eight addresses out of 128.

Vendors cluster. A temperature sensor is very likely to sit somewhere in 0x48–0x4F; an EEPROM in 0x50–0x57; a PMIC or RTC in 0x68–0x6F. These are conventions rather than rules, and they exist because parts are designed to be drop-in replacements for the parts they compete with. The effect is that the 112 addresses are not uniformly occupied: a handful of neighbourhoods are crowded and much of the space is empty.

The parts are chosen before the address map is. Address allocation is usually discovered, not designed. A schematic gets assembled from parts selected for their function, and the collision is found when somebody writes down the addresses — which is often after the board is laid out.

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — what a strap pin actually buys
   n strap pins  ->  2^n addresses reachable by one part

     0 pins   1 address     fixed. The part is where it is.
     1 pin    2 addresses   enough for two of the same part.
     2 pins   4 addresses
     3 pins   8 addresses   the common maximum on small sensors.
     7 pins   128 addresses the specification's example; vanishingly rare.

   And the window is CONTIGUOUS and ALIGNED, because the straps replace the low
   bits of a fixed base:

     base 0x48, 3 straps  ->  0x48 .. 0x4F     and nowhere else

   So "this part supports eight addresses" does not mean eight addresses of your
   choosing. It means eight SPECIFIC addresses, and if another part needs one of
   them the conflict is not resolved by strapping at all.

That last point is the one that turns an arithmetic problem into an engineering one. Two temperature sensors from different vendors, each with three strap pins and each based at 0x48, offer eight addresses each — the same eight. Sixteen strap combinations, eight usable addresses, and a guaranteed collision if you need nine devices.

2. How Real Parts Let You Move

Four mechanisms, in rough order of how often you meet them.

Fixed. The address is in the silicon and cannot change. Two of the part cannot share a segment, full stop. This is common on very small or very cheap devices where pins cost more than flexibility.

Strap pins. One or more pins are sampled and drive the low bits of the address. §3 covers what this means in silicon, because it is less simple than it sounds.

Programmable by the bus itself. Chapter 6.3 quoted the general call second bytes 04h and 06h, both of which "write programmable part of slave address by hardware". A device supporting this takes its address from the bus during initialisation. It is elegant and rare, and it has a bootstrapping problem: if several such devices are present they all take the same programmed address unless the mechanism includes some other way to distinguish them.

One-time or non-volatile programming. Some parts hold their address in OTP or EEPROM, set in production or at first power-on. This solves the collision permanently and moves the problem into manufacturing.

A controller connects to a shared two-wire bus. Three instances of the same sensor part hang off that bus. Each sensor has the same base address of hex 48 but a different strap pin setting, giving them three different effective addresses. The bus is shared by all three devices.Controllerone address space to allocateSDA and SCLevery device sees every bitSensor, straps 000base 0x48, answers 0x48Sensor, straps 001base 0x48, answers 0x49Sensor, straps 010base 0x48, answers 0x4A12
Figure 1 — three instances of one part on a single segment, separated by strap pins. Every device sees every transfer; what makes them distinguishable is that each answers a different address, and each address is the same fixed base with different low bits supplied by its pins. Note that the straps are board wiring, not bus traffic — the controller never learns them and never needs to.

3. What a Strap Pin Is in Silicon

A strap pin is not a configuration register. It is an input whose level is read as a value rather than acted on as a signal, and the details matter because they are where strapping goes wrong.

It is sampled, and when it is sampled matters. Most parts sample straps at power-on or at reset release and latch the result. A strap that is not stable at that moment produces an address the device keeps until the next reset — which is why strap pins get pulled hard rather than driven by anything that might still be initialising.

Tied, not driven. Straps are normally tied directly to supply or ground, or pulled with a resistor. A strap driven by another device's GPIO is a dependency on that device's reset timing, and a strap left floating is an address nobody can predict.

Some parts encode more than one bit per pin. A tri-level strap — low, high, or left floating with a weak internal bias — gives three states per pin; some parts decode a resistor value to give more. The specification says nothing about any of this; it is a device-level technique for getting more addresses out of fewer pins, and the datasheet is the only authority.

Some parts overlay straps on functional pins. A pin that is a strap during reset and an output afterwards saves a package pin at the cost of a real constraint: whatever is connected to it must not fight the strap during sampling, and must tolerate being driven afterwards.

The resolution itself is simple, and worth writing down as logic because it is the part that ends up in RTL:

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — resolving a strapped address
   The straps replace the LOW bits of a fixed base. With a mask of the strapped
   positions:

       strap_mask      = (1 << STRAP_BITS) - 1
       effective_addr  = (BASE_ADDR & ~strap_mask) | (strap_pins & strap_mask)
                          ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^
                          the fixed bits            the strapped bits

   Worked, for BASE_ADDR = 0x48 and three strap pins reading 101:

       strap_mask     = (1 << 3) - 1  = 0000111
       BASE & ~mask   = 1001000 & 1111000 = 1001000
       straps & mask  = 0000101 & 0000111 = 0000101
       effective      = 1001000 | 0000101 = 1001101 = 0x4D          in 0x48..0x4F

   Building the mask in eight bits and then narrowing it keeps STRAP_BITS = 0 and
   STRAP_BITS = 7 from being special cases: at 7, (1 << 7) - 1 = 0x7F, so the
   straps own the whole address, which is the specification's own example.

4. The Trap: A Strapped Address in Reserved Space

Here is the failure this chapter's RTL exists to prevent, and it is a genuine board-design mistake rather than a hypothetical.

A part is based at 0x78 and has three strap pins. Its datasheet presents this as "eight selectable addresses, 0x78–0x7F". Every one of those eight addresses is in the upper reserved group that Chapter 6.3 mapped: 1111XXX. Four of them are the 10-bit addressing prefix 11110XX; the other four are 11111XX, which includes the device-ID row.

A board using that part in that configuration has a device answering addresses the bus has reserved. It usually works — until something on the bus uses 10-bit addressing, or issues a device-ID request, or a future revision adds a part that does. Then two devices answer one address and the transfer is corrupt.

The important observation is who can detect this. Not the controller: from its side the device answers normally. Not the bus: the bus has no opinion. The device itself can — it knows its own effective address, and comparing that address against the reserved groups is four bits of logic.

5. The Resolver in Three Languages

Combinational: a mask, two gated terms, and a group test. Small enough that the interesting content is the parameterisation and the two-output split.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_address_strap_resolver.sv — SYNTHESIZABLE RTL. Base address plus strap pins, with a reserved-landing check.
   // Combines the fixed part of a device's address with the strap pins a board
   // drives, and reports whether the resulting address is legal to use.
   module i2c_address_strap_resolver #(
       // The address from the datasheet, with the strappable bits shown at their
       // default (usually zero) value.
       parameter logic [6:0] BASE_ADDR      = 7'b1001000,
       // How many of the LOW address bits the board's strap pins own.
       parameter int         STRAP_BITS     = 3,
       // The specification's own escape hatch: "If it is known that the reserved
       // address is never going to be used for its intended purpose, a reserved
       // address can be used for a slave address." Set this only as a DELIBERATE,
       // documented system decision -- never to silence the flag.
       parameter bit         ALLOW_RESERVED = 1'b0
   )(
       input  logic [STRAP_BITS-1:0] strap_pins,      // sampled at power-on
       output logic [6:0]            effective_addr,
       output logic                  addr_reserved,   // lands in 0000XXX or 1111XXX
       output logic                  addr_illegal     // reserved and not opted into
   );
       // Build the mask in eight bits and then narrow it, so STRAP_BITS = 0 and
       // STRAP_BITS = 7 are both well defined rather than edge cases.
       localparam logic [7:0] MASK8      = (8'd1 << STRAP_BITS) - 8'd1;
       localparam logic [6:0] STRAP_MASK = MASK8[6:0];

       // Zero-extend the pins to the full address width before masking. A plain
       // assignment to a wider vector zero-extends, which avoids a zero-width
       // replication when STRAP_BITS is 7.
       logic [6:0] strap_ext;
       assign strap_ext = strap_pins;

       // The strapped bits come from the pins; every other bit comes from the part.
       assign effective_addr = (BASE_ADDR & ~STRAP_MASK) | (strap_ext & STRAP_MASK);

       // Same group test as the address classifier, and deliberately so: a device
       // must not take an address that the bus has reserved for a purpose.
       assign addr_reserved = (effective_addr[6:3] == 4'b0000)
                           || (effective_addr[6:3] == 4'b1111);

       assign addr_illegal  = addr_reserved && !ALLOW_RESERVED;
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_address_strap_resolver_tb.sv — SELF-CHECKING TESTBENCH, SIMULATION ONLY. Five configurations; sweeps all 128 strap settings.
   module i2c_address_strap_resolver_tb;
       int errors = 0;

       initial begin #20000; $display("FAIL: watchdog expired"); $finish; end

       // ---- four configurations, each making a different point ----
       logic [2:0] strap3;
       logic [6:0] strap7;

       // N: an ordinary part. 0x48 base, three strap pins -> 0x48..0x4F, all legal.
       logic [6:0] addrN; logic rsvN, illN;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h48), .STRAP_BITS(3), .ALLOW_RESERVED(1'b0))
           dutN (.strap_pins(strap3), .effective_addr(addrN), .addr_reserved(rsvN), .addr_illegal(illN));

       // R: the trap. 0x78 base, three strap pins -> 0x78..0x7F, every one reserved.
       logic [6:0] addrR; logic rsvR, illR;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h78), .STRAP_BITS(3), .ALLOW_RESERVED(1'b0))
           dutR (.strap_pins(strap3), .effective_addr(addrR), .addr_reserved(rsvR), .addr_illegal(illR));

       // A: the same trap, opted into deliberately. Still reserved, no longer illegal.
       logic [6:0] addrA; logic rsvA, illA;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h78), .STRAP_BITS(3), .ALLOW_RESERVED(1'b1))
           dutA (.strap_pins(strap3), .effective_addr(addrA), .addr_reserved(rsvA), .addr_illegal(illA));

       // F: "a device with seven user-assignable address pins allows all 128
       //     addresses to be assigned" -- the specification's own example.
       logic [6:0] addrF; logic rsvF, illF;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h00), .STRAP_BITS(7), .ALLOW_RESERVED(1'b0))
           dutF (.strap_pins(strap7), .effective_addr(addrF), .addr_reserved(rsvF), .addr_illegal(illF));

       // Z: no strap pins at all. A fixed-address part.
       logic [6:0] addrZ; logic rsvZ, illZ;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h48), .STRAP_BITS(1), .ALLOW_RESERVED(1'b0))
           dutZ (.strap_pins(strap3[0:0]), .effective_addr(addrZ), .addr_reserved(rsvZ), .addr_illegal(illZ));

       int n_rsv_sweep = 0;

       initial begin
           // 1 -- an ordinary part: the straps select consecutive addresses, and the
           //      fixed bits above them never move.
           for (int s = 0; s < 8; s++) begin
               strap3 = s[2:0]; #1;
               if (addrN !== (7'h48 | s[6:0])) begin
                   $display("FAIL: strap=%0d -- effective 0x%02h, expected 0x%02h", s, addrN, 7'h48 | s[6:0]);
                   errors++;
               end
               if (rsvN !== 1'b0 || illN !== 1'b0) begin
                   $display("FAIL: strap=%0d -- 0x%02h wrongly flagged reserved", s, addrN);
                   errors++;
               end
           end

           // 2 -- THE TRAP. Every strap setting lands in the upper reserved group, so
           //      every one is flagged. This is a part whose datasheet address plus a
           //      board's strapping produce an address the bus has reserved.
           for (int s = 0; s < 8; s++) begin
               strap3 = s[2:0]; #1;
               if (rsvR !== 1'b1 || illR !== 1'b1) begin
                   $display("FAIL: strap=%0d -- 0x%02h in 1111XXX must be illegal", s, addrR);
                   errors++;
               end
           end

           // 3 -- the escape hatch. The address is STILL reserved -- that is a fact
           //      about the bus, not an opinion -- but the system has opted in, so it
           //      is no longer flagged as an error.
           strap3 = 3'd5; #1;
           if (rsvA !== 1'b1) begin
               $display("FAIL: opting in must not change whether the address is reserved"); errors++; end
           if (illA !== 1'b0) begin
               $display("FAIL: a deliberate opt-in must clear addr_illegal"); errors++; end

           // 4 -- seven strap pins reach all 128 addresses, and exactly 16 of them
           //      are reserved. This COUNTS the reserved space from a completely
           //      different direction than the classifier's sweep does.
           for (int s = 0; s < 128; s++) begin
               strap7 = s[6:0]; #1;
               if (addrF !== s[6:0]) begin
                   $display("FAIL: seven straps must reach 0x%02h, got 0x%02h", s, addrF); errors++; end
               if (rsvF) n_rsv_sweep++;
           end
           if (n_rsv_sweep != 16) begin
               $display("FAIL: %0d of 128 strap settings reserved, expected 16", n_rsv_sweep);
               errors++;
           end

           // 5 -- one strap pin moves only the lowest bit.
           strap3 = 3'b000; #1;
           if (addrZ !== 7'h48) begin $display("FAIL: one strap low -> 0x%02h", addrZ); errors++; end
           strap3 = 3'b111; #1;
           if (addrZ !== 7'h49) begin
               $display("FAIL: one strap pin must move only bit 0, got 0x%02h", addrZ); errors++; end

           if (errors == 0)
               $display("PASS: straps resolve correctly, reserved landings flagged, %0d of 128 reserved",
                        n_rsv_sweep);
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_address_strap_resolver.v — SYNTHESIZABLE RTL. The same resolver in Verilog-2005.
   // Combines the fixed part of a device's address with the strap pins a board
   // drives, and reports whether the resulting address is legal to use.
   module i2c_address_strap_resolver #(
       // The address from the datasheet, strappable bits at their default value.
       parameter [6:0] BASE_ADDR      = 7'b1001000,
       // How many of the LOW address bits the board's strap pins own.
       parameter integer STRAP_BITS   = 3,
       // The specification's own escape hatch: a reserved address MAY be used as a
       // slave address if it is known the reserved purpose will never be needed.
       // Set this only as a DELIBERATE, documented system decision.
       parameter ALLOW_RESERVED       = 1'b0
   )(
       input  wire [STRAP_BITS-1:0] strap_pins,      // sampled at power-on
       output wire [6:0]            effective_addr,
       output wire                  addr_reserved,   // lands in 0000XXX or 1111XXX
       output wire                  addr_illegal     // reserved and not opted into
   );
       // Build the mask in eight bits then narrow it, so STRAP_BITS = 0 and
       // STRAP_BITS = 7 are both well defined rather than edge cases.
       localparam [7:0] MASK8      = (8'd1 << STRAP_BITS) - 8'd1;
       localparam [6:0] STRAP_MASK = MASK8[6:0];

       // A plain assignment to a wider vector zero-extends.
       wire [6:0] strap_ext = strap_pins;

       // The strapped bits come from the pins; every other bit comes from the part.
       assign effective_addr = (BASE_ADDR & ~STRAP_MASK) | (strap_ext & STRAP_MASK);

       // Same group test as the address classifier, and deliberately so.
       assign addr_reserved = (effective_addr[6:3] == 4'b0000)
                           || (effective_addr[6:3] == 4'b1111);

       assign addr_illegal  = addr_reserved && !ALLOW_RESERVED;
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_address_strap_resolver_tb.v — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same configurations in Verilog idiom.
   module i2c_address_strap_resolver_tb;
       integer errors, s, n_rsv_sweep;

       initial begin #20000; $display("FAIL: watchdog expired"); $finish; end

       reg [2:0] strap3;
       reg [6:0] strap7;

       // N: an ordinary part. 0x48 base, three strap pins -> 0x48..0x4F, all legal.
       wire [6:0] addrN; wire rsvN, illN;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h48), .STRAP_BITS(3), .ALLOW_RESERVED(1'b0))
           dutN (.strap_pins(strap3), .effective_addr(addrN), .addr_reserved(rsvN), .addr_illegal(illN));

       // R: the trap. 0x78 base, three strap pins -> 0x78..0x7F, every one reserved.
       wire [6:0] addrR; wire rsvR, illR;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h78), .STRAP_BITS(3), .ALLOW_RESERVED(1'b0))
           dutR (.strap_pins(strap3), .effective_addr(addrR), .addr_reserved(rsvR), .addr_illegal(illR));

       // A: the same trap, opted into deliberately.
       wire [6:0] addrA; wire rsvA, illA;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h78), .STRAP_BITS(3), .ALLOW_RESERVED(1'b1))
           dutA (.strap_pins(strap3), .effective_addr(addrA), .addr_reserved(rsvA), .addr_illegal(illA));

       // F: seven user-assignable pins reach all 128 addresses.
       wire [6:0] addrF; wire rsvF, illF;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h00), .STRAP_BITS(7), .ALLOW_RESERVED(1'b0))
           dutF (.strap_pins(strap7), .effective_addr(addrF), .addr_reserved(rsvF), .addr_illegal(illF));

       // Z: a single strap pin moves only the lowest bit.
       wire [6:0] addrZ; wire rsvZ, illZ;
       i2c_address_strap_resolver #(.BASE_ADDR(7'h48), .STRAP_BITS(1), .ALLOW_RESERVED(1'b0))
           dutZ (.strap_pins(strap3[0:0]), .effective_addr(addrZ), .addr_reserved(rsvZ), .addr_illegal(illZ));

       initial begin
           errors = 0; n_rsv_sweep = 0;

           // An ordinary part: straps select consecutive addresses.
           for (s = 0; s < 8; s = s + 1) begin
               strap3 = s[2:0]; #1;
               if (addrN !== (7'h48 | s[6:0])) begin
                   $display("FAIL: strap=%0d -- effective 0x%02h, expected 0x%02h", s, addrN, 7'h48 | s[6:0]);
                   errors = errors + 1;
               end
               if (rsvN !== 1'b0 || illN !== 1'b0) begin
                   $display("FAIL: strap=%0d -- 0x%02h wrongly flagged reserved", s, addrN);
                   errors = errors + 1;
               end
           end

           // THE TRAP: every strap setting lands in the upper reserved group.
           for (s = 0; s < 8; s = s + 1) begin
               strap3 = s[2:0]; #1;
               if (rsvR !== 1'b1 || illR !== 1'b1) begin
                   $display("FAIL: strap=%0d -- 0x%02h in 1111XXX must be illegal", s, addrR);
                   errors = errors + 1;
               end
           end

           // The escape hatch: still reserved, no longer flagged.
           strap3 = 3'd5; #1;
           if (rsvA !== 1'b1) begin
               $display("FAIL: opting in must not change whether the address is reserved");
               errors = errors + 1; end
           if (illA !== 1'b0) begin
               $display("FAIL: a deliberate opt-in must clear addr_illegal"); errors = errors + 1; end

           // Seven straps reach all 128 addresses; exactly 16 are reserved.
           for (s = 0; s < 128; s = s + 1) begin
               strap7 = s[6:0]; #1;
               if (addrF !== s[6:0]) begin
                   $display("FAIL: seven straps must reach 0x%02h, got 0x%02h", s, addrF);
                   errors = errors + 1; end
               if (rsvF) n_rsv_sweep = n_rsv_sweep + 1;
           end
           if (n_rsv_sweep != 16) begin
               $display("FAIL: %0d of 128 strap settings reserved, expected 16", n_rsv_sweep);
               errors = errors + 1;
           end

           strap3 = 3'b000; #1;
           if (addrZ !== 7'h48) begin $display("FAIL: one strap low -> 0x%02h", addrZ); errors = errors + 1; end
           strap3 = 3'b111; #1;
           if (addrZ !== 7'h49) begin
               $display("FAIL: one strap pin must move only bit 0, got 0x%02h", addrZ); errors = errors + 1; end

           if (errors == 0)
               $display("PASS: straps resolve correctly, reserved landings flagged, %0d of 128 reserved",
                        n_rsv_sweep);
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_address_strap_resolver.vhd — SYNTHESIZABLE RTL. The same resolver in VHDL, with an explicit resize.
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   -- Combines the fixed part of a device's address with the strap pins a board
   -- drives, and reports whether the resulting address is legal to use.
   entity i2c_address_strap_resolver is
       generic (
           -- The address from the datasheet, strappable bits at their default value.
           BASE_ADDR      : std_logic_vector(6 downto 0) := "1001000";
           -- How many of the LOW address bits the board's strap pins own.
           STRAP_BITS     : natural := 3;
           -- The specification's own escape hatch: a reserved address MAY be used as
           -- a slave address if it is known the reserved purpose will never be
           -- needed. Set this only as a DELIBERATE, documented system decision.
           ALLOW_RESERVED : boolean := false
       );
       port (
           strap_pins     : in  std_logic_vector(STRAP_BITS - 1 downto 0);
           effective_addr : out std_logic_vector(6 downto 0);
           addr_reserved  : out std_logic;
           addr_illegal   : out std_logic
       );
   end entity;

   architecture rtl of i2c_address_strap_resolver is
       -- Build the mask in eight bits then narrow it, so STRAP_BITS = 0 and
       -- STRAP_BITS = 7 are both well defined rather than edge cases.
       constant MASK8      : unsigned(7 downto 0) := shift_left(to_unsigned(1, 8), STRAP_BITS)
                                                     - to_unsigned(1, 8);
       constant STRAP_MASK : unsigned(6 downto 0) := MASK8(6 downto 0);

       signal strap_ext : unsigned(6 downto 0);
       signal eff       : std_logic_vector(6 downto 0);
       signal rsv       : std_logic;
   begin
       -- VHDL will not widen implicitly, so the extension is explicit.
       strap_ext <= resize(unsigned(strap_pins), 7);

       -- The strapped bits come from the pins; every other bit comes from the part.
       eff <= std_logic_vector((unsigned(BASE_ADDR) and (not STRAP_MASK))
                            or (strap_ext and STRAP_MASK));

       effective_addr <= eff;

       -- Same group test as the address classifier, and deliberately so.
       rsv <= '1' when (eff(6 downto 3) = "0000" or eff(6 downto 3) = "1111") else '0';
       addr_reserved <= rsv;

       addr_illegal <= '0' when ALLOW_RESERVED else rsv;
   end architecture;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_address_strap_resolver_tb.vhd — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same configurations with assert report severity.
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_address_strap_resolver_tb is
   end entity;

   architecture sim of i2c_address_strap_resolver_tb is
       signal strap3 : std_logic_vector(2 downto 0) := (others => '0');
       signal strap1 : std_logic_vector(0 downto 0) := (others => '0');
       signal strap7 : std_logic_vector(6 downto 0) := (others => '0');

       signal addrN, addrR, addrA, addrF, addrZ : std_logic_vector(6 downto 0);
       signal rsvN, illN, rsvR, illR, rsvA, illA, rsvF, illF, rsvZ, illZ : std_logic;

       signal test_done : std_logic := '0';
   begin
       -- N: an ordinary part. 0x48 base, three strap pins -> 0x48..0x4F, all legal.
       dutN : entity work.i2c_address_strap_resolver
           generic map (BASE_ADDR => "1001000", STRAP_BITS => 3, ALLOW_RESERVED => false)
           port map (strap_pins => strap3, effective_addr => addrN,
                     addr_reserved => rsvN, addr_illegal => illN);

       -- R: the trap. 0x78 base, three strap pins -> 0x78..0x7F, every one reserved.
       dutR : entity work.i2c_address_strap_resolver
           generic map (BASE_ADDR => "1111000", STRAP_BITS => 3, ALLOW_RESERVED => false)
           port map (strap_pins => strap3, effective_addr => addrR,
                     addr_reserved => rsvR, addr_illegal => illR);

       -- A: the same trap, opted into deliberately.
       dutA : entity work.i2c_address_strap_resolver
           generic map (BASE_ADDR => "1111000", STRAP_BITS => 3, ALLOW_RESERVED => true)
           port map (strap_pins => strap3, effective_addr => addrA,
                     addr_reserved => rsvA, addr_illegal => illA);

       -- F: seven user-assignable pins reach all 128 addresses.
       dutF : entity work.i2c_address_strap_resolver
           generic map (BASE_ADDR => "0000000", STRAP_BITS => 7, ALLOW_RESERVED => false)
           port map (strap_pins => strap7, effective_addr => addrF,
                     addr_reserved => rsvF, addr_illegal => illF);

       -- Z: a single strap pin moves only the lowest bit.
       dutZ : entity work.i2c_address_strap_resolver
           generic map (BASE_ADDR => "1001000", STRAP_BITS => 1, ALLOW_RESERVED => false)
           port map (strap_pins => strap1, effective_addr => addrZ,
                     addr_reserved => rsvZ, addr_illegal => illZ);

       watchdog : process
       begin
           wait for 20 us;
           if test_done = '0' then
               report "watchdog expired -- the sweep never completed" severity failure;
           end if;
           wait;
       end process;

       stim : process
           variable errs        : natural := 0;
           variable n_rsv_sweep : natural := 0;
           variable expect      : unsigned(6 downto 0);
       begin
           -- An ordinary part: straps select consecutive addresses.
           for s in 0 to 7 loop
               strap3 <= std_logic_vector(to_unsigned(s, 3));
               wait for 1 ns;
               expect := unsigned'("1001000") or to_unsigned(s, 7);
               if unsigned(addrN) /= expect then
                   report "strap=" & integer'image(s) & ": effective address wrong"
                       severity error; errs := errs + 1;
               end if;
               if rsvN /= '0' or illN /= '0' then
                   report "strap=" & integer'image(s) & ": wrongly flagged reserved"
                       severity error; errs := errs + 1;
               end if;
           end loop;

           -- THE TRAP: every strap setting lands in the upper reserved group.
           for s in 0 to 7 loop
               strap3 <= std_logic_vector(to_unsigned(s, 3));
               wait for 1 ns;
               if rsvR /= '1' or illR /= '1' then
                   report "strap=" & integer'image(s) & ": address in 1111XXX must be illegal"
                       severity error; errs := errs + 1;
               end if;
           end loop;

           -- The escape hatch: still reserved, no longer flagged.
           strap3 <= "101"; wait for 1 ns;
           if rsvA /= '1' then
               report "opting in must not change whether the address is reserved"
                   severity error; errs := errs + 1; end if;
           if illA /= '0' then
               report "a deliberate opt-in must clear addr_illegal" severity error;
               errs := errs + 1; end if;

           -- Seven straps reach all 128 addresses; exactly 16 are reserved.
           for s in 0 to 127 loop
               strap7 <= std_logic_vector(to_unsigned(s, 7));
               wait for 1 ns;
               if unsigned(addrF) /= to_unsigned(s, 7) then
                   report "seven straps must reach every address" severity error;
                   errs := errs + 1;
               end if;
               if rsvF = '1' then n_rsv_sweep := n_rsv_sweep + 1; end if;
           end loop;
           if n_rsv_sweep /= 16 then
               report integer'image(n_rsv_sweep) & " of 128 strap settings reserved, expected 16"
                   severity error; errs := errs + 1;
           end if;

           strap1 <= "0"; wait for 1 ns;
           if unsigned(addrZ) /= unsigned'("1001000") then
               report "one strap low: wrong address" severity error; errs := errs + 1; end if;
           strap1 <= "1"; wait for 1 ns;
           if unsigned(addrZ) /= unsigned'("1001001") then
               report "one strap pin must move only bit 0" severity error; errs := errs + 1; end if;

           if errs = 0 then
               report "i2c_address_strap_resolver self-check complete: straps resolve correctly, "
                    & "reserved landings flagged, " & integer'image(n_rsv_sweep)
                    & " of 128 reserved" severity note;
           else
               report "i2c_address_strap_resolver self-check FAILED" severity error;
           end if;
           test_done <= '1';
           wait;
       end process;
   end architecture;

5a. Verified Execution and Cross-Language Parity

languagesimulatorresultcompletes at
SystemVerilogIcarus Verilog, -g2012PASS147 ns
Verilog-2005Icarus Verilog, -g2005PASS147 ns
VHDLnvc 1.23.0PASS147 ns
SystemVerilogVerilog-2005VHDL
base addressparameter logic [6:0]parameter [6:0]std_logic_vector(6 downto 0) generic
opt-in flagparameter bitparameterboolean generic
masklocalparam, built in 8 bitslocalparam, built in 8 bitsconstant unsigned, shift_left
widening the strapsassignment to a wider vectorassignment to a wider vectorexplicit resize
opt-in applied&& !ALLOW_RESERVED&& !ALLOW_RESERVED'0' when ALLOW_RESERVED else rsv

The widening row is the only genuine language difference. Verilog and SystemVerilog zero-extend on assignment to a wider vector; VHDL will not widen implicitly and needs resize from numeric_std. The explicit form is the one where a width mistake is a compile error rather than a silent truncation — and the original SystemVerilog used a replication that would have been a zero-width expression at STRAP_BITS = 7, which the plain assignment avoids entirely.

5b. What the Testbench Proves

Five configurations, each isolating one claim:

instanceconfigurationwhat it establishes
Nbase 0x48, 3 strapsan ordinary part: eight consecutive addresses, none reserved, fixed bits never move
Rbase 0x78, 3 strapsthe trap — all eight strap settings land in 1111XXX and every one is flagged
Abase 0x78, 3 straps, opted instill addr_reserved, no longer addr_illegal
Fbase 0x00, 7 strapsthe specification's own example: all 128 addresses reachable, exactly 16 reserved
Zbase 0x48, 1 strapone pin moves only bit 0 — the aligned-window property

Instance F is doing double duty. It verifies the seven-pin case, and it also counts the reserved space: sweeping all 128 strap settings and tallying addr_reserved must give 16. That is the same number Chapter 6.3 derived arithmetically and verified by an exhaustive classifier sweep — arrived at here a third time, through a different block and a different mechanism. Three independent routes to one number is the level of confidence a number the whole module rests on deserves.

Instance A asserts a negative that is easy to get wrong. It checks that opting in leaves addr_reserved set. A design that cleared both outputs would pass a test that only checked addr_illegal, and would have destroyed the diagnostic the Callout in §4 is about.

6. Mutation Testing

Four faults injected into the verified RTL. All four caught.

mutationwhat it breaksresult
strap mask one bit too widethe straps overwrite a fixed address bitFAIL — 0x40 where 0x48 expected
mask polarity swappedbase and straps exchange roles entirelyFAIL — 0x00 where 0x48 expected
reservation check misses the upper groupthe trap in §4 goes undetectedFAIL — 0x78 not flagged
addr_illegal ignores the opt-ina legitimate reclaim is reported as an errorFAIL — opt-in did not clear the flag

The first two are worth a note because they fail on the same test for different reasons, and the test that catches them is the least interesting one in the suite: an ordinary part at an ordinary address with the straps at zero. With straps at 000 the effective address should be exactly the base — and a mask that is the wrong width or the wrong polarity cannot produce that.

That is a small argument for including the trivial case in a suite that is mostly about edge cases. strap_pins = 0 is the configuration nobody thinks needs testing, and it pins down the entire mask construction in one comparison.

7. Detecting a Conflict — And Why Nothing Detects It For You

Two devices answering the same address is not an error any component reports. It is worth being precise about why, because the expectation that something would notice is what makes the symptom so confusing.

The bus cannot detect it. Both devices pull SDA low for the acknowledge. Chapter 2.5's wired-AND makes two devices pulling low electrically identical to one — the same property that lets several devices answer a general call or a 10-bit prefix. The acknowledge looks perfect.

The controller cannot detect it on the address. It sees an acknowledge, which is exactly what it expected.

The failure surfaces in the data phase, on reads. A write goes to both devices — both accept it, and if they are the same part they both do the same thing, which can go unnoticed for a long time. A read is where it breaks: both devices become transmitters and drive SDA simultaneously, and the wired-AND returns the bitwise AND of the two devices' data. Every bit either device drives low is low.

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — what a read from two devices returns
   Device A wants to transmit   0x5A  =  0101 1010
   Device B wants to transmit   0x3C  =  0011 1100

   Both release for a '1' and pull low for a '0'. The wired-AND resolves:

       observed  =  A AND B  =  0001 1000  =  0x18

   0x18 is not a value either device sent. It is not garbage either -- it is a
   perfectly stable, repeatable value, which is why it is so misleading:

     - it looks like plausible data, so a driver may not reject it
     - it is DETERMINISTIC, so the bug does not look like a bus or noise problem
     - every bit is the AND, so the result is always <= both operands bitwise
       -- readings that are systematically LOW, never high

   A sensor whose readings are always low, always stable, and always wrong in the
   same direction is the signature. "Noisy" would suggest electrical; "always a
   bit low" suggests two transmitters.

The reliable detections are a scan and a part-by-part bring-up. An address scan reports which addresses answer, so a conflict shows up as an address answering when only one device should be there — but only if you know what to expect. Removing or holding in reset one device at a time and re-scanning is slower and conclusive.

8. Muxes, Switches and Segments

When strapping cannot resolve a conflict — two parts whose windows do not overlap with a free address, or two instances of a fixed-address part — the answer is to stop having one bus.

A bus switch or multiplexer sits between the controller and two or more downstream segments and connects one at a time. Each segment is electrically separate, so the same address may appear on each. The switch is itself an I²C device with its own address, and selecting a segment is a write to it.

A controller connects to a bus switch, which is itself an addressed device on the upstream bus. The switch connects downstream to two separate segments. Each segment carries one sensor, and both sensors have the same address of hex 48. Only one segment is connected at any moment, so the two identical addresses never conflict.Controllerwrites the switch, then thesensorBus switch at 0x70one segment connected at atimeSensor at 0x48segment 0 onlySensor at 0x48segment 1 only12
Figure 2 — a bus switch splitting one segment into two, so one address can appear twice. The controller writes to the switch to choose which downstream segment is connected, and only then addresses the sensor. The two sensors are identical parts at identical addresses and never see each other, because they are never connected at the same time. The switch consumes one address of its own.

The costs are real and worth listing, because a switch is often reached for before strapping has been exhausted:

costwhy it matters
one addressthe switch itself needs one, from the same 112
two transfers per accessselect the segment, then do the work — and the select is wasted bandwidth
driver statesomething must track which segment is selected, and be right about it after a reset
added capacitance and delaya switch is in the signal path, which eats into the rise-time budget of Chapter 2.4
a new failure modea switch left on the wrong segment makes a present device look absent

There is also a distinction worth knowing because the words are used loosely. A multiplexer typically connects exactly one downstream segment at a time; a switch may connect several independently. For address-conflict resolution the mux behaviour is what you want, because the whole point is that the conflicting devices are never connected together.

Segmentation without a switch is the other option: put the two devices on genuinely separate buses, driven by two controller peripherals. That costs pins and a peripheral instead of an address and bandwidth, and it removes the driver-state failure mode entirely. On an SoC with several I²C peripherals — most of them — this is frequently the cheaper answer, and it is under-used because "add a mux" is the better-known reflex.

9. Verification Connection — The Address Map Is Testbench Configuration

An environment that models more than one slave needs the address map as data, and the useful property is that a conflict is detectable in the testbench even though it is not detectable on the bus.

Azvya Education Pvt. Ltd.VLSI Mentor
UVM CONCEPT — VERIFICATION ONLY. An address map that refuses to be ambiguous.
   // The environment knows what the bus cannot: which device is supposed to own
   // which address. That makes a conflict a CONFIGURATION error, caught at build
   // time, rather than a data corruption caught later and blamed on something else.
   class i2c_bus_map extends uvm_object;
       `uvm_object_utils(i2c_bus_map)

       // segment -> address -> owning agent name
       string owner [int][bit [6:0]];

       function void add(int segment, bit [6:0] addr, string agent_name);
           if (owner.exists(segment) && owner[segment].exists(addr))
               `uvm_fatal("I2C_MAP", $sformatf(
                   "address 0x%02h on segment %0d claimed by both '%s' and '%s'",
                   addr, segment, owner[segment][addr], agent_name))
           owner[segment][addr] = agent_name;
       endfunction

       // The reserved groups from Chapter 6.3, checked once per device rather
       // than rediscovered on a bench.
       function void check_reserved(bit [6:0] addr, string agent_name, bit opted_in);
           if ((addr[6:3] == 4'b0000 || addr[6:3] == 4'b1111) && !opted_in)
               `uvm_error("I2C_MAP", $sformatf(
                   "agent '%s' is configured at reserved address 0x%02h", agent_name, addr))
       endfunction
   endclass

Three points, and the middle one is the one that changes how an environment is built.

uvm_fatal on a duplicate is deliberate. A conflict is not a stimulus to be explored; it is a testbench that cannot express what it means. Failing at build time is far more useful than producing the bitwise-AND of two agents' responses and letting a scoreboard puzzle over it.

The map is keyed by segment. An environment that models a switch has to model that the same address is legitimate twice, on different segments — so "duplicate address" is only an error within a segment. Flattening the map would make the mux topology of §8 unrepresentable, which is exactly the topology the map is most useful for.

The reserved check belongs here, once. Every agent's address gets checked against the groups at build time, with the same opt-in the RTL of §5 exposes. That turns a recurring bring-up discovery into a single build-time message naming the agent.

10. FPGA and ASIC Implications

Strap inputs need the same treatment as any other asynchronous input, and then some. They are sampled once and latched, so a glitch at sampling time is permanent until the next reset. On an FPGA that means sampling them after a reset delay long enough for the board to have settled, and not sampling them from a pin that anything else drives during power-on.

A strapped address wants to be readable. Exposing the effective address in a register costs almost nothing and converts "which address did this device actually take" from an oscilloscope question into a register read. Chapter 6.1's last_addr is the same argument from the other direction — what was seen versus what was taken.

The reserved-landing check is four bits of logic and should never be omitted. It is the cheapest protection in this module against the most expensive class of board error, and its only cost is a parameter for the legitimate opt-in.

On an FPGA, prefer a second I²C controller over a mux when you have one. Instantiating a second controller costs logic that most FPGAs have spare, and it removes the select-state failure mode, the two-transfers-per-access overhead, and the extra capacitance in the signal path. Reaching for a mux is a habit inherited from fixed-function microcontrollers with one peripheral.

On an ASIC, make the address programmable if there is any chance of two instances. Whether by straps, by OTP, or by the general-call mechanism of Chapter 6.3, the alternative is a part that cannot be used twice on one segment — a limitation discovered by a customer rather than by you.

11. Debugging — The Two Sensors That Read Slightly Low

A thermal design that read six degrees cool on every channel

Pitfall — two identical parts strapped to the same address on one segment
Buggy Code
// A board carries two identical temperature sensors, one near the regulator and
// one near the SoC. The part has three strap pins, base address 0x48, so eight
// addresses are available and two devices need two of them.
//
// The schematic strapped them like this:
//
//     U12  A2=GND  A1=GND  A0=GND        ->  0x48
//     U13  A2=GND  A1=GND  A0=GND        ->  0x48       <-- same
//
// The intent was A0 = VDD on U13. The net was drawn to a ground fill on the same
// layer, which passes DRC, passes visual review because the pins are on opposite
// sides of the schematic sheet, and is not visible in the BOM.
//
// Firmware reads both "sensors":
//
//     t_reg = read_temp(0x48);
//     t_soc = read_temp(0x49);          // NACKs -- nothing is at 0x49
Symptom

The 0x49 read NACKs, which is noticed immediately and blamed on the second sensor being dead or unpopulated. That part of the investigation goes correctly: the sensor is present, powered, and answering -- at 0x48.

So firmware is changed to read both channels from 0x48 with different register pointers, and now the system WORKS. Readings are plausible, stable, and track the board temperature. The bug is considered closed.

Thermal validation, weeks later, finds every reading about six degrees low -- consistently, repeatably, across every board. Not noisy. Not drifting. Just low. Six degrees is enough to matter for a thermal limit and small enough to look like a calibration or a thermal-coupling problem, so the investigation goes to sensor placement, copper coupling, and the part's own accuracy specification.

An address scan shows a device at 0x48 and nothing else in the range, which is consistent with "one sensor" and is the last thing anyone questions.

Root Cause

Two devices were answering 0x48 and both were transmitting on every read.

Section 7 derives what the master then sees: both devices release for a one and pull low for a zero, so the wired-AND returns the BITWISE AND of the two temperature values. The AND of two numbers is never larger than either of them, so the reading is systematically LOW -- and because both sensors are on the same board at similar temperatures, their values differ in only a few low bits, so the AND is only slightly lower than either. Hence a small, stable, repeatable error rather than obvious garbage.

Every misleading feature of this bug follows from that:

- NOT noisy, because the AND of two stable values is stable - plausible magnitude, because the two operands are close together - always low, never high, because AND can only clear bits - and it TRACKS temperature correctly, because both operands do

The address scan was actively misleading. A scan reports which ADDRESSES answer, not how many devices answer each one -- and it cannot do otherwise, because the wired-AND makes two acknowledging devices electrically identical to one (section 7). Seeing exactly one address in the expected range was read as "one sensor" when it meant "at least one device at 0x48".

The firmware "fix" that made the system work was the step that hid the bug. It removed the NACK -- the one honest symptom -- and replaced it with a read that appeared to succeed.

Fix
// The board fix is one net. The engineering fixes are the ones that generalise.
//
//   1. BRING UP DEVICES INDIVIDUALLY. Hold each device in reset, or depopulate
//      it, and re-scan. Two devices at one address collapse to one scan entry;
//      one device at one address does not change when the OTHER device is
//      removed. This is the only cheap test that distinguishes the two cases,
//      and it takes minutes.
//
//   2. DISTRUST A NACK THAT IS "FIXED" BY CHANGING THE ADDRESS. A NACK at 0x49
//      when a device is supposed to be at 0x49 is a real signal. Making the
//      symptom go away by reading 0x48 instead answered a different question:
//      not "is the second sensor working" but "is the first sensor working".
//
//   3. READ AN IDENTIFYING REGISTER, NOT JUST DATA. Most parts expose a device
//      or manufacturer ID. Two devices ANDing their IDs almost never produce the
//      correct ID, because IDs are fixed patterns rather than close-together
//      values -- so an ID check fails loudly on a conflict where a data read
//      fails quietly. This is the single highest-value bring-up check on an I2C
//      bus and it is routinely skipped because the data read "works".
//
//   4. CHECK THE SIGN OF THE ERROR. Systematically low and stable is the
//      wired-AND signature. Noisy points at electrical problems; offset points
//      at calibration; ALWAYS LOW AND NEVER HIGH points at two transmitters,
//      because AND cannot set a bit.
//
//   5. MAKE THE MAP A REVIEWED ARTEFACT. Section 9's build-time map turns
//      "two agents claim 0x48" into a fatal at elaboration. The schematic
//      equivalent is a written address table, reviewed against the strap nets --
//      not against the intent, against the NETS. This bug passed every review
//      that looked at intent.
//
// And note what the RTL of section 5 would and would not have caught: 0x48 is not
// reserved, so addr_illegal was correctly low. A device cannot detect that
// ANOTHER device shares its address -- it has no way to know. That detection has
// to come from the system: a scan with devices isolated, or an ID read.

12. Common Misconceptions

"112 addresses is plenty." The count is not the constraint. Almost no part lets you choose its address freely, vendors cluster in the same neighbourhoods, and a part with three straps offers eight specific addresses — so two parts based at 0x48 collide no matter how many pins they have.

"A part with three strap pins supports eight addresses of my choosing." It supports eight aligned, contiguous addresses starting at its base. If another part needs one of them, strapping does not resolve the conflict.

"A strap pin is a configuration input I can change at runtime." It is sampled once, normally at power-on or reset release, and latched. Changing it afterwards changes nothing until the next reset.

"A strapped address is safe because the datasheet lists it." A datasheet listing 0x78–0x7F is listing eight addresses that are all in the upper reserved group. The datasheet is telling you what the part does, not whether the bus permits it.

"The bus or the controller will notice two devices at one address." Neither can. The wired-AND makes two acknowledging devices identical to one, and the controller sees exactly the acknowledge it expected.

"An address conflict produces garbage." It produces the bitwise AND of the two devices' data — stable, repeatable, plausible, and systematically low. That is far harder to recognise than garbage.

"An address scan proves how many devices are present." It reports which addresses answer. Two devices at one address produce one scan entry, and no scan can do better.

"Add a mux" is the reflex, and on an SoC with a spare I²C peripheral a second segment is often cheaper — no extra address, no select transfer, no select state to get wrong, and no added capacitance.

13. Reason It Through

Two temperature sensors from different vendors are both based at 0x48 with three strap pins. Can you fit three of them on one segment?

Only if their windows have three free addresses between them, which they do not by themselves: both windows are 0x48–0x4F, so three devices need three of those eight, and that works — provided nothing else on the board occupies them. The failure case is needing nine, or finding an EEPROM or another sensor already in the range. Then the options are a different part, a mux, or a second segment.

A datasheet offers addresses 0x78–0x7F. What should you notice, and what would you do?

Every one of those eight is in the upper reserved group 1111XXX, four of them being the 10-bit prefix. It is usable on a closed board that provably never uses 10-bit addressing or device ID — which is the specification's own escape hatch — but it must be a recorded architectural decision, not an accident, and it should be an explicit opt-in in any hardware that checks.

A sensor reads consistently six degrees low, stably, on every board. What does the stability and the direction tell you?

Stability rules out noise and marginal signalling, which produce variable errors. The direction is the strong clue: the wired-AND can only clear bits, so two devices transmitting simultaneously always produce a value no larger than either. Systematically low, stable, and correctly tracking is the signature of two devices at one address — and two sensors on one board have close values, which is why the error is small enough to be mistaken for calibration.

Why does reading a device-ID register find an address conflict that reading data does not?

Because the observed value is the bitwise AND of what both devices transmit. Two temperature readings are numerically close, so their AND is close to both and looks plausible. Two device IDs are fixed patterns that are not close, so their AND is almost never a valid ID — the check fails loudly instead of quietly.

A device can detect that its own address is reserved. Why can it not detect that another device shares it?

Because it has no way to observe the other device. During the acknowledge both pull SDA low and the wired-AND makes that indistinguishable from one device pulling low; during a read both transmit and neither can see the resolved level as distinct from its own intent. Reserved-address detection is self-knowledge and needs no observation; conflict detection requires comparing two devices, which only the system can do.

When is a second I²C peripheral a better answer than a bus multiplexer?

Whenever one is available. A mux costs an address from the same 112, a select transfer before every access, driver state that must survive resets, and extra capacitance in the signal path — and it adds a failure mode where a wrongly-selected segment makes a present device look absent. A second peripheral costs pins and logic, and removes all of that.

14. Understanding Check

15. Summary

The address space is not the constraint; part flexibility is. Vendors cluster, most parts offer zero to three strap pins, and a strap window is contiguous and aligned to the part's base — so two parts based at the same address collide regardless of pin count.

Parts move their address four ways: not at all, by strap pins, by bus programming via the general call, or by one-time/non-volatile programming.

A strap pin is sampled once and latched, normally at reset release, which is why straps are tied hard rather than driven by anything that might still be initialising.

Strapping can land a device in reserved space, and a part based at 0x78 with three straps lands there for every setting. The device is the only participant that can detect this, and it costs four bits of logic.

reserved is a fact and illegal is a judgement. A deliberate opt-in narrows the consequence and must not suppress the condition, or the diagnostic is gone.

Nothing detects two devices at one address. The wired-AND makes two acknowledging devices identical to one, so a read returns the bitwise AND — stable, plausible, and systematically low, which is why it is mistaken for a calibration problem.

An ID read finds a conflict that a data read hides, because IDs are fixed patterns whose AND is almost never valid, while two similar data values AND to something plausible.

Muxes cost an address, a transfer, driver state and capacitance. On an SoC with a spare peripheral, a second segment is frequently the cheaper answer and is under-used.

16. What Comes Next

Four chapters have now described addressing entirely from the outside — the byte on the wire, the extension, the map, and the board. What has not been built is the thing every one of them assumes: the hardware inside a device that watches the bus, recognises its own address, and decides whether to answer.

Chapter 6.5 closes the module with that block. It is the first piece of slave hardware in the curriculum, and it is assembled from the pieces already built — the byte capture of Chapter 6.1, the classifier of Chapter 6.3, and the framing events of Module 5 — including the ordering rule that makes a prohibition beat an address match.

Browse the full path on the I²C tutorials index. For the map a strapped address must avoid, see Reserved Addresses; for the electrical behaviour that hides a conflict, Wired-AND.

Continue learning