Skip to content
VLSI Mentor

I²C · Module 5

Repeated START — Holding the Bus Between Phases

A repeated START is not a new waveform. It is the START edge again, and what makes it a different event is that the bus was already busy. That single fact is why a classifier needs state and why a monitor that joins late cannot classify what it sees.

Chapter 5.3 established what ending a transfer costs. The STOP edge is cheap; what follows it is not, because the bus becomes released rather than free and an interval has to elapse before anyone may transmit again.

That cost raises an obvious question. A controller that has just written a register pointer to a sensor and now wants to read that register has finished one logical operation and needs to begin another — but it has no wish to hand the bus back, because handing it back means paying the interval and, on a multi-controller segment, risking that somebody else claims it first. Is there a way to begin a new addressed transfer without ever letting the bus go free?

There is, and the striking thing is that it required inventing nothing at all.

1. The Third Event That Is Not a Third Waveform

UM10204 §3.1.4, the sentences after the START and STOP definitions:

The bus stays busy if a repeated START (Sr) is generated instead of a STOP condition. In this respect, the START (S) and repeated START (Sr) conditions are functionally identical. For the remainder of this document, therefore, the S symbol is used as a generic term to represent both the START and repeated START conditions, unless Sr is particularly relevant.

Three claims, and each one matters to a different part of this chapter.

"The bus stays busy." This is the purpose. A repeated START is issued instead of a STOP, so the transition to released never happens, the bus-free interval never begins, and no other controller ever sees an opportunity. Chapter 5.3's interval is not shortened or optimised — it is simply never entered.

"Functionally identical." This is true in a precise and limited sense, and §3 unpacks it, because read carelessly it is the source of the most common misconception about this event.

"The S symbol is used as a generic term." This is a reading hazard, and §7 is where it becomes a design hazard.

The essential structural fact, stated plainly: a repeated START is the START edge. Same conductor, same direction, same shape, same driver. Put a repeated START and a first START side by side on an oscilloscope with the surrounding context cropped away and there is nothing to distinguish them, because there is nothing to distinguish. What separates them is not in the waveform at all — it is whether a transfer was already in progress when the edge arrived.

2. What It Costs to Let Go, and What Sr Preserves

Put the two sequences next to each other, because the comparison is the reason the event exists.

STOP, then a new START — the bus goes free in between

10 cycles
Ten intervals. A transfer ends with SDA rising while SCL is high, which is a STOP. Both lines then stay high for a long stretch, the bus-free interval, during which no transfer may begin. Only at the end does SDA fall again while SCL is high to make a new START.transfer 1 — busytransfer 1 — busyfree interval must elapsefree interval must elapseP — STOP: the bus is releasedP — STOP: the bus isreleasedS — START: a new transferS — START: a new transfersclsda0011111110t0t1t2t3t4t5t6t7t8t9
Figure 1 — ending and beginning again the expensive way. The STOP releases both conductors, the bus enters the released state, and a full bus-free interval must elapse before the next START is permitted. During that interval the bus belongs to nobody, which on a multi-controller segment means it belongs to whoever asks first. Conceptual figure: interval widths are chosen for legibility and carry no specification proportion.

A repeated START — the bus never goes free

10 cycles
Ten intervals. SCL is low and SDA is low at the end of a transfer phase. SDA is released high while SCL is still low, which is ordinary preparation. SCL is then released high, and after a setup interval SDA falls while SCL is high, which is the repeated START. The bus is busy throughout the figure.bus busy throughoutbus busy throughoutSDA released — SCL still lowSDA released — SCL stilllowSCL released — setup beginsSCL released — setup beginsSr — the bus never went freeSr — the bus never wentfreesclsda0111100000t0t1t2t3t4t5t6t7t8t9
Figure 2 — the same change of phase, without letting go. SDA is released during SCL's low phase, which is ordinary preparation and not framing. SCL is then released, and the SDA fall that follows — while SCL is high — is a repeated START. The bus was busy before the edge and is busy after it, so no interval elapses and no other controller is ever offered the bus. Conceptual figure.

What Sr preserves is continuous ownership. Three consequences follow, and they are worth separating because only the first is about speed.

No other controller can interpose. On a segment with one controller this is invisible. On a segment with two, it is the difference between a two-phase operation that completes and one that another controller can split down the middle. What that guarantee does and does not amount to — and where a combined transaction can still be interrupted — is Chapter 10.3, which owns atomicity properly. This chapter establishes only the mechanism.

No bus-free interval is paid. On a Standard-mode bus the interval is of the same order as a bit period, so on a two-phase operation it is a real fraction of the total time — and it is pure overhead, since no information moves during it.

Every target still resets its protocol state. This is the part that makes the whole thing work, and it comes straight from Chapter 5.2 §3: a START unconditionally resets every device's protocol state. A repeated START does too. So the controller gets the clean slate it needs to send a fresh address, without the clean slate costing it the bus.

That last point is what "functionally identical" is really pointing at, and it is why the event needed no new mechanism. The behaviour a controller wants when changing phase — everyone forgets what they were doing, everyone listens for a new address — is already what a START does. The only thing that had to change was the bus not becoming free, and that happens by simply not issuing the STOP.

3. "Functionally Identical" — True, and a Trap

The specification's phrase is accurate and it is routinely over-read. Be precise about the scope.

propertySSridentical?
the waveform on SDA and SCLfall while SCL highfall while SCL highyes
who may generate itcontroller onlycontroller onlyyes
effect on every target's protocol statefull resetfull resetyes
what follows itan address bytean address byteyes
bus state before itfreebusyno
bus state after itbusybusyyes
a bus-free interval precedes ityesnono
which timing parameter governs the interval before itbus-free timea setup parameterno

Everything about how devices react is identical. Everything about the bus state around it is not. So "functionally identical" means identical in function — in what the event does to the participants — and says nothing about the conditions under which it is legal to emit one, which differ completely.

This distinction has a direct consequence for Chapter 5.5. A first START out of an idle bus is gated by the bus-free interval, because the bus has to have been quiet. A repeated START is gated by a setup requirement measured from SCL's rising edge to the SDA fall, because the bus was never quiet and there is nothing to wait for — there is only the need for the framing edge to be unambiguous. Two different parameters, for two different situations, on the same waveform. UM10204's timing table names the second one for a repeated START specifically, and reading that row as though it governed every START is a mistake this module's next chapter has to head off.

4. The Sequence Sr Requires

Figure 2 shows a step that Figure 1 does not need, and it is easy to miss: SDA has to be brought high first, and it has to happen while SCL is low.

The reason is mechanical. A repeated START is an SDA falling edge, so SDA must be high before it can fall. But at the end of a transfer phase SDA is typically low — the last data bit was a zero, or a target was holding it low to acknowledge. So the controller has to release SDA before it can produce the edge, and it may not do that while SCL is high, because an SDA rise while SCL is high is a STOP. Releasing SDA at the wrong moment does not merely fail to produce a repeated START — it produces the exact event the controller was trying to avoid.

So the sequence is forced, and every step is load-bearing:

Azvya Education Pvt. Ltd.VLSI Mentor
ARCHITECTURAL PSEUDOCODE — the repeated-START sequence, and why each step cannot move
   state          what the controller does              why it must be here
   -----          ------------------------              -------------------
   1. SCL low     release SDA (SDA goes high)           SDA must be high to fall
                                                        later; doing this while
                                                        SCL is HIGH would be a STOP

   2. SCL low     ensure SDA has settled high           the next step samples it

   3. release SCL (SCL goes high)                       the framing edge needs a
                                                        HIGH phase to occur in

   4. wait the setup interval                           this is the tSU;STA margin
                                                        of Chapter 5.5

   5. drive SDA low  <-- THIS EDGE IS THE Sr            SDA falls while SCL is high

   6. wait the hold interval                            tHD;STA, same as for a
                                                        first START

   7. drive SCL low                                     the first clock pulse of
                                                        the new phase

   Steps 1 and 5 are BOTH SDA transitions, in opposite directions, and only one
   of them is framing. The difference is step 3: what SCL was doing. Move step 1
   after step 3 and the controller emits a STOP -- ending the transfer it was
   trying to continue, and then emitting a START into a bus-free interval it
   never waited out.

Chapter 5.5 builds the sequencer that implements exactly this, with the two intervals programmable. What matters here is that a repeated START is a sequence on the generating side even though it is a single edge on the observing side — an asymmetry that catches designers who reason about framing only from captures.

5. Why Classifying Requires State

Now the detection side, and the point this whole module has been building towards.

Chapter 5.2 built a detector with no state beyond two history bits. It could answer "did a START edge occur" completely, because the definition of the edge refers only to the two conductors. Ask it to distinguish S from Sr and it cannot — not because it is too simple, but because the information is not on the conductors.

Azvya Education Pvt. Ltd.VLSI Mentor
MATHEMATICAL DERIVATION — what each question needs
   Question 1:  "did an SDA fall occur while SCL was high?"
                                  |
                  depends only on:  scl[n-1], scl[n], sda[n-1], sda[n]
                                  |
                  -> answerable from two samples. NO state required.
                     This is Chapter 5.2's detector, and it is complete.

   Question 2:  "was that fall an S or an Sr?"
                                  |
                  depends on:  everything above, PLUS
                               "was a transfer already in progress?"
                                  |
                  -> that last term is a FUNCTION OF HISTORY, not of the
                     current samples. It is set by an earlier framing event
                     and cleared by a later one.
                  -> requires ONE BIT of state. And one bit is enough.

   The minimum state for classification is therefore a single flag:

       set   by any framing fall  (S or Sr -- both make the bus busy)
       clear by a framing rise    (P -- the bus is released)

   Read BEFORE it is updated, that flag is exactly the discriminator:
       flag == 0 at the edge  ->  S
       flag == 1 at the edge  ->  Sr

One bit. That is the entire difference between a detector and a classifier, and it is worth noticing that the bit in question is not new either — it is bus_busy, which Chapter 5.3's tracker already maintains for a completely different purpose. The classifier is an assembly of parts this module has already built, which is why it is the right place for the module's architectural payoff.

6. The Classifier in Three Languages

One edge detector producing both framing directions, one state bit, and three mutually exclusive single-cycle outputs.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_framing_classifier.sv — SYNTHESIZABLE RTL. One edge detector, one state bit, three mutually exclusive outputs.
   module i2c_framing_classifier (
       input  logic clk,
       input  logic rst_n,
       input  logic scl_in,                  // observed bus level, not drive intent
       input  logic sda_in,                  // observed bus level, not drive intent
       output logic start_pulse,             // S  -- first START of a transfer
       output logic repeated_start_pulse,    // Sr -- START while the bus was busy
       output logic stop_pulse,              // P
       output logic bus_busy
   );
       logic scl_q, sda_q;
       logic saw_fall, saw_rise;

       // The two framing edges. Both require SCL HIGH on BOTH samples, so an SDA
       // change coincident with an SCL edge is not claimed as framing.
       always_comb begin
           saw_fall = scl_q && scl_in &&  sda_q && !sda_in;
           saw_rise = scl_q && scl_in && !sda_q &&  sda_in;
       end

       always_ff @(posedge clk) begin
           if (!rst_n) begin
               scl_q <= 1'b1; sda_q <= 1'b1;
               start_pulse          <= 1'b0;
               repeated_start_pulse <= 1'b0;
               stop_pulse           <= 1'b0;
               bus_busy             <= 1'b0;
           end else begin
               // Default every event output low: these are pulses, not levels.
               start_pulse          <= 1'b0;
               repeated_start_pulse <= 1'b0;
               stop_pulse           <= 1'b0;

               // S and Sr are the SAME EDGE. Nothing in the waveform distinguishes
               // them -- only whether a transfer was already in progress does. That
               // is why a classifier needs state and an edge detector does not.
               if (saw_fall) begin
                   if (bus_busy) repeated_start_pulse <= 1'b1;
                   else          start_pulse          <= 1'b1;
                   bus_busy <= 1'b1;
               end else if (saw_rise) begin
                   // UM10204 defines STOP by the edge itself, so the edge is
                   // classified as STOP whatever the tracked state was.
                   stop_pulse <= 1'b1;
                   bus_busy   <= 1'b0;
               end

               scl_q <= scl_in;
               sda_q <= sda_in;
           end
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_framing_classifier_tb.sv — SELF-CHECKING TESTBENCH, SIMULATION ONLY. Counts each class; proves exclusivity; regresses sticky busy.
   module i2c_framing_classifier_tb;
       logic clk = 1'b0, rst_n, scl_in, sda_in;
       logic s_p, sr_p, p_p, busy;
       int   errors = 0;
       int   n_s = 0, n_sr = 0, n_p = 0;

       i2c_framing_classifier dut (.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_in),
           .start_pulse(s_p), .repeated_start_pulse(sr_p), .stop_pulse(p_p), .bus_busy(busy));

       always #5 clk = ~clk;
       initial begin #40000; $display("FAIL: watchdog expired"); $finish; end

       always @(posedge clk) if (rst_n) begin
           if (s_p)  n_s++;
           if (sr_p) n_sr++;
           if (p_p)  n_p++;
           // The three classifications are mutually exclusive by construction; an
           // edge that produced two of them would be a classifier that cannot
           // decide, which is worse than one that is wrong.
           if ((s_p + sr_p + p_p) > 1) begin
               $display("FAIL: %0t -- more than one framing class asserted at once", $time);
               errors++;
           end
       end

       task automatic expect_counts(input int es, input int esr, input int ep, input int step);
           if (n_s !== es || n_sr !== esr || n_p !== ep) begin
               $display("FAIL: step %0d -- S/Sr/P = %0d/%0d/%0d, expected %0d/%0d/%0d",
                        step, n_s, n_sr, n_p, es, esr, ep);
               errors++;
           end
       endtask

       // SDA falls while SCL is HIGH. From idle this is S; while busy it is Sr.
       task automatic sda_fall_while_high();
           sda_in = 1'b1; scl_in = 1'b1;   repeat (2) @(negedge clk);
           sda_in = 1'b0;                  repeat (2) @(negedge clk);
           scl_in = 1'b0;                  repeat (2) @(negedge clk);
       endtask

       // Bring SDA back HIGH during the LOW phase, so the next rise-while-high is
       // available. This is ordinary data movement and must classify as nothing.
       task automatic release_sda_during_low();
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
           sda_in = 1'b1;                  repeat (2) @(negedge clk);
       endtask

       task automatic sda_rise_while_high();
           scl_in = 1'b0; sda_in = 1'b0;   repeat (2) @(negedge clk);
           scl_in = 1'b1;                  repeat (2) @(negedge clk);
           sda_in = 1'b1;                  repeat (2) @(negedge clk);
       endtask

       task automatic do_bit(input logic v);
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
           sda_in = v;                     repeat (1) @(negedge clk);
           scl_in = 1'b1;                  repeat (2) @(negedge clk);
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
       endtask

       // BOUNDARY: SDA moves in the same interval SCL rises -- not framing.
       task automatic coincident_fall();
           scl_in = 1'b0; sda_in = 1'b1;   repeat (2) @(negedge clk);
           sda_in = 1'b0; scl_in = 1'b1;   repeat (2) @(negedge clk);
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
       endtask

       initial begin
           rst_n = 1'b0; scl_in = 1'b1; sda_in = 1'b1;
           repeat (2) @(negedge clk);
           if (busy !== 1'b0) begin $display("FAIL: bus_busy set after reset"); errors++; end
           rst_n = 1'b1; @(negedge clk);

           // 1 -- ordinary data must classify as nothing at all.
           do_bit(1'b0); do_bit(1'b1);
           expect_counts(0, 0, 0, 1);

           // 2 -- a fall while high from an IDLE bus is S, not Sr.
           sda_fall_while_high();
           expect_counts(1, 0, 0, 2);
           if (busy !== 1'b1) begin $display("FAIL: S did not set bus_busy"); errors++; end

           // 3 -- data, then the SAME EDGE again while busy, is Sr, not S.
           do_bit(1'b1);
           release_sda_during_low();
           sda_fall_while_high();
           expect_counts(1, 1, 0, 3);
           if (busy !== 1'b1) begin $display("FAIL: Sr cleared bus_busy"); errors++; end

           // 4 -- a second Sr also classifies as Sr: repeatable, not a one-shot.
           do_bit(1'b0);
           release_sda_during_low();
           sda_fall_while_high();
           expect_counts(1, 2, 0, 4);

           // 5 -- a rise while high is P, and releases the bus.
           sda_rise_while_high();
           expect_counts(1, 2, 1, 5);
           if (busy !== 1'b0) begin $display("FAIL: P did not clear bus_busy"); errors++; end

           // 6 -- THE KEY REGRESSION: after a P, the next fall must be S again.
           //      A classifier whose busy flag is sticky reports Sr here forever.
           sda_fall_while_high();
           expect_counts(2, 2, 1, 6);

           // 7 -- boundary case stays unclassified.
           coincident_fall();
           expect_counts(2, 2, 1, 7);

           if (errors == 0)
               $display("PASS: S=%0d Sr=%0d P=%0d -- same edge classified by bus state, exclusively",
                        n_s, n_sr, n_p);
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_framing_classifier.v — SYNTHESIZABLE RTL. The same classifier in Verilog-2005.
   module i2c_framing_classifier (
       input  wire clk,
       input  wire rst_n,
       input  wire scl_in,                  // observed bus level, not drive intent
       input  wire sda_in,                  // observed bus level, not drive intent
       output reg  start_pulse,             // S  -- first START of a transfer
       output reg  repeated_start_pulse,    // Sr -- START while the bus was busy
       output reg  stop_pulse,              // P
       output reg  bus_busy
   );
       reg  scl_q, sda_q;
       wire saw_fall, saw_rise;

       // Both framing edges require SCL HIGH on BOTH samples.
       assign saw_fall = scl_q & scl_in &  sda_q & ~sda_in;
       assign saw_rise = scl_q & scl_in & ~sda_q &  sda_in;

       always @(posedge clk) begin
           if (!rst_n) begin
               scl_q <= 1'b1; sda_q <= 1'b1;
               start_pulse          <= 1'b0;
               repeated_start_pulse <= 1'b0;
               stop_pulse           <= 1'b0;
               bus_busy             <= 1'b0;
           end else begin
               // Pulses, not levels: default low every cycle.
               start_pulse          <= 1'b0;
               repeated_start_pulse <= 1'b0;
               stop_pulse           <= 1'b0;

               // S and Sr are the SAME EDGE; only the tracked state separates them.
               if (saw_fall) begin
                   if (bus_busy) repeated_start_pulse <= 1'b1;
                   else          start_pulse          <= 1'b1;
                   bus_busy <= 1'b1;
               end else if (saw_rise) begin
                   stop_pulse <= 1'b1;
                   bus_busy   <= 1'b0;
               end

               scl_q <= scl_in;
               sda_q <= sda_in;
           end
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_framing_classifier_tb.v — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same cases in Verilog idiom.
   module i2c_framing_classifier_tb;
       reg  clk, rst_n, scl_in, sda_in;
       wire s_p, sr_p, p_p, busy;
       integer errors, n_s, n_sr, n_p;

       i2c_framing_classifier dut (.clk(clk), .rst_n(rst_n), .scl_in(scl_in), .sda_in(sda_in),
           .start_pulse(s_p), .repeated_start_pulse(sr_p), .stop_pulse(p_p), .bus_busy(busy));

       initial clk = 1'b0;
       always #5 clk = ~clk;
       initial begin #40000; $display("FAIL: watchdog expired"); $finish; end

       always @(posedge clk) if (rst_n) begin
           if (s_p)  n_s  = n_s  + 1;
           if (sr_p) n_sr = n_sr + 1;
           if (p_p)  n_p  = n_p  + 1;
           // Mutually exclusive by construction.
           if ((s_p + sr_p + p_p) > 1) begin
               $display("FAIL: %0t -- more than one framing class asserted at once", $time);
               errors = errors + 1;
           end
       end

       task expect_counts;
           input integer es; input integer esr; input integer ep; input integer step;
       begin
           if (n_s !== es || n_sr !== esr || n_p !== ep) begin
               $display("FAIL: step %0d -- S/Sr/P = %0d/%0d/%0d, expected %0d/%0d/%0d",
                        step, n_s, n_sr, n_p, es, esr, ep);
               errors = errors + 1;
           end
       end endtask

       task sda_fall_while_high; begin
           sda_in = 1'b1; scl_in = 1'b1;   repeat (2) @(negedge clk);
           sda_in = 1'b0;                  repeat (2) @(negedge clk);
           scl_in = 1'b0;                  repeat (2) @(negedge clk);
       end endtask

       task release_sda_during_low; begin
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
           sda_in = 1'b1;                  repeat (2) @(negedge clk);
       end endtask

       task sda_rise_while_high; begin
           scl_in = 1'b0; sda_in = 1'b0;   repeat (2) @(negedge clk);
           scl_in = 1'b1;                  repeat (2) @(negedge clk);
           sda_in = 1'b1;                  repeat (2) @(negedge clk);
       end endtask

       task do_bit; input v; begin
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
           sda_in = v;                     repeat (1) @(negedge clk);
           scl_in = 1'b1;                  repeat (2) @(negedge clk);
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
       end endtask

       task coincident_fall; begin
           scl_in = 1'b0; sda_in = 1'b1;   repeat (2) @(negedge clk);
           sda_in = 1'b0; scl_in = 1'b1;   repeat (2) @(negedge clk);
           scl_in = 1'b0;                  repeat (1) @(negedge clk);
       end endtask

       initial begin
           errors = 0; n_s = 0; n_sr = 0; n_p = 0;
           rst_n = 1'b0; scl_in = 1'b1; sda_in = 1'b1;
           repeat (2) @(negedge clk);
           if (busy !== 1'b0) begin $display("FAIL: bus_busy set after reset"); errors = errors + 1; end
           rst_n = 1'b1; @(negedge clk);

           do_bit(1'b0); do_bit(1'b1);
           expect_counts(0, 0, 0, 1);

           sda_fall_while_high();
           expect_counts(1, 0, 0, 2);
           if (busy !== 1'b1) begin $display("FAIL: S did not set bus_busy"); errors = errors + 1; end

           do_bit(1'b1);
           release_sda_during_low();
           sda_fall_while_high();
           expect_counts(1, 1, 0, 3);
           if (busy !== 1'b1) begin $display("FAIL: Sr cleared bus_busy"); errors = errors + 1; end

           do_bit(1'b0);
           release_sda_during_low();
           sda_fall_while_high();
           expect_counts(1, 2, 0, 4);

           sda_rise_while_high();
           expect_counts(1, 2, 1, 5);
           if (busy !== 1'b0) begin $display("FAIL: P did not clear bus_busy"); errors = errors + 1; end

           // Sticky-busy regression: after P the next fall must be S again.
           sda_fall_while_high();
           expect_counts(2, 2, 1, 6);

           coincident_fall();
           expect_counts(2, 2, 1, 7);

           if (errors == 0)
               $display("PASS: S=%0d Sr=%0d P=%0d -- same edge classified by bus state, exclusively",
                        n_s, n_sr, n_p);
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_framing_classifier.vhd — SYNTHESIZABLE RTL. The same classifier in VHDL.
   library ieee;
   use ieee.std_logic_1164.all;

   entity i2c_framing_classifier is
       port (
           clk                  : in  std_logic;
           rst_n                : in  std_logic;
           scl_in               : in  std_logic;   -- observed bus level, not drive intent
           sda_in               : in  std_logic;   -- observed bus level, not drive intent
           start_pulse          : out std_logic;   -- S  -- first START of a transfer
           repeated_start_pulse : out std_logic;   -- Sr -- START while the bus was busy
           stop_pulse           : out std_logic;   -- P
           bus_busy             : out std_logic
       );
   end entity;

   architecture rtl of i2c_framing_classifier is
       signal scl_q : std_logic := '1';
       signal sda_q : std_logic := '1';
       signal busy  : std_logic := '0';
       signal saw_fall, saw_rise : std_logic;
   begin
       -- Both framing edges require SCL HIGH on BOTH samples.
       saw_fall <= scl_q and scl_in and sda_q and (not sda_in);
       saw_rise <= scl_q and scl_in and (not sda_q) and sda_in;

       process (clk)
       begin
           if rising_edge(clk) then
               if rst_n = '0' then
                   scl_q <= '1'; sda_q <= '1';
                   start_pulse          <= '0';
                   repeated_start_pulse <= '0';
                   stop_pulse           <= '0';
                   busy                 <= '0';
               else
                   -- Pulses, not levels: default low every cycle.
                   start_pulse          <= '0';
                   repeated_start_pulse <= '0';
                   stop_pulse           <= '0';

                   -- S and Sr are the SAME EDGE; only the tracked state separates them.
                   if saw_fall = '1' then
                       if busy = '1' then
                           repeated_start_pulse <= '1';
                       else
                           start_pulse <= '1';
                       end if;
                       busy <= '1';
                   elsif saw_rise = '1' then
                       stop_pulse <= '1';
                       busy       <= '0';
                   end if;

                   scl_q <= scl_in;
                   sda_q <= sda_in;
               end if;
           end if;
       end process;

       bus_busy <= busy;
   end architecture;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_framing_classifier_tb.vhd — SELF-CHECKING TESTBENCH, SIMULATION ONLY. The same cases with assert report severity.
   library ieee;
   use ieee.std_logic_1164.all;

   entity i2c_framing_classifier_tb is
   end entity;

   architecture sim of i2c_framing_classifier_tb is
       signal clk    : std_logic := '0';
       signal rst_n  : std_logic := '0';
       signal scl_in : std_logic := '1';
       signal sda_in : std_logic := '1';
       signal s_p, sr_p, p_p, busy : std_logic;

       -- Driven by the counting process, read by the checker.
       signal n_s, n_sr, n_p : natural := 0;
       signal excl_err       : natural := 0;
       -- Set by the checker just before it suspends, so the watchdog can tell a
       -- finished run from a stalled one.
       signal test_done : std_logic := '0';
   begin
       dut : entity work.i2c_framing_classifier
           port map (clk => clk, rst_n => rst_n, scl_in => scl_in, sda_in => sda_in,
                     start_pulse => s_p, repeated_start_pulse => sr_p,
                     stop_pulse => p_p, bus_busy => busy);

       clk <= not clk after 5 ns;

       -- Watchdog. A broken design must produce a REPORTED FAILURE, not a silent
       -- stop: without this, a `wait until` against a stalled DUT simply runs to the
       -- simulator's time limit and prints nothing that identifies the problem.
       watchdog : process
       begin
           wait for 40 us;
           if test_done = '0' then
               report "watchdog expired -- the design never reached the expected state"
                   severity failure;
           end if;
           wait;
       end process;

       count : process (clk)
           variable hits : natural;
       begin
           if rising_edge(clk) then
               if rst_n = '1' then
                   hits := 0;
                   if s_p  = '1' then n_s  <= n_s  + 1; hits := hits + 1; end if;
                   if sr_p = '1' then n_sr <= n_sr + 1; hits := hits + 1; end if;
                   if p_p  = '1' then n_p  <= n_p  + 1; hits := hits + 1; end if;
                   -- The three classifications are mutually exclusive by construction.
                   if hits > 1 then excl_err <= excl_err + 1; end if;
               end if;
           end if;
       end process;

       stim : process
           variable errs : natural := 0;

           procedure waitn (n : in positive) is
           begin
               for i in 1 to n loop wait until falling_edge(clk); end loop;
           end procedure;

           procedure expect_counts (es, esr, ep, step : in natural) is
           begin
               if n_s /= es or n_sr /= esr or n_p /= ep then
                   report "step " & integer'image(step) & ": S/Sr/P = " &
                          integer'image(n_s) & "/" & integer'image(n_sr) & "/" & integer'image(n_p) &
                          ", expected " & integer'image(es) & "/" & integer'image(esr) &
                          "/" & integer'image(ep) severity error;
                   errs := errs + 1;
               end if;
           end procedure;

           procedure sda_fall_while_high is
           begin
               sda_in <= '1'; scl_in <= '1'; waitn(2);
               sda_in <= '0';                waitn(2);
               scl_in <= '0';                waitn(2);
           end procedure;

           procedure release_sda_during_low is
           begin
               scl_in <= '0';   waitn(1);
               sda_in <= '1';   waitn(2);
           end procedure;

           procedure sda_rise_while_high is
           begin
               scl_in <= '0'; sda_in <= '0'; waitn(2);
               scl_in <= '1';                waitn(2);
               sda_in <= '1';                waitn(2);
           end procedure;

           procedure do_bit (v : in std_logic) is
           begin
               scl_in <= '0';   waitn(1);
               sda_in <= v;     waitn(1);
               scl_in <= '1';   waitn(2);
               scl_in <= '0';   waitn(1);
           end procedure;

           procedure coincident_fall is
           begin
               scl_in <= '0'; sda_in <= '1';  waitn(2);
               sda_in <= '0'; scl_in <= '1';  waitn(2);
               scl_in <= '0';                 waitn(1);
           end procedure;
       begin
           waitn(2);
           if busy /= '0' then
               report "bus_busy set after reset" severity error; errs := errs + 1; end if;
           rst_n <= '1'; waitn(1);

           do_bit('0'); do_bit('1');
           expect_counts(0, 0, 0, 1);

           sda_fall_while_high;
           expect_counts(1, 0, 0, 2);
           if busy /= '1' then report "S did not set bus_busy" severity error; errs := errs + 1; end if;

           do_bit('1');
           release_sda_during_low;
           sda_fall_while_high;
           expect_counts(1, 1, 0, 3);
           if busy /= '1' then report "Sr cleared bus_busy" severity error; errs := errs + 1; end if;

           do_bit('0');
           release_sda_during_low;
           sda_fall_while_high;
           expect_counts(1, 2, 0, 4);

           sda_rise_while_high;
           expect_counts(1, 2, 1, 5);
           if busy /= '0' then report "P did not clear bus_busy" severity error; errs := errs + 1; end if;

           -- Sticky-busy regression: after P the next fall must be S again.
           sda_fall_while_high;
           expect_counts(2, 2, 1, 6);

           coincident_fall;
           expect_counts(2, 2, 1, 7);

           if excl_err /= 0 then
               report "more than one framing class asserted simultaneously" severity error;
               errs := errs + 1;
           end if;

           if errs = 0 then
               report "i2c_framing_classifier self-check complete: S=" & integer'image(n_s) &
                      " Sr=" & integer'image(n_sr) & " P=" & integer'image(n_p) &
                      " -- same edge classified by bus state, exclusively" severity note;
           else
               report "i2c_framing_classifier self-check FAILED" severity error;
           end if;
           test_done <= '1';
           wait;
       end process;
   end architecture;

6a. Two Design Decisions Worth Defending

The three outputs are mutually exclusive by construction, and the testbench checks it anyway. An edge is a fall or a rise and cannot be both, so the if/else if structure makes simultaneous assertion structurally impossible. The testbench still asserts that no two are ever high together, on every cycle of every test. That is not redundancy — it is the check that survives a future refactor. A classifier that could assert two classes at once is worse than one that picks the wrong class, because a consumer has no defined behaviour for "this edge was both a START and a STOP", and the failure would appear downstream in whatever component is least equipped to explain it.

stop_pulse is emitted on any qualified rise, regardless of bus_busy. This is a deliberate choice and the alternative is defensible, so here is the reasoning. UM10204 defines a STOP by the edge itself — "a LOW to HIGH transition on the SDA line while SCL is HIGH defines a STOP condition" — with no clause about prior state. A classifier's job is to report what the bus did, and a qualified rise is a STOP by definition. Whether a STOP arriving with no preceding START is legal is a different question, and it is a malformed-framing case that Chapter 5.5 catalogues. Keeping the classifier definitional and the legality judgement separate means the two can be verified separately, and it means the classifier does not silently swallow evidence of a malformed bus.

The asymmetry with the fall case is real and is not an inconsistency: the fall must consult bus_busy, because the specification gives the two falls different names based on exactly that. The rise has only one name.

6b. Cross-Language Parity

SystemVerilogVerilog-2005VHDL
state bitlogic bus_busy (output reg)reg bus_busy (output reg)internal signal busy, driven to the port
edge termsalways_combcontinuous assignconcurrent signal assignment
default-low pulsesassigned low each cycle, overriddensamesame
class counting in TBint incrementedinteger incrementedcounting process drives signals, checker reads them
exclusivity checksum of the three > 1sum of the three > 1hits variable per cycle, error counter signal

The VHDL classifier routes its state through an internal busy signal rather than reading the output port directly, because the design reads that bit in the same process that assigns it. The Verilog versions can read an output reg freely; VHDL's stricter port-mode rules make the internal signal the idiomatic form. The hardware is identical — one flip-flop, read and written in the same clocked process.

Verified execution. All three complete at the same simulated time with identical classifications:

languagesimulatorresultclassificationscompletes at
SystemVerilogIcarus Verilog, -g2012PASSS=2, Sr=2, P=1640 ns
Verilog-2005Icarus Verilog, -g2005PASSS=2, Sr=2, P=1640 ns
VHDLnvc 1.23.0PASSS=2, Sr=2, P=1640 ns

i2c_framing_classifier — one edge, two classes

10 cycles
Ten internal clock cycles. The observed SDA input falls while SCL is high early in the trace, and the start pulse output asserts for one cycle while bus busy rises. SDA is later released high while SCL is low, producing no output. SDA then falls again while SCL is high, and this time the repeated start pulse output asserts for one cycle instead, with bus busy already high.SDA falls — bus was freeSDA falls — bus was freeclassified Sclassified Ssame edge — bus was busysame edge — bus was busyclassified Srclassified Srclkscl_insda_instart_pulserepeated_start_pulsebus_busyt0t1t2t3t4t5t6t7t8t9
Figure 3 — the classifier resolving the same edge two different ways, from the simulation. The SDA fall in the second cycle arrives with bus_busy low and is classified S. SDA is then released during SCL's low phase, which is ordinary preparation and produces no classification. The SDA fall in the eighth cycle is the identical waveform arriving with bus_busy high, and is classified Sr. SIMULATION-DERIVED figure — the sampled model the RTL implements.

7. The Regression That Matters Most

The testbench runs seven steps. Six of them are ordinary. One is the reason the suite exists.

stepstimulusrequired classification
1ordinary data bitsnothing at all
2SDA falls while high, bus freeS — exactly one
3data, SDA released during low, SDA falls while highSr — not another S
4the same againa second Sr — repeatable
5SDA rises while highP, and bus_busy clears
6SDA falls while high, after the PS again
7SDA falls coincident with SCL risingnothing — boundary

Step 6 is the one. After a STOP, the next falling edge must classify as a first START, because the bus went free and a new transfer is beginning. A classifier whose state bit is set correctly but never cleared passes steps 1 through 5 perfectly — it reports the first S, then Sr, then Sr, then P — and from that point on reports every subsequent START as a repeated START, forever. The bus appears to work. Every transfer after the first is misclassified.

That failure mode is nastier than it sounds because of where the consequence lands. A protocol monitor consuming these events builds transactions from them; if every S after the first arrives labelled Sr, the monitor concludes that all subsequent transfers are continuations of one enormous transaction that never ends. The scoreboard sees one transfer where there were two hundred. The reported error will be about transaction boundaries or about a queue that never drains, and nothing in that error points at a state bit that failed to clear.

Step 6 costs two lines and is the difference between catching that at block level and chasing it at system level.

8. Mutation Testing

Five faults injected into the verified RTL, testbench run against each.

mutationwhat it breaksresult
ignore bus_busy — always classify as SSr is never reportedFAIL — step 3 saw S=2, Sr=0
ignore bus_busy — always classify as Srthe first START is misreportedFAIL — step 2 saw S=0, Sr=1
STOP does not clear bus_busythe step-6 failure aboveFAIL — P did not clear bus_busy
a framing fall does not set bus_busySr never reported; bus never busyFAIL — S did not set bus_busy
swap the two framing directionsevery class is the wrong oneFAIL — step 2 saw P=1

All five caught, and the useful observation is about the first two rather than the third.

Mutations 1 and 2 are the same structural fault — a classifier that has stopped consulting its state — and they fail on different steps. Always-S fails first at step 3, the first Sr. Always-Sr fails at step 2, the very first START. A suite that contained only one of those two steps would catch one mutant and miss the other, and the missed one would be a design that works correctly on every bus that never uses a repeated START at all — which is a great many buses, right up until someone adds a sensor that requires a write-then-read.

That is the argument for testing both directions of a binary decision rather than assuming symmetry. The two mutants are mirror images; the tests that catch them are not interchangeable.

9. The Monitor That Joined Late

There is a limitation here that is structural rather than a defect, and being explicit about it is the difference between a monitor that reports honestly and one that lies confidently.

The classifier's discriminator is a state bit built by observing framing events. A classifier that was not watching when a transfer began has no valid state. Enable it in the middle of an active transfer and its bus_busy reads zero — not because the bus is free, but because it has not seen the START that made the bus busy. The next repeated START it observes will be classified as a first START.

Nothing in the design can detect this. The bit is not wrong in any way the block can observe; it is simply uninitialised with respect to a bus that was already running.

Three honest responses, in increasing order of sophistication:

Reset while the bus is idle. If the classifier's reset is released when the bus is genuinely free, its state is correct by construction. This is the right answer for a controller's own classifier, whose reset is under the same designer's control.

Declare the state unknown until a STOP is seen. A STOP is the resynchronisation point: whatever was happening before it, after it the bus is released and the classifier's zero is correct. A monitor can report edges without classifying them until it has observed one STOP, and classify confidently from then on. This costs one extra state bit and is the right answer for a passive monitor.

Report the edge and the uncertainty separately. The most useful output for a debug monitor is "a framing fall occurred, and I do not yet know whether it was S or Sr", because that is true and actionable, whereas a confident wrong classification sends the investigation in the wrong direction.

This is the same class of problem as Chapter 5.2's surviving mutation, and it has the same shape: state that is correct by construction in the normal case can be silently wrong in the case where observation begins mid-stream. A bus does not wait to be watched.

10. Verification Connection — Covering the Sequence Space

A single S, Sr or P is not interesting. What is interesting is which sequences of them the verification has exercised, and this is where coverage stops being vanity and starts being a question worth answering.

The framing events form a small, enumerable transition space, and a controller's legality depends on position in it:

fromtolegal?what it means
freeSyesa transfer begins
busySryesa phase change without releasing
busyPyesa transfer ends
freeSrn/anot a distinct case — from free, that edge is an S
freePnoa STOP with no transfer to stop — malformed
busySn/anot a distinct case — from busy, that edge is an Sr

The space is small enough to cover exhaustively, which makes "did we cover it" a fair question rather than a box-ticking exercise.

Azvya Education Pvt. Ltd.VLSI Mentor
UVM CONCEPT — VERIFICATION ONLY. Covering framing transitions, not framing events.
   // Counting how many STARTs a test produced answers nothing useful. What
   // matters is which TRANSITIONS were exercised, because the classifier's whole
   // job is to be correct about position in this sequence.
   covergroup framing_sequence_cg @(posedge clk);
       option.per_instance = 1;

       // The classification actually produced this cycle.
       cp_class : coverpoint framing_class iff (framing_event) {
           bins s  = { CLASS_S  };
           bins sr = { CLASS_SR };
           bins p  = { CLASS_P  };
       }

       // The transitions -- this is the part that finds real holes. A suite that
       // never produced sr => sr has not tested a multi-phase combined transfer;
       // one that never produced p => s has not tested the step-6 regression.
       cp_seq : coverpoint framing_class iff (framing_event) {
           bins s_then_sr  = ( CLASS_S  => CLASS_SR );
           bins sr_then_sr = ( CLASS_SR => CLASS_SR );
           bins sr_then_p  = ( CLASS_SR => CLASS_P  );
           bins s_then_p   = ( CLASS_S  => CLASS_P  );
           bins p_then_s   = ( CLASS_P  => CLASS_S  );   // <- section 7, step 6
       }
   endgroup

Two things about that coverage model are deliberate.

The transition bins are the point; the event bins are bookkeeping. Knowing a test produced forty STARTs tells you nothing. Knowing it never produced p => s tells you the sticky-bus_busy bug in §8 could not have been caught, and knowing it never produced sr => sr tells you no multi-phase transfer was exercised. Those are actionable holes.

A bin is missing on purpose. There is no bin for a STOP arriving with the bus free, because that is malformed framing rather than a legal transition, and mixing legality violations into a legal-transition coverage model makes an unreachable bin look like a coverage hole. Error cases need their own model, and they are Chapter 5.5's subject.

The separation matters more generally: legal-behaviour coverage and error-injection coverage answer different questions and should not share a covergroup. A group containing both can never reach 100% in a clean run, which trains everybody to ignore its report.

11. FPGA and ASIC Implications

The classifier is one flip-flop more expensive than the detector, and that is the whole cost. There is no reason to omit classification from a controller or a monitor on resource grounds. The reason designs omit it is that they did not realise S and Sr were the same edge and assumed the distinction came for free from context.

The state bit belongs in the same reset domain as the framing detector. Splitting them — detector in an always-on domain, state bit in a peripheral-reset domain — produces exactly §9's failure on every peripheral reset, with the additional property that the bus does not need to be active for it to bite. A peripheral reset that clears bus_busy while a transfer is in progress makes the next repeated START classify as a first START.

A controller that supports repeated START needs the sequencing states, not just the classifier. §4's seven steps are a state machine on the generating side, and the two SDA transitions in it move in opposite directions with different legality conditions. A controller that can only emit S and P is meaningfully simpler, and a controller that emits Sr by reordering its existing S sequence will emit a STOP instead — which is Chapter 5.5's malformed-framing catalogue.

On an ASIC the classification is usually visible to software, and the register semantics matter. A status bit that says "repeated START detected" is an event, not a level, and needs the write-one-to-clear treatment Chapter 3.4 established — because a driver that reads a level cannot tell one repeated START from three.

12. Debugging — The Transfer That Never Ended

A monitor that reported one transaction for an entire test run

Pitfall — a bus_busy flag that is set correctly and cleared on the wrong event
Buggy Code
// A classifier is written from the specification, correctly, with one omission.
// The engineer reads that S and Sr differ by whether the bus was busy, and
// implements exactly that:
//
//   if (saw_fall) begin
//       if (bus_busy) repeated_start_pulse <= 1'b1;
//       else          start_pulse          <= 1'b1;
//       bus_busy <= 1'b1;                       // a framing fall claims the bus
//   end else if (saw_rise) begin
//       stop_pulse <= 1'b1;                     // ... and the bus is released
//   end                                         //     <-- bus_busy never cleared
//
// The omission is invisible in review because the code reads as a complete
// if/else over the two framing directions, and the STOP branch does report the
// STOP. Nothing is missing SYNTACTICALLY. What is missing is the state update
// that makes 'was the bus busy' mean anything on the next edge.
//
// It passes a first test easily. A test that does START, address, data, STOP
// exercises the set path, the S classification and the P report, and every one
// of them is right.
Symptom

Block-level tests pass. The classifier reports S, Sr and P, and a directed test for each one is green.

At system level the scoreboard reports a single transaction for the entire regression, with a payload thousands of bytes long, and then fails on a queue depth limit. The error is raised by the scoreboard, thousands of cycles after the first transfer, and names a queue -- so the investigation starts in the scoreboard, then moves to the transaction builder, then to the sequencer. All of them are correct.

Captures look perfect. Every framing waveform on the bus is legal and correctly shaped, because THE BUS IS FINE -- the DUT is generating flawless framing. The fault is entirely in the observer, which is the hardest place to look when the error message is about transaction content.

The giveaway, once someone thinks to check it, is that the monitor reports exactly one S for the whole run and everything afterwards as Sr.

Root Cause

bus_busy was set by a framing fall and never cleared by a framing rise.

After the very first STOP, the flag stayed high. Every subsequent START therefore arrived with bus_busy == 1 and was classified as a repeated START -- correctly, according to the flag, which was lying. Section 5's derivation is explicit that the discriminator must be SET by a framing fall AND CLEARED by a framing rise; implementing only half of that produces a flag that is monotonic instead of a flag that tracks bus state.

The reason this reaches system level rather than dying at block level is the structure of the block-level suite. A suite built from directed tests -- one test for S, one for Sr, one for P -- resets the DUT between tests, which clears the flag and hides the bug. The bug requires two transfers IN SEQUENCE, in one test, with a STOP between them. That sequence is the p => s transition in section 10's coverage model, and a directed-test suite organised one-classification-per-test never produces it.

The reason the symptom is so far from the cause is that misclassifying S as Sr is not an error to any consumer. It is a valid classification. The transaction builder does exactly the right thing with it -- treats the transfer as a continuation -- and keeps doing so, accumulating one unbounded transaction, until something unrelated runs out of room.

Fix
// The RTL fix is one line, in the branch that already existed:
//
//   end else if (saw_rise) begin
//       stop_pulse <= 1'b1;
//       bus_busy   <= 1'b0;          // <-- the bus is RELEASED
//   end
//
// The verification fix is the one worth keeping, and there are two parts.
//
//   1. A SEQUENCE test, not another directed test. Section 7's step 6 is the
//      whole fix: after the P, drive another framing fall and require that it
//      classifies as S. Two lines, at block level, and it fails immediately
//      against this RTL. Section 8 confirms it -- injecting this exact fault into
//      the corrected design produces 'FAIL: P did not clear bus_busy' and nothing
//      else does.
//
//   2. Coverage on TRANSITIONS rather than events. The hole here is precisely
//      the p => s bin of section 10's covergroup. A directed suite reports 100%
//      on the event coverpoint -- it produced S, Sr and P -- while never
//      producing the transition that matters. Event coverage said the suite was
//      complete; transition coverage says it never tested a second transfer.
//
// The generalisable habit: for any flag that encodes 'am I in the middle of
// something', write the SET and the CLEAR in the same review pass and name the
// event for each. A flag with a set condition and no clear condition is a latch,
// and a latch that was meant to be a state bit fails only on the SECOND
// iteration -- which is exactly the iteration a directed test does not run.
//
// And a debugging habit for the symptom shape: when a scoreboard error names a
// resource limit rather than a mismatch, suspect that something upstream is
// failing to terminate rather than producing wrong values. 'One transaction,
// unbounded payload' is a boundary bug, not a data bug, and boundaries come from
// framing.

13. Common Misconceptions

"A repeated START is a different waveform from a START." It is the same edge — same conductor, same direction, same shape. Only the prior bus state differs, and that is not visible at the instant of the edge.

"'Functionally identical' means they are interchangeable." They are identical in what they do to devices and completely different in the conditions under which emitting one is legal. §3 tabulates the split; a first START is gated by the bus-free interval and a repeated START by a setup margin.

"A repeated START is an optimisation." Speed is the least important of its three effects. The one that matters on a shared segment is that the bus never becomes free, so no other controller is ever offered it.

"To issue a repeated START, re-run the START sequence." The START sequence begins from a released bus. From a busy one, SDA is typically low and must be released during SCL's low phase first — and releasing it while SCL is high emits a STOP, which is the opposite of the intent. §4's ordering is forced.

"A classifier can work out S versus Sr from the waveform." It cannot. The discriminator is a bit of state set by an earlier event, and a classifier that was not watching when the transfer began has no valid value for it.

"If the classifier reports S, Sr and P correctly in directed tests, it works." Directed tests reset between cases and therefore never exercise the transitions. §12's bug passes every directed test and fails on the second transfer of any sequence.

"A misclassification will show up as an error near the misclassification." Misclassifying S as Sr is a valid classification that a consumer handles correctly, so the consequence accumulates silently and surfaces as an unrelated resource limit thousands of cycles later.

14. Reason It Through

A controller wants to write a register pointer and then read that register. Why is a repeated START preferable to a STOP followed by a START, and what exactly would a STOP risk?

A STOP would make the bus free, which costs the bus-free interval and — more importantly — offers the bus to any other controller on the segment. Another controller could claim it and address the same target, which for a device whose read depends on a register pointer set by the previous phase means the second phase may read from a pointer somebody else changed. A repeated START never releases the bus, so the pointer set in phase one is still the pointer in phase two. The precise extent of that guarantee is Chapter 10.3's subject.

Why can a repeated START not simply reuse the controller's START sequence?

Because the START sequence assumes a released bus, where SDA is already high and available to fall. At the end of a transfer phase SDA is typically low, so it has to be released first — and it must be released while SCL is low, because an SDA rise while SCL is high is a STOP. A controller that reuses the START sequence from a busy bus emits a STOP where it intended an Sr, ending the transfer it was trying to continue.

A classifier's bus_busy is set by a framing fall and cleared by a framing rise. Which of those two omissions is worse?

Omitting the clear is worse, and by a wide margin. Omitting the set means bus_busy is always zero, so every repeated START is reported as a first START — the bug is uniform, immediate, and shows up in the first test that uses a repeated START at all. Omitting the clear means the flag latches high after the first transfer, so the first transfer is classified perfectly and every subsequent one is wrong. A bug that gets the first iteration right and all later ones wrong survives directed testing and surfaces far from its cause, which is §12.

A passive monitor is enabled mid-transfer and immediately observes a repeated START. What does it report, and what should it report?

It reports a first START, because its bus_busy is zero — it never saw the edge that set it. What it should report is that a framing fall occurred whose classification is unknown, because that is the truth. The clean recovery is to treat the next STOP as a resynchronisation point and classify confidently from then on, since after a STOP the bus is genuinely released and a zero flag is correct.

Your block-level suite reports 100% coverage on a coverpoint over the three framing classes, and the design has §12's bug. Explain how both are true.

Because the coverpoint measures which classes were produced and the bug is about the order they are produced in. A directed suite with one test per class produces all three classes and reaches 100%, while resetting between tests so that no test ever contains a STOP followed by a START. The missing coverage is a transition — p => s — and a coverage model built on events rather than transitions is structurally unable to report it as a hole. This is the difference between coverage that answers "did we exercise the interesting region" and coverage that counts.

15. Understanding Check

16. Summary

A repeated START is the START edge, reused. Nothing in the waveform distinguishes it from a first START; only whether a transfer was already in progress does.

Its purpose is continuous ownership. Because no STOP is issued, the bus never becomes free — so the bus-free interval is not paid and no other controller is offered the bus. Speed is the least important of those effects.

"Functionally identical" scopes to effect, not to legality. Devices react identically to S and Sr; the conditions under which each may be emitted are entirely different, and they are governed by different timing parameters.

Emitting one is a forced sequence, not a single action. SDA must be released during SCL's low phase, then SCL released, then SDA driven low. Reordering the first step produces a STOP — the opposite of the intent.

Classification needs exactly one bit of state, set by any framing fall and cleared by a framing rise. That bit is bus_busy, which Chapter 5.3's tracker already maintained, so the classifier is an assembly of parts rather than a new mechanism.

Setting the bit and clearing it are separately necessary, and forgetting the clear is the worse bug — it gets the first transfer right and every later one wrong, which is the failure pattern directed tests cannot see.

A classifier that joined late has no valid state, and no self-check can detect it. The recoveries are to reset on an idle bus, to resynchronise on a STOP, or to report the uncertainty.

Cover transitions, not events. The sequence space is small enough to cover exhaustively, and it is the transitions — p => s above all — that distinguish a suite which tested two transfers from one which tested one transfer three times.

17. What Comes Next

All three framing events are now defined, detectable and distinguishable, and the hardware to do it exists in three languages. Every chapter so far has been careful to say that the intervals around these edges are specified minimums and to defer the numbers.

Chapter 5.5 stops deferring. It takes the three framing margins — the hold after a START, the setup before a repeated START, the setup before a STOP — establishes where each is measured from and to, quotes the verified values, builds the sequencer that generates all three with programmable counts and refuses an illegal configuration, and then catalogues the malformed framing those margins exist to prevent.

Browse the full path on the I²C tutorials index. For the edge this event reuses, see The START Condition; for the interval it avoids paying, The STOP Condition and Releasing the Bus.

Continue learning