Skip to content
VLSI Mentor

I²C · Module 13

I²C Arbitration Worked Examples and Corner Cases

Worked two-master captures, and the three combinations the specification explicitly declines to define. Explains why those three, and why undefined is not illegal.

Four chapters have built the mechanism. This one runs it against the cases that do not fit the pattern — and finds three combinations for which the specification declines to say what happens at all.

§3.1.8 names three combinations and calls them undefined. Not illegal. Not forbidden. Undefined — the specification does not forbid them and does not say what results.

Getting that distinction right is the difference between a monitor that is useful and one that reports a rule nobody wrote.

1. The Three Undefined Combinations

Three combinations, enumerated individually. And two qualifiers in the opening sentence that govern everything below:

"still in progress." Once arbitration has resolved, only one master is driving — and a repeated START next to a data bit is then just one master framing a transfer while nobody contests it. That is an ordinary write-then-read (Chapter 10.1), and a monitor that flagged it would flag every such transfer on every multi-master bus. §4.

"undefined." Not a violation. §3.

2. Why Those Three and Not Others

Arbitration works because the competing masters are sending comparable things.

A data bit is a level on SDA, held stable through the high phase, which the wired-AND resolves and which each master can read back and compare against its own intent (Chapter 13.3 §2). Two data bits are commensurable: one of them is a zero, the zero wins, and both masters can tell.

A framing event is not a level. It is a transition of SDA while SCL is high — which is precisely the one thing the data-valid rule forbids during a data bit (Chapter 4.2). So a framing event and a data bit are not two values to be compared; they are two different kinds of thing occupying the same interval.

There is nothing for the read-back test to mean, because the two masters are not answering the same question.

Which is why the symmetric combinations are all fine:

combinationoutcomewhy
data vs dataordinary arbitrationcomparable; the zero wins
repeated START vs repeated STARTone repeated STARTidentical framing merges, as two STARTs do (13.1 §4)
STOP vs STOPone STOPlikewise
anything vs idledefinedonly one master is acting; nothing to resolve
framing vs dataUNDEFINEDnot comparable

And the count follows: framing events come in two kinds (repeated START, STOP), data is one kind, and the pairing is unordered — so {rSTART, data}, {STOP, data}, {rSTART, STOP}. Exactly three. The specification's list is complete, and it is complete for a structural reason rather than by enumeration of experience.

3. Undefined Is Not Illegal

This matters for what a design reports, and it is the same error Chapter 12.4 §2 is built around for stretch timeouts.

the report saysa reader concludesis that supported?
viol_framing_racesome device broke a rule; find it and file a bugno — no rule was stated
undefined_kind = rSTART vs datathe bus entered a state the specification does not define; the system must avoid ityes

The second framing keeps the actual question open, and the actual question is a system-design one: how did two masters come to be doing incommensurable things at the same instant, and what should we change so they do not? A report phrased as a compliance failure sends someone hunting for a non-compliant device, and there may not be one.

§7's design therefore has no viol_* output at all. Its classification output is undefined_kind, an enumeration whose zero value means none, and §7c's test 11 is a structural check that nothing strays outside it.

4. The Qualifier: "Still In Progress"

Drop the qualifier and the whole thing inverts into a monitor that fires constantly.

A repeated START always sits next to data bits — it is defined as the thing that interrupts a transfer without releasing the bus (Chapter 10.2). So on any bus, at any moment, "a repeated START and a data bit" describes an ordinary write-then-read.

What makes the combination undefined is that two different masters are producing the two things while neither has yet withdrawn. Once arbitration has resolved, one master owns the bus and both events are its own — sequential, ordered, and entirely defined.

arb_in_progress is not an optimisation. It is the difference between the undefined case and the commonest transaction shape on the bus.

§7c's test 5 drives all three combinations with arbitration resolved and requires silence; mutation R1 drops the qualifier and that test kills it. And note where the qualifier has to come from: "more than one master still believes it owns the bus" is a fact about state inside devices, not about wires — the same epistemic limit as Chapter 13.1 §4 and the reason this monitor takes intent inputs.

5. The First Undefined Combination, Drawn

A repeated START against a data bit: two correct, incompatible readings

10 cycles
Ten intervals. A clock row is low for two intervals, high for four, low for two, then high for two. A row shows master 1's intent, which releases SDA and then pulls it low during the clock-high interval as a repeated START. A row shows master 2's intent, which holds SDA released throughout because it is sending a data bit of one. A row shows the resulting SDA line following master 1 down while the clock is high. A final row records how the event is read: as a repeated START by the slaves, and as an arbitration loss by master 2.undefined: framing vs dataundefined: framingvs dataoutcome is device-dependentoutcome isdevice-dependentSDA falls with SCL highSDA falls with SCL highslaves resynced; M2 thinks it lostslaves resynced; M2 thinksit lostSCLM1: rSTARTM2: data 1SDA lineread as0000rSTARTrSTARTrSTARTrSTART??t0t1t2t3t4t5t6t7t8t9
Master 1 issues a repeated START while master 2 is holding a data bit of one. SDA falls while SCL is high, so master 2 reads a lost arbitration and every slave reads a repeated START — both correctly, and incompatibly. What happens after depends on devices the specification does not constrain.

Master 2's row and the line row disagree from interval 4 — which by Chapter 13.3 §2 is a textbook arbitration loss, and master 2 is right to read it that way. The slaves see the same edge and, by Chapter 5.2's definition, are right to read it as a repeated START. Neither is mistaken; the event simply has two valid readings, and no rule chooses between them.

6. Worked Examples

Four captures, working from the ordinary to the pathological.

6a. Near-simultaneous STARTs, then resolution in the address

Two masters both find a free bus. A addresses 0x44 for a write; B addresses 0x42 for a read.

bits, MSB first
A sends 0x44 << 1 | 01000 1000
B sends 0x42 << 1 | 11000 0101
agreement1000four bits
bit 3A sends 1, B sends 0B wins
the bus carries1000 0101B's byte, unchanged

A loses at bit 3, stops driving, may clock out the remaining five bits, and owes a restart (Chapter 13.4). And because A lost during addressing, obligation ⑤ applies: if A has a slave function it must start listening immediately, because 0x42 might be its own address. Here it is not — but A cannot know that until it has received the whole byte, which is exactly why the obligation is unconditional on the address's value.

Note the arithmetic: B wins because 0x85 < 0x88. The winner is the smaller byte (Chapter 13.3 §4), and the R/W bit is part of that comparison — so a read of a given address beats nothing, but a write to address N beats a read of address N, because the R/W bit is the least significant bit and a write sends zero there.

6b. Arbitration resolved in the data phase

Both masters address 0x50 for a write; A then sends 0x30 and B sends 0x2F.

The address byte is identical, so arbitration does not resolve there at all — both masters drive the whole address, the slave at 0x50 acknowledges once, and both masters read that acknowledge as theirs. Arbitration then resolves in the first data byte, at the bit where 0x30 and 0x2F first differ.

Two things follow that surprise people.

The slave acknowledged a transfer that two masters believe they own. It is not wrong; it saw one address and one acknowledge.

And obligation ⑤ does not apply, because the loss was not during addressing (Chapter 13.4 §5). The winner is writing to 0x50, which both masters had chosen, so it cannot be addressing the loser.

6c. Identical transmissions: nobody loses

A and B both address 0x50 for a write and both send 0x30.

No bit ever differs, so neither master ever loses, and both drive the entire transfer to completion (Chapter 13.3 §5). The slave sees one write. Both masters believe they performed it, and both are right.

This is the case a redundant or symmetric system produces most readily, and it is the one Chapter 13.3 §11 takes a dual-controller system down with. Arbitration separates different messages and does nothing about identical ones.

6d. The undefined case reached by accident

A is mid-way through a data byte. B lost arbitration on the address a moment ago and — implementing Chapter 13.4's obligation ④ incorrectly — retries immediately rather than waiting for a free bus.

B's retry begins with a START: it pulls SDA low while SCL is high, during A's data byte. That is combination one of §1, reached not by any exotic timing but by one master getting obligation ④ wrong.

The undefined conditions are not primarily a timing hazard. They are what a violated obligation looks like on the wire.

Which reframes the whole of §1: the three combinations are the symptom set for a class of design error, and a monitor that classifies them is pointing at a master that is misbehaving somewhere else. Chapter 13.4 §4's livelock is this case repeating.

7. The Corner Monitor in Three Languages

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_arb_corner_monitor.sv — three undefined combinations, classified and not condemned
   // ARBITRATION CORNER CASES, INCLUDING THE THREE THE SPECIFICATION REFUSES TO DEFINE.
   //
   // Section 3.1.8 closes with a paragraph that is easy to skim and is the most important thing in this
   // chapter:
   //
   //   "There is an UNDEFINED CONDITION if the arbitration procedure is STILL IN PROGRESS at the moment
   //    when one master sends a repeated START or a STOP condition while the other master is still
   //    sending data. In other words, the following combinations result in an undefined condition:
   //      - Master 1 sends a repeated START condition and master 2 sends a data bit.
   //      - Master 1 sends a STOP condition and master 2 sends a data bit.
   //      - Master 1 sends a repeated START condition and master 2 sends a STOP condition."
   //
   // THREE things about that paragraph govern this whole design.
   //
   // UNDEFINED IS NOT ILLEGAL. The specification does not forbid these combinations and does not say
   // what happens. So a monitor must report them as UNDEFINED -- not as a violation. Reporting a
   // violation claims a rule the specification declines to state, which is exactly the error Chapter
   // 12.4 section 2 is built around for stretch timeouts. The output here is `undefined_kind`, and there
   // is no `viol_*` anywhere in this module.
   //
   // THE QUALIFIER MATTERS: "if the arbitration procedure is STILL IN PROGRESS". Once arbitration has
   // resolved, only one master is driving, and a repeated START next to a data bit is simply one master
   // framing while nobody contests it -- entirely defined and entirely ordinary. A monitor that ignored
   // this qualifier would flag every write-then-read on a multi-master bus, because a repeated START
   // always sits next to the data bits of the transfer it interrupts. Mutation E2 drops the qualifier.
   //
   // WHY THESE THREE AND NOT OTHERS. Arbitration works because every competing master is sending
   // COMPARABLE things: data bits, which the wired-AND resolves and which each master can read back
   // (Chapter 13.3). A framing event is not a data bit -- it is a transition of SDA while SCL is HIGH,
   // which is the one thing the data-valid rule forbids during a bit. So a framing event and a data bit
   // are not comparable, there is nothing for the read-back test to mean, and the bus carries a
   // waveform that is a valid-looking something else. The symmetric combinations are fine:
   //
   //   DATA   vs DATA    -- ordinary arbitration, resolved bit by bit
   //   RSTART vs RSTART  -- both frame identically; the wired-AND merges them into one repeated START
   //   STOP   vs STOP    -- likewise, one STOP
   //   anything vs IDLE  -- only one master is acting, so there is nothing to resolve
   //
   // It is the MIXED framing-against-data cases that have no meaning, and there are exactly three of
   // them once you note that the pairing is unordered.

   module i2c_arb_corner_monitor #(
       parameter int TICK_W = 16
   )(
       input  logic clk,
       input  logic rst_n,

       // What each master is DOING this bit. This cannot come from the wire: the wired-AND has already
       // merged the two into one waveform, and recovering who intended what is exactly the information
       // it destroyed (Chapter 13.1 section 7 makes the same point about two merged STARTs).
       input  logic [1:0] act_a,
       input  logic [1:0] act_b,

       // Section 3.1.8's qualifier. Supplied by the masters, because "arbitration is in progress" means
       // "more than one master still believes it owns the bus" -- a fact about state, not about wires.
       input  logic arb_in_progress,

       // ---- classification ----
       output logic       undefined_now,
       output logic       undefined_pulse,
       output logic [1:0] undefined_kind,     // 0 none, 1 rstart-vs-data, 2 stop-vs-data, 3 rstart-vs-stop

       // ---- the defined cases, counted so the monitor can be shown to discriminate ----
       output logic [TICK_W-1:0] n_contest,       // DATA vs DATA: ordinary arbitration
       output logic [TICK_W-1:0] n_merged_frame,  // identical framing: merged into one event
       output logic [TICK_W-1:0] n_uncontested,   // only one master acting

       // ---- totals, per kind ----
       output logic [TICK_W-1:0] n_undefined,
       output logic [TICK_W-1:0] n_kind1,
       output logic [TICK_W-1:0] n_kind2,
       output logic [TICK_W-1:0] n_kind3
   );

       localparam logic [1:0] ACT_DATA   = 2'd0,
                              ACT_RSTART = 2'd1,
                              ACT_STOP   = 2'd2,
                              ACT_IDLE   = 2'd3;

       localparam logic [1:0] UND_NONE = 2'd0,
                              UND_RS_D = 2'd1,
                              UND_ST_D = 2'd2,
                              UND_RS_ST = 2'd3;

       // The pairing is UNORDERED: which master is "master 1" in the specification's wording is
       // arbitrary, so each test covers both orders. A monitor that checked only one order would miss
       // half of every case, and would do so silently because the other order looks like a defined one.
       wire both_data   = (act_a == ACT_DATA)   && (act_b == ACT_DATA);
       wire rs_vs_data  = ((act_a == ACT_RSTART) && (act_b == ACT_DATA)) ||
                          ((act_b == ACT_RSTART) && (act_a == ACT_DATA));
       wire st_vs_data  = ((act_a == ACT_STOP)   && (act_b == ACT_DATA)) ||
                          ((act_b == ACT_STOP)   && (act_a == ACT_DATA));
       wire rs_vs_st    = ((act_a == ACT_RSTART) && (act_b == ACT_STOP)) ||
                          ((act_b == ACT_RSTART) && (act_a == ACT_STOP));
       wire same_frame  = ((act_a == ACT_RSTART) && (act_b == ACT_RSTART)) ||
                          ((act_a == ACT_STOP)   && (act_b == ACT_STOP));
       wire one_idle    = (act_a == ACT_IDLE) || (act_b == ACT_IDLE);
       wire both_idle   = (act_a == ACT_IDLE) && (act_b == ACT_IDLE);
       // "Uncontested" means exactly ONE master is acting. Both idle is not an uncontested action, it
       // is no action -- and counting it would make the total a measure of how often the bus was quiet.
       wire one_acting  = one_idle && !both_idle;

       // Explicit one-cycle history. Named plainly rather than using $past, because $past is
       // unavailable in the Verilog-2001 and VHDL ports and the three must stay structurally identical.
       logic prev_one_idle, prev_both_data, prev_same_frame;

       // The qualifier gates ONLY the undefined classification. The defined cases are still worth
       // counting when arbitration has resolved -- that is the normal life of the bus.
       wire und_now_c = arb_in_progress && !one_idle && (rs_vs_data || st_vs_data || rs_vs_st);

       always_ff @(posedge clk) begin
           if (!rst_n) begin
               undefined_now   <= 1'b0;
               undefined_pulse <= 1'b0;
               undefined_kind  <= UND_NONE;
               n_contest       <= '0;
               n_merged_frame  <= '0;
               n_uncontested   <= '0;
               n_undefined     <= '0;
               n_kind1         <= '0;
               n_kind2         <= '0;
               n_kind3         <= '0;
           end else begin
               undefined_pulse <= 1'b0;

               // Rising-edge detection on the condition, so a combination held for many cycles counts
               // once. A monitor counting every cycle would report a number proportional to the bit
               // period rather than to the number of events.
               if (und_now_c && !undefined_now) begin
                   undefined_pulse <= 1'b1;
                   n_undefined     <= n_undefined + 1'b1;
                   if (rs_vs_data) begin
                       undefined_kind <= UND_RS_D;  n_kind1 <= n_kind1 + 1'b1;
                   end else if (st_vs_data) begin
                       undefined_kind <= UND_ST_D;  n_kind2 <= n_kind2 + 1'b1;
                   end else begin
                       undefined_kind <= UND_RS_ST; n_kind3 <= n_kind3 + 1'b1;
                   end
               end
               undefined_now <= und_now_c;

               // ---- the defined cases, counted on their leading edge too ----
               if (one_acting) begin
                   if (!prev_one_idle) n_uncontested <= n_uncontested + 1'b1;
               end else if (both_data && arb_in_progress) begin
                   if (!prev_both_data) n_contest <= n_contest + 1'b1;
               end else if (same_frame) begin
                   if (!prev_same_frame) n_merged_frame <= n_merged_frame + 1'b1;
               end
           end
       end

       always_ff @(posedge clk) begin
           if (!rst_n) begin
               prev_one_idle   <= 1'b0;
               prev_both_data  <= 1'b0;
               prev_same_frame <= 1'b0;
           end else begin
               prev_one_idle   <= one_acting;
               prev_both_data  <= both_data && arb_in_progress;
               prev_same_frame <= same_frame;
           end
       end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_arb_corner_monitor_tb.sv — eleven scenarios, every pairing driven in both orders
   `timescale 1ns/1ps
   // Every one of the three undefined combinations is driven in BOTH orders, because the specification's
   // "master 1" and "master 2" are arbitrary labels and a monitor that checked one order would miss half
   // of every case while still passing a suite that only drove that order.
   //
   // The suite also drives every DEFINED combination and requires silence, because the qualifier "if the
   // arbitration procedure is still in progress" is the difference between a useful monitor and one that
   // flags every write-then-read on a multi-master bus.

   module i2c_arb_corner_monitor_tb;

       localparam int TICK_W = 16;

       localparam logic [1:0] ACT_DATA   = 2'd0,
                              ACT_RSTART = 2'd1,
                              ACT_STOP   = 2'd2,
                              ACT_IDLE   = 2'd3;
       localparam logic [1:0] UND_NONE = 2'd0, UND_RS_D = 2'd1, UND_ST_D = 2'd2, UND_RS_ST = 2'd3;

       logic clk = 1'b0, rst_n = 1'b0;
       always #5 clk = ~clk;

       logic [1:0] act_a = ACT_IDLE, act_b = ACT_IDLE;
       logic       arb_in_progress = 1'b0;

       logic       undefined_now, undefined_pulse;
       logic [1:0] undefined_kind;
       logic [TICK_W-1:0] n_contest, n_merged_frame, n_uncontested;
       logic [TICK_W-1:0] n_undefined, n_kind1, n_kind2, n_kind3;

       i2c_arb_corner_monitor #(.TICK_W(TICK_W)) dut (
           .clk(clk), .rst_n(rst_n), .act_a(act_a), .act_b(act_b),
           .arb_in_progress(arb_in_progress),
           .undefined_now(undefined_now), .undefined_pulse(undefined_pulse),
           .undefined_kind(undefined_kind),
           .n_contest(n_contest), .n_merged_frame(n_merged_frame), .n_uncontested(n_uncontested),
           .n_undefined(n_undefined), .n_kind1(n_kind1), .n_kind2(n_kind2), .n_kind3(n_kind3)
       );

       int errors = 0;
       task automatic tick(input int n); begin repeat (n) @(negedge clk); end endtask

       // Present one combination for a few cycles, then return to idle so the next one has a fresh edge.
       task automatic present(input logic [1:0] a, input logic [1:0] b, input logic inprog);
           begin
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
               arb_in_progress = inprog; act_a = a; act_b = b; tick(4);
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
           end
       endtask

       // Expect a specific undefined kind, and exactly one new event.
       task automatic expect_undef(input logic [1:0] a, input logic [1:0] b,
                                   input logic [1:0] want_kind, input string label);
           int u0;
           begin
               u0 = n_undefined;
               present(a, b, 1'b1);
               if (n_undefined != u0 + 1) begin
                   $display("FAIL: %s produced %0d undefined events, expected 1", label, n_undefined - u0);
                   errors++; end
               else if (undefined_kind !== want_kind) begin
                   $display("FAIL: %s classified as kind %0d, expected %0d", label, undefined_kind,
                            want_kind); errors++; end
           end
       endtask

       // Expect silence.
       task automatic expect_quiet(input logic [1:0] a, input logic [1:0] b, input logic inprog,
                                   input string label);
           int u0;
           begin
               u0 = n_undefined;
               present(a, b, inprog);
               if (n_undefined != u0) begin
                   $display("FAIL: %s was reported as undefined (%0d events) -- it is a DEFINED case",
                            label, n_undefined - u0); errors++; end
           end
       endtask

       initial begin
           tick(4); rst_n = 1'b1; tick(4);

           // ---- 1. reset ------------------------------------------------------------------------
           if (n_undefined !== '0 || n_kind1 !== '0 || n_kind2 !== '0 || n_kind3 !== '0) begin
               $display("FAIL: counters nonzero out of reset"); errors++; end
           if (undefined_now !== 1'b0 || undefined_kind !== UND_NONE) begin
               $display("FAIL: an undefined condition was reported out of reset"); errors++; end

           // ---- 2. the three UNDEFINED combinations, in the specification's order ----------------
           expect_undef(ACT_RSTART, ACT_DATA, UND_RS_D,  "repeated START vs data bit");
           expect_undef(ACT_STOP,   ACT_DATA, UND_ST_D,  "STOP vs data bit");
           expect_undef(ACT_RSTART, ACT_STOP, UND_RS_ST, "repeated START vs STOP");

           // ---- 3. the same three, with the masters SWAPPED --------------------------------------
           // The pairing is unordered: "master 1" and "master 2" are arbitrary labels. A monitor testing
           // only one order misses half of every case, and does so silently because the other order
           // pattern-matches a defined combination.
           expect_undef(ACT_DATA, ACT_RSTART, UND_RS_D,  "data bit vs repeated START (swapped)");
           expect_undef(ACT_DATA, ACT_STOP,   UND_ST_D,  "data bit vs STOP (swapped)");
           expect_undef(ACT_STOP, ACT_RSTART, UND_RS_ST, "STOP vs repeated START (swapped)");

           // ---- 4. the per-kind counts add up ----------------------------------------------------
           if (n_kind1 != 2 || n_kind2 != 2 || n_kind3 != 2) begin
               $display("FAIL: per-kind counts are %0d/%0d/%0d, expected 2/2/2", n_kind1, n_kind2,
                        n_kind3); errors++; end
           if (n_undefined != n_kind1 + n_kind2 + n_kind3) begin
               $display("FAIL: %0d undefined events but the kinds sum to %0d", n_undefined,
                        n_kind1 + n_kind2 + n_kind3); errors++; end

           // ---- 5. THE qualifier: the same combinations with arbitration RESOLVED ------------------
           // "if the arbitration procedure is STILL IN PROGRESS". Once it has resolved, only one master
           // is driving and a repeated START beside a data bit is an ordinary write-then-read. A monitor
           // ignoring this flags every such transfer on a multi-master bus.
           expect_quiet(ACT_RSTART, ACT_DATA, 1'b0, "repeated START vs data, arbitration RESOLVED");
           expect_quiet(ACT_STOP,   ACT_DATA, 1'b0, "STOP vs data, arbitration RESOLVED");
           expect_quiet(ACT_RSTART, ACT_STOP, 1'b0, "repeated START vs STOP, arbitration RESOLVED");

           // ---- 6. the DEFINED combinations, while arbitration is in progress --------------------
           // These are the cases the bus is built to handle, and they must be silent.
           expect_quiet(ACT_DATA,   ACT_DATA,   1'b1, "data vs data (ordinary arbitration)");
           expect_quiet(ACT_RSTART, ACT_RSTART, 1'b1, "identical repeated STARTs (merged)");
           expect_quiet(ACT_STOP,   ACT_STOP,   1'b1, "identical STOPs (merged)");

           // ---- 7. anything against an IDLE master is defined -----------------------------------
           // Only one master is acting, so there is nothing to resolve. This matters because it is the
           // state the bus is in for almost all of its life.
           expect_quiet(ACT_RSTART, ACT_IDLE, 1'b1, "repeated START vs idle");
           expect_quiet(ACT_STOP,   ACT_IDLE, 1'b1, "STOP vs idle");
           expect_quiet(ACT_DATA,   ACT_IDLE, 1'b1, "data vs idle");
           expect_quiet(ACT_IDLE,   ACT_STOP, 1'b1, "idle vs STOP");

           // ---- 8. the defined cases are COUNTED, not merely ignored -----------------------------
           // A monitor that reported nothing at all would pass tests 5 to 7. Counting the defined cases
           // is what shows it is looking.
           begin
               int c0, m0, u0;
               c0 = n_contest; m0 = n_merged_frame; u0 = n_uncontested;
               present(ACT_DATA, ACT_DATA, 1'b1);
               if (n_contest != c0 + 1) begin
                   $display("FAIL: an ordinary contest was not counted"); errors++; end
               present(ACT_STOP, ACT_STOP, 1'b1);
               if (n_merged_frame != m0 + 1) begin
                   $display("FAIL: a merged framing event was not counted"); errors++; end
               present(ACT_DATA, ACT_IDLE, 1'b1);
               if (n_uncontested != u0 + 1) begin
                   $display("FAIL: an uncontested action was not counted"); errors++; end
           end

           // ---- 9. a combination held for many cycles counts ONCE --------------------------------
           // Counting per cycle would give a number proportional to the bit period rather than to the
           // number of events, which makes the total meaningless across speed modes.
           begin
               int u0;
               u0 = n_undefined;
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
               arb_in_progress = 1'b1; act_a = ACT_STOP; act_b = ACT_DATA;
               tick(60);
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
               if (n_undefined != u0 + 1) begin
                   $display("FAIL: a combination held for 60 cycles counted %0d times, expected 1",
                            n_undefined - u0); errors++; end
           end

           // ---- 10. the level output tracks, and clears -------------------------------------------
           begin
               arb_in_progress = 1'b1; act_a = ACT_RSTART; act_b = ACT_DATA; tick(3);
               if (undefined_now !== 1'b1) begin
                   $display("FAIL: undefined_now not asserted during an undefined combination");
                   errors++; end
               act_a = ACT_DATA; tick(3);
               if (undefined_now !== 1'b0) begin
                   $display("FAIL: undefined_now still set after the combination ended"); errors++; end
               arb_in_progress = 1'b0; act_a = ACT_IDLE; act_b = ACT_IDLE; tick(3);
           end

           // ---- 11. nothing is ever reported as a VIOLATION ---------------------------------------
           // A structural check on the interface rather than on behaviour: this module has no viol_*
           // output at all, because section 3.1.8 declines to define these cases and reporting a
           // violation would claim a rule the specification does not state. The assertion here is that
           // the classification output exists and is an enumeration -- kind 0 means none.
           if (undefined_kind > UND_RS_ST) begin
               $display("FAIL: undefined_kind took a value outside its enumeration (%0d)",
                        undefined_kind); errors++; end

           if (errors == 0)
               $display("PASS: all three undefined combinations are recognised in both orders and classified, the still-in-progress qualifier is honoured so resolved arbitration is silent, defined combinations are counted rather than ignored, and nothing is reported as a violation");
           else
               $display("FAIL: %0d error(s)", errors);
           $finish;
       end

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

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_arb_corner_monitor.v — the same monitor in Verilog-2001
   // ARBITRATION CORNER CASES, INCLUDING THE THREE THE SPECIFICATION REFUSES TO DEFINE.
   //
   // Section 3.1.8 closes with a paragraph that is easy to skim and is the most important thing in this
   // chapter:
   //
   //   "There is an UNDEFINED CONDITION if the arbitration procedure is STILL IN PROGRESS at the moment
   //    when one master sends a repeated START or a STOP condition while the other master is still
   //    sending data. In other words, the following combinations result in an undefined condition:
   //      - Master 1 sends a repeated START condition and master 2 sends a data bit.
   //      - Master 1 sends a STOP condition and master 2 sends a data bit.
   //      - Master 1 sends a repeated START condition and master 2 sends a STOP condition."
   //
   // THREE things about that paragraph govern this whole design.
   //
   // UNDEFINED IS NOT ILLEGAL. The specification does not forbid these combinations and does not say
   // what happens. So a monitor must report them as UNDEFINED -- not as a violation. Reporting a
   // violation claims a rule the specification declines to state, which is exactly the error Chapter
   // 12.4 section 2 is built around for stretch timeouts. The output here is `undefined_kind`, and there
   // is no `viol_*` anywhere in this module.
   //
   // THE QUALIFIER MATTERS: "if the arbitration procedure is STILL IN PROGRESS". Once arbitration has
   // resolved, only one master is driving, and a repeated START next to a data bit is simply one master
   // framing while nobody contests it -- entirely defined and entirely ordinary. A monitor that ignored
   // this qualifier would flag every write-then-read on a multi-master bus, because a repeated START
   // always sits next to the data bits of the transfer it interrupts. Mutation E2 drops the qualifier.
   //
   // WHY THESE THREE AND NOT OTHERS. Arbitration works because every competing master is sending
   // COMPARABLE things: data bits, which the wired-AND resolves and which each master can read back
   // (Chapter 13.3). A framing event is not a data bit -- it is a transition of SDA while SCL is HIGH,
   // which is the one thing the data-valid rule forbids during a bit. So a framing event and a data bit
   // are not comparable, there is nothing for the read-back test to mean, and the bus carries a
   // waveform that is a valid-looking something else. The symmetric combinations are fine:
   //
   //   DATA   vs DATA    -- ordinary arbitration, resolved bit by bit
   //   RSTART vs RSTART  -- both frame identically; the wired-AND merges them into one repeated START
   //   STOP   vs STOP    -- likewise, one STOP
   //   anything vs IDLE  -- only one master is acting, so there is nothing to resolve
   //
   // It is the MIXED framing-against-data cases that have no meaning, and there are exactly three of
   // them once you note that the pairing is unordered.

   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   module i2c_arb_corner_monitor #(
       parameter TICK_W = 16
   )(
       input  wire  clk,
       input  wire  rst_n,

       // What each master is DOING this bit. This cannot come from the wire: the wired-AND has already
       // merged the two into one waveform, and recovering who intended what is exactly the information
       // it destroyed (Chapter 13.1 section 7 makes the same point about two merged STARTs).
       input  wire  [1:0] act_a,
       input  wire  [1:0] act_b,

       // Section 3.1.8's qualifier. Supplied by the masters, because "arbitration is in progress" means
       // "more than one master still believes it owns the bus" -- a fact about state, not about wires.
       input  wire  arb_in_progress,

       // ---- classification ----
       output reg         undefined_now,
       output reg         undefined_pulse,
       output reg   [1:0] undefined_kind,     // 0 none, 1 rstart-vs-data, 2 stop-vs-data, 3 rstart-vs-stop

       // ---- the defined cases, counted so the monitor can be shown to discriminate ----
       output reg   [TICK_W-1:0] n_contest,       // DATA vs DATA: ordinary arbitration
       output reg   [TICK_W-1:0] n_merged_frame,  // identical framing: merged into one event
       output reg   [TICK_W-1:0] n_uncontested,   // only one master acting

       // ---- totals, per kind ----
       output reg   [TICK_W-1:0] n_undefined,
       output reg   [TICK_W-1:0] n_kind1,
       output reg   [TICK_W-1:0] n_kind2,
       output reg   [TICK_W-1:0] n_kind3
   );

       localparam [1:0] ACT_DATA   = 2'd0,
                              ACT_RSTART = 2'd1,
                              ACT_STOP   = 2'd2,
                              ACT_IDLE   = 2'd3;

       localparam [1:0] UND_NONE = 2'd0,
                              UND_RS_D = 2'd1,
                              UND_ST_D = 2'd2,
                              UND_RS_ST = 2'd3;

       // The pairing is UNORDERED: which master is "master 1" in the specification's wording is
       // arbitrary, so each test covers both orders. A monitor that checked only one order would miss
       // half of every case, and would do so silently because the other order looks like a defined one.
       wire both_data   = (act_a == ACT_DATA)   && (act_b == ACT_DATA);
       wire rs_vs_data  = ((act_a == ACT_RSTART) && (act_b == ACT_DATA)) ||
                          ((act_b == ACT_RSTART) && (act_a == ACT_DATA));
       wire st_vs_data  = ((act_a == ACT_STOP)   && (act_b == ACT_DATA)) ||
                          ((act_b == ACT_STOP)   && (act_a == ACT_DATA));
       wire rs_vs_st    = ((act_a == ACT_RSTART) && (act_b == ACT_STOP)) ||
                          ((act_b == ACT_RSTART) && (act_a == ACT_STOP));
       wire same_frame  = ((act_a == ACT_RSTART) && (act_b == ACT_RSTART)) ||
                          ((act_a == ACT_STOP)   && (act_b == ACT_STOP));
       wire one_idle    = (act_a == ACT_IDLE) || (act_b == ACT_IDLE);
       wire both_idle   = (act_a == ACT_IDLE) && (act_b == ACT_IDLE);
       // "Uncontested" means exactly ONE master is acting. Both idle is not an uncontested action, it
       // is no action -- and counting it would make the total a measure of how often the bus was quiet.
       wire one_acting  = one_idle && !both_idle;

       // Explicit one-cycle history. Named plainly rather than using $past, because $past is
       // unavailable in the Verilog-2001 and VHDL ports and the three must stay structurally identical.
       reg prev_one_idle, prev_both_data, prev_same_frame;

       // The qualifier gates ONLY the undefined classification. The defined cases are still worth
       // counting when arbitration has resolved -- that is the normal life of the bus.
       wire und_now_c = arb_in_progress && !one_idle && (rs_vs_data || st_vs_data || rs_vs_st);

       always @(posedge clk) begin
           if (!rst_n) begin
               undefined_now   <= 1'b0;
               undefined_pulse <= 1'b0;
               undefined_kind  <= UND_NONE;
               n_contest       <= {TICK_W{1'b0}};
               n_merged_frame  <= {TICK_W{1'b0}};
               n_uncontested   <= {TICK_W{1'b0}};
               n_undefined     <= {TICK_W{1'b0}};
               n_kind1         <= {TICK_W{1'b0}};
               n_kind2         <= {TICK_W{1'b0}};
               n_kind3         <= {TICK_W{1'b0}};
           end else begin
               undefined_pulse <= 1'b0;

               // Rising-edge detection on the condition, so a combination held for many cycles counts
               // once. A monitor counting every cycle would report a number proportional to the bit
               // period rather than to the number of events.
               if (und_now_c && !undefined_now) begin
                   undefined_pulse <= 1'b1;
                   n_undefined     <= n_undefined + 1'b1;
                   if (rs_vs_data) begin
                       undefined_kind <= UND_RS_D;  n_kind1 <= n_kind1 + 1'b1;
                   end else if (st_vs_data) begin
                       undefined_kind <= UND_ST_D;  n_kind2 <= n_kind2 + 1'b1;
                   end else begin
                       undefined_kind <= UND_RS_ST; n_kind3 <= n_kind3 + 1'b1;
                   end
               end
               undefined_now <= und_now_c;

               // ---- the defined cases, counted on their leading edge too ----
               if (one_acting) begin
                   if (!prev_one_idle) n_uncontested <= n_uncontested + 1'b1;
               end else if (both_data && arb_in_progress) begin
                   if (!prev_both_data) n_contest <= n_contest + 1'b1;
               end else if (same_frame) begin
                   if (!prev_same_frame) n_merged_frame <= n_merged_frame + 1'b1;
               end
           end
       end

       always @(posedge clk) begin
           if (!rst_n) begin
               prev_one_idle   <= 1'b0;
               prev_both_data  <= 1'b0;
               prev_same_frame <= 1'b0;
           end else begin
               prev_one_idle   <= one_acting;
               prev_both_data  <= both_data && arb_in_progress;
               prev_same_frame <= same_frame;
           end
       end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_arb_corner_monitor_tb.v — the Verilog testbench, structurally identical
   `timescale 1ns/1ps
   // Every one of the three undefined combinations is driven in BOTH orders, because the specification's
   // "master 1" and "master 2" are arbitrary labels and a monitor that checked one order would miss half
   // of every case while still passing a suite that only drove that order.
   //
   // The suite also drives every DEFINED combination and requires silence, because the qualifier "if the
   // arbitration procedure is still in progress" is the difference between a useful monitor and one that
   // flags every write-then-read on a multi-master bus.

   // (Verilog-2001 testbench -- same stimulus, same checks.)
   module i2c_arb_corner_monitor_tb;

       localparam TICK_W = 16;

       localparam [1:0] ACT_DATA   = 2'd0,
                              ACT_RSTART = 2'd1,
                              ACT_STOP   = 2'd2,
                              ACT_IDLE   = 2'd3;
       localparam [1:0] UND_NONE = 2'd0, UND_RS_D = 2'd1, UND_ST_D = 2'd2, UND_RS_ST = 2'd3;

       reg clk = 1'b0, rst_n = 1'b0;
       always #5 clk = ~clk;

       reg [1:0] act_a = ACT_IDLE, act_b = ACT_IDLE;
       reg       arb_in_progress = 1'b0;

       wire       undefined_now, undefined_pulse;
       wire [1:0] undefined_kind;
       wire [TICK_W-1:0] n_contest, n_merged_frame, n_uncontested;
       wire [TICK_W-1:0] n_kind1;
       wire [TICK_W-1:0] n_kind2;
       wire [TICK_W-1:0] n_undefined, n_kind3;

       i2c_arb_corner_monitor #(.TICK_W(TICK_W)) dut (
           .clk(clk), .rst_n(rst_n), .act_a(act_a), .act_b(act_b),
           .arb_in_progress(arb_in_progress),
           .undefined_now(undefined_now), .undefined_pulse(undefined_pulse),
           .undefined_kind(undefined_kind),
           .n_contest(n_contest), .n_merged_frame(n_merged_frame), .n_uncontested(n_uncontested),
           .n_undefined(n_undefined), .n_kind1(n_kind1), .n_kind2(n_kind2), .n_kind3(n_kind3)
       );

       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 u0 = 0;
       integer c0 = 0;
       integer m0 = 0;

       task tick(input integer n); begin repeat (n) @(negedge clk); end endtask

       // Present one combination for a few cycles, then return to idle so the next one has a fresh edge.
       task present(input logic [1:0] a, input logic [1:0] b, input logic inprog);
           begin
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
               arb_in_progress = inprog; act_a = a; act_b = b; tick(4);
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
           end
       endtask

       // Expect a specific undefined kind, and exactly one new event.
       task expect_undef(input logic [1:0] a, input logic [1:0] b,
                                   input logic [1:0] want_kind, input [8*44:1] label);
           begin
               u0 = n_undefined;
               present(a, b, 1'b1);
               if (n_undefined != u0 + 1) begin
                   $display("FAIL: %s produced %0d undefined events, expected 1", label, n_undefined - u0);
                   errors = errors + 1; end
               else if (undefined_kind !== want_kind) begin
                   $display("FAIL: %s classified as kind %0d, expected %0d", label, undefined_kind,
                            want_kind); errors = errors + 1; end
           end
       endtask

       // Expect silence.
       task expect_quiet(input logic [1:0] a, input logic [1:0] b, input logic inprog,
                                   input [8*44:1] label);
           begin
               u0 = n_undefined;
               present(a, b, inprog);
               if (n_undefined != u0) begin
                   $display("FAIL: %s was reported as undefined (%0d events) -- it is a DEFINED case",
                            label, n_undefined - u0); errors = errors + 1; end
           end
       endtask

       initial begin
           tick(4); rst_n = 1'b1; tick(4);

           // ---- 1. reset ------------------------------------------------------------------------
           if (n_undefined !== {TICK_W{1'b0}} || n_kind1 !== {TICK_W{1'b0}} || n_kind2 !== {TICK_W{1'b0}} || n_kind3 !== {TICK_W{1'b0}}) begin
               $display("FAIL: counters nonzero out of reset"); errors = errors + 1; end
           if (undefined_now !== 1'b0 || undefined_kind !== UND_NONE) begin
               $display("FAIL: an undefined condition was reported out of reset"); errors = errors + 1; end

           // ---- 2. the three UNDEFINED combinations, in the specification's order ----------------
           expect_undef(ACT_RSTART, ACT_DATA, UND_RS_D,  "repeated START vs data bit");
           expect_undef(ACT_STOP,   ACT_DATA, UND_ST_D,  "STOP vs data bit");
           expect_undef(ACT_RSTART, ACT_STOP, UND_RS_ST, "repeated START vs STOP");

           // ---- 3. the same three, with the masters SWAPPED --------------------------------------
           // The pairing is unordered: "master 1" and "master 2" are arbitrary labels. A monitor testing
           // only one order misses half of every case, and does so silently because the other order
           // pattern-matches a defined combination.
           expect_undef(ACT_DATA, ACT_RSTART, UND_RS_D,  "data bit vs repeated START (swapped)");
           expect_undef(ACT_DATA, ACT_STOP,   UND_ST_D,  "data bit vs STOP (swapped)");
           expect_undef(ACT_STOP, ACT_RSTART, UND_RS_ST, "STOP vs repeated START (swapped)");

           // ---- 4. the per-kind counts add up ----------------------------------------------------
           if (n_kind1 != 2 || n_kind2 != 2 || n_kind3 != 2) begin
               $display("FAIL: per-kind counts are %0d/%0d/%0d, expected 2/2/2", n_kind1, n_kind2,
                        n_kind3); errors = errors + 1; end
           if (n_undefined != n_kind1 + n_kind2 + n_kind3) begin
               $display("FAIL: %0d undefined events but the kinds sum to %0d", n_undefined,
                        n_kind1 + n_kind2 + n_kind3); errors = errors + 1; end

           // ---- 5. THE qualifier: the same combinations with arbitration RESOLVED ------------------
           // "if the arbitration procedure is STILL IN PROGRESS". Once it has resolved, only one master
           // is driving and a repeated START beside a data bit is an ordinary write-then-read. A monitor
           // ignoring this flags every such transfer on a multi-master bus.
           expect_quiet(ACT_RSTART, ACT_DATA, 1'b0, "repeated START vs data, arbitration RESOLVED");
           expect_quiet(ACT_STOP,   ACT_DATA, 1'b0, "STOP vs data, arbitration RESOLVED");
           expect_quiet(ACT_RSTART, ACT_STOP, 1'b0, "repeated START vs STOP, arbitration RESOLVED");

           // ---- 6. the DEFINED combinations, while arbitration is in progress --------------------
           // These are the cases the bus is built to handle, and they must be silent.
           expect_quiet(ACT_DATA,   ACT_DATA,   1'b1, "data vs data (ordinary arbitration)");
           expect_quiet(ACT_RSTART, ACT_RSTART, 1'b1, "identical repeated STARTs (merged)");
           expect_quiet(ACT_STOP,   ACT_STOP,   1'b1, "identical STOPs (merged)");

           // ---- 7. anything against an IDLE master is defined -----------------------------------
           // Only one master is acting, so there is nothing to resolve. This matters because it is the
           // state the bus is in for almost all of its life.
           expect_quiet(ACT_RSTART, ACT_IDLE, 1'b1, "repeated START vs idle");
           expect_quiet(ACT_STOP,   ACT_IDLE, 1'b1, "STOP vs idle");
           expect_quiet(ACT_DATA,   ACT_IDLE, 1'b1, "data vs idle");
           expect_quiet(ACT_IDLE,   ACT_STOP, 1'b1, "idle vs STOP");

           // ---- 8. the defined cases are COUNTED, not merely ignored -----------------------------
           // A monitor that reported nothing at all would pass tests 5 to 7. Counting the defined cases
           // is what shows it is looking.
           begin
               c0 = n_contest; m0 = n_merged_frame; u0 = n_uncontested;
               present(ACT_DATA, ACT_DATA, 1'b1);
               if (n_contest != c0 + 1) begin
                   $display("FAIL: an ordinary contest was not counted"); errors = errors + 1; end
               present(ACT_STOP, ACT_STOP, 1'b1);
               if (n_merged_frame != m0 + 1) begin
                   $display("FAIL: a merged framing event was not counted"); errors = errors + 1; end
               present(ACT_DATA, ACT_IDLE, 1'b1);
               if (n_uncontested != u0 + 1) begin
                   $display("FAIL: an uncontested action was not counted"); errors = errors + 1; end
           end

           // ---- 9. a combination held for many cycles counts ONCE --------------------------------
           // Counting per cycle would give a number proportional to the bit period rather than to the
           // number of events, which makes the total meaningless across speed modes.
           begin
               u0 = n_undefined;
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
               arb_in_progress = 1'b1; act_a = ACT_STOP; act_b = ACT_DATA;
               tick(60);
               act_a = ACT_IDLE; act_b = ACT_IDLE; arb_in_progress = 1'b0; tick(3);
               if (n_undefined != u0 + 1) begin
                   $display("FAIL: a combination held for 60 cycles counted %0d times, expected 1",
                            n_undefined - u0); errors = errors + 1; end
           end

           // ---- 10. the level output tracks, and clears -------------------------------------------
           begin
               arb_in_progress = 1'b1; act_a = ACT_RSTART; act_b = ACT_DATA; tick(3);
               if (undefined_now !== 1'b1) begin
                   $display("FAIL: undefined_now not asserted during an undefined combination");
                   errors = errors + 1; end
               act_a = ACT_DATA; tick(3);
               if (undefined_now !== 1'b0) begin
                   $display("FAIL: undefined_now still set after the combination ended"); errors = errors + 1; end
               arb_in_progress = 1'b0; act_a = ACT_IDLE; act_b = ACT_IDLE; tick(3);
           end

           // ---- 11. nothing is ever reported as a VIOLATION ---------------------------------------
           // A structural check on the interface rather than on behaviour: this module has no viol_*
           // output at all, because section 3.1.8 declines to define these cases and reporting a
           // violation would claim a rule the specification does not state. The assertion here is that
           // the classification output exists and is an enumeration -- kind 0 means none.
           if (undefined_kind > UND_RS_ST) begin
               $display("FAIL: undefined_kind took a value outside its enumeration (%0d)",
                        undefined_kind); errors = errors + 1; end

           if (errors == 0)
               $display("PASS: all three undefined combinations are recognised in both orders and classified, the still-in-progress qualifier is honoured so resolved arbitration is silent, defined combinations are counted rather than ignored, and nothing is reported as a violation");
           else
               $display("FAIL: %0d error(s)", errors);
           $finish;
       end

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

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_arb_corner_monitor.vhd — the same monitor in VHDL
   -- ARBITRATION CORNER CASES, INCLUDING THE THREE THE SPECIFICATION REFUSES TO DEFINE -- the VHDL form.
   --
   --   UM10204 section 3.1.8: "There is an UNDEFINED CONDITION if the arbitration procedure is STILL IN
   --   PROGRESS at the moment when one master sends a repeated START or a STOP condition while the other
   --   master is still sending data. In other words, the following combinations result in an undefined
   --   condition:
   --     - Master 1 sends a repeated START condition and master 2 sends a data bit.
   --     - Master 1 sends a STOP condition and master 2 sends a data bit.
   --     - Master 1 sends a repeated START condition and master 2 sends a STOP condition."
   --
   -- UNDEFINED IS NOT ILLEGAL. There is no viol_* output anywhere in this entity: reporting a violation
   -- would claim a rule the specification declines to state, which is the error Chapter 12.4 section 2 is
   -- built around for stretch timeouts.
   --
   -- THE QUALIFIER MATTERS. Once arbitration has resolved, only one master is driving and a repeated
   -- START beside a data bit is an ordinary write-then-read. A monitor ignoring "still in progress" flags
   -- every such transfer on a multi-master bus.

   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_arb_corner_monitor is
       generic (
           TICK_W : natural := 16
       );
       port (
           clk   : in std_logic;
           rst_n : in std_logic;

           -- What each master is DOING. This cannot come from the wire: the wired-AND has already merged
           -- the two into one waveform, and recovering who intended what is exactly what it destroyed.
           act_a : in unsigned(1 downto 0);
           act_b : in unsigned(1 downto 0);

           arb_in_progress : in std_logic;

           undefined_now   : out std_logic;
           undefined_pulse : out std_logic;
           undefined_kind  : out unsigned(1 downto 0);

           n_contest      : out unsigned(TICK_W-1 downto 0);
           n_merged_frame : out unsigned(TICK_W-1 downto 0);
           n_uncontested  : out unsigned(TICK_W-1 downto 0);

           n_undefined : out unsigned(TICK_W-1 downto 0);
           n_kind1     : out unsigned(TICK_W-1 downto 0);
           n_kind2     : out unsigned(TICK_W-1 downto 0);
           n_kind3     : out unsigned(TICK_W-1 downto 0)
       );
   end entity;

   architecture rtl of i2c_arb_corner_monitor is

       constant ACT_DATA   : unsigned(1 downto 0) := "00";
       constant ACT_RSTART : unsigned(1 downto 0) := "01";
       constant ACT_STOP   : unsigned(1 downto 0) := "10";
       constant ACT_IDLE   : unsigned(1 downto 0) := "11";

       constant UND_NONE  : unsigned(1 downto 0) := "00";
       constant UND_RS_D  : unsigned(1 downto 0) := "01";
       constant UND_ST_D  : unsigned(1 downto 0) := "10";
       constant UND_RS_ST : unsigned(1 downto 0) := "11";

       -- The pairing is UNORDERED: which master the specification calls "master 1" is arbitrary, so each
       -- term covers both orders. A monitor checking one order misses half of every case, silently,
       -- because the other order pattern-matches a defined combination.
       signal both_data, rs_vs_data, st_vs_data, rs_vs_st : std_logic;
       signal same_frame, one_idle, both_idle, one_acting : std_logic;
       signal und_now_c : std_logic;

       signal s_now  : std_logic := '0';
       signal r_kind : unsigned(1 downto 0) := UND_NONE;
       signal r_nc, r_nm, r_nu : unsigned(TICK_W-1 downto 0) := (others => '0');
       signal r_nud, r_k1, r_k2, r_k3 : unsigned(TICK_W-1 downto 0) := (others => '0');

       signal prev_one_acting, prev_both_data, prev_same_frame : std_logic := '0';

   begin

       both_data  <= '1' when (act_a = ACT_DATA and act_b = ACT_DATA) else '0';
       rs_vs_data <= '1' when ((act_a = ACT_RSTART and act_b = ACT_DATA) or
                               (act_b = ACT_RSTART and act_a = ACT_DATA)) else '0';
       st_vs_data <= '1' when ((act_a = ACT_STOP and act_b = ACT_DATA) or
                               (act_b = ACT_STOP and act_a = ACT_DATA)) else '0';
       rs_vs_st   <= '1' when ((act_a = ACT_RSTART and act_b = ACT_STOP) or
                               (act_b = ACT_RSTART and act_a = ACT_STOP)) else '0';
       same_frame <= '1' when ((act_a = ACT_RSTART and act_b = ACT_RSTART) or
                               (act_a = ACT_STOP   and act_b = ACT_STOP)) else '0';
       one_idle   <= '1' when (act_a = ACT_IDLE or act_b = ACT_IDLE) else '0';
       both_idle  <= '1' when (act_a = ACT_IDLE and act_b = ACT_IDLE) else '0';
       -- "Uncontested" means exactly ONE master is acting. Both idle is no action, not an uncontested
       -- one, and counting it would make the total a measure of how often the bus was quiet.
       one_acting <= one_idle and (not both_idle);

       und_now_c <= '1' when (arb_in_progress = '1' and one_idle = '0' and
                              (rs_vs_data = '1' or st_vs_data = '1' or rs_vs_st = '1')) else '0';

       undefined_now  <= s_now;
       undefined_kind <= r_kind;
       n_contest      <= r_nc;
       n_merged_frame <= r_nm;
       n_uncontested  <= r_nu;
       n_undefined    <= r_nud;
       n_kind1        <= r_k1;
       n_kind2        <= r_k2;
       n_kind3        <= r_k3;

       process (clk) is
       begin
           if rising_edge(clk) then
               if rst_n = '0' then
                   s_now           <= '0';
                   undefined_pulse <= '0';
                   r_kind          <= UND_NONE;
                   r_nc            <= (others => '0');
                   r_nm            <= (others => '0');
                   r_nu            <= (others => '0');
                   r_nud           <= (others => '0');
                   r_k1            <= (others => '0');
                   r_k2            <= (others => '0');
                   r_k3            <= (others => '0');
                   prev_one_acting <= '0';
                   prev_both_data  <= '0';
                   prev_same_frame <= '0';
               else
                   undefined_pulse <= '0';

                   -- Rising-edge detection, so a combination held for many cycles counts ONCE. Counting
                   -- per cycle would make the total proportional to the bit period rather than to the
                   -- number of events, and therefore meaningless across speed modes.
                   if und_now_c = '1' and s_now = '0' then
                       undefined_pulse <= '1';
                       r_nud           <= r_nud + 1;
                       if rs_vs_data = '1' then
                           r_kind <= UND_RS_D;  r_k1 <= r_k1 + 1;
                       elsif st_vs_data = '1' then
                           r_kind <= UND_ST_D;  r_k2 <= r_k2 + 1;
                       else
                           r_kind <= UND_RS_ST; r_k3 <= r_k3 + 1;
                       end if;
                   end if;
                   s_now <= und_now_c;

                   -- the defined cases, counted on their leading edge too
                   if one_acting = '1' then
                       if prev_one_acting = '0' then r_nu <= r_nu + 1; end if;
                   elsif both_data = '1' and arb_in_progress = '1' then
                       if prev_both_data = '0' then r_nc <= r_nc + 1; end if;
                   elsif same_frame = '1' then
                       if prev_same_frame = '0' then r_nm <= r_nm + 1; end if;
                   end if;

                   prev_one_acting <= one_acting;
                   if both_data = '1' and arb_in_progress = '1' then
                       prev_both_data <= '1';
                   else
                       prev_both_data <= '0';
                   end if;
                   prev_same_frame <= same_frame;
               end if;
           end if;
       end process;

   end architecture;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_arb_corner_monitor_tb.vhd — the VHDL testbench, single-writer throughout
   -- The VHDL testbench. Every undefined combination is driven in BOTH orders, and every DEFINED
   -- combination is driven and required to be silent -- because the "still in progress" qualifier is the
   -- difference between a useful monitor and one that flags every write-then-read on a multi-master bus.

   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_arb_corner_monitor_tb is
   end entity;

   architecture tb of i2c_arb_corner_monitor_tb is

       constant TICK_W : natural := 16;

       constant ACT_DATA   : unsigned(1 downto 0) := "00";
       constant ACT_RSTART : unsigned(1 downto 0) := "01";
       constant ACT_STOP   : unsigned(1 downto 0) := "10";
       constant ACT_IDLE   : unsigned(1 downto 0) := "11";

       constant UND_NONE  : unsigned(1 downto 0) := "00";
       constant UND_RS_D  : unsigned(1 downto 0) := "01";
       constant UND_ST_D  : unsigned(1 downto 0) := "10";
       constant UND_RS_ST : unsigned(1 downto 0) := "11";

       signal clk   : std_logic := '0';
       signal rst_n : std_logic := '0';

       signal act_a, act_b : unsigned(1 downto 0) := ACT_IDLE;
       signal arb_in_progress : std_logic := '0';

       signal undefined_now, undefined_pulse : std_logic;
       signal undefined_kind : unsigned(1 downto 0);
       signal n_contest, n_merged_frame, n_uncontested : unsigned(TICK_W-1 downto 0);
       signal n_undefined, n_kind1, n_kind2, n_kind3 : unsigned(TICK_W-1 downto 0);

       signal done   : boolean := false;
       signal errors : integer := 0;

   begin

       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_arb_corner_monitor
           generic map (TICK_W => TICK_W)
           port map (clk => clk, rst_n => rst_n, act_a => act_a, act_b => act_b,
                     arb_in_progress => arb_in_progress,
                     undefined_now => undefined_now, undefined_pulse => undefined_pulse,
                     undefined_kind => undefined_kind,
                     n_contest => n_contest, n_merged_frame => n_merged_frame,
                     n_uncontested => n_uncontested,
                     n_undefined => n_undefined, n_kind1 => n_kind1,
                     n_kind2 => n_kind2, n_kind3 => n_kind3);

       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 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;

           procedure present(a, b : in unsigned(1 downto 0); inprog : in std_logic) is
           begin
               act_a <= ACT_IDLE; act_b <= ACT_IDLE; arb_in_progress <= '0'; tick(3);
               arb_in_progress <= inprog; act_a <= a; act_b <= b; tick(4);
               act_a <= ACT_IDLE; act_b <= ACT_IDLE; arb_in_progress <= '0'; tick(3);
           end procedure;

           procedure expect_undef(a, b : in unsigned(1 downto 0);
                                  want_kind : in unsigned(1 downto 0); tag : in string) is
               variable u0 : integer;
           begin
               u0 := to_integer(n_undefined);
               present(a, b, '1');
               if to_integer(n_undefined) /= u0 + 1 then
                   chk(false, tag & " did not produce exactly one undefined event");
               else
                   chk(undefined_kind = want_kind, tag & " was classified as the wrong kind");
               end if;
           end procedure;

           procedure expect_quiet(a, b : in unsigned(1 downto 0); inprog : in std_logic;
                                  tag : in string) is
               variable u0 : integer;
           begin
               u0 := to_integer(n_undefined);
               present(a, b, inprog);
               chk(to_integer(n_undefined) = u0,
                   tag & " was reported as undefined -- it is a DEFINED case");
           end procedure;

           variable c0, m0, u0 : integer;

       begin
           tick(4); rst_n <= '1'; tick(4);

           -- 1. reset
           chk(n_undefined = 0 and n_kind1 = 0 and n_kind2 = 0 and n_kind3 = 0,
               "counters nonzero out of reset");
           chk(undefined_now = '0' and undefined_kind = UND_NONE,
               "an undefined condition was reported out of reset");

           -- 2. the three UNDEFINED combinations, in the specification's order
           expect_undef(ACT_RSTART, ACT_DATA, UND_RS_D,  "repeated START vs data bit");
           expect_undef(ACT_STOP,   ACT_DATA, UND_ST_D,  "STOP vs data bit");
           expect_undef(ACT_RSTART, ACT_STOP, UND_RS_ST, "repeated START vs STOP");

           -- 3. the same three, with the masters SWAPPED (the pairing is unordered)
           expect_undef(ACT_DATA, ACT_RSTART, UND_RS_D,  "data bit vs repeated START (swapped)");
           expect_undef(ACT_DATA, ACT_STOP,   UND_ST_D,  "data bit vs STOP (swapped)");
           expect_undef(ACT_STOP, ACT_RSTART, UND_RS_ST, "STOP vs repeated START (swapped)");

           -- 4. the per-kind counts add up
           chk(n_kind1 = 2 and n_kind2 = 2 and n_kind3 = 2, "the per-kind counts are wrong");
           chk(n_undefined = n_kind1 + n_kind2 + n_kind3,
               "the undefined total does not equal the sum of the kinds");

           -- 5. THE qualifier: the same combinations with arbitration RESOLVED must be silent
           expect_quiet(ACT_RSTART, ACT_DATA, '0', "repeated START vs data, arbitration RESOLVED");
           expect_quiet(ACT_STOP,   ACT_DATA, '0', "STOP vs data, arbitration RESOLVED");
           expect_quiet(ACT_RSTART, ACT_STOP, '0', "repeated START vs STOP, arbitration RESOLVED");

           -- 6. the DEFINED combinations while arbitration is in progress
           expect_quiet(ACT_DATA,   ACT_DATA,   '1', "data vs data (ordinary arbitration)");
           expect_quiet(ACT_RSTART, ACT_RSTART, '1', "identical repeated STARTs (merged)");
           expect_quiet(ACT_STOP,   ACT_STOP,   '1', "identical STOPs (merged)");

           -- 7. anything against an IDLE master is defined
           expect_quiet(ACT_RSTART, ACT_IDLE, '1', "repeated START vs idle");
           expect_quiet(ACT_STOP,   ACT_IDLE, '1', "STOP vs idle");
           expect_quiet(ACT_DATA,   ACT_IDLE, '1', "data vs idle");
           expect_quiet(ACT_IDLE,   ACT_STOP, '1', "idle vs STOP");

           -- 8. the defined cases are COUNTED, not merely ignored
           c0 := to_integer(n_contest);
           m0 := to_integer(n_merged_frame);
           u0 := to_integer(n_uncontested);
           present(ACT_DATA, ACT_DATA, '1');
           chk(to_integer(n_contest) = c0 + 1, "an ordinary contest was not counted");
           present(ACT_STOP, ACT_STOP, '1');
           chk(to_integer(n_merged_frame) = m0 + 1, "a merged framing event was not counted");
           present(ACT_DATA, ACT_IDLE, '1');
           chk(to_integer(n_uncontested) = u0 + 1, "an uncontested action was not counted");

           -- 9. a combination held for many cycles counts ONCE
           u0 := to_integer(n_undefined);
           act_a <= ACT_IDLE; act_b <= ACT_IDLE; arb_in_progress <= '0'; tick(3);
           arb_in_progress <= '1'; act_a <= ACT_STOP; act_b <= ACT_DATA;
           tick(60);
           act_a <= ACT_IDLE; act_b <= ACT_IDLE; arb_in_progress <= '0'; tick(3);
           chk(to_integer(n_undefined) = u0 + 1,
               "a combination held for 60 cycles counted more than once");

           -- 10. the level output tracks, and clears
           arb_in_progress <= '1'; act_a <= ACT_RSTART; act_b <= ACT_DATA; tick(3);
           chk(undefined_now = '1', "undefined_now not asserted during an undefined combination");
           act_a <= ACT_DATA; tick(3);
           chk(undefined_now = '0', "undefined_now still set after the combination ended");
           arb_in_progress <= '0'; act_a <= ACT_IDLE; act_b <= ACT_IDLE; tick(3);

           -- 11. the classification stays inside its enumeration; there is no violation output at all
           chk(undefined_kind <= UND_RS_ST, "undefined_kind took a value outside its enumeration");

           if errors = 0 then
               report "i2c_arb_corner_monitor self-check complete: all three undefined combinations are recognised in both orders and classified, the still-in-progress qualifier is honoured so resolved arbitration is silent, defined combinations are counted rather than ignored, and nothing is reported as a violation" severity note;
           else
               report "FAILURES in i2c_arb_corner_monitor" severity error;
           end if;

           done <= true;
           wait;
       end process;

   end architecture;

7a. Five Decisions Worth Defending

There is no viol_* output. §3's argument. The classification is undefined_kind, an enumeration with a none value.

arb_in_progress gates the undefined classification and nothing else. §4. The defined combinations are still counted when arbitration has resolved, because that is the bus's normal life and counting it is how the monitor demonstrates it is looking.

Every pairing is tested in both orders. The specification's "master 1" and "master 2" are arbitrary labels, so each term covers both assignments. Mutation R2 checks one order only, and the failure is silent in the other direction because it pattern-matches a defined combination.

The condition is edge-detected, so a combination held for many cycles counts once. Counting per cycle would make the total proportional to the bit period rather than to the number of events — meaningless across speed modes. Mutation R3 removes the edge detection and §7c's test 9 catches it with a 60-cycle hold.

Intent comes in as act_a / act_b, never inferred from the wire. Chapter 13.1 §4's limit, for the fourth and last time in this curriculum: the wired-AND destroyed the attribution, and "what is each master doing" is precisely the attribution.

7b. Verified Execution

Azvya Education Pvt. Ltd.VLSI Mentor
terminal — three simulators, one result, one finish time
   $ iverilog -g2012 -o d5 i2c_arb_corner_monitor.sv i2c_arb_corner_monitor_tb.sv && ./d5
   PASS: all three undefined combinations are recognised in both orders and classified, the
   still-in-progress qualifier is honoured so resolved arbitration is silent, defined
   combinations are counted rather than ignored, and nothing is reported as a violation
   i2c_arb_corner_monitor_tb.sv:190: $finish called at 2730000 (1ps)

   $ iverilog -g2005 -o v5 i2c_arb_corner_monitor.v i2c_arb_corner_monitor_tb.v && ./v5
   PASS: all three undefined combinations are recognised in both orders and classified, the
   still-in-progress qualifier is honoured so resolved arbitration is silent, defined
   combinations are counted rather than ignored, and nothing is reported as a violation
   i2c_arb_corner_monitor_tb.v:195: $finish called at 2730000 (1ps)

   $ nvc -a i2c_arb_corner_monitor.vhd i2c_arb_corner_monitor_tb.vhd
   $ nvc -e i2c_arb_corner_monitor_tb && nvc -r i2c_arb_corner_monitor_tb --stop-time=500us
   ** Note: 2730ns+1: i2c_arb_corner_monitor self-check complete: all three undefined
      combinations are recognised in both orders and classified, the still-in-progress qualifier
      is honoured so resolved arbitration is silent, defined combinations are counted rather than
      ignored, and nothing is reported as a violation

All three at 2730 ns — the shortest run in the two modules, because classification is combinational.

7c. What the Testbench Proves

#stimuluswhat it establishes
1resetnothing classified
2the three combinations, in the specification's ordereach recognised and classified correctly
3the same three, swappedrecognised in both orders
4the per-kind countssum to the total
5the same three with arbitration resolvedsilence — §4's qualifier
6data vs data, rSTART vs rSTART, STOP vs STOPsilence — the defined combinations
7anything against an idle mastersilence
8the defined combinationscounted, not merely ignored
9a combination held for 60 cyclescounts once
10the level outputtracks, and clears
11the classificationstays inside its enumeration

Test 5 is the qualifier, and it is the most important test in the chapter. Without it a monitor that ignored arb_in_progress passes everything else and then fires on every write-then-read in production.

Test 3 exists because the specification's labels are arbitrary. A monitor testing (act_a == STOP) && (act_b == DATA) and not the reverse misses half of every case — and misses it silently, because the reverse assignment looks like a combination it does not check rather than like an error.

Test 8 is what stops the monitor being vacuously correct. A block that reported nothing at all would pass tests 5, 6 and 7 perfectly. Counting the defined combinations separately — contests, merged framing, uncontested actions — is the evidence that it is discriminating rather than silent.

Test 6's middle case is the subtle one. Two masters issuing identical repeated STARTs merge into one, exactly as two STARTs do, and that must not be classified undefined — mutation R4 makes it so, and the test that kills it is the one asserting a merged framing event was counted.

Test 9 is about what a total means. Held for 60 cycles, an undefined condition must count once; otherwise the number reported depends on the bit period and cannot be compared between a Standard-mode capture and a Fast-mode one.

8. Mutation Testing

Five defects injected into the SystemVerilog monitor.

#injected defectoutcome
R1the still-in-progress qualifier is ignoredkilled — test 5
R2only one order of each pairing is recognisedkilled — test 3
R3the condition is counted every cyclekilled — test 9
R4identical framing events are classified undefinedkilled — test 8
R5the kind is never latched, so every event reports nonekilled — test 2

Five injected, five killed, none surviving a first run — and the reason is the same one Chapter 11.8 §8 records for the spike filter: this block's behaviour is defined by an enumeration and two qualifiers, so the tests were written from the enumeration outward. Every cell of the classification and both sides of both qualifiers were covered before any scenario was written, and every mutation in the list attacks a cell or a qualifier because there is nowhere else for a defect to be.

R5 is the mutation worth dwelling on, because it breaks nothing functional. Failing to latch the kind leaves the detection, the counting and the per-kind totals all correct — n_kind1 still increments. What is lost is the report: undefined_kind reads none while n_undefined says an event occurred. A consumer would see a count with no classification, which is worse than either alone, because the two outputs now contradict each other.

That is the third time in these two modules that a mutation removes evidence rather than function — Chapter 11.8's spike count and Chapter 13.2's wait count are the others. A monitor's outputs are its entire purpose, and a mutation suite that only checks behaviour will not protect them.

9. Verification Connection — Asserting the Absence of a Rule

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_corner_props.sv — the properties that exist, and the one that must not
   // What must NOT be written:
   //
   //     property p_no_framing_race;
   //        @(posedge clk) !(arb_in_progress && rs_vs_data) ;
   //     endproperty
   //     assert property (p_no_framing_race);
   //
   // That asserts the combination cannot occur -- a PROHIBITION the specification does not state. It
   // says the outcome is undefined, which is a statement about consequences, not about legality. An
   // assertion like this fails on a bus that is behaving exactly as documented, and the failure gets
   // waived, which is how a suite teaches its reviewers to ignore it.

   // What CAN be asserted is a COVERAGE-style observation: the condition occurred, and here is which.
   // Cover, not assert, because occurrence is information rather than error.
   cover property (@(posedge clk) arb_in_progress && rs_vs_data);
   cover property (@(posedge clk) arb_in_progress && st_vs_data);
   cover property (@(posedge clk) arb_in_progress && rs_vs_st);

   // And what CAN be asserted about the monitor itself: it must not classify a DEFINED combination.
   // This is the property that catches mutation R4, and it is about the observer rather than the bus.
   property p_defined_combinations_are_silent;
      @(posedge clk) (both_data || same_frame || one_idle) |-> !undefined_now;
   endproperty
   assert property (p_defined_combinations_are_silent)
      else $error("a defined combination was classified as undefined");

   // The qualifier, as a property. Section 4: with arbitration resolved these combinations are the
   // commonest transaction shape on the bus, so this assertion is what stops the monitor firing
   // continuously in production.
   property p_resolved_arbitration_is_silent;
      @(posedge clk) (!arb_in_progress) |-> !undefined_now;
   endproperty
   assert property (p_resolved_arbitration_is_silent)
      else $error("an undefined condition was reported with arbitration already resolved -- that is an ordinary write-then-read");

   // And the consistency property that catches mutation R5: a reported event must carry a
   // classification. Two outputs that contradict each other are worse than either alone.
   property p_count_implies_kind;
      @(posedge clk) undefined_pulse |-> (undefined_kind != UND_NONE);
   endproperty
   assert property (p_count_implies_kind)
      else $error("an undefined event was counted without a classification");
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_corner_cov.sv — a 3x2 table, and the cells that must stay empty
   covergroup i2c_corner_cg with function sample(int act_a, int act_b, bit in_prog,
                                                int loss_bit, bit identical_msgs,
                                                bit lost_in_addressing);
      // The full combination matrix, because section 2's argument is that the THREE undefined cases
      // are exactly the framing-versus-data cells and every other cell is defined. Covering the whole
      // matrix is how a report states that the classification was exercised rather than assumed.
      pairing: coverpoint {act_a, act_b} {
         bins data_data     = {4'b0000};
         bins rs_data       = {4'b0100, 4'b0001};   // both orders
         bins st_data       = {4'b1000, 4'b0010};
         bins rs_st         = {4'b0110, 4'b1001};
         bins same_framing  = {4'b0101, 4'b1010};
         bins with_idle     = {4'b1100, 4'b0011, 4'b1101, 4'b0111, 4'b1110, 4'b1011};
      }
      progress: coverpoint in_prog { bins resolved = {0}; bins in_progress = {1}; }

      // THE cross. Three cells of it -- the framing-versus-data pairings crossed with in_progress --
      // are the undefined region. The SAME pairings crossed with `resolved` are the commonest
      // transaction on the bus and must be covered too, because that is the pair of cells mutation
      // R1 conflates.
      pairing_x_progress: cross pairing, progress;

      // Where arbitration resolved, from Chapter 13.3's coverage carried forward -- because section
      // 6's worked examples turn on WHICH phase the loss happened in, and obligation 5 depends on it.
      decided_at: coverpoint loss_bit {
         bins first    = {7};
         bins address  = {[1:6]};
         bins rw_bit   = {0};                        // the R/W bit decides: a write beats a read
         bins never    = {-1};                       // identical messages, section 6c
      }
      phase: coverpoint lost_in_addressing { bins addressing = {1}; bins data = {0}; }
      decided_x_phase: cross decided_at, phase;

      // And the case a symmetric system produces most readily and a suite least often.
      identical: coverpoint identical_msgs { bins differing = {0}; bins identical = {1}; }
   endgroup

10. FPGA and ASIC Implications

The monitor is combinational classification plus a handful of counters — around 100 flops at TICK_W = 16. It is a bring-up and soak instrument, not a shipping feature: its value is in answering "did this bus ever enter an undefined state, and which one" during integration.

What belongs in silicon is narrower and worth keeping. A single flag — an undefined framing condition occurred — plus the kind, is a handful of gates and turns an otherwise unattributable corruption into a diagnosis. That is the same argument Chapter 12.3 §10 makes for keeping the Hs-mode check while dropping the accounting.

The intent inputs are the implementation cost, and they are internal. act_a and act_b are each master's own view of what it is doing, so on a real bus the monitor has to live inside a master and see only its own half — plus whatever it can infer about the other. That halves its power and is unavoidable: Chapter 13.1 §4. A full two-intent monitor exists only in a testbench or in a bus analyser with probes inside both devices.

Avoiding the conditions is a design obligation, not a monitoring one. §6d is the practical statement: get Chapter 13.4's obligation ④ right — wait for a free bus — and the combinations become almost unreachable. A master that retries only when the bus is free cannot issue a START during another master's data byte, because the bus is not free then.

And Hs-mode removes the problem by construction. The specification notes that "the arbitration procedure always finishes after a preceding master code transmission in F/S-mode" — so in Hs-mode the contest is settled during the F/S-mode master code that precedes the high-speed transfer, and never during the Hs-mode transfer itself. One master owns the bus for the whole high-speed phase, so framing and data cannot come from different masters. That is a structural answer rather than a numeric one, and it is Chapter 14.2's subject.

11. Debugging — The Corrupted Byte With No Non-Compliant Device

Pitfall — treating an undefined condition as a device fault
Buggy Code
// A three-master bus: an application processor, a management controller and an FPGA. All three
// implement arbitration correctly. All three read back SDA and SCL. All three detect loss and stop
// driving immediately.
//
// The FPGA's retry logic, written from section 3.1.8's summary:
//
//     // "A master that loses the arbitration ... must restart its transaction when the bus is
//     //  free." -- so retry once the bus looks free.
//     always_ff @(posedge clk)
//        if (arb_lost) begin
//           retry_pending <= 1'b1;
//           sda_oe <= 1'b0;                    // obligation 2: correct
//        end
//        else if (retry_pending && sda_in && scl_in) begin   // "bus free"
//           retry_pending <= 1'b0;
//           issue_start   <= 1'b1;             // obligation 4 ... nearly
//        end
//
// The bug is sda_in && scl_in -- a LEVEL, exactly Chapter 13.1 section 3's error. Both lines are
// high for half of every bit, so "bus free" is true in the middle of the winner's transfer.
Symptom

Occasional corrupted bytes on the application processor's writes to an EEPROM. Roughly one in four hundred, rising with bus load, and always in the middle of a multi-byte page write rather than at its start.

Every device was audited against Table 10 and against section 3.1.8's five obligations. Every device passed. Arbitration was captured resolving correctly dozens of times. No device ever failed to stop driving after losing, no device ever drove SDA while inactive, and no timing parameter was out of specification anywhere.

So a bus analyser was configured to flag protocol violations, and it reported none -- which was taken as evidence that the problem was not on the bus at all, and the investigation moved to the EEPROM's page-buffer handling for a week.

What found it was a capture triggered on the corruption rather than on a violation. In the middle of the application processor's third data byte, SDA fell while SCL was high. Every slave on the bus read that as a repeated START and reset its bit counter; the EEPROM abandoned the page write it was part-way through; and the application processor, whose data bit happened to be a one, read the low line as an arbitration loss and withdrew.

The edge came from the FPGA issuing a retry START -- because sda_in && scl_in had been true during the winner's high phase, so its "bus free" test had passed mid-transfer.

The analyser reported no violation because there was none. A START is a legal waveform. Section 3.1.8 calls the resulting combination UNDEFINED, and an analyser looking for prohibited waveforms finds nothing to prohibit.

Root Cause

Two layers, and the second is the one that cost the week.

The FPGA's obligation-4 implementation tested a LEVEL rather than a DURATION. "Bus free" requires both lines released continuously for at least tBUF (Chapter 13.1 section 3); both lines being high for one sample is what a busy bus looks like half the time. So the FPGA issued a START during another master's data byte, producing section 3.1.8's first undefined combination: a repeated START against a data bit.

And the search strategy was wrong because the mental model was. The bus was searched for a NON-COMPLIANT DEVICE, and there was none -- every device was behaving exactly as specified. The combination is UNDEFINED, which means the specification declines to say what results; it does not mean somebody broke a rule. A violation-hunting analyser is therefore structurally unable to find it, and its silence was read as exoneration rather than as the absence of the thing it looks for.

The corruption appearing mid-byte rather than at a transfer boundary was the clue that was available from day one: an arbitration problem resolves at a boundary, and this did not.

12. Common Misconceptions

"The undefined combinations are illegal." The specification neither forbids them nor says what results. Reporting them as violations claims a rule nobody wrote.

"They are a timing hazard." Mostly they are the symptom of a violated obligation — usually a retry issued before the bus is genuinely free. §6d.

"There are three of them because experience found three." There are three for a structural reason: two kinds of framing event, one kind of data, and an unordered pairing. The list is complete by construction.

"A repeated START next to a data bit is always undefined." Only while arbitration is still in progress. Otherwise it is an ordinary write-then-read, which is most transactions on most buses.

"Two masters issuing identical repeated STARTs is undefined." Identical framing merges into one event, exactly as two STARTs do. Only framing against data is undefined.

"A protocol analyser will catch it." Not one searching for prohibited waveforms — a START is legal. Only a monitor that classifies combinations will report it.

"Arbitration always resolves in the address byte." Identical addresses resolve in the data (§6b), and identical transmissions never resolve at all.

"A read beats a write to the same address." The other way round: the R/W bit is the least significant bit of the address byte, a write sends zero there, and the smaller byte wins.

13. Reason It Through

Why are there exactly three undefined combinations?

Because framing events come in two kinds — repeated START and STOP — data is one kind, the undefined cases are precisely framing against framing-incompatible data plus the two-framing mixed case, and the pairing is unordered. {rSTART, data}, {STOP, data}, {rSTART, STOP}. The enumeration is complete for a structural reason, not an empirical one.

Why is a framing event not comparable with a data bit?

Because a data bit is a level held stable through the high phase, which the wired-AND resolves and each master can read back. A framing event is a transition of SDA while SCL is high — the one thing the data-valid rule forbids during a bit. They are different kinds of thing occupying the same interval, so the read-back test has nothing to mean.

Two masters, one sends a repeated START and the other a data bit of one. What does each party conclude, and who is wrong?

The sending master believes its framing succeeded. Every slave reads SDA falling with SCL high as a repeated START and resets its bit counter. The data-sending master sent a one, sees a zero, and concludes it lost arbitration. All three are correct, and their conclusions are incompatible — which is what "undefined" is naming.

Why would dropping the arb_in_progress qualifier make a monitor useless rather than merely noisy?

Because a repeated START beside a data bit is an ordinary write-then-read — the commonest transaction shape on the bus. Without the qualifier the monitor fires on essentially every transfer, so its output carries no information at all.

Two masters both address 0x50 and both write 0x30. What happens, and what does arbitration contribute?

Nothing ever differs, so neither loses and both drive the whole transfer. The slave sees one write and both masters believe they performed it. Arbitration contributes nothing, because it separates different messages and these were identical.

A bus corrupts bytes mid-transfer, every device passes every compliance check, and a protocol analyser reports no violations. What should that combination suggest?

An undefined condition rather than a non-compliant device. Nothing prohibited is happening, so a violation-hunting analyser is structurally unable to find it — its silence is the absence of what it searches for. And mid-byte corruption rules arbitration out, because arbitration resolves at byte boundaries.

14. Understanding Check

15. Summary

Three combinations are undefined, and the list is complete by construction — two kinds of framing event, one kind of data, unordered pairing.

Framing is not comparable with data. A data bit is a level the wired-AND resolves; a framing event is a transition of SDA while SCL is high. The read-back test has nothing to mean between them.

Undefined is not illegal. Report the classification, never a violation — a viol_* output here claims a rule nobody wrote, and sends someone hunting a device that is behaving correctly.

Every party can be correct and reach an incompatible conclusion. The data-sending master reads a lost arbitration; the slaves read a repeated START; both by the book.

The "still in progress" qualifier is load-bearing. Without it the monitor fires on the commonest transaction on the bus.

The conditions are mostly a symptom, not a hazard. Get the retry obligation right — wait for a genuinely free bus — and they become nearly unreachable.

A violation-hunting analyser cannot find them, because nothing prohibited happens. Its silence is the absence of what it searches for.

And arbitration resolves where the messages first differ — possibly in the data, possibly never, and the R/W bit is part of the comparison.

16. What Comes Next

Module 13 is complete, and with it the causal chain Chapter 2.5 opened.

That chapter established a single electrical rule: on an open-drain bus a device may pull a line low or release it, and the line reads low if anyone pulls. Eleven modules later, that one rule has turned out to be the entire reason the bus works:

the rule produceschapter
no contention between drivers2.2
a START and STOP that cannot be confused with data5.2
an acknowledge that costs nothing to implement7.2
clock stretching, with no protocol support12.2
clock synchronization between masters13.2
arbitration, with no arbiter13.3

Six mechanisms, one electrical rule, and no dedicated logic for any of them. That is the design achievement of I²C, and it is why the protocol has outlived nearly everything it was contemporary with.

It also explains the module's recurring limit. A wired-AND is a lossy function: it preserves the value on the wire perfectly and destroys the attribution completely. So every question of the form who — who ended this transaction, who is holding the clock, who started, who won — is unanswerable from the bus alone, and every honest monitor in this curriculum takes an intent input beside the pin.

Module 14 turns from mechanism to magnitude. The protocol has been speed-agnostic throughout: every chapter so far applies unchanged at 100 kHz and at 1 MHz. Module 14 asks what actually changes between Standard-mode, Fast-mode and Fast-mode Plus — and finds that the answer is almost nothing in the protocol and a great deal electrically, which is why Chapter 11.7's pull-up window turned out to be empty at the table's own limits.

And it takes up High-speed mode, which is the one mode that changes the protocol structurally rather than numerically: a master code, a current-source pull-up, a bridged bus, and — as this chapter's §10 notes — arbitration that must be finished before the high-speed phase begins.

Continue learning