I²C · Module 13
Why Multiple I²C Masters Exist
The systems that end up with two masters on one bus, and the rule that is not enough to keep them apart. Includes the finding that a collision leaves no trace on the wire.
Every chapter so far has taken one thing on faith. arb_lost has been an input since Chapter 10.3 — a signal handed in from outside, never explained. Chapter 12.4 closed by promising the mechanism behind it.
This module builds it. And it starts with a question that sounds like systems architecture and turns out to be electrical:
Why would anyone put two masters on a bus that was designed around one?
1. The Systems That End Up With Two Masters
Nobody sets out to build a multi-master I²C bus. They arrive at one, and almost always by one of four routes.
A management controller alongside the application. A board's main SoC talks to its sensors; a separate baseboard management controller must also read those sensors — including while the SoC is held in reset, which is precisely when the SoC cannot proxy for it. IPMI's IPMB is specified this way, and UM10204 lists it as an I²C application: "I²C based · Multi-master · Simple Request/Response Protocol". The second master is not a convenience, it is the entire point of the controller.
A housekeeping microcontroller. Fan control, thermal shutdown, power sequencing. These must keep working when the application processor is busy, wedged or being reflashed, so they get their own bus access.
A debug or production-test path. A header that lets a test fixture read the same EEPROM the product reads, without the product's cooperation.
And the one that arrives by accident: an FPGA added for telemetry, given an I²C master core because that was the obvious way to make it read a sensor — on a bus the board designer had assumed was single-master. Chapter 12.1 §11 is the same accident with clock stretching, and it is the same lesson: an assumption recorded in a review document is invisible to the revision that violates it.
Notice what the diagram does not contain: any arbitration hardware, any priority input, any central decision point. There is nowhere to put one — and §3 of Chapter 13.3 shows that none is needed.
2. The Rule, and Why It Is Not Enough
UM10204 §3.1.8 opens with a rule and then, in the very next sentence, concedes that the rule does not close the case.
Read that twice, because the reasoning is subtler than it looks.
Both masters obeyed the rule. Each observed a free bus. Each was entitled to start. Neither did anything wrong.
And the result is a single valid START that two masters believe is theirs. Not a malformed waveform, not a glitch — a valid START, indistinguishable from one master's.
The gap is not a defect in the rule; it is a property of time. "The bus is free" is a fact about the past, and starting is an action in the future. Between observing and acting there is a window, and tHD;STA is how wide it is: 4.0 µs in Standard-mode, 0.6 µs in Fast-mode, 0.26 µs at Fast-mode Plus (Chapter 11.5 §1). Any two masters whose decisions land inside that window both produce a legitimate START.
No amount of checking before you start can close it. That is why arbitration exists and why, per Table 2, it is not optional.
3. Two Notions of "Free", and Why Conflating Them Breaks Both Ways
The rule says a master may start only if the bus is free. So what is "free"?
There are two answers in the specification and they are not the same one.
tBUF | "the bus is free" | |
|---|---|---|
| defined as | the interval from a STOP to the next START | the precondition of §3.1.8 |
| where | Table 10, Chapter 11.6 | §3.1.8 |
| exists when | a STOP has occurred | always |
| at power-up | undefined — no STOP has happened | must still be satisfiable |
A design that keys start permission on tBUF alone never grants the first master after reset, because no STOP has occurred and the interval it measures does not exist. That is mutation N2 in §8, and it is the more dangerous of the two mistakes because it produces a bus that is silent rather than wrong.
A design that keys it on the idle level grants permission during another master's high phase, because both lines are high for half of every bit on a perfectly busy bus. That is mutation N1, and it is Chapter 12.4 §11's confusion in a new place: a level is not a duration.
The condition that is neither too strong nor too weak is: both lines released, continuously, for at least T_BUF. §6's monitor implements exactly that, tracks stop_seen separately as information rather than as a gate, and §7's tests 2 and 3 pin both halves.
4. The Collision Leaves No Trace on the Wire
Here is the finding that shaped this chapter's design, and it is worth stating as starkly as the specification does.
Two masters start inside tHD;STA. Each pulls SDA low while SCL is high. The wired-AND merges two pull-downs into one edge — because a pull-down is a command and two commands to the same value are indistinguishable from one (Chapter 12.2 §2).
So §3.1.8's wording is exact and deliberate: the two STARTs "result in a valid START condition on the bus". Singular. There is one edge.
An observer with only SDA and SCL counts one START and cannot know that two masters own it.
That has three consequences, and together they are the reason this module has four more chapters.
A bus monitor cannot detect a collision. Not a limitation of any particular monitor — the information was destroyed by the electrical layer before any observer could sample it. §6's block therefore derives arbitration_needed from the two masters' requests, which is intent, and §7's test 7 asserts that the wire shows exactly one START for a merged pair. Asserting an impossibility is the most useful thing that test does.
So nothing can announce the collision, and arbitration must resolve it without anyone detecting it. That is Chapter 13.3's subject, and it is why arbitration is per-bit and self-discovering rather than negotiated.
And this is the same epistemic limit the module has met twice before. Chapter 10.2 §4: from the wire alone, a STOP followed by a START is two transactions, and no monitor can recover the intent behind them. Chapter 12.1 §5: a stretch is a disagreement between intent and observation, invisible on either alone.
5. The START Byte: a Master That Polls in Software
Table 2's footnote [3] recommends a START byte for software-emulated multi-master systems, and §3.1.15 explains a problem that only exists once there are two masters.
The design of that byte is worth admiring for a moment, because every bit of it is doing a job.
0000 0001 — seven consecutive zeros. A software master polling SDA slowly can miss a single short low, but it cannot miss seven bit-times of continuous low. The value is chosen to be the longest run of zeros a byte can carry while still ending in a one, and the trailing one is what lets the line be released for the acknowledge slot.
No device may acknowledge it. So the acknowledge slot is guaranteed to read high, and the byte cannot be confused with an address — including with the reserved addresses of Chapter 6.3.
The acknowledge pulse exists anyway, purely so that the sequence is nine bits like everything else. A receiver counting bits does not need a special case.
And the repeated START afterwards is what a hardware slave actually synchronizes to. §3.1.15 says a hardware receiver "resets upon receipt of the repeated START condition Sr and therefore ignores the START byte" — so the whole procedure is invisible to conventional slaves. It costs nine bit-times and changes nothing for anyone who does not need it.
Why this belongs in a multi-master chapter: a slow software master cannot poll fast enough to notice a tHD;STA-width window, so it would lose every race against a hardware master and never get the bus. The START byte widens the announcement from one edge to seven bit-times, which is the difference between a master that can participate and one that cannot.
6. The Bus-Free Monitor in Three Languages
// WHY MULTIPLE MASTERS ARE UNSAFE WITHOUT ARBITRATION. UM10204 section 3.1.8 opens with a rule and
// then immediately concedes that the rule is not enough:
//
// "A master may start a transfer only if the bus is free. Two masters may generate a START
// condition within the minimum hold time (tHD;STA) of the START condition which results in a
// VALID START CONDITION ON THE BUS. Arbitration is then required to determine which master
// will complete its transmission."
//
// Read that twice. Both masters obeyed the rule. Both observed a free bus, both were entitled to
// start, and the result is a single valid START that two masters believe is theirs. Nothing was
// violated -- the rule is simply insufficient, because "the bus is free" is a fact about the past
// and starting is an action in the future.
//
// This block is that gap, made measurable. It tracks bus state from the wire, grants start
// permission to two requesters, and reports the case where it granted BOTH. That last output is
// not an error flag: it is the precondition for arbitration, and the whole of Chapters 13.2 to
// 13.5 exists because it can be asserted.
//
// TWO NOTIONS OF FREE, and conflating them is the trap:
//
// tBUF -- section 3.1.16's bus-free time, measured from a STOP to the next START. It only
// exists if a STOP was seen. (Chapter 11.6)
// "bus free" -- section 3.1.8's precondition: both lines released long enough that no transfer
// can be in progress. A master powering up on a quiet bus has seen no STOP, and
// must still be able to start.
//
// A design that keyed start permission on tBUF alone would never grant the first master after
// reset. A design that keyed it on the idle LEVEL would grant during someone else's high phase.
// Both lines high, continuously, for at least T_BUF is the condition that is neither too strong
// nor too weak -- and mutations A2 and A3 are those two mistakes.
module i2c_bus_free_monitor #(
parameter int TICK_W = 20,
// Fast-mode, in ticks of a 100 MHz sample clock.
parameter int T_BUF = 130, // tBUF(min) = 1.3 us
parameter int T_HD_STA = 60 // tHD;STA(min) = 0.6 us
)(
input logic clk,
input logic rst_n,
// the observed bus
input logic sda_in,
input logic scl_in,
// two masters asking for the bus
input logic req_a,
input logic req_b,
// ---- bus state ----
output logic bus_free, // both lines released for at least T_BUF
output logic [TICK_W-1:0] free_ticks, // how long they have been released
output logic stop_seen, // a STOP has occurred since reset
output logic start_det,
output logic stop_det,
output logic in_transfer,
// ---- start permission ----
output logic grant_a,
output logic grant_b,
// ---- THE point of the block ----
// Both masters were granted in the same window, so the bus now carries ONE START that two
// masters believe is theirs. Note what this output is NOT derived from: the wire. Two STARTs
// inside tHD;STA produce a single SDA edge, because the wired-AND merges two pull-downs into
// one -- which is why section 3.1.8 says the result "is a valid START condition on the bus",
// singular. A collision is invisible to any observer that has only the bus, and `n_starts`
// below counts ONE for it. The detection here comes from the two REQUESTS, which is intent.
output logic arbitration_needed,
output logic [TICK_W-1:0] n_arb_needed,
output logic [TICK_W-1:0] n_starts,
output logic [TICK_W-1:0] n_double_grant,
output logic [TICK_W-1:0] min_free_at_start // the shortest free interval any START followed
);
logic sda_q, scl_q;
wire sda_rise = sda_in && !sda_q;
wire sda_fall = !sda_in && sda_q;
// Framing by definition: SDA moving while SCL is HIGH.
assign start_det = sda_fall && scl_in;
assign stop_det = sda_rise && scl_in;
// The idle SIGNATURE is a level; the free CONDITION is a duration. Keeping them as separate
// names is the whole of this block's correctness.
wire idle_now = sda_in && scl_in;
logic [TICK_W-1:0] free_q;
wire [TICK_W-1:0] free_now = free_q + 1'b1; // "including this cycle"
// Sized once: a part-select of a parameter is not portable.
localparam logic [TICK_W-1:0] T_BUF_W = T_BUF;
// T_HD_STA is the width of the window inside which two masters' STARTs merge into one. It is
// deliberately NOT used in a comparison here: there is nothing on the wire to compare it
// against, which is the chapter's point. It is exposed so a testbench can drive that window.
localparam logic [TICK_W-1:0] T_HD_STA_W = T_HD_STA;
wire unused_hd_sta = (T_HD_STA_W == T_HD_STA_W);
always_ff @(posedge clk) begin
if (!rst_n) begin
sda_q <= 1'b1;
scl_q <= 1'b1;
free_q <= '0;
bus_free <= 1'b0;
stop_seen <= 1'b0;
in_transfer <= 1'b0;
arbitration_needed <= 1'b0;
n_arb_needed <= '0;
n_starts <= '0;
n_double_grant <= '0;
// A MINIMUM tracker starts at all-ones (Chapter 11.2 section 6a).
min_free_at_start <= {TICK_W{1'b1}};
end else begin
sda_q <= sda_in;
scl_q <= scl_in;
arbitration_needed <= 1'b0;
// ---- the free interval ----
if (idle_now) begin
free_q <= free_now;
// The condition is a DURATION. Asserting bus_free on the level alone would grant
// permission during another master's HIGH phase, which is mutation A3.
bus_free <= (free_now >= T_BUF_W);
end else begin
free_q <= '0;
bus_free <= 1'b0;
end
// ---- framing ----
if (start_det) begin
in_transfer <= 1'b1;
// Exactly ONE increment, however many masters drove this START. The wired-AND has
// already merged them, and no amount of logic here can unmerge them.
n_starts <= n_starts + 1'b1;
if (free_q < min_free_at_start) min_free_at_start <= free_q;
end else if (stop_det) begin
in_transfer <= 1'b0;
stop_seen <= 1'b1;
end
// ---- the double grant ----
// Both masters saw a free bus in the same cycle and both are entitled to start. This
// is not a fault in either of them, and it is not a fault here: it is the condition
// section 3.1.8 says arbitration exists to resolve.
if (bus_free && req_a && req_b) begin
arbitration_needed <= 1'b1;
n_arb_needed <= n_arb_needed + 1'b1;
n_double_grant <= n_double_grant + 1'b1;
end
end
end
// Permission is combinational and identical for both requesters. There is deliberately no
// priority, no round-robin and no tie-break: section 3.1.8 says "there is no central master,
// nor any order of priority on the bus". A monitor that broke the tie here would be modelling
// a bus that does not exist -- and would hide the very case Chapters 13.3 and 13.4 resolve.
assign grant_a = bus_free && req_a;
assign grant_b = bus_free && req_b;
assign free_ticks = free_q;
endmodule `timescale 1ns/1ps
// 100 MHz sample clock. Fast-mode: T_BUF = 130 ticks (1.3 us), T_HD_STA = 60 ticks (0.6 us).
//
// The stimulus drives the bus as a wired-AND of two masters' intents, because that is what a
// multi-master bus IS -- and because the collision this block reports cannot be produced any other
// way. All stimulus is driven on the negedge: Module 9's race lesson.
module i2c_bus_free_monitor_tb;
localparam int TICK_W = 20;
localparam int T_BUF = 130;
localparam int T_HD_STA = 60;
logic clk = 1'b0, rst_n = 1'b0;
always #5 clk = ~clk;
// Two masters' open-drain intents. 1 = released.
logic a_sda = 1'b1, a_scl = 1'b1;
logic b_sda = 1'b1, b_scl = 1'b1;
wire sda = a_sda && b_sda; // the wired-AND
wire scl = a_scl && b_scl;
logic req_a = 1'b0, req_b = 1'b0;
logic bus_free, stop_seen, start_det, stop_det, in_transfer;
logic [TICK_W-1:0] free_ticks;
logic grant_a, grant_b, arbitration_needed;
logic [TICK_W-1:0] n_arb_needed, n_starts, n_double_grant, min_free_at_start;
i2c_bus_free_monitor #(.TICK_W(TICK_W), .T_BUF(T_BUF), .T_HD_STA(T_HD_STA)) dut (
.clk(clk), .rst_n(rst_n), .sda_in(sda), .scl_in(scl),
.req_a(req_a), .req_b(req_b),
.bus_free(bus_free), .free_ticks(free_ticks), .stop_seen(stop_seen),
.start_det(start_det), .stop_det(stop_det), .in_transfer(in_transfer),
.grant_a(grant_a), .grant_b(grant_b),
.arbitration_needed(arbitration_needed), .n_arb_needed(n_arb_needed),
.n_starts(n_starts), .n_double_grant(n_double_grant),
.min_free_at_start(min_free_at_start)
);
int errors = 0;
task automatic tick(input int n); begin repeat (n) @(negedge clk); end endtask
// Master A drives a START: SDA falls while SCL is high.
task automatic a_start(); begin a_sda = 1'b0; tick(T_HD_STA); a_scl = 1'b0; tick(20); end endtask
task automatic b_start(); begin b_sda = 1'b0; tick(T_HD_STA); b_scl = 1'b0; tick(20); end endtask
// Master A drives a STOP: release SCL, then SDA rises while SCL is high.
task automatic a_stop();
begin a_sda = 1'b0; a_scl = 1'b1; tick(20); a_sda = 1'b1; tick(10); end
endtask
// Let the bus go fully idle for n ticks.
task automatic idle(input int n);
begin a_sda = 1'b1; a_scl = 1'b1; b_sda = 1'b1; b_scl = 1'b1; tick(n); end
endtask
initial begin
tick(4); rst_n = 1'b1; tick(4);
// ---- 1. reset: nothing claimed, and a MINIMUM tracker at its maximum ----------------
if (min_free_at_start !== {TICK_W{1'b1}}) begin
$display("FAIL: min_free_at_start did not start at its maximum"); errors++; end
if (n_arb_needed !== '0 || n_starts !== '0 || stop_seen !== 1'b0) begin
$display("FAIL: counters or stop_seen nonzero out of reset"); errors++; end
// ---- 2. a quiet bus becomes free after T_BUF, and NOT before -------------------------
// The precondition of section 3.1.8 is a DURATION. A block asserting it on the idle level
// would grant permission during another master's high phase.
// The bus has been idle since reset, so the free counter is already running. Interrupt it
// first: the property under test is that the count RESTARTS whenever a line goes low.
a_scl = 1'b0; tick(5); a_scl = 1'b1;
tick(2);
if (bus_free !== 1'b0) begin
$display("FAIL: bus_free asserted 2 ticks after the bus was released (T_BUF = %0d)",
T_BUF); errors++; end
tick(T_BUF - 6);
if (bus_free !== 1'b0) begin
$display("FAIL: bus_free asserted before T_BUF elapsed"); errors++; end
tick(6);
if (bus_free !== 1'b1) begin
$display("FAIL: bus_free not asserted after T_BUF idle ticks"); errors++; end
// ---- 3. a master may start on a quiet bus with NO preceding STOP ---------------------
// A design that required tBUF -- which is defined from a STOP -- would never grant the
// first master after power-up. stop_seen must still be 0 here.
if (stop_seen !== 1'b0) begin
$display("FAIL: stop_seen set with no STOP having occurred"); errors++; end
req_a = 1'b1;
tick(1);
if (grant_a !== 1'b1) begin
$display("FAIL: a master was refused permission on a quiet bus with no preceding STOP");
errors++; end
req_a = 1'b0;
// ---- 4. THE case: both masters granted in the same window ----------------------------
// Section 3.1.8's precondition. Neither master is at fault and neither is refused -- there
// is "no central master, nor any order of priority on the bus".
begin
int before_d;
before_d = n_double_grant;
req_a = 1'b1; req_b = 1'b1;
tick(1);
if (grant_a !== 1'b1 || grant_b !== 1'b1) begin
$display("FAIL: with both masters requesting, grants were a=%b b=%b -- the monitor broke the tie, and the bus has no priority order",
grant_a, grant_b); errors++; end
tick(2);
if (n_double_grant == before_d) begin
$display("FAIL: a double grant was not reported"); errors++; end
req_a = 1'b0; req_b = 1'b0;
end
// ---- 5. the bus stops being free the instant either line is pulled low --------------
a_scl = 1'b0;
tick(2);
if (bus_free !== 1'b0) begin
$display("FAIL: bus_free still set with SCL held low"); errors++; end
req_a = 1'b1; tick(1);
if (grant_a !== 1'b0) begin
$display("FAIL: permission granted on a busy bus"); errors++; end
req_a = 1'b0;
idle(T_BUF + 10);
// ---- 6. a single START is counted, and is not a collision ----------------------------
begin
int s0, c0;
s0 = n_starts; c0 = n_arb_needed;
a_start();
if (n_starts != s0 + 1) begin
$display("FAIL: a START was not counted"); errors++; end
if (n_arb_needed != c0) begin
$display("FAIL: a single START was reported as needing arbitration"); errors++; end
if (in_transfer !== 1'b1) begin
$display("FAIL: in_transfer not set after a START"); errors++; end
a_stop();
if (stop_seen !== 1'b1) begin
$display("FAIL: stop_seen not set after a STOP"); errors++; end
end
// ---- 7. TWO masters starting inside tHD;STA produce exactly ONE start_det --------------
// The most important test in the chapter, and it asserts an IMPOSSIBILITY. Section 3.1.8
// says two STARTs within tHD;STA "result in a valid START condition on the bus" -- one,
// singular -- because the wired-AND merges two pull-downs into a single edge.
//
// So a collision leaves NO trace on the wire. An observer with only SDA and SCL counts one
// START and cannot know two masters own it. That is why arbitration has to resolve the
// situation without anyone detecting it, and why Chapter 13.3 exists.
idle(T_BUF + 10);
begin
int s0, c0;
s0 = n_starts; c0 = n_arb_needed;
a_sda = 1'b0; // A starts: SDA falls with SCL high
tick(T_HD_STA / 2); // ... and B joins INSIDE tHD;STA
b_sda = 1'b0; // SDA is already low: NO second edge exists
tick(T_HD_STA);
a_scl = 1'b0; b_scl = 1'b0;
tick(20);
if (n_starts != s0 + 1) begin
$display("FAIL: two masters starting inside tHD;STA produced %0d STARTs -- the wired-AND merges them into ONE edge and no logic can unmerge it",
n_starts - s0); errors++; end
if (n_arb_needed != c0) begin
$display("FAIL: the monitor claimed to detect a collision from the WIRE -- it cannot; that information only exists in the masters' intent");
errors++; end
end
idle(T_BUF + 10);
// ---- 8. two STARTs well SEPARATED are not a collision -------------------------------
// The negative case. A monitor that flagged every pair of STARTs would pass test 7 and be
// useless -- ordinary back-to-back transactions would all look like collisions.
begin
int c0;
c0 = n_arb_needed;
a_start(); a_stop();
idle(T_BUF + 10);
a_start(); a_stop();
if (n_arb_needed != c0) begin
$display("FAIL: two well-separated STARTs were reported as a collision (%0d)",
n_arb_needed - c0); errors++; end
end
// ---- 9. the shortest free interval before any START is recorded ----------------------
// A MINIMUM tracker: the worst case is the smallest, and it must not be erased by a later
// generous interval.
idle(T_BUF + 500);
begin
int peak;
a_start(); a_stop();
peak = min_free_at_start;
idle(T_BUF + 5000);
a_start(); a_stop();
if (min_free_at_start != peak) begin
$display("FAIL: min_free_at_start rose to %0d after a longer interval (was %0d)",
min_free_at_start, peak); errors++; end
end
// ---- 10. an idle bus produces no STARTs and no collisions ---------------------------
begin
int s0, c0;
idle(20);
s0 = n_starts; c0 = n_arb_needed;
idle(600);
if (n_starts != s0 || n_arb_needed != c0) begin
$display("FAIL: an idle bus produced starts or collisions"); errors++; end
end
if (errors == 0)
$display("PASS: bus-free is a duration not a level, a quiet bus is startable with no preceding STOP, both masters are granted with no priority order, and two STARTs inside tHD;STA are one START with two owners");
else
$display("FAIL: %0d error(s)", errors);
$finish;
end
initial begin
#2000000;
$display("FAIL: watchdog expired");
$finish;
end
endmodule // WHY MULTIPLE MASTERS ARE UNSAFE WITHOUT ARBITRATION. UM10204 section 3.1.8 opens with a rule and
// then immediately concedes that the rule is not enough:
//
// "A master may start a transfer only if the bus is free. Two masters may generate a START
// condition within the minimum hold time (tHD;STA) of the START condition which results in a
// VALID START CONDITION ON THE BUS. Arbitration is then required to determine which master
// will complete its transmission."
//
// Read that twice. Both masters obeyed the rule. Both observed a free bus, both were entitled to
// start, and the result is a single valid START that two masters believe is theirs. Nothing was
// violated -- the rule is simply insufficient, because "the bus is free" is a fact about the past
// and starting is an action in the future.
//
// This block is that gap, made measurable. It tracks bus state from the wire, grants start
// permission to two requesters, and reports the case where it granted BOTH. That last output is
// not an error flag: it is the precondition for arbitration, and the whole of Chapters 13.2 to
// 13.5 exists because it can be asserted.
//
// TWO NOTIONS OF FREE, and conflating them is the trap:
//
// tBUF -- section 3.1.16's bus-free time, measured from a STOP to the next START. It only
// exists if a STOP was seen. (Chapter 11.6)
// "bus free" -- section 3.1.8's precondition: both lines released long enough that no transfer
// can be in progress. A master powering up on a quiet bus has seen no STOP, and
// must still be able to start.
//
// A design that keyed start permission on tBUF alone would never grant the first master after
// reset. A design that keyed it on the idle LEVEL would grant during someone else's high phase.
// Both lines high, continuously, for at least T_BUF is the condition that is neither too strong
// nor too weak -- and mutations A2 and A3 are those two mistakes.
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
module i2c_bus_free_monitor #(
parameter TICK_W = 20,
// Fast-mode, in ticks of a 100 MHz sample clock.
parameter T_BUF = 130, // tBUF(min) = 1.3 us
parameter T_HD_STA = 60 // tHD;STA(min) = 0.6 us
)(
input wire clk,
input wire rst_n,
// the observed bus
input wire sda_in,
input wire scl_in,
// two masters asking for the bus
input wire req_a,
input wire req_b,
// ---- bus state ----
output reg bus_free, // both lines released for at least T_BUF
output wire [TICK_W-1:0] free_ticks, // how long they have been released
output reg stop_seen, // a STOP has occurred since reset
output wire start_det,
output wire stop_det,
output reg in_transfer,
// ---- start permission ----
output wire grant_a,
output wire grant_b,
// ---- THE point of the block ----
// Both masters were granted in the same window, so the bus now carries ONE START that two
// masters believe is theirs. Note what this output is NOT derived from: the wire. Two STARTs
// inside tHD;STA produce a single SDA edge, because the wired-AND merges two pull-downs into
// one -- which is why section 3.1.8 says the result "is a valid START condition on the bus",
// singular. A collision is invisible to any observer that has only the bus, and `n_starts`
// below counts ONE for it. The detection here comes from the two REQUESTS, which is intent.
output reg arbitration_needed,
output reg [TICK_W-1:0] n_arb_needed,
output reg [TICK_W-1:0] n_starts,
output reg [TICK_W-1:0] n_double_grant,
output reg [TICK_W-1:0] min_free_at_start // the shortest free interval any START followed
);
reg sda_q, scl_q;
wire sda_rise = sda_in && !sda_q;
wire sda_fall = !sda_in && sda_q;
// Framing by definition: SDA moving while SCL is HIGH.
assign start_det = sda_fall && scl_in;
assign stop_det = sda_rise && scl_in;
// The idle SIGNATURE is a level; the free CONDITION is a duration. Keeping them as separate
// names is the whole of this block's correctness.
wire idle_now = sda_in && scl_in;
reg [TICK_W-1:0] free_q;
wire [TICK_W-1:0] free_now = free_q + 1'b1; // "including this cycle"
// Sized once: a part-select of a parameter is not portable.
localparam [TICK_W-1:0] T_BUF_W = T_BUF;
// T_HD_STA is the width of the window inside which two masters' STARTs merge into one. It is
// deliberately NOT used in a comparison here: there is nothing on the wire to compare it
// against, which is the chapter's point. It is exposed so a testbench can drive that window.
localparam [TICK_W-1:0] T_HD_STA_W = T_HD_STA;
wire unused_hd_sta = (T_HD_STA_W == T_HD_STA_W);
always @(posedge clk) begin
if (!rst_n) begin
sda_q <= 1'b1;
scl_q <= 1'b1;
free_q <= {TICK_W{1'b0}};
bus_free <= 1'b0;
stop_seen <= 1'b0;
in_transfer <= 1'b0;
arbitration_needed <= 1'b0;
n_arb_needed <= {TICK_W{1'b0}};
n_starts <= {TICK_W{1'b0}};
n_double_grant <= {TICK_W{1'b0}};
// A MINIMUM tracker starts at all-ones (Chapter 11.2 section 6a).
min_free_at_start <= {TICK_W{1'b1}};
end else begin
sda_q <= sda_in;
scl_q <= scl_in;
arbitration_needed <= 1'b0;
// ---- the free interval ----
if (idle_now) begin
free_q <= free_now;
// The condition is a DURATION. Asserting bus_free on the level alone would grant
// permission during another master's HIGH phase, which is mutation A3.
bus_free <= (free_now >= T_BUF_W);
end else begin
free_q <= {TICK_W{1'b0}};
bus_free <= 1'b0;
end
// ---- framing ----
if (start_det) begin
in_transfer <= 1'b1;
// Exactly ONE increment, however many masters drove this START. The wired-AND has
// already merged them, and no amount of logic here can unmerge them.
n_starts <= n_starts + 1'b1;
if (free_q < min_free_at_start) min_free_at_start <= free_q;
end else if (stop_det) begin
in_transfer <= 1'b0;
stop_seen <= 1'b1;
end
// ---- the double grant ----
// Both masters saw a free bus in the same cycle and both are entitled to start. This
// is not a fault in either of them, and it is not a fault here: it is the condition
// section 3.1.8 says arbitration exists to resolve.
if (bus_free && req_a && req_b) begin
arbitration_needed <= 1'b1;
n_arb_needed <= n_arb_needed + 1'b1;
n_double_grant <= n_double_grant + 1'b1;
end
end
end
// Permission is combinational and identical for both requesters. There is deliberately no
// priority, no round-robin and no tie-break: section 3.1.8 says "there is no central master,
// nor any order of priority on the bus". A monitor that broke the tie here would be modelling
// a bus that does not exist -- and would hide the very case Chapters 13.3 and 13.4 resolve.
assign grant_a = bus_free && req_a;
assign grant_b = bus_free && req_b;
assign free_ticks = free_q;
endmodule `timescale 1ns/1ps
// 100 MHz sample clock. Fast-mode: T_BUF = 130 ticks (1.3 us), T_HD_STA = 60 ticks (0.6 us).
//
// The stimulus drives the bus as a wired-AND of two masters' intents, because that is what a
// multi-master bus IS -- and because the collision this block reports cannot be produced any other
// way. All stimulus is driven on the negedge: Module 9's race lesson.
// (Verilog-2001 testbench -- same stimulus, same checks.)
module i2c_bus_free_monitor_tb;
localparam TICK_W = 20;
localparam T_BUF = 130;
localparam T_HD_STA = 60;
reg clk = 1'b0, rst_n = 1'b0;
always #5 clk = ~clk;
// Two masters' open-drain intents. 1 = released.
reg a_sda = 1'b1, a_scl = 1'b1;
reg b_sda = 1'b1, b_scl = 1'b1;
wire sda = a_sda && b_sda; // the wired-AND
wire scl = a_scl && b_scl;
reg req_a = 1'b0, req_b = 1'b0;
wire bus_free, stop_seen, start_det, stop_det, in_transfer;
wire [TICK_W-1:0] free_ticks;
wire grant_a, grant_b, arbitration_needed;
wire [TICK_W-1:0] n_arb_needed, n_starts, n_double_grant, min_free_at_start;
i2c_bus_free_monitor #(.TICK_W(TICK_W), .T_BUF(T_BUF), .T_HD_STA(T_HD_STA)) dut (
.clk(clk), .rst_n(rst_n), .sda_in(sda), .scl_in(scl),
.req_a(req_a), .req_b(req_b),
.bus_free(bus_free), .free_ticks(free_ticks), .stop_seen(stop_seen),
.start_det(start_det), .stop_det(stop_det), .in_transfer(in_transfer),
.grant_a(grant_a), .grant_b(grant_b),
.arbitration_needed(arbitration_needed), .n_arb_needed(n_arb_needed),
.n_starts(n_starts), .n_double_grant(n_double_grant),
.min_free_at_start(min_free_at_start)
);
integer errors = 0;
// Hoisted to module scope: Verilog-2001 permits a variable declaration only at
// module level or in a NAMED block, and every call site below is sequential.
integer before_d = 0;
integer s0 = 0;
integer c0 = 0;
integer peak = 0;
task tick(input integer n); begin repeat (n) @(negedge clk); end endtask
// Master A drives a START: SDA falls while SCL is high.
task a_start(); begin a_sda = 1'b0; tick(T_HD_STA); a_scl = 1'b0; tick(20); end endtask
task b_start(); begin b_sda = 1'b0; tick(T_HD_STA); b_scl = 1'b0; tick(20); end endtask
// Master A drives a STOP: release SCL, then SDA rises while SCL is high.
task a_stop();
begin a_sda = 1'b0; a_scl = 1'b1; tick(20); a_sda = 1'b1; tick(10); end
endtask
// Let the bus go fully idle for n ticks.
task idle(input integer n);
begin a_sda = 1'b1; a_scl = 1'b1; b_sda = 1'b1; b_scl = 1'b1; tick(n); end
endtask
initial begin
tick(4); rst_n = 1'b1; tick(4);
// ---- 1. reset: nothing claimed, and a MINIMUM tracker at its maximum ----------------
if (min_free_at_start !== {TICK_W{1'b1}}) begin
$display("FAIL: min_free_at_start did not start at its maximum"); errors = errors + 1; end
if (n_arb_needed !== {TICK_W{1'b0}} || n_starts !== {TICK_W{1'b0}} || stop_seen !== 1'b0) begin
$display("FAIL: counters or stop_seen nonzero out of reset"); errors = errors + 1; end
// ---- 2. a quiet bus becomes free after T_BUF, and NOT before -------------------------
// The precondition of section 3.1.8 is a DURATION. A block asserting it on the idle level
// would grant permission during another master's high phase.
// The bus has been idle since reset, so the free counter is already running. Interrupt it
// first: the property under test is that the count RESTARTS whenever a line goes low.
a_scl = 1'b0; tick(5); a_scl = 1'b1;
tick(2);
if (bus_free !== 1'b0) begin
$display("FAIL: bus_free asserted 2 ticks after the bus was released (T_BUF = %0d)",
T_BUF); errors = errors + 1; end
tick(T_BUF - 6);
if (bus_free !== 1'b0) begin
$display("FAIL: bus_free asserted before T_BUF elapsed"); errors = errors + 1; end
tick(6);
if (bus_free !== 1'b1) begin
$display("FAIL: bus_free not asserted after T_BUF idle ticks"); errors = errors + 1; end
// ---- 3. a master may start on a quiet bus with NO preceding STOP ---------------------
// A design that required tBUF -- which is defined from a STOP -- would never grant the
// first master after power-up. stop_seen must still be 0 here.
if (stop_seen !== 1'b0) begin
$display("FAIL: stop_seen set with no STOP having occurred"); errors = errors + 1; end
req_a = 1'b1;
tick(1);
if (grant_a !== 1'b1) begin
$display("FAIL: a master was refused permission on a quiet bus with no preceding STOP");
errors = errors + 1; end
req_a = 1'b0;
// ---- 4. THE case: both masters granted in the same window ----------------------------
// Section 3.1.8's precondition. Neither master is at fault and neither is refused -- there
// is "no central master, nor any order of priority on the bus".
begin
before_d = n_double_grant;
req_a = 1'b1; req_b = 1'b1;
tick(1);
if (grant_a !== 1'b1 || grant_b !== 1'b1) begin
$display("FAIL: with both masters requesting, grants were a=%b b=%b -- the monitor broke the tie, and the bus has no priority order",
grant_a, grant_b); errors = errors + 1; end
tick(2);
if (n_double_grant == before_d) begin
$display("FAIL: a double grant was not reported"); errors = errors + 1; end
req_a = 1'b0; req_b = 1'b0;
end
// ---- 5. the bus stops being free the instant either line is pulled low --------------
a_scl = 1'b0;
tick(2);
if (bus_free !== 1'b0) begin
$display("FAIL: bus_free still set with SCL held low"); errors = errors + 1; end
req_a = 1'b1; tick(1);
if (grant_a !== 1'b0) begin
$display("FAIL: permission granted on a busy bus"); errors = errors + 1; end
req_a = 1'b0;
idle(T_BUF + 10);
// ---- 6. a single START is counted, and is not a collision ----------------------------
begin
s0 = n_starts; c0 = n_arb_needed;
a_start();
if (n_starts != s0 + 1) begin
$display("FAIL: a START was not counted"); errors = errors + 1; end
if (n_arb_needed != c0) begin
$display("FAIL: a single START was reported as needing arbitration"); errors = errors + 1; end
if (in_transfer !== 1'b1) begin
$display("FAIL: in_transfer not set after a START"); errors = errors + 1; end
a_stop();
if (stop_seen !== 1'b1) begin
$display("FAIL: stop_seen not set after a STOP"); errors = errors + 1; end
end
// ---- 7. TWO masters starting inside tHD;STA produce exactly ONE start_det --------------
// The most important test in the chapter, and it asserts an IMPOSSIBILITY. Section 3.1.8
// says two STARTs within tHD;STA "result in a valid START condition on the bus" -- one,
// singular -- because the wired-AND merges two pull-downs into a single edge.
//
// So a collision leaves NO trace on the wire. An observer with only SDA and SCL counts one
// START and cannot know two masters own it. That is why arbitration has to resolve the
// situation without anyone detecting it, and why Chapter 13.3 exists.
idle(T_BUF + 10);
begin
s0 = n_starts; c0 = n_arb_needed;
a_sda = 1'b0; // A starts: SDA falls with SCL high
tick(T_HD_STA / 2); // ... and B joins INSIDE tHD;STA
b_sda = 1'b0; // SDA is already low: NO second edge exists
tick(T_HD_STA);
a_scl = 1'b0; b_scl = 1'b0;
tick(20);
if (n_starts != s0 + 1) begin
$display("FAIL: two masters starting inside tHD;STA produced %0d STARTs -- the wired-AND merges them into ONE edge and no logic can unmerge it",
n_starts - s0); errors = errors + 1; end
if (n_arb_needed != c0) begin
$display("FAIL: the monitor claimed to detect a collision from the WIRE -- it cannot; that information only exists in the masters' intent");
errors = errors + 1; end
end
idle(T_BUF + 10);
// ---- 8. two STARTs well SEPARATED are not a collision -------------------------------
// The negative case. A monitor that flagged every pair of STARTs would pass test 7 and be
// useless -- ordinary back-to-back transactions would all look like collisions.
begin
c0 = n_arb_needed;
a_start(); a_stop();
idle(T_BUF + 10);
a_start(); a_stop();
if (n_arb_needed != c0) begin
$display("FAIL: two well-separated STARTs were reported as a collision (%0d)",
n_arb_needed - c0); errors = errors + 1; end
end
// ---- 9. the shortest free interval before any START is recorded ----------------------
// A MINIMUM tracker: the worst case is the smallest, and it must not be erased by a later
// generous interval.
idle(T_BUF + 500);
begin
a_start(); a_stop();
peak = min_free_at_start;
idle(T_BUF + 5000);
a_start(); a_stop();
if (min_free_at_start != peak) begin
$display("FAIL: min_free_at_start rose to %0d after a longer interval (was %0d)",
min_free_at_start, peak); errors = errors + 1; end
end
// ---- 10. an idle bus produces no STARTs and no collisions ---------------------------
begin
idle(20);
s0 = n_starts; c0 = n_arb_needed;
idle(600);
if (n_starts != s0 || n_arb_needed != c0) begin
$display("FAIL: an idle bus produced starts or collisions"); errors = errors + 1; end
end
if (errors == 0)
$display("PASS: bus-free is a duration not a level, a quiet bus is startable with no preceding STOP, both masters are granted with no priority order, and two STARTs inside tHD;STA are one START with two owners");
else
$display("FAIL: %0d error(s)", errors);
$finish;
end
initial begin
#2000000;
$display("FAIL: watchdog expired");
$finish;
end
endmodule -- WHY MULTIPLE MASTERS ARE UNSAFE WITHOUT ARBITRATION -- the VHDL form. Structurally identical to the
-- SystemVerilog: the same two notions of "free", the same deliberate absence of any tie-break, and the
-- same central admission that a collision leaves no trace on the wire.
--
-- UM10204 section 3.1.8: "A master may start a transfer only if the bus is free. Two masters may
-- generate a START condition within the minimum hold time (tHD;STA) of the START condition which
-- results in a valid START condition on the bus. Arbitration is then required to determine which
-- master will complete its transmission."
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_bus_free_monitor is
generic (
TICK_W : natural := 20;
T_BUF : natural := 130; -- tBUF(min) = 1.3 us at 100 MHz, Fast-mode
T_HD_STA : natural := 60 -- tHD;STA(min) = 0.6 us
);
port (
clk : in std_logic;
rst_n : in std_logic;
sda_in : in std_logic;
scl_in : in std_logic;
req_a : in std_logic;
req_b : in std_logic;
bus_free : out std_logic;
free_ticks : out unsigned(TICK_W-1 downto 0);
stop_seen : out std_logic;
start_det : out std_logic;
stop_det : out std_logic;
in_transfer : out std_logic;
grant_a : out std_logic;
grant_b : out std_logic;
arbitration_needed : out std_logic;
n_arb_needed : out unsigned(TICK_W-1 downto 0);
n_starts : out unsigned(TICK_W-1 downto 0);
n_double_grant : out unsigned(TICK_W-1 downto 0);
min_free_at_start : out unsigned(TICK_W-1 downto 0)
);
end entity;
architecture rtl of i2c_bus_free_monitor is
constant T_BUF_U : unsigned(TICK_W-1 downto 0) := to_unsigned(T_BUF, TICK_W);
-- T_HD_STA is the width of the window inside which two masters' STARTs merge into one. It is
-- deliberately NOT compared against anything: there is nothing on the wire to compare it to,
-- which is this block's point. It is a generic so a testbench can drive that window.
signal sda_q, scl_q : std_logic := '1';
signal start_s, stop_s, idle_now : std_logic;
signal s_bus_free : std_logic := '0';
signal s_stop_seen : std_logic := '0';
signal s_in_transfer : std_logic := '0';
signal free_q : unsigned(TICK_W-1 downto 0) := (others => '0');
signal free_now : unsigned(TICK_W-1 downto 0);
signal r_arb : std_logic := '0';
signal r_narb : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_nst : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_ndg : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_minf : unsigned(TICK_W-1 downto 0) := (others => '1');
begin
-- Framing by definition: SDA moving while SCL is HIGH.
start_s <= '1' when (sda_in = '0' and sda_q = '1' and scl_in = '1') else '0';
stop_s <= '1' when (sda_in = '1' and sda_q = '0' and scl_in = '1') else '0';
-- The idle SIGNATURE is a level; the free CONDITION is a duration.
idle_now <= '1' when (sda_in = '1' and scl_in = '1') else '0';
free_now <= free_q + 1;
start_det <= start_s;
stop_det <= stop_s;
in_transfer <= s_in_transfer;
bus_free <= s_bus_free;
stop_seen <= s_stop_seen;
free_ticks <= free_q;
arbitration_needed <= r_arb;
n_arb_needed <= r_narb;
n_starts <= r_nst;
n_double_grant <= r_ndg;
min_free_at_start <= r_minf;
-- Permission is identical for both requesters. There is deliberately no priority, no round-robin
-- and no tie-break: section 3.1.8 says "there is no central master, nor any order of priority on
-- the bus". A monitor that broke the tie would be modelling a bus that does not exist.
grant_a <= s_bus_free and req_a;
grant_b <= s_bus_free and req_b;
process (clk) is
begin
if rising_edge(clk) then
if rst_n = '0' then
sda_q <= '1';
scl_q <= '1';
free_q <= (others => '0');
s_bus_free <= '0';
s_stop_seen <= '0';
s_in_transfer <= '0';
r_arb <= '0';
r_narb <= (others => '0');
r_nst <= (others => '0');
r_ndg <= (others => '0');
-- A MINIMUM tracker starts at all-ones.
r_minf <= (others => '1');
else
sda_q <= sda_in;
scl_q <= scl_in;
r_arb <= '0';
-- the free interval: a DURATION, restarted whenever either line goes low
if idle_now = '1' then
free_q <= free_now;
if free_now >= T_BUF_U then
s_bus_free <= '1';
else
s_bus_free <= '0';
end if;
else
free_q <= (others => '0');
s_bus_free <= '0';
end if;
-- framing
if start_s = '1' then
s_in_transfer <= '1';
-- Exactly ONE increment, however many masters drove this START. The wired-AND
-- has already merged them and no logic here can unmerge them.
r_nst <= r_nst + 1;
if free_q < r_minf then
r_minf <= free_q;
end if;
elsif stop_s = '1' then
s_in_transfer <= '0';
s_stop_seen <= '1';
end if;
-- the double grant: both masters saw a free bus in the same cycle and both are
-- entitled to start. Not a fault in either of them, and not a fault here.
if s_bus_free = '1' and req_a = '1' and req_b = '1' then
r_arb <= '1';
r_narb <= r_narb + 1;
r_ndg <= r_ndg + 1;
end if;
end if;
end if;
end process;
end architecture; -- The VHDL testbench. Same ten checks. The bus is the wired-AND of two masters' open-drain intents,
-- driven from a single stimulus process so that VHDL's one-driver-per-signal rule is respected.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_bus_free_monitor_tb is
end entity;
architecture tb of i2c_bus_free_monitor_tb is
constant TICK_W : natural := 20;
constant T_BUF : natural := 130;
constant T_HD_STA : natural := 60;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
-- two masters' intents; 1 = released
signal a_sda, a_scl : std_logic := '1';
signal b_sda, b_scl : std_logic := '1';
signal sda, scl : std_logic;
signal req_a, req_b : std_logic := '0';
signal bus_free, stop_seen, start_det, stop_det, in_transfer : std_logic;
signal free_ticks : unsigned(TICK_W-1 downto 0);
signal grant_a, grant_b, arbitration_needed : std_logic;
signal n_arb_needed, n_starts, n_double_grant, min_free_at_start : unsigned(TICK_W-1 downto 0);
signal done : boolean := false;
signal errors : integer := 0;
begin
sda <= a_sda and b_sda; -- the wired-AND
scl <= a_scl and b_scl;
clk_gen : process is
begin
while not done loop
clk <= '0'; wait for 5 ns;
clk <= '1'; wait for 5 ns;
end loop;
wait;
end process;
dut : entity work.i2c_bus_free_monitor
generic map (TICK_W => TICK_W, T_BUF => T_BUF, T_HD_STA => T_HD_STA)
port map (clk => clk, rst_n => rst_n, sda_in => sda, scl_in => scl,
req_a => req_a, req_b => req_b,
bus_free => bus_free, free_ticks => free_ticks, stop_seen => stop_seen,
start_det => start_det, stop_det => stop_det, in_transfer => in_transfer,
grant_a => grant_a, grant_b => grant_b,
arbitration_needed => arbitration_needed, n_arb_needed => n_arb_needed,
n_starts => n_starts, n_double_grant => n_double_grant,
min_free_at_start => min_free_at_start);
stim : process is
procedure tick(n : in integer) is
begin
for i in 1 to n loop
wait until falling_edge(clk);
end loop;
end procedure;
procedure a_start is
begin a_sda <= '0'; tick(T_HD_STA); a_scl <= '0'; tick(20); end procedure;
procedure a_stop is
begin a_sda <= '0'; a_scl <= '1'; tick(20); a_sda <= '1'; tick(10); end procedure;
procedure idle(n : in integer) is
begin a_sda <= '1'; a_scl <= '1'; b_sda <= '1'; b_scl <= '1'; tick(n); end procedure;
procedure chk(cond : in boolean; msg : in string) is
begin
if not cond then
report "FAIL: " & msg severity error;
errors <= errors + 1;
wait for 0 ns;
end if;
end procedure;
variable before_d, s0, c0, l0, peak : integer;
begin
tick(4); rst_n <= '1'; tick(4);
-- 1. reset
chk(min_free_at_start = (min_free_at_start'range => '1'),
"min_free_at_start did not start at its maximum");
chk(n_arb_needed = 0 and n_starts = 0 and stop_seen = '0',
"counters or stop_seen nonzero out of reset");
-- 2. the free condition is a DURATION, restarted whenever a line goes low
a_scl <= '0'; tick(5); a_scl <= '1';
tick(2);
chk(bus_free = '0', "bus_free asserted 2 ticks after the bus was released");
tick(T_BUF - 6);
chk(bus_free = '0', "bus_free asserted before T_BUF elapsed");
tick(6);
chk(bus_free = '1', "bus_free not asserted after T_BUF idle ticks");
-- 3. a quiet bus is startable with NO preceding STOP
chk(stop_seen = '0', "stop_seen set with no STOP having occurred");
req_a <= '1';
tick(1);
chk(grant_a = '1',
"a master was refused permission on a quiet bus with no preceding STOP");
req_a <= '0';
-- 4. THE case: both masters granted in the same window, with no priority order
before_d := to_integer(n_double_grant);
req_a <= '1'; req_b <= '1';
tick(1);
chk(grant_a = '1' and grant_b = '1',
"the monitor broke the tie -- the bus has no order of priority");
tick(2);
chk(to_integer(n_double_grant) > before_d, "a double grant was not reported");
req_a <= '0'; req_b <= '0';
-- 5. the bus stops being free the instant either line is pulled low
a_scl <= '0';
tick(2);
chk(bus_free = '0', "bus_free still set with SCL held low");
req_a <= '1'; tick(1);
chk(grant_a = '0', "permission granted on a busy bus");
req_a <= '0';
idle(T_BUF + 10);
-- 6. a single START is counted and is not a collision
s0 := to_integer(n_starts); c0 := to_integer(n_arb_needed);
a_start;
chk(to_integer(n_starts) = s0 + 1, "a START was not counted");
chk(to_integer(n_arb_needed) = c0, "a single START was reported as needing arbitration");
chk(in_transfer = '1', "in_transfer not set after a START");
a_stop;
chk(stop_seen = '1', "stop_seen not set after a STOP");
-- 7. TWO masters starting inside tHD;STA produce exactly ONE start_det. The wired-AND merges
-- two pull-downs into one edge, so a collision leaves NO trace on the wire.
idle(T_BUF + 10);
s0 := to_integer(n_starts); c0 := to_integer(n_arb_needed);
a_sda <= '0';
tick(T_HD_STA / 2);
b_sda <= '0'; -- SDA is already low: no second edge exists
tick(T_HD_STA);
a_scl <= '0'; b_scl <= '0';
tick(20);
chk(to_integer(n_starts) = s0 + 1,
"two masters starting inside tHD;STA produced more than one START -- the wired-AND merges them into ONE edge");
chk(to_integer(n_arb_needed) = c0,
"the monitor claimed to detect a collision from the WIRE -- that information only exists in intent");
idle(T_BUF + 10);
-- 8. two well-separated STARTs are not a collision
c0 := to_integer(n_arb_needed);
a_start; a_stop;
idle(T_BUF + 10);
a_start; a_stop;
chk(to_integer(n_arb_needed) = c0,
"two well-separated STARTs were reported as a collision");
-- 9. the shortest free interval before any START is kept
idle(T_BUF + 500);
a_start; a_stop;
peak := to_integer(min_free_at_start);
idle(T_BUF + 5000);
a_start; a_stop;
chk(to_integer(min_free_at_start) = peak,
"min_free_at_start rose after a longer interval");
-- 10. an idle bus produces no STARTs and no collisions
idle(20);
s0 := to_integer(n_starts); c0 := to_integer(n_arb_needed);
idle(600);
chk(to_integer(n_starts) = s0 and to_integer(n_arb_needed) = c0,
"an idle bus produced starts or collisions");
if errors = 0 then
report "i2c_bus_free_monitor self-check complete: bus-free is a duration not a level, a quiet bus is startable with no preceding STOP, both masters are granted with no priority order, and two STARTs inside tHD;STA are one START with two owners" severity note;
else
report "FAILURES in i2c_bus_free_monitor" severity error;
end if;
done <= true;
wait;
end process;
end architecture;6a. Five Decisions Worth Defending
bus_free is a duration, and it is restarted whenever either line goes low. §3's argument. Mutations N1 and N4 are the level-based and never-restarted variants, and §7's tests 2 and 5 catch both.
stop_seen is reported, not used as a gate. Whether a STOP preceded the current idle period is genuinely useful information — it distinguishes a bus that has been quiet since power-up from one that just finished a transfer — but making it a condition for start permission would silence the first master after reset. Mutation N2 is that gate.
There is no tie-break, deliberately. Both requesters get the same combinational permission. §3.1.8 is explicit: "there is no central master, nor any order of priority on the bus." A monitor that resolved the tie would be modelling a bus that does not exist, and — worse — would hide the very case the rest of the module resolves. Mutation N3 gives master A priority, and §7's test 4 kills it.
arbitration_needed comes from the requests, never from the wire. §4 is the argument. The wire cannot supply it.
min_free_at_start is a MINIMUM tracker, so it starts at all-ones. The interesting free interval is the shortest one any START followed — that is the margin a design actually ran with. Mutations N5 and N6 are the wrong initialiser and the latest-value tracker, the same pair Chapter 11.2 §6a establishes.
6b. Verified Execution
$ iverilog -g2012 -o d1 i2c_bus_free_monitor.sv i2c_bus_free_monitor_tb.sv && ./d1
PASS: bus-free is a duration not a level, a quiet bus is startable with no preceding STOP,
both masters are granted with no priority order, and two STARTs inside tHD;STA are one START
with two owners
i2c_bus_free_monitor_tb.sv:211: $finish called at 77520000 (1ps)
$ iverilog -g2005 -o v1 i2c_bus_free_monitor.v i2c_bus_free_monitor_tb.v && ./v1
PASS: bus-free is a duration not a level, a quiet bus is startable with no preceding STOP,
both masters are granted with no priority order, and two STARTs inside tHD;STA are one START
with two owners
i2c_bus_free_monitor_tb.v:213: $finish called at 77520000 (1ps)
$ nvc -a i2c_bus_free_monitor.vhd i2c_bus_free_monitor_tb.vhd
$ nvc -e i2c_bus_free_monitor_tb && nvc -r i2c_bus_free_monitor_tb --stop-time=2000us
** Note: 77520ns+1: i2c_bus_free_monitor self-check complete: bus-free is a duration not a
level, a quiet bus is startable with no preceding STOP, both masters are granted with no
priority order, and two STARTs inside tHD;STA are one START with two ownersAll three at 77520 ns.
7. What the Testbench Proves
The bus is driven as the wired-AND of two masters' open-drain intents, because that is what a multi-master bus is — and because the collision of §4 cannot be produced any other way.
| # | stimulus | what it establishes |
|---|---|---|
| 1 | reset | min_free_at_start reads its maximum; nothing claimed |
| 2 | a line pulled low, then released | bus_free only after T_BUF, not before |
| 3 | a quiet bus with no preceding STOP | a master is still granted permission |
| 4 | both masters requesting | both granted — no priority order |
| 5 | either line pulled low | permission withdrawn immediately |
| 6 | a single START | counted; not flagged as needing arbitration |
| 7 | two masters starting inside tHD;STA | exactly one START on the wire, and no collision detected from it |
| 8 | two well-separated STARTs | not a collision |
| 9 | a short free interval, then a long one | the minimum is not erased |
| 10 | an idle bus | nothing reported |
Test 7 is the most important test in the chapter, and it asserts an impossibility. Master A pulls SDA low; master B joins half a tHD;STA later, while SDA is already low. There is no second edge — the wired-AND has nothing to add — so n_starts advances by exactly one. The test then asserts that n_arb_needed did not advance, which is the monitor admitting it cannot see the collision from the bus.
A test that asserted the opposite — that two STARTs are detected — would be demanding behaviour no implementation can provide, and a design contorted to satisfy it would be reporting a guess.
Test 4 is the no-priority test, and it looks like a bug report until you read §3.1.8. Both masters ask; both are granted; the monitor does nothing to separate them. That is correct, and it is the condition arbitration exists for.
Tests 2 and 3 are the two halves of §3's argument and neither is sufficient alone. Test 2 rejects the level-based reading; test 3 rejects the tBUF-gated one. A design can pass either in isolation while failing the other.
Test 8 is the negative case for test 7's counting. A monitor that flagged every pair of STARTs would pass nothing useful — ordinary back-to-back transactions would all look like collisions — and it is the cheapest possible guard against a detector that is merely noisy.
8. Mutation Testing
Six defects injected into the SystemVerilog monitor.
| # | injected defect | outcome |
|---|---|---|
| N1 | bus_free asserted on the idle level rather than the duration | killed — test 2 |
| N2 | the free condition also requires a preceding STOP (tBUF semantics) | killed — test 3 |
| N3 | master A is given priority over master B | killed — test 4 |
| N4 | the free counter is not restarted when a line goes low | killed — test 2 |
| N5 | the minimum tracker starts at zero | killed — test 1 |
| N6 | the minimum tracker keeps the latest value | killed — test 9 |
Six injected, six killed. Two worth recording.
N2 is the mutation whose failure mode is silence. Requiring a preceding STOP is not an unreasonable reading — tBUF is defined from a STOP, and the specification uses the phrase "bus free time" for it. The result is a master that never starts after power-up, on a bus where nothing is wrong and no error is reported. A defect that produces silence is far harder to find than one that produces a wrong value, which is why test 3 asserts permission is granted in a state that has no tBUF at all.
N3 is the mutation that looks like an improvement. Giving one master priority removes a race, which is exactly the instinct a designer brings from every other bus. It is wrong here for a reason that is worth being precise about: the priority would have to be agreed by every master on the bus, and I²C provides no way to agree anything before arbitration. A master that deferred to req_a would be deferring to a signal that exists only inside one device. The tie is not broken before the START; it is resolved during the transfer, by the wire.
9. Verification Connection — Asserting What Cannot Be Observed
// The precondition of section 3.1.8, as a property. Note that it constrains the MASTER, not the
// bus: "a master may start a transfer only if the bus is free".
property p_start_only_when_free;
@(posedge clk) $rose(my_start) |-> $past(bus_free);
endproperty
assert property (p_start_only_when_free)
else $error("this master started a transfer without a free bus");
// Free is a DURATION. Writing it as a level is the commonest error, and as a property the
// difference is visible: the antecedent has to reach back T_BUF cycles, not one.
property p_free_means_a_duration;
@(posedge clk) bus_free |-> ($past(sda_in && scl_in, 1) throughout
(sda_in && scl_in)[*T_BUF]);
endproperty
assert property (p_free_means_a_duration)
else $error("bus_free was asserted without T_BUF of continuous idle behind it");
// THE property of this chapter, and it is a NEGATIVE one about the observer rather than the
// design: a collision must NOT be claimed from the bus. Section 4's argument says the information
// is not there, so any monitor that reports it from sda/scl alone is reporting a guess -- and a
// guess in a monitor is worse than a gap, because it gets believed.
property p_no_collision_claimed_from_the_wire;
@(posedge clk) (start_det && !(req_a && req_b)) |-> !arbitration_needed;
endproperty
assert property (p_no_collision_claimed_from_the_wire)
else $error("a collision was reported for a START with only one requester -- that cannot be known from the bus");
// And the counting property that makes the impossibility concrete: however many masters drove a
// START, the wire shows one edge and the monitor counts one.
property p_merged_start_counts_once;
@(posedge clk) start_det |=> (n_starts == $past(n_starts) + 1);
endproperty
assert property (p_merged_start_counts_once)
else $error("a START was counted more than once -- the wired-AND produces exactly one edge"); covergroup i2c_mm_cg with function sample(int free_ticks, int t_buf, int gap_between_starts,
int t_hd_sta, bit both_req, int n_masters_driving);
// The free interval RELATIVE to T_BUF, with a single-value bin on the boundary. Absolute bins
// are not portable across speed modes -- the same reasoning Chapter 11.1 section 9 applies.
free_margin: coverpoint (free_ticks - t_buf) {
bins too_short = {[$:-1]}; // a violation of the precondition
bins exact = {0}; // exactly T_BUF: legal
bins tight = {[1:20]};
bins ample = {[21:$]};
}
// THE coverpoint for this chapter: how close together two masters' STARTs were, measured
// against tHD;STA. The bin that matters is `inside`, because that is the only region where a
// collision is possible at all -- and a suite that never lands there has not tested
// multi-master behaviour however many masters it instantiated.
race_window: coverpoint (gap_between_starts - t_hd_sta) {
bins simultaneous = {[$:-1000]}; // decisions in the same cycle
bins inside = {[-999:0]}; // within tHD;STA: ONE START, two owners
bins just_outside = {[1:100]}; // two STARTs, sequential, no contest
bins separated = {[101:$]};
}
// How many masters actually drove the START. This is ENVIRONMENT knowledge, not bus knowledge
// -- section 4 -- so it comes from the agents rather than from a monitor. Covering it is what
// lets a report say "we exercised a genuine two-master START" rather than "we ran two agents".
drivers: coverpoint n_masters_driving { bins one = {1}; bins two = {2}; bins three_plus = {[3:$]}; }
race_x_drivers: cross race_window, drivers;
// And the single-master case, which must be covered because it is the state the bus is in for
// almost all of its life and the one a multi-master design is most likely to regress.
solo: coverpoint both_req { bins one_requester = {0}; bins two_requesters = {1}; }
endgroup10. FPGA and ASIC Implications
The monitor is two counters and a handful of comparators — around 90 flops at TICK_W = 20. At 100 MHz, 20 bits covers 10.5 ms of idle, which is ample: unlike a stretch (Chapter 12.1 §10), a free interval has no reason to be unbounded, because the interesting question is only whether it exceeded T_BUF. Saturating at the maximum is harmless here and that is worth noting as a contrast — it is the one counter in these two modules that may safely saturate.
Multi-master support is not a feature you add; it is a set of things you must not have assumed. Concretely, on a bus that may acquire a second master:
| assumption | what it costs when it fails |
|---|---|
| SCL may be push-pull | contention — Chapter 12.1 §11 |
| SDA is only driven by me during my transfer | corrupted data with no error reported |
| my high phase ends when my counter says so | a collapsed high phase — Chapter 12.2 §3 |
| a START I see is a START I caused | a transfer conducted against the wrong target |
None of those is a line of code to add. Each is a line of reasoning to remove.
Both masters must be able to read back what they drive, on SDA and on SCL. That is two extra input paths with synchronisers, and it is the hardware precondition for everything in the next three chapters. A master whose SDA is output-only cannot arbitrate, cannot know it lost, and will corrupt the winner's data silently.
And the tHD;STA window shrinks with speed. 4.0 µs at Standard-mode, 0.26 µs at Fast-mode Plus — a factor of fifteen. So the probability that two independent masters collide rises with the bus rate, which is the opposite of the intuition that a faster bus is a safer one. At Fm+ the window is roughly 26 system clocks at 100 MHz, and two masters polling the same "is it free" flag will land inside it regularly.
11. Debugging — The Bus That Worked Until the Management Controller Woke Up
Pitfall — a second master added to a bus whose first master assumed it was alone
// A sensor hub, Fast-mode, single master, in production for three years. The master's I2C core
// was written in-house and was correct for a single-master bus:
//
// // SDA: open-drain output. There is no input path -- nothing else drives this line.
// assign sda_pad_o = 1'b0;
// assign sda_pad_oe = ~sda_tx_bit; // release for a 1, pull low for a 0
// // sda_pad_i is not connected to the core at all.
//
// // "bus free" check before starting a transfer:
// wire bus_free = sda_pad_i_raw & scl_pad_i_raw; // both lines high RIGHT NOW
//
// Two things there are load-bearing and neither is written down.
//
// SDA has no read-back path, so the core cannot compare what it drove against what appeared. On a
// single-master bus there is nothing to compare against, so this is not a defect -- it is an
// absence that only becomes a defect when the assumption changes.
//
// And bus_free is a LEVEL. Both lines are high for half of every bit on a busy bus, so this
// check passes in the middle of somebody else's transfer. Again: harmless when nobody else exists.A board revision added a management controller so that fan speeds could be read while the application processor was held in reset for firmware updates. It was given a standard I2C master core, open-drain, correct.
The bus mostly worked. Roughly one sensor reading in three hundred came back wrong -- not a timeout, not a NACK, but a plausible temperature that was simply not the real one. And it was always the APPLICATION processor's reading that was wrong; the management controller's readings were correct every time.
That asymmetry drove the investigation for two weeks in the wrong direction. The application processor's core was three years old and trusted; the management controller was new. So the new thing was suspected, its timing was measured against Table 10 and found compliant, and its core was swapped for a vendor IP with identical results.
A two-channel capture with both masters' internal transfer-active signals brought out alongside SDA and SCL is what settled it -- and the picture was unambiguous. Occasionally both masters issued a START within a few hundred nanoseconds of each other. The bus showed ONE START. Both cores then clocked out their own address, and the wired-AND carried the bitwise result of the two.
The management controller's core read SDA back, saw a bit it had not sent, concluded it had lost, and stopped driving -- correctly, per section 3.1.8. The application processor's core had no read-back path at all, so it noticed nothing, kept driving, and completed a transfer against whichever slave had been addressed by the merged address.
The wrong temperature was a real reading from the wrong sensor.
Two independent faults in the older core, both of them invisible on a single-master bus.
First, bus_free was a LEVEL rather than a duration, so the core would start transfers in the middle of another master's bit. Section 3.1.8's precondition is that the bus IS free, and both lines being high for one sample is not evidence of that -- it is what a busy bus looks like half the time.
Second, and decisively, SDA had no read-back path. Section 3.1.8 requires that "during every bit, while SCL is HIGH, each master checks to see if the SDA level matches what it has sent". A master that cannot perform that check cannot detect that it lost, and a master that cannot detect that it lost does not stop driving. Table 2 lists arbitration as MANDATORY for a multi-master configuration and NOT APPLICABLE for a single-master one, which is exactly the transition this board made without changing that core.
The asymmetry in the symptom was the diagnosis, read backwards: the master that behaved correctly withdrew, so the master that misbehaved was the one whose transfers completed -- and therefore the one whose data was wrong. The correct device looked innocent because it WAS innocent.
12. Common Misconceptions
"Multi-master is an optional extra." Table 2 lists synchronization and arbitration as M — mandatory — for a multi-master configuration. What is optional is clock stretching; what is not applicable is these two on a single-master bus.
"Checking the bus is free prevents collisions." It cannot. tHD;STA is a window in which two masters can both observe a free bus and both start legitimately — 0.26 µs at Fast-mode Plus. §2 is the argument.
"tBUF is the bus-free condition." tBUF is measured from a STOP and does not exist at power-up. Gating start permission on it silences the first master after reset — mutation N2, whose failure mode is silence.
"Both lines high means the bus is free." It means both lines are high now, which is true for half of every bit of a busy transfer. Free is a duration.
"A bus monitor can tell you two masters collided." It cannot. The wired-AND merges two pull-downs into one edge, so the wire shows a single valid START. §4, and §7's test 7 asserts exactly that impossibility.
"Give one master priority and the problem goes away." Priority would have to be agreed by every master, and I²C provides no way to agree anything before arbitration. §3.1.8: "there is no central master, nor any order of priority on the bus."
"A faster bus is safer." The collision window is tHD;STA, which shrinks from 4.0 µs to 0.26 µs between Standard-mode and Fast-mode Plus — so collisions become more likely as the bus gets faster.
"The START byte is for addressing." It is 0000 0001, no device may acknowledge it, and a hardware slave ignores it entirely. It exists to widen a master's announcement from one edge to seven bit-times so a software-polled master can notice it.
13. Reason It Through
A master checks that the bus is free and then starts. Why is that not sufficient?
Because "free" is a fact about the past and starting is an action in the future, and tHD;STA is how wide the gap between them is. Two masters whose decisions land inside that window both observe a free bus, both start legitimately, and produce one valid START. No pre-check can close a window that exists by construction.
Why can a bus monitor with a perfect capture of SDA and SCL not detect a collision?
Because the wired-AND merges two pull-downs into one edge — a pull-down is a command, and two identical commands are indistinguishable from one. §3.1.8 says the result is "a valid START condition", singular. The attribution was destroyed by the electrical layer before any observer could sample it.
A design gates start permission on tBUF having elapsed since the last STOP. What happens at power-up, and why is that failure mode especially bad?
No STOP has occurred, so the interval does not exist and permission is never granted — the master is silent forever. It is worse than a wrong value because nothing is reported: no error, no timeout, no malformed waveform. A bus that says nothing is much harder to diagnose than one that says something wrong.
Two masters share a bus. One reads SDA back and one does not. Which one's data will be wrong, and which one will look guilty?
The one that does not read back will complete a transfer against a merged address, so its data is wrong. The one that does read back detects the loss and withdraws — so it vanishes from the capture, and the misbehaving master is the only thing still talking. The correct device looks innocent because it is, and the incorrect one looks like the only participant.
Why does the collision probability rise with bus speed?
Because the window in which two STARTs merge is tHD;STA, and that shrinks by a factor of fifteen from Standard-mode's 4.0 µs to Fast-mode Plus's 0.26 µs. Two masters polling the same free-bus condition will land inside a narrower window more often, not less.
Why must a two-master UVM environment avoid a shared scheduler, and what replaces it for reproducibility?
Because the subject under test is what happens when two masters act without coordination; a shared scheduler enforces exactly the coordination the bus lacks, making the race window unreachable. Reproducibility comes from the seed, with a randomised relative offset distributed to concentrate inside and around tHD;STA.
14. Understanding Check
15. Summary
Nobody designs a multi-master bus; they arrive at one — via a management controller, a housekeeping MCU, a test path, or an FPGA added three revisions later.
The precondition is real and insufficient. A master may start only if the bus is free, and two masters can both satisfy that within tHD;STA and both produce a valid START. No pre-check closes a window that exists by construction.
Synchronization and arbitration are mandatory for multi-master and not applicable to single-master. That is why a core can be entirely correct alone and non-compliant the moment a second master appears, with nothing in it changed.
Free is a duration, and it is not tBUF. A level-based test passes mid-transfer; a tBUF-gated test never grants the first master after reset, and its failure mode is silence.
A collision leaves no trace on the wire. The wired-AND merges two pull-downs into one edge, so an observer counts one START and cannot know two masters own it. Detection requires intent, which is why arbitration must resolve the case without anyone detecting it.
The wired-AND is lossy, and what it loses is always the attribution — the same limit that makes a STOP-then-START ambiguous and a stretch invisible on one wire.
There is no priority and no arbiter, because there is no way to agree on one before arbitration begins.
And the collision window narrows as the bus speeds up, so a faster bus is a more contended one.
16. What Comes Next
Chapter 13.2 takes the first thing two masters must do once they are both on the wire: agree on a clock.
The mechanism will be familiar — Chapter 12.2 used §3.1.7 for a purpose it was not written for, and this is the purpose it was written for. Two sentences of that section give a complete combining rule, and the rule has a consequence worth anticipating:
The synchronized clock belongs to neither master. Its low phase is the longest of theirs and its high phase the shortest, so both masters end up running a clock neither of them configured.
The chapter builds both masters and the wired-AND between them in one module, because the rule lives in the connection rather than in either participant — and it finds that the masters converge, which is what the word "synchronization" is actually naming.
Continue learning
Related tutorials
- Related topic
tSU;STO and tBUF — STOP and Bus-Free Time
The only parameter in Table 10 measured between two transactions rather than inside one — which makes it the only one a single transfer cannot violate, and the one a busy multi-master bus violates most often.
- Related topic
Losing I²C Arbitration — Detection, Correct Behaviour and Bus Ownership
The specification places five distinct obligations on a losing master, and stopping is only the second. Includes the one almost always missed — why a combined master-slave must become a slave immediately.
- Related topic
The STOP Condition and Releasing the Bus
STOP is SDA rising while SCL is high — the mirror edge of START, with obligations that are not mirrored at all. Releasing the bus is not the same as making it available, and the interval between the two is where a whole class of intermittent failure lives.
- Related topic
Repeated START in Practice — Why Not STOP Then START
Two sequences that look nearly identical in a driver's source are completely different on the wire. This chapter measures the difference, builds the passive monitor that tells them apart from two wires alone, and names exactly what a STOP costs on a shared bus.
