I²C · Module 3
The Shared Bus — Many Targets, Sometimes Many Masters
One pair of conductors serves an entire board. Physically the bus is a broadcast medium — every device sees everything — while participation is logically selective. That gap is the central abstraction of I²C, and it also opens the multi-controller case.
Chapter 3.1 established responsibilities for two participants. Real boards have more than two, and this is where the arrangement earns the name bus rather than link.
The step is larger than adding devices to a diagram. Once several targets are attached to the same two conductors, a contradiction appears that has to be resolved before anything else works: every device is wired to see every bit, and yet only one of them is supposed to answer. How that contradiction is resolved is the central abstraction of I\u00B2C, and everything in Modules 6 through 11 is machinery serving it.
1. One Pair of Conductors, Many Devices
Module 1 derived why sharing is the only lever that stops interconnect cost tracking device count, and Module 2 showed how devices can share a conductor without harming one another. Putting those together gives the arrangement every I\u00B2C board uses.
The figure is drawn as rails rather than as a fan of links, and that is not a stylistic choice. A fan suggests separate connections that happen to originate together. Rails are the truth: there is one SDA conductor and one SCL conductor on the board, and every device taps the same copper.
2. Physical Broadcast, Logical Selection
Now the contradiction, stated as precisely as possible because the whole protocol is its resolution.
Physically, the bus is a broadcast medium. A pattern of levels on SDA is present at every attached device\u0027s input simultaneously. Target A cannot avoid seeing traffic meant for Target C; there is no wire to cut and no direction to exploit. Module 2 made this concrete — the resolved level on a shared node is a single value that every participant reads.
Logically, participation must be selective. Exactly one target should accept a byte the controller writes, and exactly one should supply a byte the controller reads. If two respond, their contributions collide on the conductor; if none responds, the transfer has no counterpart.
So the bus offers broadcast and the system needs selection. Something has to bridge the gap, and Chapter 1.2 already identified what: sharing destroyed the addressing a dedicated wire provided for free, so identity must be carried in the information itself. Each target has a logical identity; the controller transmits the identity of the participant it means; every target compares what it heard against its own; the ones that do not match ignore what follows.
This chapter deliberately stops there on the subject of identity. How wide it is, how it is encoded, where the direction bit sits, which values are reserved and how a target signals that it heard its own name are all specified exactly, and they belong to Modules 6 and 7. What Module 3 owes you is the reason an identity field must exist at all and the architectural consequence that follows from it.
3. RTL Connection — Selection Without Encoding
Selection is a genuine piece of logic, and it can be modelled honestly without touching address encoding. The model below takes an identity presented on the bus and produces one enable per target.
Everything protocol-specific is deliberately absent: there is no serial link, no framing, and the identity arrives as a parallel value rather than as bits clocked over SDA. What survives is the architecture — one broadcast input, several comparators, at most one enable — and one property worth proving rather than asserting.
3a. SystemVerilog
module target_decoder #(
parameter int NUM_TARGETS = 4,
parameter int ID_WIDTH = 3
)(
input logic sel_valid,
input logic [ID_WIDTH-1:0] bus_id,
input logic [NUM_TARGETS*ID_WIDTH-1:0] target_ids,
output logic [NUM_TARGETS-1:0] target_sel
);
// Physically every target sees bus_id. Logically at most one should match.
always_comb begin
for (int unsigned i = 0; i < NUM_TARGETS; i++)
target_sel[i] = sel_valid &&
(bus_id == target_ids[i*ID_WIDTH +: ID_WIDTH]);
end
endmoduleThe invariant is expressed with $onehot0, which is exactly the right predicate: zero selected is legal (the identity belongs to nobody on this bus) and one selected is legal; only more than one indicates a fault. Note that the fault is not in the decoder — the comparison is correct — it is in the configuration, and that distinction is the point of the last test.
module target_decoder_tb;
localparam int NUM_TARGETS = 4, ID_WIDTH = 3;
logic sel_valid;
logic [ID_WIDTH-1:0] bus_id;
logic [NUM_TARGETS*ID_WIDTH-1:0] target_ids;
logic [NUM_TARGETS-1:0] target_sel;
int errors = 0;
target_decoder #(.NUM_TARGETS(NUM_TARGETS), .ID_WIDTH(ID_WIDTH)) dut (.*);
task automatic chk(input logic [NUM_TARGETS-1:0] exp, input string what);
#1;
if (target_sel !== exp) begin
$error("%s: target_sel=%b expected %b", what, target_sel, exp); errors++;
end
endtask
initial begin
// Four targets with UNIQUE identities 1, 2, 5, 6.
target_ids = {3'd6, 3'd5, 3'd2, 3'd1}; // index 3,2,1,0
sel_valid = 1'b0; bus_id = 3'd2;
chk(4'b0000, "idle: no identity presented, nobody selected");
sel_valid = 1'b1;
bus_id = 3'd1; chk(4'b0001, "identity 1 selects target 0");
bus_id = 3'd2; chk(4'b0010, "identity 2 selects target 1");
bus_id = 3'd6; chk(4'b1000, "identity 6 selects target 3");
bus_id = 3'd7; chk(4'b0000, "unmatched identity selects nobody");
// At most one may be selected while identities are unique.
bus_id = 3'd5; #1;
if (!$onehot0(target_sel)) begin
$error("more than one target selected with unique identities"); errors++; end
// ADDRESS CONFLICT: two targets configured with the SAME identity.
// The decode is correct; the CONFIGURATION is not -- and the symptom is
// two targets participating at once, which is what onehot0 detects.
target_ids = {3'd6, 3'd2, 3'd2, 3'd1}; // targets 1 and 2 both = 2
bus_id = 3'd2; #1;
if (target_sel !== 4'b0110) begin
$error("duplicate identity should select BOTH -- got %b", target_sel); errors++; end
if ($onehot0(target_sel)) begin
$error("onehot0 failed to detect the duplicate-identity conflict"); errors++; end
if (errors==0) $display("PASS: one physical bus, one logically selected target; duplicates are detectable");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule3b. Verilog
Same logic. Verilog-2005 has no $onehot0, so the testbench counts set bits with a function — which is a fair illustration of what the SystemVerilog built-in is doing and why having it matters when the property appears in a dozen places.
module target_decoder #(
parameter NUM_TARGETS = 4,
parameter ID_WIDTH = 3
)(
input wire sel_valid,
input wire [ID_WIDTH-1:0] bus_id,
input wire [NUM_TARGETS*ID_WIDTH-1:0] target_ids,
output reg [NUM_TARGETS-1:0] target_sel
);
integer i;
always @(*) begin
for (i = 0; i < NUM_TARGETS; i = i + 1)
target_sel[i] = sel_valid &&
(bus_id == target_ids[i*ID_WIDTH +: ID_WIDTH]);
end
endmodule module target_decoder_tb;
parameter NUM_TARGETS = 4, ID_WIDTH = 3;
reg sel_valid;
reg [ID_WIDTH-1:0] bus_id;
reg [NUM_TARGETS*ID_WIDTH-1:0] target_ids;
wire [NUM_TARGETS-1:0] target_sel;
integer errors;
target_decoder #(.NUM_TARGETS(NUM_TARGETS), .ID_WIDTH(ID_WIDTH)) dut (
.sel_valid(sel_valid), .bus_id(bus_id),
.target_ids(target_ids), .target_sel(target_sel));
// Verilog-2005 has no $onehot0; count the set bits instead.
function integer popcount;
input [NUM_TARGETS-1:0] v;
integer k;
begin
popcount = 0;
for (k = 0; k < NUM_TARGETS; k = k + 1) popcount = popcount + v[k];
end
endfunction
task chk;
input [NUM_TARGETS-1:0] exp;
input [8*48:1] what;
begin
#1;
if (target_sel !== exp) begin
$display("FAIL: %0s: target_sel=%b expected %b", what, target_sel, exp);
errors = errors + 1;
end
end
endtask
initial begin
errors = 0;
target_ids = {3'd6, 3'd5, 3'd2, 3'd1};
sel_valid = 1'b0; bus_id = 3'd2;
chk(4'b0000, "idle: nobody selected");
sel_valid = 1'b1;
bus_id = 3'd1; chk(4'b0001, "identity 1 selects target 0");
bus_id = 3'd2; chk(4'b0010, "identity 2 selects target 1");
bus_id = 3'd6; chk(4'b1000, "identity 6 selects target 3");
bus_id = 3'd7; chk(4'b0000, "unmatched identity selects nobody");
bus_id = 3'd5; #1;
if (popcount(target_sel) > 1) begin
$display("FAIL: more than one target selected with unique identities");
errors = errors + 1;
end
// ADDRESS CONFLICT: targets 1 and 2 share identity 2.
target_ids = {3'd6, 3'd2, 3'd2, 3'd1};
bus_id = 3'd2; #1;
if (target_sel !== 4'b0110) begin
$display("FAIL: duplicate identity should select both, got %b", target_sel);
errors = errors + 1;
end
if (popcount(target_sel) <= 1) begin
$display("FAIL: the duplicate-identity conflict was not detectable");
errors = errors + 1;
end
if (errors==0) $display("PASS: one physical bus, one logically selected target; duplicates are detectable");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule3c. VHDL
The VHDL version makes the slicing explicit — target_ids((i+1)*ID_WIDTH-1 downto i*ID_WIDTH) says exactly which bits belong to target i, where SystemVerilog\u0027s +: part-select says the same thing more briefly. The testbench defines popcount as a function over an unconstrained std_logic_vector, which is idiomatic and reusable.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity target_decoder is
generic (
NUM_TARGETS : positive := 4;
ID_WIDTH : positive := 3
);
port (
sel_valid : in std_logic;
bus_id : in std_logic_vector(ID_WIDTH-1 downto 0);
target_ids : in std_logic_vector(NUM_TARGETS*ID_WIDTH-1 downto 0);
target_sel : out std_logic_vector(NUM_TARGETS-1 downto 0)
);
end entity;
architecture rtl of target_decoder is
begin
-- Physically every target sees bus_id. Logically at most one should match.
process (sel_valid, bus_id, target_ids)
begin
for i in 0 to NUM_TARGETS-1 loop
if sel_valid = '1' and
bus_id = target_ids((i+1)*ID_WIDTH-1 downto i*ID_WIDTH) then
target_sel(i) <= '1';
else
target_sel(i) <= '0';
end if;
end loop;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity target_decoder_tb is
end entity;
architecture sim of target_decoder_tb is
constant NUM_TARGETS : positive := 4;
constant ID_WIDTH : positive := 3;
signal sel_valid : std_logic := '0';
signal bus_id : std_logic_vector(ID_WIDTH-1 downto 0) := (others => '0');
signal target_ids : std_logic_vector(NUM_TARGETS*ID_WIDTH-1 downto 0);
signal target_sel : std_logic_vector(NUM_TARGETS-1 downto 0);
function popcount (v : std_logic_vector) return natural is
variable n : natural := 0;
begin
for i in v'range loop
if v(i) = '1' then n := n + 1; end if;
end loop;
return n;
end function;
begin
dut : entity work.target_decoder
generic map (NUM_TARGETS => NUM_TARGETS, ID_WIDTH => ID_WIDTH)
port map (sel_valid => sel_valid, bus_id => bus_id,
target_ids => target_ids, target_sel => target_sel);
stim : process
procedure chk (exp : in std_logic_vector(NUM_TARGETS-1 downto 0);
what : in string) is
begin
wait for 1 ns;
assert target_sel = exp report "target_decoder: " & what severity error;
end procedure;
begin
-- Unique identities 1, 2, 5, 6 for targets 0..3.
target_ids <= "110" & "101" & "010" & "001";
sel_valid <= '0'; bus_id <= "010";
chk("0000", "idle: nobody should be selected");
sel_valid <= '1';
bus_id <= "001"; chk("0001", "identity 1 must select target 0");
bus_id <= "010"; chk("0010", "identity 2 must select target 1");
bus_id <= "110"; chk("1000", "identity 6 must select target 3");
bus_id <= "111"; chk("0000", "unmatched identity must select nobody");
bus_id <= "101"; wait for 1 ns;
assert popcount(target_sel) <= 1
report "target_decoder: more than one target selected with unique identities"
severity error;
-- ADDRESS CONFLICT: targets 1 and 2 both configured as identity 2.
target_ids <= "110" & "010" & "010" & "001";
bus_id <= "010"; wait for 1 ns;
assert target_sel = "0110"
report "target_decoder: duplicate identity should select both" severity error;
assert popcount(target_sel) > 1
report "target_decoder: duplicate-identity conflict was not detectable" severity error;
report "target_decoder self-check complete" severity note;
wait;
end process;
end architecture;3d. What the Models Establish, and What They Abstract
Generics, port widths, the flat target_ids packing, the stimulus and the expected results match across all three, and all three complete at the same simulated time.
What is abstracted away, stated so nothing here is mistaken for an address decoder: the identity arrives as a parallel value, not as bits clocked over SDA; there is no framing to tell a target when an identity begins; there is no direction bit; no reserved values are honoured; and no target signals that it recognised itself. Real address matching is Module 6.5, and it consumes a serial bit stream rather than a bus-wide value.
What the model does establish is the architectural claim of this chapter, now verified rather than asserted: a broadcast input plus per-participant comparison yields at most one active participant when identities are unique — and when they are not, the very same correct logic activates two, which is precisely the symptom a duplicate-address board exhibits. The last test in each testbench deliberately configures two targets with the same identity and requires that both are selected, because a model that quietly suppressed the second one would hide the failure mode the next chapter has to solve.
4. When More Than One Participant Can Be a Controller
Everything so far assumed one controller. The bus does not require that, and the architectural consequence is worth understanding now even though the mechanism is two modules away.
Nothing in the electrical arrangement privileges one participant. Module 2 established that every device attached to the rails has the same powers: pull a line LOW, release it, read the resolved level. A device capable of initiating — one with the request path from Chapter 3.1 — can therefore be attached alongside another such device, and both are physically able to start activity.
Two observations, and then a deliberate stop.
The electrical layer already made this safe. If two controllers act at the same instant, they are both open-drain participants pulling LOW or releasing — which Chapter 2.2 showed cannot produce the destructive supply-to-ground conflict that ruled push-pull out, and Chapter 2.5 showed resolves to a defined level. Simultaneous activity by two controllers is electrically harmless. That is not luck; it is the property the architecture was chosen for, and it is why a multi-controller bus is possible at all.
It is not yet logically resolved. Electrically harmless is not the same as correct: two overlapping transfers would still corrupt each other\u0027s meaning, and something must decide which one proceeds. The mechanism uses a capability Module 2 already gave every participant — release a line and observe that it is still held — and that is as far as this chapter goes.
Module 13 develops the resolution bit by bit, what the losing participant must do, and how two controllers\u0027 clocks reconcile. Do not try to infer it here. What Module 3 owes you is the architectural fact that the bus supports more than one potential controller, and the reason that is not an electrical problem.
5. Verification Connection — One Bus Monitor, Many Target Models
The shared-bus topology has a direct and slightly surprising consequence for how a verification environment is shaped, and it follows from Module 2 rather than from anything new.
Because the bus is a broadcast medium, one monitor sees all of it. There is no per-target traffic to observe separately — every transfer, to every target, appears on the same two conductors. So an environment does not need a monitor per device; it needs one monitor on the bus, and that monitor reconstructs every transfer regardless of which target it concerned.
Target models, by contrast, are naturally per-device, because each one has its own identity, its own register state and its own behaviour. The shape that falls out is asymmetric:
controller agent generates transfers
|
v
shared I2C interface <-- one physical bus
|
+----> target model A (identity 1, own registers)
+----> target model B (identity 2, own registers)
+----> target model C (identity 3, own registers)
|
v
monitor ONE monitor, sees every transfer
|
v
scoreboard correlates transfers against target stateTwo points earn their place here, both inherited directly from Chapter 2.5:
The monitor observes the resolved bus, not any participant\u0027s intent. It cannot be wired to a target model\u0027s internal state or to the controller agent\u0027s request — those are private, and a monitor built on them would be blind to exactly the cases where intent and the bus disagree.
The scoreboard needs to know which target a transfer concerned, and it learns that the same way the targets do: by decoding the identity the monitor reconstructed. That is why reconstruction is the hard component and why it belongs to the monitor rather than being split across target models.
The duplicate-identity case from §3 is also a verification target in its own right. An environment that can configure two models with the same identity can prove that the design under test detects or survives the collision — a test worth having precisely because real boards produce that situation. Modules 19 through 21 build all of this properly.
6. Debugging — The Device That Answered Somebody Else\u0027s Question
Two targets, one identity \u2014 a bus that works until the second device is fitted
Pitfall \u2014 assuming physically distinct devices are logically distinguishable
// A board carries two identical temperature sensors, one near the power stage and
// one near the connector, to profile the thermal gradient. They are the same part
// number, fitted to the same bus, and the designer reasons:
//
// "They are two separate physical ICs on two separate footprints,
// so the controller can read them separately."
//
// The schematic looks completely reasonable. The build passes assembly inspection.
// In firmware, each sensor is read in turn and the two readings are logged.
//
// Nothing in the design captures the fact that both parts answer to the SAME
// logical identity, because the identity is fixed in the silicon and neither
// footprint does anything to change it.With only one sensor fitted, everything works and both firmware reads return sensible, identical values -- which is initially read as "the board is uniform". With both fitted, reads become unreliable in a way that is hard to characterise: sometimes a plausible value, sometimes a value that is neither sensor's, sometimes a transfer that fails outright. The failure rate changes with temperature, because the two sensors' readings diverge more when the board is hot and their differing bytes conflict more visibly on the conductor. On a capture the transfer looks structurally correct -- the identity goes out, a response comes back -- so the investigation starts in firmware and in the sensor datasheet. Both are fine.
Physical distinctness and logical distinctness are different things, and the bus only has the second. Both sensors compare the broadcast identity against their own fixed identity, both match, and both therefore participate in the same transfer. When the controller reads, BOTH targets supply bytes onto the same conductor: by Module 2's wired-AND rule any participant pulling LOW wins, so the value the controller receives is the bitwise AND of the two sensors' bytes. That is why the result is often neither reading and why it tracks their divergence -- when the two bytes are equal the AND is that value and the bus looks healthy. Note what is NOT broken: the decode logic in each sensor is correct, the wiring is correct, the firmware is correct, and no electrical rule is violated. The fault is a CONFIGURATION collision on a broadcast medium, which is exactly what the duplicate-identity test in this chapter's testbenches reproduces.
// The identity has to be made unique, or the devices have to be separated. In
// rough order of preference:
//
// 1. Use a part variant with a configurable identity, where a strap pin or an
// order option selects between several fixed identities. Many devices that
// are commonly fitted in multiples offer exactly this.
// 2. Put the second device on a SEPARATE SEGMENT, so the two never see the same
// transfer. Chapter 3.3 develops segmentation and the components that do it.
// 3. Choose a different part for one position.
//
// What does NOT work: firmware sequencing, retries, or reading "more carefully".
// Both devices answer every matching transfer and nothing in software can make a
// broadcast medium selective.
//
// The verification that catches it before the board exists: configure two target
// models with the same identity and assert that at most one is selected. That is
// the $onehot0 property in this chapter -- it fails immediately, at elaboration of
// the test rather than in a thermal chamber. A single-target-model environment can
// never expose it, which is why the testbenches here deliberately build the
// conflicting configuration and require it to be detectable.The engineering lesson: on a broadcast bus, identity is the only thing that separates participants — not position, not footprint, not part count. Any design that relies on physical distinctness for logical separation has an unhandled collision waiting for the moment the second device is populated. The tell is a fault that appears only when a particular combination of parts is fitted, and whose corrupted values look like a plausible mixture of two correct ones rather than like noise.
7. Common Misconceptions
8. Reason It Through
Work these through before reading the answers.
A board has six targets on one bus. A colleague proposes adding a seventh and asks how many additional controller pins that requires.
Answer, and the reason it is the point of Module 1. None. The seventh device attaches to conductors that already exist. That is the property the shared topology was chosen for: the controller\u0027s interconnect cost does not track the device count. What does grow is the electrical load on the two rails — which is Chapter 3.3\u0027s subject and the real limit on how many devices a segment can carry.
During a transfer to Target B, what is Target A doing?
Reasoning. Receiving every bit and ignoring it, having compared the transmitted identity against its own and found no match. It is not disconnected, not gated and not unaware — it is choosing not to participate. That is why a device with a decode fault or a duplicate identity can disrupt a transfer that has nothing to do with it.
Two controllers are attached to one bus and both begin a transfer in the same instant. Is the board damaged?
Answer. No, and knowing why is the payoff of Module 2. Both are open-drain participants that can only pull LOW or release, so the conductor resolves to a defined level and no supply-to-ground path exists through either device. The transfers will interfere logically and one must give way — that is Module 13 — but there is no electrical fault. Compare this with Chapter 2.2: had the bus used push-pull outputs, the same situation would have been the destructive case, and multi-controller operation would have been impossible.
Why can a single monitor verify a bus with eight targets, when each target needs its own model?
Reasoning. Because the two jobs relate to the medium differently. Monitoring is an observation of the conductor, and there is one conductor carrying every transfer. Modelling is a reproduction of device behaviour, and each device has its own identity and state. Broadcast makes observation central and per-device duplication unnecessary; it does nothing to unify the devices themselves.
9. Understanding Check
10. Summary
One pair of conductors serves an entire board. SDA and SCL are rails that every participant taps, and adding a target is an attachment rather than a new interface — the scaling property Module 1 identified and Module 2 made electrically safe.
That creates the arrangement this chapter exists to explain: the bus is physically broadcast and logically selective. Every device receives every bit, and exactly one is supposed to answer. Bridging that gap requires identity carried in the transmitted information, because sharing the conductors destroyed the implicit addressing a private wire provided. Encoding, width, direction bits and reserved values belong to Modules 6 and 7; the architectural necessity belongs here.
Selection is a convention each device implements, not something the wiring enforces — which is why two parts sharing an identity both answer, and why the controller then receives the wired-AND of their responses rather than either one.
The bus also supports more than one participant capable of being a controller. Simultaneous activity is electrically harmless, precisely because Module 2 removed the ability to drive HIGH; what remains is a logical question that Module 13 answers using each participant\u0027s ability to release a line and observe that it is still held.
For verification the shape is asymmetric: one monitor on the bus sees every transfer because the medium is broadcast, while target models are per-device because devices have their own identity and state.
11. What Comes Next
The topology is now understood in the abstract, and every claim in it has an electrical cost that this chapter quietly deferred. Chapter 3.3 makes the bus a real schematic: where the pull-ups actually go and what happens when two modules each bring their own, what a device\u0027s supply rail decides, how loading accumulates as devices are added, and when a single segment stops being viable — including how to handle the duplicate-identity collision this chapter diagnosed.
Browse the full path on the I\u00B2C tutorials index. For the electrical properties this chapter relies on, see Wired-AND and Why Push-Pull Fails.
Continue learning
Related tutorials
- Related topic
From Parallel Buses to Two Wires
Derive the bus rather than meet it. Trading wires for time gives serialisation; trading exclusivity for coordination gives a shared medium; losing the wire as an implicit address forces a logical one. Each step is a deliberate exchange, and what falls out is a two-wire addressed bus — which is what Philips specified as I²C.
- Related topic
Reasoning About Multi-Master Arbitration
Arbitration worked on a waveform rather than from a definition: what a controller can know, why the winner never learns it won, and a measured comparison of three loss-detection rules — one missing 9.3 % of losses entirely, another driving for six more bits onto the winner’s transfer.
- Related topic
Why Chips on a Board Need a Bus
A connection between two chips is not a wire. It is a pin on each package, a routed trace, the board area and layers that trace consumes, and an I/O cell driving it — and all of that is paid for again for every device added. This is the cost structure that makes dedicating an interface per peripheral stop scaling, and that forces a board to share one set of wires instead.
- Related topic
SDA and SCL — The Two-Wire Contract
Name the two conductors, establish that both are bidirectional shared nodes rather than any device's output, and define what a free bus looks like. Then state precisely the question the rest of the module exists to answer: what happens when two devices attached to the same wire disagree about its level?
