I²C · Module 2
Wired-AND — Many Drivers, One Line, No Contention
Put several open-drain devices on one node and a logic function appears in the wiring: any participant asserting LOW wins, and HIGH requires unanimous release. Derive dominant LOW, see why two devices pulling together is agreement rather than conflict, and watch three of the protocol's mechanisms become predictable.
Chapter 2.4 completed the node: strong switched paths to ground from every attached device, one weak permanent path to the supply. Everything needed is now present, and this chapter does nothing but put more than one device on it and read off the consequences.
That turns out to be the point at which the electrical architecture stops being a circuit fact and becomes the reason the protocol looks the way it does. Nothing new is introduced here. What emerges was already implied by Chapters 2.3 and 2.4 — and the bus's three most distinctive behaviours fall out of it.
1. Several Devices, One Node
Three devices share a conductor. Each can pull it LOW or release it. A resistor restores it when nothing is pulling.
Work through the combinations, and notice that there is nothing subtle to work out — each case is decided by the same rule.
All three release. No switch connects the conductor to ground, so the resistor is the only path anywhere. The conductor rises to HIGH.
One pulls, two release. One low-impedance path to ground exists, and it dominates the weak resistor. The conductor is LOW.
Two pull, one releases. Two parallel paths to ground. The conductor is LOW — no more LOW than before, and no differently.
All three pull. Three parallel paths. Still LOW.
The pattern is immediate and has no exceptions: the conductor is HIGH only when every device has released, and LOW whenever any device is pulling.
2. Asserting and Releasing, in Time
Three open-drain devices sharing one conductor
10 cyclesTwo moments in that figure deserve attention.
At the interval where two devices pull together, nothing special happens. The conductor is LOW, exactly as it was when one device pulled. There is no conflict, no intermediate voltage, no current between the devices — both are sinks connected to the same ground, so they agree by construction. This is the case that would have been destructive with push-pull outputs and is completely unremarkable here.
At the interval where A releases while B is still pulling, the line does not rise. A has stopped contributing, and the level is now B's business. This is Chapter 2.3's "release is not drive HIGH" made visible, and it is the single most consequential observation in the module.
3. The Function in the Wiring
Write the behaviour as a table, treating "pulling" as a device asserting itself and reading off the conductor's level.
| Device A | Device B | Device C | Conductor |
|---|---|---|---|
| released | released | released | HIGH |
| pulling | released | released | LOW |
| released | pulling | released | LOW |
| released | released | pulling | LOW |
| pulling | pulling | released | LOW |
| pulling | pulling | pulling | LOW |
The conductor is HIGH only on the row where every device released. In logic terms, if each device's released state is treated as a 1 and pulling as a 0, then the conductor's level is the logical AND of what every device is contributing — and no gate was fitted to compute it. The wiring does it.
That is why the arrangement is called a wired-AND: a logic function implemented by the connection itself rather than by a component.
The name is worth handling carefully, because it is a frequent source of confusion. Whether this looks like an AND or an OR depends entirely on which polarity you call true. Describe it in terms of released = 1 and it is an AND, as above. Describe it in terms of asserting = 1 and the same circuit reads as an OR — any device asserting produces an asserted bus. Both descriptions are correct about the same hardware, which is why arguing about the label is unproductive.
The physical behaviour is what to remember, and it survives every naming convention: any participant can force the conductor LOW; making it HIGH requires unanimous release. Some engineers call this dominant LOW, which has the advantage of describing the behaviour instead of a polarity convention.
4. Why Multi-Device Operation Is Now Safe
Step back and notice how thoroughly Chapter 2.2's problem has been dissolved rather than managed.
With push-pull outputs, the number of attached devices was a hazard: every additional device was another potential source of a supply-to-ground conflict, and safety depended on all of them behaving.
Here, the number of attached devices barely matters. Every device can do exactly one thing — connect the conductor to ground — and any number of them doing it simultaneously produces the same result as one doing it. There is no combination of participants, well-behaved or otherwise, that creates an undefined level or a current path between devices. A device that powers up in an unexpected state, mis-decodes something, or holds its switch closed when it should not, produces a bus that is stuck LOW. That is a functional problem, and a debuggable one, but it is not an electrical fault.
The cost, already paid in Chapter 2.4, is that the one path upward is weak and the rising edge takes time. That is the entire price of safe sharing, and the rest of the curriculum is the protocol living within it.
5. What the Protocol Builds From This
Everything so far has been electrical. This section is where it becomes clear that the protocol was not designed alongside the electrical layer but out of it.
Recall the capability from Chapter 2.3: a device that releases and then reads its own input learns whether anybody else is asserting the conductor. Combine that with dominant LOW and three distinct mechanisms become not just possible but nearly inevitable.
A device can answer on a line it does not own. Because any participant may pull the conductor LOW, a device that has just been sent something can assert the shared data line to indicate it is there and responding, without needing a dedicated wire and without the original sender having to stop using the bus. Module 7 develops what that answer means and exactly when it happens.
A device can ask for time. The clock line is shared and subject to the same rule, so a device that needs longer before the transfer continues can simply hold SCL LOW. Whoever is generating the timing will find the line not rising when released and will wait, because the line does not rise until every participant releases it. Module 12 develops this.
A device can discover that it has lost. A participant that releases the conductor and reads back LOW knows that somebody else is asserting it. In a system with more than one potential initiator, that observation is enough to detect that another initiator is active and disagreeing — using no extra signal, no arbiter and no negotiation. Module 13 develops this.
6. Modelling the Wired-AND Bus
The truth table in §3 is a claim about composition, and composition is exactly what a simulation can prove exhaustively. Three participants give eight combinations; all eight can be checked, so nothing is left to argument.
The model extends Chapter 2.3's participant to three instances on one conductor. Each participant contributes 0 or high impedance and nothing else, one weak pull-up establishes HIGH, and each participant reads the resolved line back separately — because the whole point is that what a participant contributes and what it observes are different quantities.
6a. SystemVerilog
// Models the board-level conductor and its pull-up. Not synthesizable RTL:
// synthesis builds neither a wire nor a resistor. The synthesizable part is
// each participant's drive_low intent plus the I/O cell (Chapter 2.3, §6-§7).
module wired_and_bus (
input logic [2:0] drive_low, // one bit of intent per participant
output logic [2:0] sees, // what each participant reads back
output wire line // the resolved conductor -- a NET
);
pullup (line); // weak 1: establishes HIGH only when unopposed
// Three independent open-drain contributions. Each is 0 or high-impedance;
// none can contribute 1. Net resolution does the wired-AND for free.
assign line = drive_low[0] ? 1'b0 : 1'bz;
assign line = drive_low[1] ? 1'b0 : 1'bz;
assign line = drive_low[2] ? 1'b0 : 1'bz;
// Observation is per-participant and separate from intent. All three read
// the same conductor, so all three must agree with each other.
assign sees = {3{1'b0}} | {line, line, line};
endmoduleTwo SystemVerilog assertions state the invariant the chapter derived. They are deliberately small — Module 21 owns the protocol assertion library; these two are about the electrical contract.
One detail in them is worth more than the invariants themselves, because it is a trap that catches people writing their first checker on a resolved net. A plain immediate assertion inside always_comb re-evaluates the instant any input changes — including partway through a time step, before a multi-driver net has finished resolving. Set drive_low to 001 and, for a vanishingly short moment, the simulator has seen the new intent but not yet the new line value, so a plain immediate assertion reports a violation that never really existed. The fix is a deferred immediate assertion, written assert #0, which postpones evaluation to the end of the time step and therefore judges only settled values. That is what these use.
// Bound to the bus model. Both properties are immediate (combinational): the
// conductor resolves within the same instant, so there is no clock here and
// nothing to sample -- which is itself the lesson. These are electrical
// invariants, not protocol timing.
module wired_and_props (
input logic [2:0] drive_low,
input logic line
);
// INVARIANT 1 -- ANY pull forces LOW. If any participant requests LOW, the
// observed conductor must be LOW. This is what makes a single participant
// able to assert the bus regardless of the others.
//
// `assert #0` is a DEFERRED immediate assertion: it is evaluated at the end
// of the time step, after the multi-driver net has resolved. A plain
// `assert` here would fire on the transient between "intent changed" and
// "net settled" and report violations that do not exist.
always_comb begin
if (|drive_low)
assert #0 (line === 1'b0)
else $error("any participant pulling must force the line LOW (drive_low=%b line=%b)",
drive_low, line);
end
// INVARIANT 2 -- observed HIGH implies UNANIMOUS release. The contrapositive
// of invariant 1, and the more useful direction in practice: seeing HIGH is
// evidence about EVERY participant, not just about this one.
always_comb begin
if (line === 1'b1)
assert #0 (drive_low === 3'b000)
else $error("line is HIGH while a participant still pulls (drive_low=%b)", drive_low);
end
endmoduleThe testbench walks all eight combinations, checks the resolved level against the wired-AND rule, and separately checks the property that matters most for later modules: a participant that has released still observes whatever the other participants are doing.
module wired_and_bus_tb;
logic [2:0] drive_low;
logic [2:0] sees;
wire line;
logic expected; // module scope: a block-scope variable WITH an
int errors = 0; // initialiser is static, so it would be set once
wired_and_bus dut (.drive_low(drive_low), .sees(sees), .line(line));
// The checker above is bound in a simulator that supports deferred
// assertions. The testbench ALSO performs the same two checks below, after
// settling, so the invariants are verified either way -- which is why the
// sweep is written to check the resolved level explicitly rather than
// relying on the assertions alone.
initial begin
// EXHAUSTIVE: three participants, eight combinations, no gaps.
for (int unsigned c = 0; c < 8; c++) begin
drive_low = c[2:0];
#1;
// The wired-AND rule: LOW if any bit set, HIGH only if none.
expected = (c == 0) ? 1'b1 : 1'b0;
if (line !== expected) begin
$error("drive_low=%b: line=%b expected %b", drive_low, line, expected);
errors++;
end
// One conductor means one observed level for everybody.
if (sees !== {3{line}}) begin
$error("drive_low=%b: participants disagree (sees=%b line=%b)",
drive_low, sees, line);
errors++;
end
// INVARIANT 1 -- any pull forces LOW (checked after settling).
if ((|drive_low) && line !== 1'b0) begin
$error("drive_low=%b: a participant pulls but line=%b", drive_low, line);
errors++;
end
// INVARIANT 2 -- observed HIGH implies unanimous release.
if (line === 1'b1 && drive_low !== 3'b000) begin
$error("line is HIGH while drive_low=%b", drive_low);
errors++;
end
end
// RELEASE AND OBSERVE -- the property the protocol is built on.
// Participant 0 releases while participant 1 keeps pulling.
drive_low = 3'b010; #1;
if (sees[0] !== 1'b0) begin
$error("participant 0 released but must still observe the held LOW"); errors++;
end
// Now participant 1 also releases: only then does the line rise, and
// participant 0's observation changes without participant 0 doing anything.
drive_low = 3'b000; #1;
if (sees[0] !== 1'b1) begin
$error("line must rise once every participant has released"); errors++;
end
if (errors == 0) $display("PASS: all 8 combinations obey wired-AND; release-and-observe works");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule6b. Verilog
Same conductor, same pull-up, same exhaustive sweep. Verilog has no concurrent-assertion construct, so the invariants are written as explicit procedural checks — which is a fair illustration of what SystemVerilog assertions buy: the same invariant, expressed once and checked continuously rather than at points the testbench remembers to look.
module wired_and_bus (
input wire [2:0] drive_low,
output wire [2:0] sees,
output wire line
);
pullup (line);
assign line = drive_low[0] ? 1'b0 : 1'bz;
assign line = drive_low[1] ? 1'b0 : 1'bz;
assign line = drive_low[2] ? 1'b0 : 1'bz;
assign sees = {line, line, line};
endmodule module wired_and_bus_tb;
reg [2:0] drive_low;
wire [2:0] sees;
wire line;
integer c, errors;
reg expected;
wired_and_bus dut (.drive_low(drive_low), .sees(sees), .line(line));
initial begin
errors = 0;
for (c = 0; c < 8; c = c + 1) begin
drive_low = c[2:0];
#1;
expected = (c == 0) ? 1'b1 : 1'b0;
if (line !== expected) begin
$display("FAIL: drive_low=%b line=%b expected %b", drive_low, line, expected);
errors = errors + 1;
end
if (sees !== {3{line}}) begin
$display("FAIL: drive_low=%b participants disagree", drive_low);
errors = errors + 1;
end
// INVARIANT 1 -- any pull forces LOW.
if ((|drive_low) && line !== 1'b0) begin
$display("FAIL: a participant pulls but line=%b", line);
errors = errors + 1;
end
// INVARIANT 2 -- HIGH implies unanimous release.
if (line === 1'b1 && drive_low !== 3'b000) begin
$display("FAIL: line HIGH while drive_low=%b", drive_low);
errors = errors + 1;
end
end
drive_low = 3'b010; #1;
if (sees[0] !== 1'b0) begin
$display("FAIL: released participant must observe the held LOW");
errors = errors + 1;
end
drive_low = 3'b000; #1;
if (sees[0] !== 1'b1) begin
$display("FAIL: line must rise after unanimous release");
errors = errors + 1;
end
if (errors == 0) $display("PASS: all 8 combinations obey wired-AND; release-and-observe works");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule6c. VHDL
The same composition through std_logic resolution, with the pull-up as a driver contributing weak 'H' exactly as in Chapter 2.3. VHDL's assert inside the stimulus process plays the role of both the checks and the invariants.
library ieee;
use ieee.std_logic_1164.all;
entity wired_and_bus is
port (
drive_low : in std_logic_vector(2 downto 0);
sees : out std_logic_vector(2 downto 0);
line : inout std_logic -- resolved: four drivers total
);
end entity;
architecture sim of wired_and_bus is
begin
-- The pull-up: weak 'H'. Beaten by any strong '0', beats 'Z'.
line <= 'H';
-- Three open-drain contributions. None can contribute '1'.
line <= '0' when drive_low(0) = '1' else 'Z';
line <= '0' when drive_low(1) = '1' else 'Z';
line <= '0' when drive_low(2) = '1' else 'Z';
-- Per-participant observation, normalised so 'H' reads as '1'.
sees <= (others => to_X01(line));
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity wired_and_bus_tb is
end entity;
architecture sim of wired_and_bus_tb is
signal drive_low : std_logic_vector(2 downto 0) := (others => '0');
signal sees : std_logic_vector(2 downto 0);
signal line : std_logic;
begin
dut : entity work.wired_and_bus
port map (drive_low => drive_low, sees => sees, line => line);
stim : process
variable expected : std_logic;
begin
-- EXHAUSTIVE: all eight combinations of three participants.
for c in 0 to 7 loop
drive_low <= std_logic_vector(to_unsigned(c, 3));
wait for 1 ns;
if c = 0 then expected := '1'; else expected := '0'; end if;
assert to_X01(line) = expected
report "wired_and_bus: resolved level disagrees with the wired-AND rule"
severity error;
-- INVARIANT 1 -- any pull forces LOW.
if drive_low /= "000" then
assert to_X01(line) = '0'
report "wired_and_bus: a participant pulls but the line is not LOW"
severity error;
end if;
-- INVARIANT 2 -- HIGH implies unanimous release.
if to_X01(line) = '1' then
assert drive_low = "000"
report "wired_and_bus: line is HIGH while a participant still pulls"
severity error;
end if;
-- One conductor, so every participant observes the same level.
assert sees = (sees'range => to_X01(line))
report "wired_and_bus: participants disagree about the conductor"
severity error;
end loop;
-- RELEASE AND OBSERVE.
drive_low <= "010"; wait for 1 ns;
assert to_X01(sees(0)) = '0'
report "wired_and_bus: released participant must still observe the held LOW"
severity error;
drive_low <= "000"; wait for 1 ns;
assert to_X01(sees(0)) = '1'
report "wired_and_bus: line must rise after unanimous release"
severity error;
report "wired_and_bus self-check complete" severity note;
wait;
end process;
end architecture;6d. What the Three Models Establish
Ports, the number of participants, the pull-up, the stimulus sweep and the expected results are identical across the three. All eight combinations are checked in each, so the wired-AND rule is not asserted — it is demonstrated exhaustively.
The three differ only in how the invariants are expressed: SystemVerilog states them as concurrent assertions that hold continuously, Verilog as procedural checks the testbench performs at each step, and VHDL as assert statements inside the stimulus process. The first is stronger in a real environment, because a concurrent assertion fires on any violation whether or not the test was looking — which is the practical argument for assertions over checks, made concrete on a five-line property.
All of these are simulation models of a bus, not synthesizable designs. The conductor and the pull-up are board objects. What synthesises is each participant's drive_low intent and the I/O cell behind it, as Chapter 2.3 §6 and §7 established.
7. Verification Connection — What a Driver Controls and a Monitor Observes
The model above makes a verification architecture point that is easy to state and expensive to get wrong, and it is the reason this chapter matters to DV engineers as much as to designers.
A testbench component driving a participant on this bus must not assign the bus a value. It has no such capability — no participant does. What it controls is exactly one bit: this participant's drive_low intent. Whether the conductor is LOW or HIGH is then determined by every participant together, and the component finds out by reading the line like everyone else.
That splits cleanly into components, and the split is the lesson:
| Component | Controls | Observes | Must never |
|---|---|---|---|
| Driver | its own participant's drive_low | the resolved line, to know what happened | assign the bus a level, or assume its intent became the level |
| Monitor | nothing | the resolved line only | reconstruct activity from any driver's intent |
| Scoreboard | nothing | reconstructed transfers | assume the driver's request equals what appeared on the bus |
The abstraction chain runs one way, and each arrow is a genuine translation rather than a rename:
sequence item "read two bytes from device 0x48"
| (intent, no conductors mentioned)
v
driver intent drive_low asserted and released over time
| (this participant only)
v
electrical model 0 or high-impedance contributed to a shared net
|
v
resolved bus what the conductor actually became
| (every participant's contribution combined)
v
monitor samples the resolved bus -- and nothing else
|
v
reconstruction "device 0x48 was read, two bytes, outcome X"A concise illustration of the boundary, and the point is the comment on the forbidden line:
// Inside a driver for one participant on the shared bus.
task automatic assert_low_for(int unsigned cycles);
vif.drive_low <= 1'b1; // CORRECT: request that this
repeat (cycles) @(posedge vif.clk); // participant pull the line
vif.drive_low <= 1'b0; // CORRECT: release -- NOT drive HIGH
// vif.line <= 1'b1; // WRONG, and wrong in two separate ways:
// 1. no participant can drive the line HIGH -- the capability was
// deliberately removed in Chapter 2.3;
// 2. the line is a RESOLVED net, so a testbench assigning it is adding
// a driver the real system does not have, and the bug it hides is
// exactly the one a real bus would expose.
endtaskWhy this matters later, stated without teaching the mechanisms: a monitor that trusted driver intent instead of the resolved line would be blind to any situation where a participant's request and the bus disagree. Those situations are not exotic — they are precisely where clock stretching and arbitration live, which is why an environment built on the wrong abstraction cannot verify either. Modules 12, 13 and 19 to 21 develop all of it.
8. Debugging the Wired-AND Bus
The driver that verified its own wishes — trusting intent instead of the resolved bus
Pitfall — checking what we requested rather than what the conductor did
// A testbench component for one participant. It asserts the line LOW for a while,
// then releases and immediately confirms the bus is HIGH again:
drive_low = 1'b1;
#100;
drive_low = 1'b0; // release
#1;
if (drive_low == 1'b0) // <-- THE BUG: checking our own intent
pass_count++; // and calling it a bus check
// The check is tautological. drive_low is an output of this component, so it
// always equals what we just wrote. The test passes on every run, including runs
// where the conductor never rose at all.
// A subtler variant of the same bug reads the line but only ever runs with ONE
// participant present, so intent and resolved level can never disagree.Green regressions, and hardware that fails the moment a second participant is active. The testbench reports full pass rates while never having observed the conductor. Symptoms in the lab: a controller that proceeds as though a line rose when another device is still holding it LOW, transfers that go wrong only when a particular peripheral is fitted, and behaviour that changes when devices are added or removed -- because the bug's severity depends on whether anybody else is contributing. On a single-participant bench it is undetectable by construction.
The component conflated DRIVE INTENT with OBSERVED LEVEL -- the two quantities Chapter 2.1 separated and this chapter exists to keep apart. On a dedicated output they are the same fact, and that habit is what leaks in. On a shared conductor the level is a function of EVERY participant, so a component can only learn it by reading the line. Checking drive_low proves that the component's own register holds what the component wrote, which is true unconditionally and therefore establishes nothing about the bus. The deeper failure is architectural rather than a typo: the environment had no component whose job was to observe the resolved conductor, so there was nothing for a scoreboard to compare against even if somebody had wanted to.
// Read the CONDUCTOR, and read it with a participant that is NOT us. Release,
// then observe what the shared line actually resolved to:
drive_low = 1'b1;
#100;
drive_low = 1'b0; // release: stop contributing
#1;
if (line !== 1'b1) // observe the RESOLVED conductor
$error("released, but the line did not rise -- somebody is holding it");
// And the structural fix, which is the one that matters: the environment needs a
// passive monitor that samples ONLY the resolved line and never any driver's
// intent. Then a scoreboard can compare requested activity against observed
// activity -- and the two disagreeing becomes a finding rather than an
// invisibility.
//
// The test that catches the original bug: run with a SECOND participant holding
// the line LOW while the first releases. Intent says released, the conductor says
// LOW, and any check built on intent passes while any check built on observation
// correctly fails. A single-participant bench can never expose it, which is why
// multi-participant stimulus is not an optional extra on a shared bus.The engineering lesson generalises past this bus. On any shared medium, a component's own output is never evidence about the medium's state. The question a reviewer should ask of any testbench for a resolved net is simply: which signal is this check reading? If it reads something the testbench itself drives, the check is tautological — it will pass forever and prove nothing. If it reads the resolved conductor, it can fail, and a check that can fail is the only kind worth having.
9. Common Misconceptions
10. Reason It Through
Work this through before reading the answers.
Two devices share SDA. Device A releases the line and immediately reads its own input. It reads LOW. Device B is pulling.
What has A learned? That another participant is asserting the conductor. A contributed nothing after releasing, so the LOW cannot be its own doing.
Could A have learned this if the bus used push-pull outputs? No, and the reason is structural rather than practical. With push-pull, A releasing is not a state that exists — A would be driving HIGH, which means opposing B's LOW, which is the electrical fault from Chapter 2.2. The observation is only available because "release" is a real state in which the device still has a working input while contributing nothing.
Why does this matter for a system with two potential initiators? Because it gives a participant a way to detect that another one is active and disagreeing, without an arbiter, an extra signal or any negotiation — just an ordinary input and the dominant-LOW rule. Module 13 turns that into a complete mechanism, including what the losing participant must do.
What would A conclude if it released and read HIGH? That nothing else is currently pulling — subject to one qualification that the next chapter develops. Reading immediately after releasing can be misleading, because the line does not reach HIGH instantly: it has to charge through the pull-up. A device that samples too soon can read LOW simply because the edge has not finished, not because anyone is asserting. The distinction between "held LOW by a participant" and "still rising" is exactly the kind of judgement Chapter 2.6 is about.
11. Understanding Check
12. Summary
Put several open-drain devices on one node with a pull-up and a rule appears that has no exceptions: the conductor is LOW whenever any device is pulling, and HIGH only when every device has released.
That is a logic function implemented by the wiring rather than by a component — a wired-AND when released is treated as 1, a wired-OR under the opposite convention, and dominant LOW under either. The label is a polarity choice; the behaviour is not.
Several devices pulling together is agreement, not conflict. All are sinks to the same ground, no current flows between them, and the result is identical to one device pulling. The hazard from Chapter 2.2 does not scale with device count because no participant has the capability required to create it.
Releasing is not raising. A device that releases while another still pulls sees the line stay LOW — and a device that releases and reads its own input thereby learns whether anybody else is asserting. That single capability, combined with dominant LOW, is what the protocol's three most distinctive mechanisms are made of: answering on a shared line, holding the clock to ask for time, and detecting another active participant by releasing and observing. They are applications of the electrical architecture, not features layered on top of it.
It also changes what a testbench must model: not what a device drove, but the resolved level every participant contributed to.
13. What Comes Next
The model is complete and idealised. Chapter 2.6 closes Module 2 by putting it on a real board — where edges have shape, levels are thresholds rather than values, the capacitance comes from identifiable places, and two devices may not even share a supply voltage. That is also where the difference between "held LOW by a participant" and "still rising" becomes a judgement an engineer has to make while looking at a capture.
Browse the full path on the I²C tutorials index.
Continue learning
Related tutorials
- Related topic
I²C SDA Arbitration — Wired-AND Decides Bit by Bit
Arbitration with no arbiter, no priority and no protocol exchange — resolved by one asymmetric test each master performs on itself. Settles what 'no information is lost' actually means.
- 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
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.
