Skip to content
VLSI Mentor

I²C · Module 14

High-Speed Mode — Master Code, Current Source and Bridging

The master code entry sequence, why a reserved code beats it on a single bit, why the SCLH current source must be switched off at every acknowledge so a slave can still stretch, and the arithmetic showing that 3.4 Mbit/s is a rounded number.

Chapter 14.1 found that the three mainstream modes differ in four numbers and share one protocol. High-speed mode is the exception. It changes the protocol.

Not much, and not permanently — but it adds a condition that must precede it, a state the bus can be in, an electrical regime that only one device may activate, and a rule about when that regime must be switched off so that a slave can still stretch the clock.

That last one is the interesting part, and it is the reason Table 12 carries a rise-time parameter no other mode has.

1. What Hs-Mode Actually Changes

Read the exception clause carefully. Arbitration and clock synchronization — the two mechanisms Module 13 was entirely about — do not happen during an Hs-mode transfer.

That is not a relaxation. It is a precondition. The bus can dispense with arbitration during the fast phase only because arbitration has already been completed, at F/S speed, immediately before it:

So the structure of an Hs-mode transfer is: a slow preamble whose only job is to pick a winner, then a fast phase with exactly one master on it. Everything else in this chapter follows from that sentence.

2. The Entry Sequence

Three steps, all at Fast-mode speed. And the master code does two jobs at once:

Which gives the arithmetic: 0000 1XXX has three free bits, so eight codes, one of which is reserved for test — leaving seven usable Hs-mode masters in practice, not eight. The specification says eight and then removes one in a parenthesis, which is the kind of detail that only matters once.

And the not-acknowledge is not a failure. It is mandatory. No device may acknowledge the master code, so the ninth bit must read high, and a master that treats a high ninth bit as an error has inverted the rule. §8's sequencer treats a low ninth bit as the violation — because a low there means either a device acknowledged something it must not, or a competing master is still driving.

3. Arbitration Decides Whether Hs-Mode Happens At All

This is where Module 13 pays off, and it comes down to one bit.

Write the first byte as b7 down to b0. The master code is b7..b4 = 0000 with b3 = 1. A reserved code 0000 0XXX has b3 = 0.

The two agree on the first four bits. They diverge at b3 — the fifth bit transmitted, MSB first — where the reserved code drives low and the master code releases high. The wired-AND of Chapter 13.3 resolves that to low, and the Hs-mode master reads back a zero where it sent a one. It has lost.

A single bit, five bits into a transfer, decides whether the bus spends the next few microseconds at 3.4 Mbit/s or at 400 kbit/s.

And the loser's obligations are exactly the ones Chapter 13.4 established, plus one specific to Hs-mode:

Note the asymmetry. A losing master still adapts its input filters to Hs-mode — because it has to be able to watch a 3.4 MHz bus go past without mistaking it for noise — while never enabling its own current source. Adapting to listen is not the same as adapting to drive, and mutation T9 in §9 is the version that confuses the two.

4. The Current Source

Three facts in there, each load-bearing.

Only the clock gets a current source. SDAH stays plain open-drain. The clock is the line whose rising edge is on the critical path — every device samples data on it — so it is the one worth spending silicon on. Data can settle during the long low phase.

Only one master's current source, ever. Two current sources into one line would sum, and the rise time is specified, not merely bounded below. Table 11 gives ICS as 3 to 12 mA; two masters at 12 mA would produce an edge outside the specified window in the fast direction, which §14.1 §6 showed is a real violation and not a bonus.

The 1:2 ratio is stated, not derived. An Hs-mode master generates tHIGH : tLOW of 1 : 2. At 3.4 MHz that is roughly 98 ns high and 196 ns low against Table 12 minima of 60 ns and 160 ns — so the ratio is comfortably legal, and it is a generation rule rather than a limit. It exists because the low phase is where SDAH changes and settles.

5. The Rule That Makes trCL1 Exist

Here is the mechanism this chapter is really about.

Work out why that is necessary.

A current source is an active pull-up. It fights a device trying to hold the line low far harder than a resistor does — that is the whole point of it. But clock stretching, the mechanism of Module 12, works precisely by a slave holding SCL low against the pull-up. So while the current source is enabled, stretching is either impossible or expensive, depending on how much current the slave's pull-down has to sink.

The specification's answer is not to forbid stretching in Hs-mode. It is to switch the current source off at exactly the points where a slave is allowed to stretch:

The two rules interlock exactly. Stretching is legal only after an acknowledge; the current source is disabled after every acknowledge. One rule is a protocol permission and the other is the electrical arrangement that makes the permission real.

5a. And Now Table 12 Makes Sense

During those intervals the rise is produced by the resistor alone, because the current source is off. So the rise time there is slower — and Table 12 gives it its own row:

trCL1 is exactly twice trCL at both capacitance corners. That is not a coincidence or a safety factor — it is the difference between a current-source-assisted edge and a resistor-only edge, written into the table.

A timing parameter exists because a protocol permission exists. trCL1 is clock stretching, expressed as a rise time.

This is the single most satisfying detail in the Hs-mode specification, and it is invisible unless you read §5.3.2 and Table 12 together. §8's sequencer implements the rule and counts the disable events, because counting them is the only way a testbench can tell the difference between a master that honours the obligation and one that does not — the data is unaffected either way.

6. Where 3.4 Mbit/s Comes From

Chapter 14.1 §4 established that in all three mainstream modes the four Table 10 limits sum exactly to the period. It is natural to test that against Hs-mode. It fails — and the way it fails is informative.

Table 12's phase minima and current-source-enabled edge maxima:

cornertLOWtHIGHtrCLtfCLsum1/fSCLH(max)verdict
Cb = 100 pF160604040300 ns294.12 nsover by 5.88 ns
Cb = 400 pF3201208080600 ns588.24 nsover by 11.76 ns

Over by 2.00 % at both corners. The same ratio twice is not an accident, and it points straight at the cause. Ask instead what frequency the budget would be exact at:

cornerbudget sumimplied exact ratespecification headline
Cb = 100 pF300 ns1/300 ns = 3.3333 MHz3.4 MHz
Cb = 400 pF600 ns1/600 ns = 1.6667 MHz1.7 MHz

Three and a third megahertz, and one and two thirds. Both rounded up to one decimal place, and rounding 3.3333 up to 3.4 is an increase of exactly 2.00 %.

The Hs-mode timing budget is internally exact at 3⅓ MHz. "3.4 Mbit/s" is that number rounded, and the 2 % discrepancy at both corners is the rounding, appearing twice.

The engineering consequence is real and is not a quibble. At a true 3.4 MHz you cannot sit on the phase minima and the edge maxima simultaneously. The period is 294.12 ns, the phases claim 220 ns of it, and that leaves 74.12 ns for both edges against a Table 12 allowance of 80 ns. The edges have to come in about 7.4 % under their own limits.

Which is exactly what the current source is for. A resistor pull-up sized to the constraints of Chapter 14.4 cannot deliver a 37 ns rise into 100 pF; a 3 to 12 mA current source can. The current source is not there to make Hs-mode comfortable — it is there to make the headline number reachable at all.

7. The Entry Sequence, Drawn

Entering Hs-mode: the current source comes on at tH and goes off again at every Sr and every acknowledge

10 cycles
Ten intervals across the boundary between the Fast-mode preamble and the high-speed phase. The first two intervals are the last master-code bit and the not-acknowledge bit, both at Fast-mode speed. The third shows a slave stretching SCLH low, which is permitted before tH. The fourth is tH, where the clock is released high and the active master enables its current source. The fifth is the repeated START, at which the current source is switched off again. The sixth shows it restored once the line is released. Two data bits follow, then an acknowledge at which the current source goes off once more. A row beneath tracks the current source state and another tracks which speed regime the bus is in.F/S-mode preambleF/S-mode preambleHs-modeHs-modestretching is legal before tHstretching is legal beforetHtH: current source enabledtH: current source enabledSr: off again, trCL1 appliesSr: off again, trCL1appliesACK: off again, trCL1 appliesACK: off again, trCL1appliessclhcsrcoffoffoffONoffONONONoffONspeedF/SF/SF/SHsHsHsHsHsHsHseventb0NACKstretchtHSrreldatadataACKrelt0t1t2t3t4t5t6t7t8t9

The shape to take away: the current source is off more often than people assume. It is off for the whole preamble, off at every repeated START, and off at every acknowledge — and each of those off-intervals is a trCL1 interval where the rise is twice as slow and a slave may legally hold the line.

8. Switching Back, And The Bridge

Leaving Hs-mode is a single event with a hard deadline.

So the revert budget is Fast-mode tBUF(min) = 1.3 µs — a number from Chapter 11.6, reused here as a mode-switching deadline. Every device on the bus has 1.3 µs to put its input filters, set-up and hold times, and slope control back to Fast-mode settings.

And a repeated START does not leave Hs-mode. That is what makes the mode economical:

The preamble costs a START, eight bits and a not-acknowledge at 400 kbit/s — about 25 µs. A single 2-byte Hs-mode transfer takes about 6 µs. So one transfer per preamble is slower than just using Fast-mode; the mode only pays when several transfers are linked behind one master code. That arithmetic is worth doing before choosing Hs-mode, and §13 does it.

8a. The Bridge

That description is what Table 11's two resistance rows mean:

symbolparametervaluewhat it describes
RonLtransfer gate on resistance, VOL level, IOL = 3 mAmax 50 ΩTR1/TR2 passing a low through
RonHtransfer gate on resistance, both signals at VDDmin 50 kΩthe same transistor, both sides high — the level-shift state

One transistor, two regimes, three orders of magnitude apart. A low passes through at 50 Ω; once both sides are high it becomes at least 50 kΩ and the two halves of the bus float to their own supplies independently. That is the entire level-shift mechanism, and it needs no control signal.

8b. What The F/S Segment Sees

This is a lovely piece of design. While the fast segment is running at 3.4 Mbit/s, the slow segment is held with SCL high and SDA low — which is the set-up for a STOP condition. So when the bridge finally releases SDA, the F/S devices see a clean STOP and conclude that the transaction they were watching has ended normally.

They never learn that anything happened at 3.4 Mbit/s. From the F/S segment's point of view the entire Hs-mode burst was one very long low period on SDA followed by a STOP.

And there is an escape hatch, which is worth knowing about because it is the only way out of a wedged bridge:

A 1 µs low on the slow side forces the bridge closed regardless of what the fast side is doing. It is the same philosophy the bus-clear procedure of Module 15 will apply to a stuck data line: a recovery path that requires no cooperation from the party that may be broken.

8c. Table 8 Is The Argument For Bridging

Two rows in that table are the whole point.

Fast → Fast collapses to 100 kbit/s the moment a Standard-mode device is present. Two Fast-mode devices talking to each other are dragged to a quarter of their rate by a third device that is not involved in the transfer, because they share one wire and one set of edges.

Hs → Hs stays at 3.4 Mbit/s in the same system. Not because Hs-mode devices are immune, but because the bridge has physically disconnected them from the slow devices for the duration.

Isolation is the only thing that breaks the "slowest device wins" rule. Everything else on a shared bus is governed by it.

That is why the bridge exists, and it is why Hs-mode is genuinely a different architecture rather than a faster setting. The speed comes from the isolation as much as from the current source.

9. The Entry Sequencer in Three Languages

The design is the master-side state machine for entry and exit. It encodes nine obligations drawn from §5.3.1 through §5.3.3, and the one worth watching is number six — the current source coming off at every acknowledge — because it is the one a working implementation can omit without any functional symptom.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_sequencer.sv — the nine entry and exit obligations as one state machine
   // -----------------------------------------------------------------------------
   // i2c_hs_sequencer.sv
   // Master-side High-speed mode entry and exit sequencer (UM10204 5.3.1 - 5.3.3).
   //
   // Hs-mode is not a faster I2C. It is a short F/S-mode preamble that hands the
   // bus to a different electrical regime, and this block is the state machine that
   // performs that handover. It encodes nine obligations from the specification:
   //
   //   1. Entry requires exactly S + 8-bit master code (0000 1XXX) + not-acknowledge,
   //      ALL of which happen at F/S-mode speed.
   //   2. No device may acknowledge the master code, so the master expects the ninth
   //      bit to read HIGH. A LOW there is a protocol violation, reported but not
   //      silently ignored.
   //   3. Arbitration and clock synchronization happen ONLY during the master code
   //      and the not-acknowledge bit. Once the not-acknowledge is past, one winning
   //      master remains and no further arbitration occurs.
   //   4. A competitor sending a reserved code 0000 0XXX wins, because the two codes
   //      agree on the first four bits and diverge at b3 -- the fifth bit sent --
   //      where the reserved code drives LOW. The loser must not enter Hs-mode; it
   //      waits for a STOP to learn the bus is free again.
   //   5. At tH -- after the not-acknowledge AND once every device has released SCLH
   //      so the line is genuinely HIGH -- the winner enables its SCLH current
   //      source and switches to the Hs bit rate. Any device may stretch before tH.
   //   6. After the repeated START, and after EVERY acknowledge or not-acknowledge
   //      bit, the active master DISABLES the current source. That is what makes a
   //      slave's clock stretch physically possible at those points, and it is why
   //      Table 12 carries a separate trCL1 whose limit is double trCL.
   //   7. It re-enables the current source only once all devices have released and
   //      SCLH has reached HIGH again.
   //   8. Hs-mode survives repeated STARTs. Only a STOP returns the bus to F/S-mode.
   //   9. At tFS the current source goes off and the device must be back in its
   //      Fast-mode configuration within the Fast-mode tBUF, i.e. 1.3 us.
   //
   // Obligation 6 is the one that is almost always missed in a first implementation:
   // a master that leaves its current source on across an acknowledge has taken away
   // the slave's ability to stretch, and nothing on the wire will tell it so.
   // -----------------------------------------------------------------------------

   module i2c_hs_sequencer #(
      // Fast-mode tBUF(min) = 1.3 us. In 20 ns ticks that is 65.
      parameter int TBUF_TICKS = 65,
      parameter int CNT_W      = 12
   ) (
      input  logic             clk,
      input  logic             rst_n,

      // ---- command interface -------------------------------------------------
      input  logic             start_req,     // begin an Hs transfer (issue S + master code)
      input  logic [2:0]       master_code,   // the XXX of 0000 1XXX
      input  logic             sr_req,        // repeated START: continue in Hs-mode
      input  logic             stop_req,      // STOP: leave Hs-mode

      // ---- bus interface -----------------------------------------------------
      input  logic             bit_tick,      // one pulse per bit cell (F/S rate during the code)
      input  logic             sda_line,      // the wired-AND SDA value, sampled at SCL HIGH
      input  logic             scl_released,  // every device has released SCLH and it reads HIGH
      input  logic             ack_phase,     // pulses at each ACK/NACK bit boundary in Hs-mode
      input  logic             fs_start_seen, // a START observed on the F/S segment after tFS

      // ---- outputs -----------------------------------------------------------
      output logic             sda_drive_low, // what this master drives during the master code
      output logic             cs_enable,     // SCLH current-source pull-up enabled
      output logic             is_hs,         // operating in the Hs-mode electrical regime
      output logic             arb_lost,
      output logic             ack_violation, // a device acknowledged the master code
      output logic             revert_done,   // back in Fast-mode configuration
      output logic             revert_late,   // a START arrived before the Fast-mode tBUF elapsed

      output logic [3:0]        state,
      output logic [CNT_W-1:0]  stretch_waits, // times the sequencer had to wait for a release
      output logic [CNT_W-1:0]  cs_disables,   // current-source disable events
      output logic [CNT_W-1:0]  hs_segments,   // Sr-separated Hs segments in this burst
      output logic [CNT_W-1:0]  revert_ticks   // ticks taken from tFS to revert_done
   );

      localparam [3:0] S_FS_IDLE = 4'd0,  // F/S-mode, current source off
                       S_START   = 4'd1,  // START condition, at F/S speed
                       S_CODE    = 4'd2,  // 8-bit master code, arbitration live
                       S_ACKBIT  = 4'd3,  // ninth bit: must read HIGH (not-acknowledge)
                       S_WAIT_TH = 4'd4,  // waiting for SCLH release -> tH
                       S_HS_SR   = 4'd5,  // repeated START in Hs-mode, current source off
                       S_HS_HOLD = 4'd6,  // current source off, waiting for a release
                       S_HS_DATA = 4'd7,  // Hs data, current source on
                       S_TFS     = 4'd8,  // STOP seen, reverting to Fast-mode
                       S_LOST    = 4'd9;  // lost arbitration, waiting for the bus to free

      // Sized constant. A part-select of a parameter (TBUF_TICKS[CNT_W-1:0]) is read as
      // zero by some tools, which would make the revert window collapse silently.
      localparam [CNT_W-1:0] TBUF_LIM = TBUF_TICKS;

      // The master code as an 8-bit first byte: b7..b4 = 0000, b3 = 1, b2..b0 = code.
      wire [7:0] code_byte = {4'b0000, 1'b1, master_code};

      logic [2:0]        bit_idx;      // 7 downto 0, MSB first
      logic              code_bit;
      logic [CNT_W-1:0]  tfs_cnt;

      always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
            state         <= S_FS_IDLE;
            sda_drive_low <= 1'b0;
            cs_enable     <= 1'b0;
            is_hs         <= 1'b0;
            arb_lost      <= 1'b0;
            ack_violation <= 1'b0;
            revert_done   <= 1'b0;
            revert_late   <= 1'b0;
            bit_idx       <= 3'd7;
            code_bit      <= 1'b1;
            stretch_waits <= {CNT_W{1'b0}};
            cs_disables   <= {CNT_W{1'b0}};
            hs_segments   <= {CNT_W{1'b0}};
            revert_ticks  <= {CNT_W{1'b0}};
            tfs_cnt       <= {CNT_W{1'b0}};
         end else begin
            case (state)

               // ---------------------------------------------------------------
               // F/S-mode. The current source is off and stays off: only the
               // active master in Hs-mode may ever enable it.
               // ---------------------------------------------------------------
               S_FS_IDLE: begin
                  cs_enable     <= 1'b0;
                  is_hs         <= 1'b0;
                  sda_drive_low <= 1'b0;
                  if (start_req) begin
                     arb_lost      <= 1'b0;
                     ack_violation <= 1'b0;
                     revert_done   <= 1'b0;
                     revert_late   <= 1'b0;
                     hs_segments   <= {CNT_W{1'b0}};
                     state         <= S_START;
                  end
               end

               // START condition, generated at F/S-mode speed.
               S_START: begin
                  sda_drive_low <= 1'b1;         // SDA pulled LOW while SCL is HIGH
                  if (bit_tick) begin
                     bit_idx  <= 3'd7;
                     code_bit <= code_byte[7];
                     state    <= S_CODE;
                  end
               end

               // ---------------------------------------------------------------
               // The 8-bit master code, sent MSB first, with arbitration live on
               // every bit. Obligation 4: a master that drove HIGH and reads LOW
               // has lost, and must drop off immediately.
               // ---------------------------------------------------------------
               S_CODE: begin
                  sda_drive_low <= !code_byte[bit_idx];
                  if (bit_tick) begin
                     if (code_byte[bit_idx] && !sda_line) begin
                        // Drove a 1, the line came back 0: someone else is driving
                        // LOW here. Conclusive loss.
                        arb_lost      <= 1'b1;
                        sda_drive_low <= 1'b0;
                        cs_enable     <= 1'b0;
                        state         <= S_LOST;
                     end else if (bit_idx == 3'd0) begin
                        state <= S_ACKBIT;
                     end else begin
                        bit_idx  <= bit_idx - 1'b1;
                        code_bit <= code_byte[bit_idx - 1'b1];
                     end
                  end
               end

               // ---------------------------------------------------------------
               // The ninth bit. No device may acknowledge the master code, so the
               // line must read HIGH. Arbitration is still live on this bit; after
               // it, one winning master remains and arbitration is over.
               // ---------------------------------------------------------------
               S_ACKBIT: begin
                  sda_drive_low <= 1'b0;         // release so the line can float HIGH
                  if (bit_tick) begin
                     if (!sda_line) begin
                        // Someone drove the acknowledge slot LOW. Either a device
                        // acknowledged the master code, which is forbidden, or a
                        // competing master is still driving. Report and abandon.
                        ack_violation <= 1'b1;
                        arb_lost      <= 1'b1;
                        cs_enable     <= 1'b0;
                        state         <= S_LOST;
                     end else begin
                        state <= S_WAIT_TH;
                     end
                  end
               end

               // ---------------------------------------------------------------
               // Between the not-acknowledge and tH. Any device may stretch here,
               // so the master waits for a genuine release rather than assuming
               // the line is HIGH. tH is the instant the current source comes on.
               // ---------------------------------------------------------------
               S_WAIT_TH: begin
                  if (scl_released) begin
                     cs_enable   <= 1'b1;       // tH
                     is_hs       <= 1'b1;
                     hs_segments <= hs_segments + 1'b1;
                     state       <= S_HS_SR;
                  end else begin
                     stretch_waits <= stretch_waits + 1'b1;
                  end
               end

               // ---------------------------------------------------------------
               // Obligation 6, first half. A repeated START is one of the two
               // points where the current source must come off.
               // ---------------------------------------------------------------
               S_HS_SR: begin
                  cs_enable   <= 1'b0;
                  cs_disables <= cs_disables + 1'b1;
                  state       <= S_HS_HOLD;
               end

               // Obligation 7: re-enable only once everybody has let go and the
               // line has actually reached HIGH. This is the interval whose rise
               // time Table 12 calls trCL1 and allows to be twice as long.
               S_HS_HOLD: begin
                  if (scl_released) begin
                     cs_enable <= 1'b1;
                     state     <= S_HS_DATA;
                  end else begin
                     stretch_waits <= stretch_waits + 1'b1;
                  end
               end

               // ---------------------------------------------------------------
               // Hs data. Obligation 6, second half: every acknowledge bit takes
               // the current source off again.
               //
               // stop_req and sr_req are accepted only here, by design. A master
               // does not issue a STOP or a repeated START while it is waiting for
               // another device to release the clock -- it has not finished the
               // current bit yet. Requests raised in S_HS_SR or S_HS_HOLD are
               // therefore ignored rather than queued.
               // ---------------------------------------------------------------
               S_HS_DATA: begin
                  if (stop_req) begin
                     cs_enable    <= 1'b0;      // tFS
                     tfs_cnt      <= {CNT_W{1'b0}};
                     revert_ticks <= {CNT_W{1'b0}};
                     state        <= S_TFS;
                  end else if (sr_req) begin
                     hs_segments <= hs_segments + 1'b1;
                     state       <= S_HS_SR;    // stays in Hs-mode
                  end else if (ack_phase) begin
                     cs_enable   <= 1'b0;
                     cs_disables <= cs_disables + 1'b1;
                     state       <= S_HS_HOLD;
                  end
               end

               // ---------------------------------------------------------------
               // tFS. The current source is already off. The device must be back
               // in its Fast-mode configuration within the Fast-mode tBUF.
               // ---------------------------------------------------------------
               S_TFS: begin
                  cs_enable <= 1'b0;
                  tfs_cnt   <= tfs_cnt + 1'b1;
                  if (fs_start_seen && (tfs_cnt + 1'b1 < TBUF_LIM)) begin
                     // A START arrived on the F/S segment before the Fast-mode bus free
                     // time had elapsed. UM10204 5.3.7 puts this obligation on the bridge:
                     // TR3 must open fast enough that tBUF is honoured. Record the actual
                     // gap rather than the nominal one.
                     revert_late  <= 1'b1;
                     is_hs        <= 1'b0;
                     revert_done  <= 1'b1;
                     revert_ticks <= tfs_cnt + 1'b1;
                     state        <= S_FS_IDLE;
                  end else if (tfs_cnt + 1'b1 >= TBUF_LIM) begin
                     is_hs        <= 1'b0;
                     revert_done  <= 1'b1;
                     revert_ticks <= tfs_cnt + 1'b1;
                     state        <= S_FS_IDLE;
                  end
               end

               // ---------------------------------------------------------------
               // Lost arbitration. A losing master adapts its input filters and
               // then does exactly one thing: waits for a STOP. It must never
               // enable its current source.
               // ---------------------------------------------------------------
               S_LOST: begin
                  cs_enable     <= 1'b0;
                  is_hs         <= 1'b0;
                  sda_drive_low <= 1'b0;
                  if (stop_req) state <= S_FS_IDLE;
               end

               default: state <= S_FS_IDLE;
            endcase
         end
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_sequencer_tb.sv — eleven scenarios, including the obligation a naive implementation always misses
   `timescale 1ns/1ps
   // -----------------------------------------------------------------------------
   // i2c_hs_sequencer_tb.sv
   // Independent oracle for i2c_hs_sequencer.
   //
   // The bench models the OTHER devices on the bus -- a competing master and a
   // stretching slave -- and drives the wired-AND itself, so the DUT is judged
   // against a line value the bench computed, never against its own idea of it.
   //
   // The obligation the suite exists to pin down is number 6: the current source
   // must come off after the repeated START and after EVERY acknowledge. A master
   // that holds it on still passes a naive functional test, because the data is
   // unaffected; what breaks is the slave's ability to stretch, and that is
   // invisible unless something counts the disable events.
   // -----------------------------------------------------------------------------
   module i2c_hs_sequencer_tb;

      localparam integer TBUF_TICKS = 65;   // Fast-mode tBUF(min) 1.3 us in 20 ns ticks

      localparam [3:0] S_FS_IDLE = 4'd0, S_START  = 4'd1, S_CODE    = 4'd2,
                       S_ACKBIT  = 4'd3, S_WAIT_TH= 4'd4, S_HS_SR   = 4'd5,
                       S_HS_HOLD = 4'd6, S_HS_DATA= 4'd7, S_TFS     = 4'd8,
                       S_LOST    = 4'd9;

      logic        clk = 1'b0;
      logic        rst_n = 1'b0;
      logic        start_req = 1'b0;
      logic [2:0]  master_code = 3'b001;
      logic        sr_req = 1'b0;
      logic        stop_req = 1'b0;
      logic        bit_tick = 1'b0;
      logic        scl_released = 1'b1;
      logic        ack_phase = 1'b0;
      logic        fs_start_seen = 1'b0;

      // The competing master, modelled by the bench.
      logic        rival_active = 1'b0;
      logic [7:0]  rival_byte   = 8'b0000_0111;   // a reserved code 0000 0XXX
      logic        slave_acks_code = 1'b0;        // a device that wrongly ACKs the master code

      logic       sda_drive_low, cs_enable, is_hs, arb_lost, ack_violation;
      logic       revert_done, revert_late;
      logic [3:0] state;
      logic [11:0] stretch_waits, cs_disables, hs_segments, revert_ticks;

      integer errors = 0;
      integer n;
      logic [2:0] dut_bit_idx;

      // ------------------------------------------------------------------
      // The wired-AND. Whoever pulls LOW wins; the line is HIGH only if nobody
      // is pulling. The bench owns this, not the DUT.
      // ------------------------------------------------------------------
      logic  ack_low   = 1'b0;
      // A competing master drives during the code byte and releases for the
      // acknowledge slot, so the rival is gated on S_CODE. The separate ack_low
      // signal models a device that wrongly drives the acknowledge bit itself.
      wire rival_low = rival_active && (state == S_CODE) && !rival_byte[dut_bit_idx];
      wire sda_line  = !(sda_drive_low || rival_low || ack_low);

      i2c_hs_sequencer #(.TBUF_TICKS(TBUF_TICKS), .CNT_W(12)) dut (
         .clk(clk), .rst_n(rst_n),
         .start_req(start_req), .master_code(master_code),
         .sr_req(sr_req), .stop_req(stop_req),
         .bit_tick(bit_tick), .sda_line(sda_line),
         .scl_released(scl_released), .ack_phase(ack_phase),
         .fs_start_seen(fs_start_seen),
         .sda_drive_low(sda_drive_low), .cs_enable(cs_enable), .is_hs(is_hs),
         .arb_lost(arb_lost), .ack_violation(ack_violation),
         .revert_done(revert_done), .revert_late(revert_late),
         .state(state), .stretch_waits(stretch_waits), .cs_disables(cs_disables),
         .hs_segments(hs_segments), .revert_ticks(revert_ticks));

      always #10 clk = ~clk;                 // 20 ns tick
      always @(*) dut_bit_idx = dut.bit_idx;  // the bench tracks which bit is on the wire

      // Every check reads registered outputs, so step must return at a point where the
      // non-blocking updates from the posedge have already settled: hence the negedge.
      task step;  begin @(posedge clk); @(negedge clk); end endtask

      task pulse_bit;   // one F/S bit cell
         begin @(negedge clk); bit_tick = 1'b1; @(posedge clk); @(negedge clk); bit_tick = 1'b0; end
      endtask

      task pulse_ack;
         begin @(negedge clk); ack_phase = 1'b1; @(posedge clk); @(negedge clk); ack_phase = 1'b0; end
      endtask

      task do_reset;
         begin
            @(negedge clk);
            rst_n = 1'b0; start_req = 1'b0; sr_req = 1'b0; stop_req = 1'b0;
            bit_tick = 1'b0; ack_phase = 1'b0; scl_released = 1'b1;
            rival_active = 1'b0; ack_low = 1'b0; fs_start_seen = 1'b0;
            repeat (3) @(posedge clk);
            @(negedge clk); rst_n = 1'b1;
            @(posedge clk);
         end
      endtask

      task ck_int (input [200*8:1] what, input integer got, input integer exp);
         begin
            if (got !== exp) begin
               $display("  FAIL %0s: got %0d expected %0d", what, got, exp);
               errors = errors + 1;
            end
         end
      endtask

      task ck_bit (input [200*8:1] what, input got, input exp);
         begin
            if (got !== exp) begin
               $display("  FAIL %0s: got %0b expected %0b", what, got, exp);
               errors = errors + 1;
            end
         end
      endtask

      // Drive S + the 8 code bits + the ninth bit.
      task send_preamble;
         begin
            @(negedge clk); start_req = 1'b1;
            @(posedge clk);
            @(negedge clk); start_req = 1'b0;
            pulse_bit;                       // leaves S_START -> S_CODE
            for (n = 0; n < 8; n = n + 1) pulse_bit;   // the 8 code bits
            pulse_bit;                       // the ninth (not-acknowledge) bit
         end
      endtask

      initial begin
         $display("=== i2c_hs_sequencer: Hs-mode entry and exit obligations ===");

         // ----------------------------------------------------------------
         // T1. A clean entry. Nobody competes, nobody stretches. The current
         //     source must be OFF for the whole F/S preamble and come on only
         //     at tH.
         // ----------------------------------------------------------------
         do_reset;
         ck_bit("T1 cs off at idle", cs_enable, 1'b0);
         @(negedge clk); start_req = 1'b1; @(posedge clk); @(negedge clk); start_req = 1'b0;
         pulse_bit;
         ck_int("T1 in S_CODE", state, S_CODE);
         ck_bit("T1 cs still off during the master code", cs_enable, 1'b0);
         ck_bit("T1 not yet in Hs", is_hs, 1'b0);
         for (n = 0; n < 8; n = n + 1) pulse_bit;
         ck_int("T1 in S_ACKBIT", state, S_ACKBIT);
         ck_bit("T1 cs still off at the ninth bit", cs_enable, 1'b0);
         pulse_bit;                            // ninth bit reads HIGH: a proper NACK
         $display("T1  clean Hs-mode entry");
         ck_bit("T1 no ack violation", ack_violation, 1'b0);
         ck_bit("T1 no arbitration loss", arb_lost, 1'b0);
         step;                                 // S_WAIT_TH sees scl_released -> tH
         ck_bit("T1 cs ON at tH", cs_enable, 1'b1);
         ck_bit("T1 is_hs at tH", is_hs, 1'b1);
         ck_int("T1 one Hs segment", hs_segments, 1);

         // ----------------------------------------------------------------
         // T2. Obligation 6 and 7 across a repeated START. Entering Hs the
         //     sequencer passes through S_HS_SR, which must drop the current
         //     source, and must not restore it until scl_released.
         // ----------------------------------------------------------------
         @(negedge clk); scl_released = 1'b0;  // a slave holds SCLH LOW
         step;
         ck_int("T2 in S_HS_SR or S_HS_HOLD", (state == S_HS_SR || state == S_HS_HOLD), 1);
         step;
         ck_bit("T2 cs OFF after the repeated START", cs_enable, 1'b0);
         ck_int("T2 one disable event", cs_disables, 1);
         step; step;
         ck_bit("T2 cs still OFF while the slave stretches", cs_enable, 1'b0);
         @(negedge clk); scl_released = 1'b1;
         step;
         $display("T2  current source off across Sr, restored on release");
         ck_bit("T2 cs back ON after release", cs_enable, 1'b1);
         ck_int("T2 now in S_HS_DATA", state, S_HS_DATA);
         if (stretch_waits == 0) begin
            $display("  FAIL T2 stretch_waits: expected a nonzero wait count");
            errors = errors + 1;
         end

         // ----------------------------------------------------------------
         // T3. THE OBLIGATION THAT IS USUALLY MISSED. Every acknowledge bit
         //     takes the current source off. Three acknowledges in a row must
         //     produce three more disable events, and each must wait for a
         //     release before the source comes back.
         // ----------------------------------------------------------------
         for (n = 0; n < 3; n = n + 1) begin
            @(negedge clk); scl_released = 1'b0;
            pulse_ack;
            step;
            ck_bit("T3 cs OFF after an acknowledge", cs_enable, 1'b0);
            @(negedge clk); scl_released = 1'b1;
            step; step;
            ck_bit("T3 cs ON again after release", cs_enable, 1'b1);
         end
         $display("T3  current source cycles off and on at every acknowledge");
         ck_int("T3 four disable events in total", cs_disables, 4);

         // ----------------------------------------------------------------
         // T4. Hs-mode survives a repeated START. Only a STOP leaves it.
         // ----------------------------------------------------------------
         @(negedge clk); sr_req = 1'b1; @(posedge clk); @(negedge clk); sr_req = 1'b0;
         step; step; step;
         $display("T4  a repeated START keeps the bus in Hs-mode");
         ck_bit("T4 still in Hs", is_hs, 1'b1);
         ck_int("T4 two Hs segments", hs_segments, 2);
         ck_int("T4 five disable events", cs_disables, 5);

         // ----------------------------------------------------------------
         // T5. tFS and the revert. The current source goes off at once; the
         //     Fast-mode configuration must be restored within tBUF.
         // ----------------------------------------------------------------
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         step;
         ck_bit("T5 cs OFF at tFS", cs_enable, 1'b0);
         ck_int("T5 in S_TFS", state, S_TFS);
         ck_bit("T5 revert not yet done", revert_done, 1'b0);
         for (n = 0; n < TBUF_TICKS + 3; n = n + 1) step;
         $display("T5  revert to Fast-mode completes inside tBUF");
         ck_bit("T5 revert done", revert_done, 1'b1);
         ck_bit("T5 revert not late", revert_late, 1'b0);
         ck_bit("T5 out of Hs", is_hs, 1'b0);
         ck_int("T5 revert took tBUF ticks", revert_ticks, TBUF_TICKS);
         ck_int("T5 back at F/S idle", state, S_FS_IDLE);

         // ----------------------------------------------------------------
         // T6. LOSING ARBITRATION TO A RESERVED CODE. The rival sends
         //     0000 0111. Our code is 0000 1001. They agree on b7..b4 and
         //     diverge at b3, the fifth bit sent, where the rival drives LOW.
         //     We must lose there, stop driving, and never enable the current
         //     source. The bridge stays closed and the transfer stays F/S.
         // ----------------------------------------------------------------
         do_reset;
         master_code  = 3'b001;               // our code byte = 0000 1001
         rival_byte   = 8'b0000_0111;         // reserved code 0000 0111
         @(negedge clk); rival_active = 1'b1;
         @(negedge clk); start_req = 1'b1; @(posedge clk); @(negedge clk); start_req = 1'b0;
         pulse_bit;                           // -> S_CODE, bit_idx = 7
         // b7, b6, b5, b4 all agree (both 0): no loss yet.
         for (n = 0; n < 4; n = n + 1) begin
            pulse_bit;
            ck_bit("T6 no loss on the agreeing bits", arb_lost, 1'b0);
         end
         // b3: we drive 1, the rival drives 0. The line reads 0 and we lose.
         pulse_bit;
         $display("T6  lost arbitration at b3 to a reserved 0000 0XXX code");
         ck_bit("T6 arbitration lost", arb_lost, 1'b1);
         ck_int("T6 in S_LOST", state, S_LOST);
         ck_bit("T6 released SDA", sda_drive_low, 1'b0);
         ck_bit("T6 current source never enabled", cs_enable, 1'b0);
         ck_bit("T6 never entered Hs", is_hs, 1'b0);
         ck_int("T6 no Hs segment was opened", hs_segments, 0);
         // A losing master does exactly one thing: waits for a STOP.
         for (n = 0; n < 20; n = n + 1) begin
            step;
            ck_bit("T6 still waiting, cs off", cs_enable, 1'b0);
         end
         ck_int("T6 still in S_LOST", state, S_LOST);
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         step;
         ck_int("T6 STOP frees the bus", state, S_FS_IDLE);

         // ----------------------------------------------------------------
         // T7. WINNING against a rival whose code diverges upward. Our code
         //     0000 1001 vs a rival 0000 1101: they agree through b3 and
         //     diverge at b2, where WE drive 0 and they drive 1. We win and
         //     must proceed to Hs-mode normally.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b001;                // 0000 1001
         rival_byte  = 8'b0000_1101;          // 0000 1101
         @(negedge clk); rival_active = 1'b1;
         send_preamble;
         $display("T7  won arbitration at b2, entry proceeds");
         ck_bit("T7 did not lose", arb_lost, 1'b0);
         ck_bit("T7 no ack violation", ack_violation, 1'b0);
         step;
         ck_bit("T7 cs ON at tH", cs_enable, 1'b1);
         ck_bit("T7 in Hs", is_hs, 1'b1);

         // ----------------------------------------------------------------
         // T8. A DEVICE ACKNOWLEDGES THE MASTER CODE. No device is allowed to,
         //     so the ninth bit reading LOW is a protocol violation. The
         //     sequencer must report it and must NOT enter Hs-mode: entering
         //     would switch the electrical regime on a bus that just proved it
         //     is not behaving to spec.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b010;
         @(negedge clk); start_req = 1'b1; @(posedge clk); @(negedge clk); start_req = 1'b0;
         pulse_bit;
         for (n = 0; n < 8; n = n + 1) pulse_bit;
         ck_int("T8 at the ninth bit", state, S_ACKBIT);
         @(negedge clk); ack_low = 1'b1;      // a device pulls the acknowledge slot LOW
         pulse_bit;
         $display("T8  a device acknowledged the master code");
         ck_bit("T8 ack violation reported", ack_violation, 1'b1);
         ck_bit("T8 did not enter Hs", is_hs, 1'b0);
         ck_bit("T8 current source never enabled", cs_enable, 1'b0);
         ck_int("T8 no Hs segment", hs_segments, 0);
         @(negedge clk); ack_low = 1'b0;

         // ----------------------------------------------------------------
         // T9. A START on the F/S segment arriving inside tBUF. UM10204 5.3.7
         //     requires the gap between the STOP and the next START to satisfy
         //     the Fast-mode tBUF; a START at tick 20 of 65 violates it and the
         //     actual gap, not the nominal one, must be reported.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b011;
         send_preamble;
         step;
         ck_bit("T9 in Hs before the STOP", is_hs, 1'b1);
         step; step;                          // tH -> Sr -> release -> S_HS_DATA
         ck_int("T9 reached the Hs data phase", state, S_HS_DATA);
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         step;
         ck_int("T9 in S_TFS", state, S_TFS);
         for (n = 0; n < 18; n = n + 1) step;
         @(negedge clk); fs_start_seen = 1'b1;
         step;
         @(negedge clk); fs_start_seen = 1'b0;
         $display("T9  a START inside tBUF is reported as a late revert");
         ck_bit("T9 revert flagged late", revert_late, 1'b1);
         ck_bit("T9 revert still completed", revert_done, 1'b1);
         ck_bit("T9 left Hs", is_hs, 1'b0);
         if (revert_ticks >= TBUF_TICKS) begin
            $display("  FAIL T9 revert_ticks: got %0d, expected the actual short gap (< %0d)",
                     revert_ticks, TBUF_TICKS);
            errors = errors + 1;
         end

         // ----------------------------------------------------------------
         // T10. A START arriving AFTER tBUF is not a violation. Same stimulus
         //      as T9, later.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b100;
         send_preamble;
         step; step; step;                    // reach the Hs data phase
         ck_int("T10 reached the Hs data phase", state, S_HS_DATA);
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         for (n = 0; n < TBUF_TICKS + 5; n = n + 1) step;
         @(negedge clk); fs_start_seen = 1'b1;
         step;
         @(negedge clk); fs_start_seen = 1'b0;
         $display("T10 a START after tBUF is legal");
         ck_bit("T10 revert not late", revert_late, 1'b0);
         ck_bit("T10 revert done", revert_done, 1'b1);
         ck_int("T10 revert took the full tBUF", revert_ticks, TBUF_TICKS);

         // ----------------------------------------------------------------
         // T11. Stretching BEFORE tH. Between the not-acknowledge and tH any
         //      device may hold SCLH LOW, and the master must wait rather than
         //      assume. The current source must stay off for the whole wait.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b101;
         @(negedge clk); scl_released = 1'b0;   // stretch from the outset
         send_preamble;
         ck_int("T11 waiting at tH", state, S_WAIT_TH);
         for (n = 0; n < 12; n = n + 1) begin
            step;
            ck_bit("T11 cs off while stretched before tH", cs_enable, 1'b0);
            ck_bit("T11 not in Hs while stretched", is_hs, 1'b0);
         end
         ck_int("T11 still waiting", state, S_WAIT_TH);
         @(negedge clk); scl_released = 1'b1;
         step;
         $display("T11 the master waits for a release before tH");
         ck_bit("T11 cs ON once released", cs_enable, 1'b1);
         ck_bit("T11 in Hs once released", is_hs, 1'b1);
         if (stretch_waits < 12) begin
            $display("  FAIL T11 stretch_waits: got %0d, expected at least 12", stretch_waits);
            errors = errors + 1;
         end

         if (errors == 0)
            $display("=== i2c_hs_sequencer: ALL CHECKS PASSED ===");
         else
            $display("=== i2c_hs_sequencer: %0d CHECK(S) FAILED ===", errors);
         $finish;
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_sequencer.v — the same sequencer in Verilog-2001
   // -----------------------------------------------------------------------------
   // i2c_hs_sequencer.sv
   // Master-side High-speed mode entry and exit sequencer (UM10204 5.3.1 - 5.3.3).
   //
   // Hs-mode is not a faster I2C. It is a short F/S-mode preamble that hands the
   // bus to a different electrical regime, and this block is the state machine that
   // performs that handover. It encodes nine obligations from the specification:
   //
   //   1. Entry requires exactly S + 8-bit master code (0000 1XXX) + not-acknowledge,
   //      ALL of which happen at F/S-mode speed.
   //   2. No device may acknowledge the master code, so the master expects the ninth
   //      bit to read HIGH. A LOW there is a protocol violation, reported but not
   //      silently ignored.
   //   3. Arbitration and clock synchronization happen ONLY during the master code
   //      and the not-acknowledge bit. Once the not-acknowledge is past, one winning
   //      master remains and no further arbitration occurs.
   //   4. A competitor sending a reserved code 0000 0XXX wins, because the two codes
   //      agree on the first four bits and diverge at b3 -- the fifth bit sent --
   //      where the reserved code drives LOW. The loser must not enter Hs-mode; it
   //      waits for a STOP to learn the bus is free again.
   //   5. At tH -- after the not-acknowledge AND once every device has released SCLH
   //      so the line is genuinely HIGH -- the winner enables its SCLH current
   //      source and switches to the Hs bit rate. Any device may stretch before tH.
   //   6. After the repeated START, and after EVERY acknowledge or not-acknowledge
   //      bit, the active master DISABLES the current source. That is what makes a
   //      slave's clock stretch physically possible at those points, and it is why
   //      Table 12 carries a separate trCL1 whose limit is double trCL.
   //   7. It re-enables the current source only once all devices have released and
   //      SCLH has reached HIGH again.
   //   8. Hs-mode survives repeated STARTs. Only a STOP returns the bus to F/S-mode.
   //   9. At tFS the current source goes off and the device must be back in its
   //      Fast-mode configuration within the Fast-mode tBUF, i.e. 1.3 us.
   //
   // Obligation 6 is the one that is almost always missed in a first implementation:
   // a master that leaves its current source on across an acknowledge has taken away
   // the slave's ability to stretch, and nothing on the wire will tell it so.
   // -----------------------------------------------------------------------------

   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   module i2c_hs_sequencer #(
      // Fast-mode tBUF(min) = 1.3 us. In 20 ns ticks that is 65.
      parameter TBUF_TICKS = 65,
      parameter CNT_W      = 12
   ) (
      input  wire             clk,
      input  wire             rst_n,

      // ---- command interface -------------------------------------------------
      input  wire             start_req,     // begin an Hs transfer (issue S + master code)
      input  wire [2:0]       master_code,   // the XXX of 0000 1XXX
      input  wire             sr_req,        // repeated START: continue in Hs-mode
      input  wire             stop_req,      // STOP: leave Hs-mode

      // ---- bus interface -----------------------------------------------------
      input  wire             bit_tick,      // one pulse per bit cell (F/S rate during the code)
      input  wire             sda_line,      // the wired-AND SDA value, sampled at SCL HIGH
      input  wire             scl_released,  // every device has released SCLH and it reads HIGH
      input  wire             ack_phase,     // pulses at each ACK/NACK bit boundary in Hs-mode
      input  wire             fs_start_seen, // a START observed on the F/S segment after tFS

      // ---- outputs -----------------------------------------------------------
      output reg              sda_drive_low, // what this master drives during the master code
      output reg              cs_enable,     // SCLH current-source pull-up enabled
      output reg              is_hs,         // operating in the Hs-mode electrical regime
      output reg              arb_lost,
      output reg              ack_violation, // a device acknowledged the master code
      output reg              revert_done,   // back in Fast-mode configuration
      output reg              revert_late,   // a START arrived before the Fast-mode tBUF elapsed

      output reg [3:0]        state,
      output reg [CNT_W-1:0]  stretch_waits, // times the sequencer had to wait for a release
      output reg [CNT_W-1:0]  cs_disables,   // current-source disable events
      output reg [CNT_W-1:0]  hs_segments,   // Sr-separated Hs segments in this burst
      output reg [CNT_W-1:0]  revert_ticks   // ticks taken from tFS to revert_done
   );

      localparam [3:0] S_FS_IDLE = 4'd0,  // F/S-mode, current source off
                       S_START   = 4'd1,  // START condition, at F/S speed
                       S_CODE    = 4'd2,  // 8-bit master code, arbitration live
                       S_ACKBIT  = 4'd3,  // ninth bit: must read HIGH (not-acknowledge)
                       S_WAIT_TH = 4'd4,  // waiting for SCLH release -> tH
                       S_HS_SR   = 4'd5,  // repeated START in Hs-mode, current source off
                       S_HS_HOLD = 4'd6,  // current source off, waiting for a release
                       S_HS_DATA = 4'd7,  // Hs data, current source on
                       S_TFS     = 4'd8,  // STOP seen, reverting to Fast-mode
                       S_LOST    = 4'd9;  // lost arbitration, waiting for the bus to free

      // Sized constant. A part-select of a parameter (TBUF_TICKS[CNT_W-1:0]) is read as
      // zero by some tools, which would make the revert window collapse silently.
      localparam [CNT_W-1:0] TBUF_LIM = TBUF_TICKS;

      // The master code as an 8-bit first byte: b7..b4 = 0000, b3 = 1, b2..b0 = code.
      wire [7:0] code_byte = {4'b0000, 1'b1, master_code};

      reg [2:0]        bit_idx;      // 7 downto 0, MSB first
      reg              code_bit;
      reg [CNT_W-1:0]  tfs_cnt;

      always @(posedge clk or negedge rst_n) begin
         if (!rst_n) begin
            state         <= S_FS_IDLE;
            sda_drive_low <= 1'b0;
            cs_enable     <= 1'b0;
            is_hs         <= 1'b0;
            arb_lost      <= 1'b0;
            ack_violation <= 1'b0;
            revert_done   <= 1'b0;
            revert_late   <= 1'b0;
            bit_idx       <= 3'd7;
            code_bit      <= 1'b1;
            stretch_waits <= {CNT_W{1'b0}};
            cs_disables   <= {CNT_W{1'b0}};
            hs_segments   <= {CNT_W{1'b0}};
            revert_ticks  <= {CNT_W{1'b0}};
            tfs_cnt       <= {CNT_W{1'b0}};
         end else begin
            case (state)

               // ---------------------------------------------------------------
               // F/S-mode. The current source is off and stays off: only the
               // active master in Hs-mode may ever enable it.
               // ---------------------------------------------------------------
               S_FS_IDLE: begin
                  cs_enable     <= 1'b0;
                  is_hs         <= 1'b0;
                  sda_drive_low <= 1'b0;
                  if (start_req) begin
                     arb_lost      <= 1'b0;
                     ack_violation <= 1'b0;
                     revert_done   <= 1'b0;
                     revert_late   <= 1'b0;
                     hs_segments   <= {CNT_W{1'b0}};
                     state         <= S_START;
                  end
               end

               // START condition, generated at F/S-mode speed.
               S_START: begin
                  sda_drive_low <= 1'b1;         // SDA pulled LOW while SCL is HIGH
                  if (bit_tick) begin
                     bit_idx  <= 3'd7;
                     code_bit <= code_byte[7];
                     state    <= S_CODE;
                  end
               end

               // ---------------------------------------------------------------
               // The 8-bit master code, sent MSB first, with arbitration live on
               // every bit. Obligation 4: a master that drove HIGH and reads LOW
               // has lost, and must drop off immediately.
               // ---------------------------------------------------------------
               S_CODE: begin
                  sda_drive_low <= !code_byte[bit_idx];
                  if (bit_tick) begin
                     if (code_byte[bit_idx] && !sda_line) begin
                        // Drove a 1, the line came back 0: someone else is driving
                        // LOW here. Conclusive loss.
                        arb_lost      <= 1'b1;
                        sda_drive_low <= 1'b0;
                        cs_enable     <= 1'b0;
                        state         <= S_LOST;
                     end else if (bit_idx == 3'd0) begin
                        state <= S_ACKBIT;
                     end else begin
                        bit_idx  <= bit_idx - 1'b1;
                        code_bit <= code_byte[bit_idx - 1'b1];
                     end
                  end
               end

               // ---------------------------------------------------------------
               // The ninth bit. No device may acknowledge the master code, so the
               // line must read HIGH. Arbitration is still live on this bit; after
               // it, one winning master remains and arbitration is over.
               // ---------------------------------------------------------------
               S_ACKBIT: begin
                  sda_drive_low <= 1'b0;         // release so the line can float HIGH
                  if (bit_tick) begin
                     if (!sda_line) begin
                        // Someone drove the acknowledge slot LOW. Either a device
                        // acknowledged the master code, which is forbidden, or a
                        // competing master is still driving. Report and abandon.
                        ack_violation <= 1'b1;
                        arb_lost      <= 1'b1;
                        cs_enable     <= 1'b0;
                        state         <= S_LOST;
                     end else begin
                        state <= S_WAIT_TH;
                     end
                  end
               end

               // ---------------------------------------------------------------
               // Between the not-acknowledge and tH. Any device may stretch here,
               // so the master waits for a genuine release rather than assuming
               // the line is HIGH. tH is the instant the current source comes on.
               // ---------------------------------------------------------------
               S_WAIT_TH: begin
                  if (scl_released) begin
                     cs_enable   <= 1'b1;       // tH
                     is_hs       <= 1'b1;
                     hs_segments <= hs_segments + 1'b1;
                     state       <= S_HS_SR;
                  end else begin
                     stretch_waits <= stretch_waits + 1'b1;
                  end
               end

               // ---------------------------------------------------------------
               // Obligation 6, first half. A repeated START is one of the two
               // points where the current source must come off.
               // ---------------------------------------------------------------
               S_HS_SR: begin
                  cs_enable   <= 1'b0;
                  cs_disables <= cs_disables + 1'b1;
                  state       <= S_HS_HOLD;
               end

               // Obligation 7: re-enable only once everybody has let go and the
               // line has actually reached HIGH. This is the interval whose rise
               // time Table 12 calls trCL1 and allows to be twice as long.
               S_HS_HOLD: begin
                  if (scl_released) begin
                     cs_enable <= 1'b1;
                     state     <= S_HS_DATA;
                  end else begin
                     stretch_waits <= stretch_waits + 1'b1;
                  end
               end

               // ---------------------------------------------------------------
               // Hs data. Obligation 6, second half: every acknowledge bit takes
               // the current source off again.
               //
               // stop_req and sr_req are accepted only here, by design. A master
               // does not issue a STOP or a repeated START while it is waiting for
               // another device to release the clock -- it has not finished the
               // current bit yet. Requests raised in S_HS_SR or S_HS_HOLD are
               // therefore ignored rather than queued.
               // ---------------------------------------------------------------
               S_HS_DATA: begin
                  if (stop_req) begin
                     cs_enable    <= 1'b0;      // tFS
                     tfs_cnt      <= {CNT_W{1'b0}};
                     revert_ticks <= {CNT_W{1'b0}};
                     state        <= S_TFS;
                  end else if (sr_req) begin
                     hs_segments <= hs_segments + 1'b1;
                     state       <= S_HS_SR;    // stays in Hs-mode
                  end else if (ack_phase) begin
                     cs_enable   <= 1'b0;
                     cs_disables <= cs_disables + 1'b1;
                     state       <= S_HS_HOLD;
                  end
               end

               // ---------------------------------------------------------------
               // tFS. The current source is already off. The device must be back
               // in its Fast-mode configuration within the Fast-mode tBUF.
               // ---------------------------------------------------------------
               S_TFS: begin
                  cs_enable <= 1'b0;
                  tfs_cnt   <= tfs_cnt + 1'b1;
                  if (fs_start_seen && (tfs_cnt + 1'b1 < TBUF_LIM)) begin
                     // A START arrived on the F/S segment before the Fast-mode bus free
                     // time had elapsed. UM10204 5.3.7 puts this obligation on the bridge:
                     // TR3 must open fast enough that tBUF is honoured. Record the actual
                     // gap rather than the nominal one.
                     revert_late  <= 1'b1;
                     is_hs        <= 1'b0;
                     revert_done  <= 1'b1;
                     revert_ticks <= tfs_cnt + 1'b1;
                     state        <= S_FS_IDLE;
                  end else if (tfs_cnt + 1'b1 >= TBUF_LIM) begin
                     is_hs        <= 1'b0;
                     revert_done  <= 1'b1;
                     revert_ticks <= tfs_cnt + 1'b1;
                     state        <= S_FS_IDLE;
                  end
               end

               // ---------------------------------------------------------------
               // Lost arbitration. A losing master adapts its input filters and
               // then does exactly one thing: waits for a STOP. It must never
               // enable its current source.
               // ---------------------------------------------------------------
               S_LOST: begin
                  cs_enable     <= 1'b0;
                  is_hs         <= 1'b0;
                  sda_drive_low <= 1'b0;
                  if (stop_req) state <= S_FS_IDLE;
               end

               default: state <= S_FS_IDLE;
            endcase
         end
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_sequencer_tb.v — the same eleven scenarios, independently
   `timescale 1ns/1ps
   // -----------------------------------------------------------------------------
   // i2c_hs_sequencer_tb.sv
   // Independent oracle for i2c_hs_sequencer.
   //
   // The bench models the OTHER devices on the bus -- a competing master and a
   // stretching slave -- and drives the wired-AND itself, so the DUT is judged
   // against a line value the bench computed, never against its own idea of it.
   //
   // The obligation the suite exists to pin down is number 6: the current source
   // must come off after the repeated START and after EVERY acknowledge. A master
   // that holds it on still passes a naive functional test, because the data is
   // unaffected; what breaks is the slave's ability to stretch, and that is
   // invisible unless something counts the disable events.
   // -----------------------------------------------------------------------------
   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   module i2c_hs_sequencer_tb;

      localparam integer TBUF_TICKS = 65;   // Fast-mode tBUF(min) 1.3 us in 20 ns ticks

      localparam [3:0] S_FS_IDLE = 4'd0, S_START  = 4'd1, S_CODE    = 4'd2,
                       S_ACKBIT  = 4'd3, S_WAIT_TH= 4'd4, S_HS_SR   = 4'd5,
                       S_HS_HOLD = 4'd6, S_HS_DATA= 4'd7, S_TFS     = 4'd8,
                       S_LOST    = 4'd9;

      reg        clk = 1'b0;
      reg        rst_n = 1'b0;
      reg        start_req = 1'b0;
      reg [2:0]  master_code = 3'b001;
      reg        sr_req = 1'b0;
      reg        stop_req = 1'b0;
      reg        bit_tick = 1'b0;
      reg        scl_released = 1'b1;
      reg        ack_phase = 1'b0;
      reg        fs_start_seen = 1'b0;

      // The competing master, modelled by the bench.
      reg        rival_active = 1'b0;
      reg [7:0]  rival_byte   = 8'b0000_0111;   // a reserved code 0000 0XXX
      reg        slave_acks_code = 1'b0;        // a device that wrongly ACKs the master code

      wire       sda_drive_low, cs_enable, is_hs, arb_lost, ack_violation;
      wire       revert_done, revert_late;
      wire [3:0] state;
      wire [11:0] stretch_waits, cs_disables, hs_segments, revert_ticks;

      integer errors = 0;
      integer n;
      reg [2:0] dut_bit_idx;

      // ------------------------------------------------------------------
      // The wired-AND. Whoever pulls LOW wins; the line is HIGH only if nobody
      // is pulling. The bench owns this, not the DUT.
      // ------------------------------------------------------------------
      reg  ack_low   = 1'b0;
      // A competing master drives during the code byte and releases for the
      // acknowledge slot, so the rival is gated on S_CODE. The separate ack_low
      // signal models a device that wrongly drives the acknowledge bit itself.
      wire rival_low = rival_active && (state == S_CODE) && !rival_byte[dut_bit_idx];
      wire sda_line  = !(sda_drive_low || rival_low || ack_low);

      i2c_hs_sequencer #(.TBUF_TICKS(TBUF_TICKS), .CNT_W(12)) dut (
         .clk(clk), .rst_n(rst_n),
         .start_req(start_req), .master_code(master_code),
         .sr_req(sr_req), .stop_req(stop_req),
         .bit_tick(bit_tick), .sda_line(sda_line),
         .scl_released(scl_released), .ack_phase(ack_phase),
         .fs_start_seen(fs_start_seen),
         .sda_drive_low(sda_drive_low), .cs_enable(cs_enable), .is_hs(is_hs),
         .arb_lost(arb_lost), .ack_violation(ack_violation),
         .revert_done(revert_done), .revert_late(revert_late),
         .state(state), .stretch_waits(stretch_waits), .cs_disables(cs_disables),
         .hs_segments(hs_segments), .revert_ticks(revert_ticks));

      always #10 clk = ~clk;                 // 20 ns tick
      always @(*) dut_bit_idx = dut.bit_idx;  // the bench tracks which bit is on the wire

      // Every check reads registered outputs, so step must return at a point where the
      // non-blocking updates from the posedge have already settled: hence the negedge.
      task step;  begin @(posedge clk); @(negedge clk); end endtask

      task pulse_bit;   // one F/S bit cell
         begin @(negedge clk); bit_tick = 1'b1; @(posedge clk); @(negedge clk); bit_tick = 1'b0; end
      endtask

      task pulse_ack;
         begin @(negedge clk); ack_phase = 1'b1; @(posedge clk); @(negedge clk); ack_phase = 1'b0; end
      endtask

      task do_reset;
         begin
            @(negedge clk);
            rst_n = 1'b0; start_req = 1'b0; sr_req = 1'b0; stop_req = 1'b0;
            bit_tick = 1'b0; ack_phase = 1'b0; scl_released = 1'b1;
            rival_active = 1'b0; ack_low = 1'b0; fs_start_seen = 1'b0;
            repeat (3) @(posedge clk);
            @(negedge clk); rst_n = 1'b1;
            @(posedge clk);
         end
      endtask

      task ck_int (input [200*8:1] what, input integer got, input integer exp);
         begin
            if (got !== exp) begin
               $display("  FAIL %0s: got %0d expected %0d", what, got, exp);
               errors = errors + 1;
            end
         end
      endtask

      task ck_bit (input [200*8:1] what, input got, input exp);
         begin
            if (got !== exp) begin
               $display("  FAIL %0s: got %0b expected %0b", what, got, exp);
               errors = errors + 1;
            end
         end
      endtask

      // Drive S + the 8 code bits + the ninth bit.
      task send_preamble;
         begin
            @(negedge clk); start_req = 1'b1;
            @(posedge clk);
            @(negedge clk); start_req = 1'b0;
            pulse_bit;                       // leaves S_START -> S_CODE
            for (n = 0; n < 8; n = n + 1) pulse_bit;   // the 8 code bits
            pulse_bit;                       // the ninth (not-acknowledge) bit
         end
      endtask

      initial begin
         $display("=== i2c_hs_sequencer: Hs-mode entry and exit obligations ===");

         // ----------------------------------------------------------------
         // T1. A clean entry. Nobody competes, nobody stretches. The current
         //     source must be OFF for the whole F/S preamble and come on only
         //     at tH.
         // ----------------------------------------------------------------
         do_reset;
         ck_bit("T1 cs off at idle", cs_enable, 1'b0);
         @(negedge clk); start_req = 1'b1; @(posedge clk); @(negedge clk); start_req = 1'b0;
         pulse_bit;
         ck_int("T1 in S_CODE", state, S_CODE);
         ck_bit("T1 cs still off during the master code", cs_enable, 1'b0);
         ck_bit("T1 not yet in Hs", is_hs, 1'b0);
         for (n = 0; n < 8; n = n + 1) pulse_bit;
         ck_int("T1 in S_ACKBIT", state, S_ACKBIT);
         ck_bit("T1 cs still off at the ninth bit", cs_enable, 1'b0);
         pulse_bit;                            // ninth bit reads HIGH: a proper NACK
         $display("T1  clean Hs-mode entry");
         ck_bit("T1 no ack violation", ack_violation, 1'b0);
         ck_bit("T1 no arbitration loss", arb_lost, 1'b0);
         step;                                 // S_WAIT_TH sees scl_released -> tH
         ck_bit("T1 cs ON at tH", cs_enable, 1'b1);
         ck_bit("T1 is_hs at tH", is_hs, 1'b1);
         ck_int("T1 one Hs segment", hs_segments, 1);

         // ----------------------------------------------------------------
         // T2. Obligation 6 and 7 across a repeated START. Entering Hs the
         //     sequencer passes through S_HS_SR, which must drop the current
         //     source, and must not restore it until scl_released.
         // ----------------------------------------------------------------
         @(negedge clk); scl_released = 1'b0;  // a slave holds SCLH LOW
         step;
         ck_int("T2 in S_HS_SR or S_HS_HOLD", (state == S_HS_SR || state == S_HS_HOLD), 1);
         step;
         ck_bit("T2 cs OFF after the repeated START", cs_enable, 1'b0);
         ck_int("T2 one disable event", cs_disables, 1);
         step; step;
         ck_bit("T2 cs still OFF while the slave stretches", cs_enable, 1'b0);
         @(negedge clk); scl_released = 1'b1;
         step;
         $display("T2  current source off across Sr, restored on release");
         ck_bit("T2 cs back ON after release", cs_enable, 1'b1);
         ck_int("T2 now in S_HS_DATA", state, S_HS_DATA);
         if (stretch_waits == 0) begin
            $display("  FAIL T2 stretch_waits: expected a nonzero wait count");
            errors = errors + 1;
         end

         // ----------------------------------------------------------------
         // T3. THE OBLIGATION THAT IS USUALLY MISSED. Every acknowledge bit
         //     takes the current source off. Three acknowledges in a row must
         //     produce three more disable events, and each must wait for a
         //     release before the source comes back.
         // ----------------------------------------------------------------
         for (n = 0; n < 3; n = n + 1) begin
            @(negedge clk); scl_released = 1'b0;
            pulse_ack;
            step;
            ck_bit("T3 cs OFF after an acknowledge", cs_enable, 1'b0);
            @(negedge clk); scl_released = 1'b1;
            step; step;
            ck_bit("T3 cs ON again after release", cs_enable, 1'b1);
         end
         $display("T3  current source cycles off and on at every acknowledge");
         ck_int("T3 four disable events in total", cs_disables, 4);

         // ----------------------------------------------------------------
         // T4. Hs-mode survives a repeated START. Only a STOP leaves it.
         // ----------------------------------------------------------------
         @(negedge clk); sr_req = 1'b1; @(posedge clk); @(negedge clk); sr_req = 1'b0;
         step; step; step;
         $display("T4  a repeated START keeps the bus in Hs-mode");
         ck_bit("T4 still in Hs", is_hs, 1'b1);
         ck_int("T4 two Hs segments", hs_segments, 2);
         ck_int("T4 five disable events", cs_disables, 5);

         // ----------------------------------------------------------------
         // T5. tFS and the revert. The current source goes off at once; the
         //     Fast-mode configuration must be restored within tBUF.
         // ----------------------------------------------------------------
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         step;
         ck_bit("T5 cs OFF at tFS", cs_enable, 1'b0);
         ck_int("T5 in S_TFS", state, S_TFS);
         ck_bit("T5 revert not yet done", revert_done, 1'b0);
         for (n = 0; n < TBUF_TICKS + 3; n = n + 1) step;
         $display("T5  revert to Fast-mode completes inside tBUF");
         ck_bit("T5 revert done", revert_done, 1'b1);
         ck_bit("T5 revert not late", revert_late, 1'b0);
         ck_bit("T5 out of Hs", is_hs, 1'b0);
         ck_int("T5 revert took tBUF ticks", revert_ticks, TBUF_TICKS);
         ck_int("T5 back at F/S idle", state, S_FS_IDLE);

         // ----------------------------------------------------------------
         // T6. LOSING ARBITRATION TO A RESERVED CODE. The rival sends
         //     0000 0111. Our code is 0000 1001. They agree on b7..b4 and
         //     diverge at b3, the fifth bit sent, where the rival drives LOW.
         //     We must lose there, stop driving, and never enable the current
         //     source. The bridge stays closed and the transfer stays F/S.
         // ----------------------------------------------------------------
         do_reset;
         master_code  = 3'b001;               // our code byte = 0000 1001
         rival_byte   = 8'b0000_0111;         // reserved code 0000 0111
         @(negedge clk); rival_active = 1'b1;
         @(negedge clk); start_req = 1'b1; @(posedge clk); @(negedge clk); start_req = 1'b0;
         pulse_bit;                           // -> S_CODE, bit_idx = 7
         // b7, b6, b5, b4 all agree (both 0): no loss yet.
         for (n = 0; n < 4; n = n + 1) begin
            pulse_bit;
            ck_bit("T6 no loss on the agreeing bits", arb_lost, 1'b0);
         end
         // b3: we drive 1, the rival drives 0. The line reads 0 and we lose.
         pulse_bit;
         $display("T6  lost arbitration at b3 to a reserved 0000 0XXX code");
         ck_bit("T6 arbitration lost", arb_lost, 1'b1);
         ck_int("T6 in S_LOST", state, S_LOST);
         ck_bit("T6 released SDA", sda_drive_low, 1'b0);
         ck_bit("T6 current source never enabled", cs_enable, 1'b0);
         ck_bit("T6 never entered Hs", is_hs, 1'b0);
         ck_int("T6 no Hs segment was opened", hs_segments, 0);
         // A losing master does exactly one thing: waits for a STOP.
         for (n = 0; n < 20; n = n + 1) begin
            step;
            ck_bit("T6 still waiting, cs off", cs_enable, 1'b0);
         end
         ck_int("T6 still in S_LOST", state, S_LOST);
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         step;
         ck_int("T6 STOP frees the bus", state, S_FS_IDLE);

         // ----------------------------------------------------------------
         // T7. WINNING against a rival whose code diverges upward. Our code
         //     0000 1001 vs a rival 0000 1101: they agree through b3 and
         //     diverge at b2, where WE drive 0 and they drive 1. We win and
         //     must proceed to Hs-mode normally.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b001;                // 0000 1001
         rival_byte  = 8'b0000_1101;          // 0000 1101
         @(negedge clk); rival_active = 1'b1;
         send_preamble;
         $display("T7  won arbitration at b2, entry proceeds");
         ck_bit("T7 did not lose", arb_lost, 1'b0);
         ck_bit("T7 no ack violation", ack_violation, 1'b0);
         step;
         ck_bit("T7 cs ON at tH", cs_enable, 1'b1);
         ck_bit("T7 in Hs", is_hs, 1'b1);

         // ----------------------------------------------------------------
         // T8. A DEVICE ACKNOWLEDGES THE MASTER CODE. No device is allowed to,
         //     so the ninth bit reading LOW is a protocol violation. The
         //     sequencer must report it and must NOT enter Hs-mode: entering
         //     would switch the electrical regime on a bus that just proved it
         //     is not behaving to spec.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b010;
         @(negedge clk); start_req = 1'b1; @(posedge clk); @(negedge clk); start_req = 1'b0;
         pulse_bit;
         for (n = 0; n < 8; n = n + 1) pulse_bit;
         ck_int("T8 at the ninth bit", state, S_ACKBIT);
         @(negedge clk); ack_low = 1'b1;      // a device pulls the acknowledge slot LOW
         pulse_bit;
         $display("T8  a device acknowledged the master code");
         ck_bit("T8 ack violation reported", ack_violation, 1'b1);
         ck_bit("T8 did not enter Hs", is_hs, 1'b0);
         ck_bit("T8 current source never enabled", cs_enable, 1'b0);
         ck_int("T8 no Hs segment", hs_segments, 0);
         @(negedge clk); ack_low = 1'b0;

         // ----------------------------------------------------------------
         // T9. A START on the F/S segment arriving inside tBUF. UM10204 5.3.7
         //     requires the gap between the STOP and the next START to satisfy
         //     the Fast-mode tBUF; a START at tick 20 of 65 violates it and the
         //     actual gap, not the nominal one, must be reported.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b011;
         send_preamble;
         step;
         ck_bit("T9 in Hs before the STOP", is_hs, 1'b1);
         step; step;                          // tH -> Sr -> release -> S_HS_DATA
         ck_int("T9 reached the Hs data phase", state, S_HS_DATA);
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         step;
         ck_int("T9 in S_TFS", state, S_TFS);
         for (n = 0; n < 18; n = n + 1) step;
         @(negedge clk); fs_start_seen = 1'b1;
         step;
         @(negedge clk); fs_start_seen = 1'b0;
         $display("T9  a START inside tBUF is reported as a late revert");
         ck_bit("T9 revert flagged late", revert_late, 1'b1);
         ck_bit("T9 revert still completed", revert_done, 1'b1);
         ck_bit("T9 left Hs", is_hs, 1'b0);
         if (revert_ticks >= TBUF_TICKS) begin
            $display("  FAIL T9 revert_ticks: got %0d, expected the actual short gap (< %0d)",
                     revert_ticks, TBUF_TICKS);
            errors = errors + 1;
         end

         // ----------------------------------------------------------------
         // T10. A START arriving AFTER tBUF is not a violation. Same stimulus
         //      as T9, later.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b100;
         send_preamble;
         step; step; step;                    // reach the Hs data phase
         ck_int("T10 reached the Hs data phase", state, S_HS_DATA);
         @(negedge clk); stop_req = 1'b1; @(posedge clk); @(negedge clk); stop_req = 1'b0;
         for (n = 0; n < TBUF_TICKS + 5; n = n + 1) step;
         @(negedge clk); fs_start_seen = 1'b1;
         step;
         @(negedge clk); fs_start_seen = 1'b0;
         $display("T10 a START after tBUF is legal");
         ck_bit("T10 revert not late", revert_late, 1'b0);
         ck_bit("T10 revert done", revert_done, 1'b1);
         ck_int("T10 revert took the full tBUF", revert_ticks, TBUF_TICKS);

         // ----------------------------------------------------------------
         // T11. Stretching BEFORE tH. Between the not-acknowledge and tH any
         //      device may hold SCLH LOW, and the master must wait rather than
         //      assume. The current source must stay off for the whole wait.
         // ----------------------------------------------------------------
         do_reset;
         master_code = 3'b101;
         @(negedge clk); scl_released = 1'b0;   // stretch from the outset
         send_preamble;
         ck_int("T11 waiting at tH", state, S_WAIT_TH);
         for (n = 0; n < 12; n = n + 1) begin
            step;
            ck_bit("T11 cs off while stretched before tH", cs_enable, 1'b0);
            ck_bit("T11 not in Hs while stretched", is_hs, 1'b0);
         end
         ck_int("T11 still waiting", state, S_WAIT_TH);
         @(negedge clk); scl_released = 1'b1;
         step;
         $display("T11 the master waits for a release before tH");
         ck_bit("T11 cs ON once released", cs_enable, 1'b1);
         ck_bit("T11 in Hs once released", is_hs, 1'b1);
         if (stretch_waits < 12) begin
            $display("  FAIL T11 stretch_waits: got %0d, expected at least 12", stretch_waits);
            errors = errors + 1;
         end

         if (errors == 0)
            $display("=== i2c_hs_sequencer: ALL CHECKS PASSED ===");
         else
            $display("=== i2c_hs_sequencer: %0d CHECK(S) FAILED ===", errors);
         $finish;
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_sequencer.vhd — the same sequencer in VHDL-2008
   -- ---------------------------------------------------------------------------
   -- i2c_hs_sequencer.vhd
   -- Master-side High-speed mode entry and exit sequencer (UM10204 5.3.1 - 5.3.3).
   -- Behavioural twin of i2c_hs_sequencer.sv / .v.
   --
   -- Hs-mode is not a faster I2C. It is a short F/S-mode preamble that hands the
   -- bus to a different electrical regime, and this block is the state machine that
   -- performs that handover. It encodes nine obligations from the specification:
   --
   --   1. Entry requires exactly S + 8-bit master code (0000 1XXX) + not-acknowledge,
   --      ALL of which happen at F/S-mode speed.
   --   2. No device may acknowledge the master code, so the master expects the ninth
   --      bit to read HIGH. A LOW there is a protocol violation, reported.
   --   3. Arbitration and clock synchronization happen ONLY during the master code
   --      and the not-acknowledge bit.
   --   4. A competitor sending a reserved code 0000 0XXX wins, because the two codes
   --      agree on the first four bits and diverge at b3 -- the fifth bit sent --
   --      where the reserved code drives LOW. The loser must not enter Hs-mode.
   --   5. At tH -- after the not-acknowledge AND once every device has released SCLH
   --      so the line is genuinely HIGH -- the winner enables its SCLH current
   --      source and switches to the Hs bit rate. Any device may stretch before tH.
   --   6. After the repeated START, and after EVERY acknowledge or not-acknowledge
   --      bit, the active master DISABLES the current source. That is what makes a
   --      slave's clock stretch physically possible at those points, and it is why
   --      Table 12 carries a separate trCL1 whose limit is double trCL.
   --   7. It re-enables the current source only once all devices have released and
   --      SCLH has reached HIGH again.
   --   8. Hs-mode survives repeated STARTs. Only a STOP returns the bus to F/S-mode.
   --   9. At tFS the current source goes off and the device must be back in its
   --      Fast-mode configuration within the Fast-mode tBUF, i.e. 1.3 us.
   --
   -- Obligation 6 is the one that is almost always missed in a first implementation:
   -- a master that leaves its current source on across an acknowledge has taken away
   -- the slave's ability to stretch, and nothing on the wire will tell it so.
   -- ---------------------------------------------------------------------------

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

   entity i2c_hs_sequencer is
      generic (
         -- Fast-mode tBUF(min) = 1.3 us. In 20 ns ticks that is 65.
         TBUF_TICKS : integer := 65;
         CNT_W      : integer := 12
      );
      port (
         clk   : in std_logic;
         rst_n : in std_logic;

         -- command interface
         start_req   : in std_logic;                     -- begin an Hs transfer
         master_code : in std_logic_vector(2 downto 0);  -- the XXX of 0000 1XXX
         sr_req      : in std_logic;                     -- repeated START, stay in Hs
         stop_req    : in std_logic;                     -- STOP, leave Hs-mode

         -- bus interface
         bit_tick      : in std_logic;   -- one pulse per bit cell at the F/S rate
         sda_line      : in std_logic;   -- the wired-AND SDA value
         scl_released  : in std_logic;   -- everyone released SCLH and it reads HIGH
         ack_phase     : in std_logic;   -- pulses at each ACK/NACK boundary in Hs-mode
         fs_start_seen : in std_logic;   -- a START observed on the F/S segment after tFS

         -- outputs
         sda_drive_low : out std_logic;  -- what this master drives during the code
         cs_enable     : out std_logic;  -- SCLH current-source pull-up enabled
         is_hs         : out std_logic;  -- operating in the Hs-mode electrical regime
         arb_lost      : out std_logic;
         ack_violation : out std_logic;  -- a device acknowledged the master code
         revert_done   : out std_logic;
         revert_late   : out std_logic;  -- a START arrived before the Fast-mode tBUF

         state         : out unsigned(3 downto 0);
         stretch_waits : out unsigned(CNT_W-1 downto 0);
         cs_disables   : out unsigned(CNT_W-1 downto 0);
         hs_segments   : out unsigned(CNT_W-1 downto 0);
         revert_ticks  : out unsigned(CNT_W-1 downto 0)
      );
   end entity i2c_hs_sequencer;

   architecture rtl of i2c_hs_sequencer is

      -- The numeric encoding is part of the interface, so it is spelled out rather
      -- than left to an enumeration's position.
      constant ST_FS_IDLE : integer := 0;   -- F/S-mode, current source off
      constant ST_START   : integer := 1;   -- START condition, at F/S speed
      constant ST_CODE    : integer := 2;   -- 8-bit master code, arbitration live
      constant ST_ACKBIT  : integer := 3;   -- ninth bit: must read HIGH
      constant ST_WAIT_TH : integer := 4;   -- waiting for SCLH release -> tH
      constant ST_HS_SR   : integer := 5;   -- repeated START, current source off
      constant ST_HS_HOLD : integer := 6;   -- current source off, awaiting a release
      constant ST_HS_DATA : integer := 7;   -- Hs data, current source on
      constant ST_TFS     : integer := 8;   -- STOP seen, reverting to Fast-mode
      constant ST_LOST    : integer := 9;   -- lost arbitration, awaiting a STOP

      signal st       : integer := ST_FS_IDLE;
      signal bit_idx  : integer := 7;       -- 7 downto 0, MSB first
      signal tfs_cnt  : integer := 0;
      signal n_wait   : integer := 0;
      signal n_dis    : integer := 0;
      signal n_seg    : integer := 0;

      -- The master code as an 8-bit first byte: b7..b4 = 0000, b3 = 1, b2..b0 = code.
      signal code_byte : std_logic_vector(7 downto 0);

   begin

      code_byte <= "0000" & '1' & master_code;
      state     <= to_unsigned(st, 4);

      process (clk, rst_n)
      begin
         if rst_n = '0' then
            st            <= ST_FS_IDLE;
            sda_drive_low <= '0';
            cs_enable     <= '0';
            is_hs         <= '0';
            arb_lost      <= '0';
            ack_violation <= '0';
            revert_done   <= '0';
            revert_late   <= '0';
            bit_idx       <= 7;
            tfs_cnt       <= 0;
            n_wait        <= 0;
            n_dis         <= 0;
            n_seg         <= 0;
            stretch_waits <= (others => '0');
            cs_disables   <= (others => '0');
            hs_segments   <= (others => '0');
            revert_ticks  <= (others => '0');

         elsif rising_edge(clk) then
            case st is

               -- F/S-mode. The current source is off and stays off: only the active
               -- master in Hs-mode may ever enable it.
               when ST_FS_IDLE =>
                  cs_enable     <= '0';
                  is_hs         <= '0';
                  sda_drive_low <= '0';
                  if start_req = '1' then
                     arb_lost      <= '0';
                     ack_violation <= '0';
                     revert_done   <= '0';
                     revert_late   <= '0';
                     n_seg         <= 0;
                     hs_segments   <= (others => '0');
                     st            <= ST_START;
                  end if;

               -- START condition, generated at F/S-mode speed.
               when ST_START =>
                  sda_drive_low <= '1';            -- SDA pulled LOW while SCL is HIGH
                  if bit_tick = '1' then
                     bit_idx <= 7;
                     st      <= ST_CODE;
                  end if;

               -- The 8-bit master code, MSB first, arbitration live on every bit.
               -- Obligation 4: a master that drove HIGH and reads LOW has lost, and
               -- must drop off immediately.
               when ST_CODE =>
                  if code_byte(bit_idx) = '1' then
                     sda_drive_low <= '0';
                  else
                     sda_drive_low <= '1';
                  end if;
                  if bit_tick = '1' then
                     if code_byte(bit_idx) = '1' and sda_line = '0' then
                        -- Drove a 1, the line came back 0: conclusive loss.
                        arb_lost      <= '1';
                        sda_drive_low <= '0';
                        cs_enable     <= '0';
                        st            <= ST_LOST;
                     elsif bit_idx = 0 then
                        st <= ST_ACKBIT;
                     else
                        bit_idx <= bit_idx - 1;
                     end if;
                  end if;

               -- The ninth bit. No device may acknowledge the master code, so the
               -- line must read HIGH. Arbitration is still live on this bit.
               when ST_ACKBIT =>
                  sda_drive_low <= '0';            -- release so the line can float HIGH
                  if bit_tick = '1' then
                     if sda_line = '0' then
                        -- Either a device acknowledged the master code, which is
                        -- forbidden, or a competing master is still driving.
                        ack_violation <= '1';
                        arb_lost      <= '1';
                        cs_enable     <= '0';
                        st            <= ST_LOST;
                     else
                        st <= ST_WAIT_TH;
                     end if;
                  end if;

               -- Between the not-acknowledge and tH. Any device may stretch here, so
               -- the master waits for a genuine release rather than assuming.
               when ST_WAIT_TH =>
                  if scl_released = '1' then
                     cs_enable   <= '1';           -- tH
                     is_hs       <= '1';
                     n_seg       <= n_seg + 1;
                     hs_segments <= to_unsigned(n_seg + 1, CNT_W);
                     st          <= ST_HS_SR;
                  else
                     n_wait        <= n_wait + 1;
                     stretch_waits <= to_unsigned(n_wait + 1, CNT_W);
                  end if;

               -- Obligation 6, first half. A repeated START is one of the two points
               -- where the current source must come off.
               when ST_HS_SR =>
                  cs_enable   <= '0';
                  n_dis       <= n_dis + 1;
                  cs_disables <= to_unsigned(n_dis + 1, CNT_W);
                  st          <= ST_HS_HOLD;

               -- Obligation 7: re-enable only once everybody has let go and the line
               -- has actually reached HIGH. This is the interval whose rise time
               -- Table 12 calls trCL1 and allows to be twice as long.
               when ST_HS_HOLD =>
                  if scl_released = '1' then
                     cs_enable <= '1';
                     st        <= ST_HS_DATA;
                  else
                     n_wait        <= n_wait + 1;
                     stretch_waits <= to_unsigned(n_wait + 1, CNT_W);
                  end if;

               -- Hs data. Obligation 6, second half: every acknowledge bit takes the
               -- current source off again.
               --
               -- stop_req and sr_req are accepted only here, by design. A master does
               -- not issue a STOP or a repeated START while it is waiting for another
               -- device to release the clock.
               when ST_HS_DATA =>
                  if stop_req = '1' then
                     cs_enable    <= '0';          -- tFS
                     tfs_cnt      <= 0;
                     revert_ticks <= (others => '0');
                     st           <= ST_TFS;
                  elsif sr_req = '1' then
                     n_seg       <= n_seg + 1;
                     hs_segments <= to_unsigned(n_seg + 1, CNT_W);
                     st          <= ST_HS_SR;      -- stays in Hs-mode
                  elsif ack_phase = '1' then
                     cs_enable   <= '0';
                     n_dis       <= n_dis + 1;
                     cs_disables <= to_unsigned(n_dis + 1, CNT_W);
                     st          <= ST_HS_HOLD;
                  end if;

               -- tFS. The current source is already off. The device must be back in
               -- its Fast-mode configuration within the Fast-mode tBUF.
               when ST_TFS =>
                  cs_enable <= '0';
                  tfs_cnt   <= tfs_cnt + 1;
                  if fs_start_seen = '1' and (tfs_cnt + 1) < TBUF_TICKS then
                     -- A START arrived on the F/S segment before the Fast-mode bus
                     -- free time had elapsed. UM10204 5.3.7 puts this obligation on
                     -- the bridge: TR3 must open fast enough that tBUF is honoured.
                     -- Record the actual gap rather than the nominal one.
                     revert_late  <= '1';
                     is_hs        <= '0';
                     revert_done  <= '1';
                     revert_ticks <= to_unsigned(tfs_cnt + 1, CNT_W);
                     st           <= ST_FS_IDLE;
                  elsif (tfs_cnt + 1) >= TBUF_TICKS then
                     is_hs        <= '0';
                     revert_done  <= '1';
                     revert_ticks <= to_unsigned(tfs_cnt + 1, CNT_W);
                     st           <= ST_FS_IDLE;
                  end if;

               -- Lost arbitration. A losing master adapts its input filters and then
               -- does exactly one thing: waits for a STOP. It must never enable its
               -- current source.
               when ST_LOST =>
                  cs_enable     <= '0';
                  is_hs         <= '0';
                  sda_drive_low <= '0';
                  if stop_req = '1' then
                     st <= ST_FS_IDLE;
                  end if;

               when others =>
                  st <= ST_FS_IDLE;

            end case;
         end if;
      end process;

   end architecture rtl;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_sequencer_tb.vhd — the same eleven scenarios, with the bit index rebuilt from the protocol
   -- ---------------------------------------------------------------------------
   -- i2c_hs_sequencer_tb.vhd
   -- Independent oracle for i2c_hs_sequencer. Behavioural twin of the
   -- SystemVerilog and Verilog benches.
   --
   -- The bench models the OTHER devices on the bus -- a competing master and a
   -- stretching slave -- and drives the wired-AND itself, so the DUT is judged
   -- against a line value the bench computed, never against its own idea of it.
   --
   -- The obligation the suite exists to pin down is number 6: the current source
   -- must come off after the repeated START and after EVERY acknowledge. A master
   -- that holds it on still passes a naive functional test, because the data is
   -- unaffected; what breaks is the slave's ability to stretch.
   -- ---------------------------------------------------------------------------

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

   entity i2c_hs_sequencer_tb is
   end entity i2c_hs_sequencer_tb;

   architecture sim of i2c_hs_sequencer_tb is

      constant TBUF_TICKS : integer := 65;   -- Fast-mode tBUF(min) 1.3 us in 20 ns ticks
      constant CNT_W      : integer := 12;
      constant TCLK       : time    := 20 ns;

      constant ST_FS_IDLE : integer := 0;
      constant ST_CODE    : integer := 2;
      constant ST_ACKBIT  : integer := 3;
      constant ST_WAIT_TH : integer := 4;
      constant ST_HS_SR   : integer := 5;
      constant ST_HS_HOLD : integer := 6;
      constant ST_HS_DATA : integer := 7;
      constant ST_TFS     : integer := 8;
      constant ST_LOST    : integer := 9;

      signal clk           : std_logic := '0';
      signal rst_n         : std_logic := '0';
      signal start_req     : std_logic := '0';
      signal master_code   : std_logic_vector(2 downto 0) := "001";
      signal sr_req        : std_logic := '0';
      signal stop_req      : std_logic := '0';
      signal bit_tick      : std_logic := '0';
      signal scl_released  : std_logic := '1';
      signal ack_phase     : std_logic := '0';
      signal fs_start_seen : std_logic := '0';

      -- The competing master, modelled by the bench.
      signal rival_active    : std_logic := '0';
      signal rival_byte      : std_logic_vector(7 downto 0) := "00000111";
      signal ack_low         : std_logic := '0';

      signal sda_drive_low, cs_enable, is_hs, arb_lost, ack_violation : std_logic;
      signal revert_done, revert_late : std_logic;
      signal st_o : unsigned(3 downto 0);
      signal stretch_waits, cs_disables, hs_segments, revert_ticks : unsigned(CNT_W-1 downto 0);

      -- The wired-AND. Whoever pulls LOW wins; the line is HIGH only if nobody is
      -- pulling. The bench owns this, not the DUT.
      signal rival_low : std_logic;
      signal sda_line  : std_logic;
      signal dut_bit_idx : integer;

      signal halt : boolean := false;

   begin

      dut : entity work.i2c_hs_sequencer
         generic map (TBUF_TICKS => TBUF_TICKS, CNT_W => CNT_W)
         port map (
            clk => clk, rst_n => rst_n,
            start_req => start_req, master_code => master_code,
            sr_req => sr_req, stop_req => stop_req,
            bit_tick => bit_tick, sda_line => sda_line,
            scl_released => scl_released, ack_phase => ack_phase,
            fs_start_seen => fs_start_seen,
            sda_drive_low => sda_drive_low, cs_enable => cs_enable, is_hs => is_hs,
            arb_lost => arb_lost, ack_violation => ack_violation,
            revert_done => revert_done, revert_late => revert_late,
            state => st_o, stretch_waits => stretch_waits, cs_disables => cs_disables,
            hs_segments => hs_segments, revert_ticks => revert_ticks);

      -- The bench tracks which bit is on the wire by watching the DUT's own index.
      -- VHDL has no cross-module hierarchical read here, so the index is rebuilt
      -- from the observable protocol: it starts at 7 on entry to ST_CODE and
      -- decrements on each accepted bit_tick.
      idx : process (clk, rst_n)
         variable idx_v : integer := 7;
      begin
         if rst_n = '0' then
            idx_v := 7;
         elsif rising_edge(clk) then
            if to_integer(st_o) = ST_CODE then
               if bit_tick = '1' and idx_v > 0 then
                  idx_v := idx_v - 1;
               end if;
            elsif to_integer(st_o) < ST_CODE then
               idx_v := 7;      -- re-arm only BEFORE the code byte, never after it
            end if;
         end if;
         dut_bit_idx <= idx_v;
      end process;

      -- A competing master drives during the code byte and releases for the
      -- acknowledge slot, so the rival is gated on ST_CODE. The separate ack_low
      -- signal models a device that wrongly drives the acknowledge bit itself.
      rival_low <= '1' when (rival_active = '1' and to_integer(st_o) = ST_CODE
                             and rival_byte(dut_bit_idx) = '0') else '0';
      sda_line  <= '0' when (sda_drive_low = '1' or rival_low = '1' or ack_low = '1') else '1';

      clkgen : process
      begin
         while not halt loop
            clk <= '0'; wait for TCLK/2;
            clk <= '1'; wait for TCLK/2;
         end loop;
         wait;
      end process;

      stim : process
         variable err : integer := 0;

         procedure ck_int (what : string; got : integer; exp : integer) is
         begin
            if got /= exp then
               report "  FAIL " & what & ": got " & integer'image(got)
                      & " expected " & integer'image(exp) severity note;
               err := err + 1;
            end if;
         end procedure;

         procedure ck_bit (what : string; got : std_logic; exp : std_logic) is
         begin
            if got /= exp then
               report "  FAIL " & what & ": got " & std_logic'image(got)
                      & " expected " & std_logic'image(exp) severity note;
               err := err + 1;
            end if;
         end procedure;

         -- Every check reads registered outputs, so step returns at a point where
         -- the signal updates from the rising edge have already settled.
         procedure step is
         begin
            wait until rising_edge(clk);
            wait until falling_edge(clk);
         end procedure;

         procedure pulse_bit is          -- one F/S bit cell
         begin
            wait until falling_edge(clk);
            bit_tick <= '1';
            wait until rising_edge(clk);
            wait until falling_edge(clk);
            bit_tick <= '0';
         end procedure;

         procedure pulse_ack is
         begin
            wait until falling_edge(clk);
            ack_phase <= '1';
            wait until rising_edge(clk);
            wait until falling_edge(clk);
            ack_phase <= '0';
         end procedure;

         procedure do_reset is
         begin
            wait until falling_edge(clk);
            rst_n <= '0'; start_req <= '0'; sr_req <= '0'; stop_req <= '0';
            bit_tick <= '0'; ack_phase <= '0'; scl_released <= '1';
            rival_active <= '0'; ack_low <= '0'; fs_start_seen <= '0';
            for k in 0 to 2 loop wait until rising_edge(clk); end loop;
            wait until falling_edge(clk);
            rst_n <= '1';
            wait until rising_edge(clk);
         end procedure;

         -- S + the 8 code bits + the ninth bit.
         procedure send_preamble is
         begin
            wait until falling_edge(clk);
            start_req <= '1';
            wait until rising_edge(clk);
            wait until falling_edge(clk);
            start_req <= '0';
            pulse_bit;                              -- leaves ST_START -> ST_CODE
            for k in 0 to 7 loop pulse_bit; end loop;   -- the 8 code bits
            pulse_bit;                              -- the ninth (not-acknowledge) bit
         end procedure;

      begin
         report "=== i2c_hs_sequencer: Hs-mode entry and exit obligations ===" severity note;

         -- T1. A clean entry. The current source must be OFF for the whole F/S
         --     preamble and come on only at tH.
         do_reset;
         ck_bit("T1 cs off at idle", cs_enable, '0');
         wait until falling_edge(clk); start_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); start_req <= '0';
         pulse_bit;
         ck_int("T1 in ST_CODE", to_integer(st_o), ST_CODE);
         ck_bit("T1 cs still off during the master code", cs_enable, '0');
         ck_bit("T1 not yet in Hs", is_hs, '0');
         for k in 0 to 7 loop pulse_bit; end loop;
         ck_int("T1 in ST_ACKBIT", to_integer(st_o), ST_ACKBIT);
         ck_bit("T1 cs still off at the ninth bit", cs_enable, '0');
         pulse_bit;                            -- ninth bit reads HIGH: a proper NACK
         report "T1  clean Hs-mode entry" severity note;
         ck_bit("T1 no ack violation", ack_violation, '0');
         ck_bit("T1 no arbitration loss", arb_lost, '0');
         step;                                 -- ST_WAIT_TH sees scl_released -> tH
         ck_bit("T1 cs ON at tH", cs_enable, '1');
         ck_bit("T1 is_hs at tH", is_hs, '1');
         ck_int("T1 one Hs segment", to_integer(hs_segments), 1);

         -- T2. Obligations 6 and 7 across a repeated START.
         wait until falling_edge(clk); scl_released <= '0';   -- a slave holds SCLH LOW
         step;
         if to_integer(st_o) /= ST_HS_SR and to_integer(st_o) /= ST_HS_HOLD then
            report "  FAIL T2 expected ST_HS_SR or ST_HS_HOLD" severity note;
            err := err + 1;
         end if;
         step;
         ck_bit("T2 cs OFF after the repeated START", cs_enable, '0');
         ck_int("T2 one disable event", to_integer(cs_disables), 1);
         step; step;
         ck_bit("T2 cs still OFF while the slave stretches", cs_enable, '0');
         wait until falling_edge(clk); scl_released <= '1';
         step;
         report "T2  current source off across Sr, restored on release" severity note;
         ck_bit("T2 cs back ON after release", cs_enable, '1');
         ck_int("T2 now in ST_HS_DATA", to_integer(st_o), ST_HS_DATA);
         if to_integer(stretch_waits) = 0 then
            report "  FAIL T2 stretch_waits: expected a nonzero wait count" severity note;
            err := err + 1;
         end if;

         -- T3. THE OBLIGATION THAT IS USUALLY MISSED. Every acknowledge bit takes
         --     the current source off, and each must wait for a release.
         for k in 0 to 2 loop
            wait until falling_edge(clk); scl_released <= '0';
            pulse_ack;
            step;
            ck_bit("T3 cs OFF after an acknowledge", cs_enable, '0');
            wait until falling_edge(clk); scl_released <= '1';
            step; step;
            ck_bit("T3 cs ON again after release", cs_enable, '1');
         end loop;
         report "T3  current source cycles off and on at every acknowledge" severity note;
         ck_int("T3 four disable events in total", to_integer(cs_disables), 4);

         -- T4. Hs-mode survives a repeated START. Only a STOP leaves it.
         wait until falling_edge(clk); sr_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); sr_req <= '0';
         step; step; step;
         report "T4  a repeated START keeps the bus in Hs-mode" severity note;
         ck_bit("T4 still in Hs", is_hs, '1');
         ck_int("T4 two Hs segments", to_integer(hs_segments), 2);
         ck_int("T4 five disable events", to_integer(cs_disables), 5);

         -- T5. tFS and the revert.
         wait until falling_edge(clk); stop_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); stop_req <= '0';
         step;
         ck_bit("T5 cs OFF at tFS", cs_enable, '0');
         ck_int("T5 in ST_TFS", to_integer(st_o), ST_TFS);
         ck_bit("T5 revert not yet done", revert_done, '0');
         for k in 0 to TBUF_TICKS + 2 loop step; end loop;
         report "T5  revert to Fast-mode completes inside tBUF" severity note;
         ck_bit("T5 revert done", revert_done, '1');
         ck_bit("T5 revert not late", revert_late, '0');
         ck_bit("T5 out of Hs", is_hs, '0');
         ck_int("T5 revert took tBUF ticks", to_integer(revert_ticks), TBUF_TICKS);
         ck_int("T5 back at F/S idle", to_integer(st_o), ST_FS_IDLE);

         -- T6. LOSING ARBITRATION TO A RESERVED CODE. The rival sends 0000 0111,
         --     ours is 0000 1001. They agree on b7..b4 and diverge at b3, the fifth
         --     bit sent, where the rival drives LOW.
         do_reset;
         master_code <= "001";                 -- our code byte = 0000 1001
         rival_byte  <= "00000111";            -- reserved code 0000 0111
         wait until falling_edge(clk); rival_active <= '1';
         wait until falling_edge(clk); start_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); start_req <= '0';
         pulse_bit;                            -- -> ST_CODE, bit index 7
         -- b7, b6, b5, b4 all agree (both 0): no loss yet.
         for k in 0 to 3 loop
            pulse_bit;
            ck_bit("T6 no loss on the agreeing bits", arb_lost, '0');
         end loop;
         -- b3: we drive 1, the rival drives 0. The line reads 0 and we lose.
         pulse_bit;
         report "T6  lost arbitration at b3 to a reserved 0000 0XXX code" severity note;
         ck_bit("T6 arbitration lost", arb_lost, '1');
         ck_int("T6 in ST_LOST", to_integer(st_o), ST_LOST);
         ck_bit("T6 released SDA", sda_drive_low, '0');
         ck_bit("T6 current source never enabled", cs_enable, '0');
         ck_bit("T6 never entered Hs", is_hs, '0');
         ck_int("T6 no Hs segment was opened", to_integer(hs_segments), 0);
         -- A losing master does exactly one thing: waits for a STOP.
         for k in 0 to 19 loop
            step;
            ck_bit("T6 still waiting, cs off", cs_enable, '0');
         end loop;
         ck_int("T6 still in ST_LOST", to_integer(st_o), ST_LOST);
         wait until falling_edge(clk); stop_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); stop_req <= '0';
         step;
         ck_int("T6 STOP frees the bus", to_integer(st_o), ST_FS_IDLE);

         -- T7. WINNING against a rival whose code diverges upward: 0000 1001 vs
         --     0000 1101 agree through b3 and diverge at b2, where WE drive 0.
         do_reset;
         master_code <= "001";                 -- 0000 1001
         rival_byte  <= "00001101";            -- 0000 1101
         wait until falling_edge(clk); rival_active <= '1';
         send_preamble;
         report "T7  won arbitration at b2, entry proceeds" severity note;
         ck_bit("T7 did not lose", arb_lost, '0');
         ck_bit("T7 no ack violation", ack_violation, '0');
         step;
         ck_bit("T7 cs ON at tH", cs_enable, '1');
         ck_bit("T7 in Hs", is_hs, '1');

         -- T8. A DEVICE ACKNOWLEDGES THE MASTER CODE. No device is allowed to, so a
         --     LOW ninth bit is a protocol violation and Hs-mode must NOT be entered.
         do_reset;
         master_code <= "010";
         wait until falling_edge(clk); start_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); start_req <= '0';
         pulse_bit;
         for k in 0 to 7 loop pulse_bit; end loop;
         ck_int("T8 at the ninth bit", to_integer(st_o), ST_ACKBIT);
         wait until falling_edge(clk); ack_low <= '1';   -- a device pulls the slot LOW
         pulse_bit;
         report "T8  a device acknowledged the master code" severity note;
         ck_bit("T8 ack violation reported", ack_violation, '1');
         ck_bit("T8 did not enter Hs", is_hs, '0');
         ck_bit("T8 current source never enabled", cs_enable, '0');
         ck_int("T8 no Hs segment", to_integer(hs_segments), 0);
         wait until falling_edge(clk); ack_low <= '0';

         -- T9. A START on the F/S segment arriving inside tBUF violates UM10204
         --     5.3.7, and the ACTUAL gap must be reported, not the nominal one.
         do_reset;
         master_code <= "011";
         send_preamble;
         step;
         ck_bit("T9 in Hs before the STOP", is_hs, '1');
         step; step;                           -- tH -> Sr -> release -> ST_HS_DATA
         ck_int("T9 reached the Hs data phase", to_integer(st_o), ST_HS_DATA);
         wait until falling_edge(clk); stop_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); stop_req <= '0';
         step;
         ck_int("T9 in ST_TFS", to_integer(st_o), ST_TFS);
         for k in 0 to 17 loop step; end loop;
         wait until falling_edge(clk); fs_start_seen <= '1';
         step;
         wait until falling_edge(clk); fs_start_seen <= '0';
         report "T9  a START inside tBUF is reported as a late revert" severity note;
         ck_bit("T9 revert flagged late", revert_late, '1');
         ck_bit("T9 revert still completed", revert_done, '1');
         ck_bit("T9 left Hs", is_hs, '0');
         if to_integer(revert_ticks) >= TBUF_TICKS then
            report "  FAIL T9 revert_ticks: got " & integer'image(to_integer(revert_ticks))
                   & ", expected the actual short gap" severity note;
            err := err + 1;
         end if;

         -- T10. A START arriving AFTER tBUF is not a violation.
         do_reset;
         master_code <= "100";
         send_preamble;
         step; step; step;                     -- reach the Hs data phase
         ck_int("T10 reached the Hs data phase", to_integer(st_o), ST_HS_DATA);
         wait until falling_edge(clk); stop_req <= '1';
         wait until rising_edge(clk); wait until falling_edge(clk); stop_req <= '0';
         for k in 0 to TBUF_TICKS + 4 loop step; end loop;
         wait until falling_edge(clk); fs_start_seen <= '1';
         step;
         wait until falling_edge(clk); fs_start_seen <= '0';
         report "T10 a START after tBUF is legal" severity note;
         ck_bit("T10 revert not late", revert_late, '0');
         ck_bit("T10 revert done", revert_done, '1');
         ck_int("T10 revert took the full tBUF", to_integer(revert_ticks), TBUF_TICKS);

         -- T11. Stretching BEFORE tH: the master must wait rather than assume, and
         --      the current source must stay off for the whole wait.
         do_reset;
         master_code <= "101";
         wait until falling_edge(clk); scl_released <= '0';   -- stretch from the outset
         send_preamble;
         ck_int("T11 waiting at tH", to_integer(st_o), ST_WAIT_TH);
         for k in 0 to 11 loop
            step;
            ck_bit("T11 cs off while stretched before tH", cs_enable, '0');
            ck_bit("T11 not in Hs while stretched", is_hs, '0');
         end loop;
         ck_int("T11 still waiting", to_integer(st_o), ST_WAIT_TH);
         wait until falling_edge(clk); scl_released <= '1';
         step;
         report "T11 the master waits for a release before tH" severity note;
         ck_bit("T11 cs ON once released", cs_enable, '1');
         ck_bit("T11 in Hs once released", is_hs, '1');
         if to_integer(stretch_waits) < 12 then
            report "  FAIL T11 stretch_waits: got "
                   & integer'image(to_integer(stretch_waits))
                   & ", expected at least 12" severity note;
            err := err + 1;
         end if;

         if err = 0 then
            report "=== i2c_hs_sequencer: ALL CHECKS PASSED ===" severity note;
         else
            report "=== i2c_hs_sequencer: " & integer'image(err)
                   & " CHECK(S) FAILED ===" severity note;
         end if;
         halt <= true;
         wait;
      end process;

   end architecture sim;

9a. Seven Decisions Worth Defending

The current source is never enabled outside Hs-mode, and the code says so in three places. S_FS_IDLE, S_LOST and S_TFS all assign it low unconditionally rather than relying on having left it low. §5.3.1's "only during Hs-mode" is a property worth making structurally true rather than incidentally true, and mutation T9 shows what a single missed place costs.

tH waits for an observed release, not for a counted delay. §5.3.2 says other devices may stretch before tH, so the master cannot know how long the wait is. S_WAIT_TH sits on scl_released and counts how many cycles it waited — which turns a timing assumption into an observation, and makes the waiting measurable by the bench.

Every current-source disable is counted. cs_disables exists only for verification, and it is the only thing that can distinguish a compliant master from mutation T1 or T2. The data is identical in all three cases. This is the same argument as Chapter 13.5 §7's counters: a monitor that reports nothing passes every negative test.

A low ninth bit is the violation, not a high one. §2's rule inverted is the commonest misreading of Hs-mode entry, and ack_violation is deliberately named for the device that acknowledged rather than for the master that saw it. Mutation T5 accepts the low bit and enters Hs-mode anyway, which is the dangerous direction: switching the electrical regime on a bus that has just proved it is not behaving to specification.

Arbitration is tested against the line, not against an expectation. if (code_byte[bit_idx] && !sda_line) compares intent with the wire, which is the only test that works — the wired-AND destroys attribution, as Chapter 13.3 established, so the master can learn that it lost and never to whom. Mutation T4 inverts the comparison.

stop_req and sr_req are accepted only in the data state, by design, and the source says why. A master does not issue a STOP while waiting for another device to release the clock — it has not finished the current bit. Requests raised in S_HS_SR or S_HS_HOLD are ignored rather than queued, and that is a decision rather than an oversight, so it is documented where someone changing it will read it.

The revert deadline is observed against a real START, not asserted. revert_late fires when a START appears on the F/S segment before Fast-mode tBUF has elapsed, and revert_ticks then reports the actual gap rather than the nominal one. A block that reported the nominal figure would be describing its own configuration rather than the bus.

9b. Verified Execution

Azvya Education Pvt. Ltd.VLSI Mentor
terminal — three simulators, eleven scenarios, one finish time
   $ iverilog -g2012 -o d i2c_hs_sequencer.sv i2c_hs_sequencer_tb.sv && ./d
   === i2c_hs_sequencer: Hs-mode entry and exit obligations ===
   T1  clean Hs-mode entry
   T2  current source off across Sr, restored on release
   T3  current source cycles off and on at every acknowledge
   T4  a repeated START keeps the bus in Hs-mode
   T5  revert to Fast-mode completes inside tBUF
   T6  lost arbitration at b3 to a reserved 0000 0XXX code
   T7  won arbitration at b2, entry proceeds
   T8  a device acknowledged the master code
   T9  a START inside tBUF is reported as a late revert
   T10 a START after tBUF is legal
   T11 the master waits for a release before tH
   === i2c_hs_sequencer: ALL CHECKS PASSED ===
   i2c_hs_sequencer_tb.sv:382: $finish called at 8540000 (1ps)

   $ iverilog -g2005 -o v i2c_hs_sequencer.v i2c_hs_sequencer_tb.v && ./v
   === i2c_hs_sequencer: Hs-mode entry and exit obligations ===
   T1  clean Hs-mode entry
   T2  current source off across Sr, restored on release
   T3  current source cycles off and on at every acknowledge
   T4  a repeated START keeps the bus in Hs-mode
   T5  revert to Fast-mode completes inside tBUF
   T6  lost arbitration at b3 to a reserved 0000 0XXX code
   T7  won arbitration at b2, entry proceeds
   T8  a device acknowledged the master code
   T9  a START inside tBUF is reported as a late revert
   T10 a START after tBUF is legal
   T11 the master waits for a release before tH
   === i2c_hs_sequencer: ALL CHECKS PASSED ===
   i2c_hs_sequencer_tb.v:383: $finish called at 8540000 (1ps)

   $ nvc --std=2008 -a i2c_hs_sequencer.vhd i2c_hs_sequencer_tb.vhd
   $ nvc --std=2008 -e i2c_hs_sequencer_tb && nvc --std=2008 -r i2c_hs_sequencer_tb --stop-time=500us
   ** Note: 0ms+0: === i2c_hs_sequencer: Hs-mode entry and exit obligations ===
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 520ns+1: T1  clean Hs-mode entry
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 680ns+1: T2  current source off across Sr, restored on release
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 1100ns+1: T3  current source cycles off and on at every acknowledge
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 1200ns+1: T4  a repeated START keeps the bus in Hs-mode
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 2620ns+1: T5  revert to Fast-mode completes inside tBUF
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 3us+1: T6  lost arbitration at b3 to a reserved 0000 0XXX code
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 4us+1: T7  won arbitration at b2, entry proceeds
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 4560ns+1: T8  a device acknowledged the master code
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 5640ns+1: T9  a START inside tBUF is reported as a late revert
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:362
   ** Note: 7720ns+1: T10 a START after tBUF is legal
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:384
   ** Note: 8540ns+1: T11 the master waits for a release before tH
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122
   ** Note: 8540ns+1: === i2c_hs_sequencer: ALL CHECKS PASSED ===
      Process :i2c_hs_sequencer_tb:stim at i2c_hs_sequencer_tb.vhd:122

All three at 8540 ns. The VHDL bench is not a transliteration: it has no hierarchical access to the sequencer's bit index, so it rebuilds the index from the observable protocol — starting at seven on entry to the code state and decrementing on each accepted bit tick. That reconstruction is what makes the VHDL run an independent check rather than a copy, and getting it wrong was this module's most instructive bug: an early version re-armed the index when the state left the code byte, which made the bench's rival master drive the acknowledge slot and produced a spurious arbitration loss in test 7.

9c. What The Testbench Proves

#scenariowhat it establishes
1a clean entry, nobody competingthe current source is off for the whole preamble and on only at tH
2a slave stretching across the repeated STARToff at Sr, stays off, restored only on release
3three acknowledges in a rowthree more disable events, each waiting for a release
4a repeated START mid-transferstill in Hs-mode; a second segment opened
5STOP, then the revertcompletes at exactly Fast-mode tBUF
6a rival sending 0000 0111loses at b3; never enters Hs; never enables the current source
7a rival sending 0000 1101wins at b2; entry proceeds normally
8a device acknowledging the master codereported, and Hs-mode not entered
9a START inside tBUF after tFSflagged late, with the actual gap
10a START after tBUFnot flagged
11stretching held from before the preamblethe master waits; current source off throughout

Test 3 is the chapter's central obligation and it is the only test that can see it. Three acknowledges, three disable events, each followed by a wait for release. Remove the disable and the data is unchanged — which is why tests 1, 4, 5, 6 and 7 all still pass under mutation T2 and only test 3 fails.

Test 6 checks twenty cycles of doing nothing. After losing, the sequencer is asserted to hold the current source off for twenty consecutive cycles and to stay in the lost state until a STOP. A losing master's correctness is mostly a matter of what it does not do, and that has to be asserted over an interval rather than at an instant.

Test 7 exists because test 6 alone would pass a sequencer that always loses. A block that dropped out on any disagreement would satisfy every check in test 6. Winning at b2 against a rival whose code is numerically larger is the positive case, and without it the arbitration logic is only proved in one direction.

Test 9 and test 10 are the same stimulus at two different times. The only difference is whether the START arrives before or after tBUF, and the pair establishes that the deadline is a real comparison rather than a constant. Mutation T7 shifts it by one tick and test 5 catches the shift in revert_ticks.

Test 11 asserts an absence twelve times over. With SCLH held low from before the preamble, the sequencer must sit in S_WAIT_TH with the current source off and is_hs low — checked on every one of twelve cycles, because a block that enabled the source one cycle early would satisfy a single end-of-wait check.

10. Mutation Testing

Eleven defects injected into the SystemVerilog sequencer.

#injected defectoutcome
T1current source left ON across the repeated STARTkilled — test 2
T2current source left ON across an acknowledgekilled — test 3
T3re-enabled without waiting for a releasekilled — 7 checks
T4the arbitration test invertedkilled — test 6
T5a LOW ninth bit accepted, Hs-mode entered anywaykilled — test 8
T6Hs-mode entered at tH without waitingkilled — 15 checks, test 11
T7the tBUF comparison off by onekilled — test 5's revert_ticks
T8a late revert never flaggedkilled — test 9
T9a losing master enables its current sourcekilled — 20 checks
T10the segment opened by a repeated START not countedkilled — test 4
T11SDA actively pulled LOW on losing arbitrationkilled — test 6

Eleven of eleven. Three observations.

T1 and T2 kill with two and three checks. The smallest counts in the table, for the most serious defects in it. That asymmetry is the argument for the chapter: these are the defects with almost no observable surface, and a suite not specifically looking for them will not find them. Compare T9, which fires twenty checks because a losing master enabling a current source violates an assertion held across the whole waiting interval.

T11 replaced a mutation that was provably equivalent. The original T11 deleted sda_drive_low <= 1'b0; from the loss branch and survived. That is not a bench hole: losing arbitration requires that this master drove a one on the deciding bit, and the same case statement has already executed sda_drive_low <= !code_byte[bit_idx], which for a one is zero. The driver is therefore already released at the instant of loss and the explicit clear is defensive rather than functional. The clear is kept — a reader should not have to reconstruct that argument to be sure the master stops driving — and the mutation was replaced with one that drives SDA low, which is observable and is killed. The equivalence and its proof are recorded in the suite itself.

T6's count of fifteen is the wait, not the entry. Entering Hs-mode without waiting for a release breaks test 11's twelve per-cycle assertions plus three more at the end. A defect inside a loop kills proportionally to the loop, which is why asserting an absence repeatedly is worth the lines it costs.

11. Verification Connection — Covering A Mode You Cannot Assert Into

Hs-mode presents a specific verification problem: the interesting property is a relationship between two things that are never both visible at once. The current source is an analogue behaviour; the acknowledge is a protocol event; and the obligation links them.

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_hs_cs_checker.sv — the acknowledge-to-current-source obligation, as an assertion and a cover
   // THE OBLIGATION: after every repeated START and every ACK or NACK, the active
   // master must DISABLE its SCLH current source, and must not re-enable it until
   // every device has released and SCLH has actually reached a HIGH level.
   //
   // This cannot be checked from the data, because the data is identical whether
   // the obligation is honoured or not. It has to be checked against the current
   // source control itself -- which means the interface has to EXPOSE it. A VIP
   // that models Hs-mode with a bit rate and nothing else cannot check this at all,
   // and that is the most common way the obligation escapes verification.
   interface i2c_hs_cs_if (input logic clk);
      logic cs_enable;      // the current-source pull-up control
      logic ack_phase;      // an ACK or NACK bit boundary in Hs-mode
      logic sr_phase;       // a repeated START in Hs-mode
      logic scl_released;   // everyone released and SCLH read HIGH
      logic is_hs;
   endinterface

   module i2c_hs_cs_checker (i2c_hs_cs_if bus);

      // 1. The disable is immediate. Not "eventually" -- the next cycle, because a
      //    slave that wants to stretch will pull low at the start of the low phase
      //    and a source still enabled at that moment has already fought it.
      property p_off_after_ack;
         @(posedge bus.clk) disable iff (!bus.is_hs)
            (bus.ack_phase || bus.sr_phase) |=> !bus.cs_enable;
      endproperty
      a_off_after_ack : assert property (p_off_after_ack)
         else $error("current source still enabled after an acknowledge or repeated START");

      // 2. It stays off until a genuine release. `scl_released` is an OBSERVATION,
      //    so this is the assertion that a counted delay would violate.
      property p_stay_off_until_release;
         @(posedge bus.clk) disable iff (!bus.is_hs)
            !bus.cs_enable && !bus.scl_released |=> !bus.cs_enable;
      endproperty
      a_stay_off : assert property (p_stay_off_until_release)
         else $error("current source re-enabled while a device was still holding SCLH");

      // 3. Never outside Hs-mode. Section 5.3.1: "only during Hs-mode".
      a_never_in_fs : assert property (
         @(posedge bus.clk) !bus.is_hs |-> !bus.cs_enable)
         else $error("current source enabled outside Hs-mode");

      // ---- COVERAGE, which is where the real risk lives ----------------------
      // The assertions above are vacuously true on a bus where nobody ever
      // stretches in Hs-mode. These cover points are what prove the stimulus
      // reached the situation the assertions describe.
      covergroup cg_hs_stretch @(posedge bus.clk);
         option.per_instance = 1;
         cp_stretch_after_ack : coverpoint {bus.ack_phase, !bus.scl_released} {
            bins stretched_at_ack = {2'b11};   // an ACK while somebody is holding
            bins clean_ack        = {2'b10};
         }
         cp_segments : coverpoint bus.sr_phase iff (bus.is_hs) {
            bins linked_transfer = {1'b1};     // Sr WITHOUT leaving Hs-mode
         }
      endgroup

      cg_hs_stretch cg = new();
   endmodule

What to randomise, and what not to. Randomise the master code across its eight values and the stretch duration after each acknowledge — those are the dimensions that matter. Do not randomise whether the master code is acknowledged: a device acknowledging it is a DUT bug, not a stimulus variation, and injecting it belongs in a directed error test where the expected response is the ack_violation report of §9.

12. FPGA and ASIC Implications

Hs-mode needs an analogue block, and that is usually the decision. A 3 to 12 mA controlled current source with a specified 40 ns edge into 100 pF is not something an FPGA I/O can produce. There is no generic FPGA primitive for it, which means Hs-mode on an FPGA requires an external Hs-mode-capable buffer or it is not Hs-mode. This single fact, more than any other, is why the mode is rare.

The input filter has to be five times tighter. Table 11 gives tSP as 0…10 ns in Hs-mode against Table 10's 0…50 ns in Fast-mode. A digital filter that suppresses 50 ns spikes by sampling a 20 MHz clock cannot suppress 10 ns ones; the sampling clock has to rise proportionally, and on an FPGA that means a dedicated clock region for the I²C front end.

The two input filters must be switchable, and the switch is timed. §5.3.3 requires every connected device — including losing masters and all slaves — to adapt its filters between t1 and tH, and to switch back within Fast-mode tBUF at tFS. That is two reconfigurations per transaction, on a deadline, in every device on the bus. It is a considerable amount of machinery for a mode that will then run for a few microseconds.

Slope control on SDAH only, for slaves. §5.3.3 is specific: "For slave devices, slope control is applicable for the SDAH output stage only" — a slave never drives the clock except to stretch it, and stretching is a pull-down whose edge rate matters less. It also notes that "both the Fast-mode and Hs-mode requirements may be fulfilled without switching its internal circuit", which is the one place the specification lets a device skip a reconfiguration.

Do the amortisation arithmetic before choosing the mode. The preamble is a START, eight bits and a not-acknowledge at F/S speed. At 400 kbit/s that is roughly 25 µs. A two-byte Hs-mode transfer is about 6 µs. One transfer behind one preamble is slower than Fast-mode; the mode pays only when transfers are linked behind a single master code, which is why §5.3.2 mentions linking explicitly.

A bridge is a separate device with its own timing obligations. §5.3.8 requires the bridge's actions at t1, tH and tFS to be "so fast that it does not affect the SDAH and SCLH lines", and requires TR1 and TR2 to close within Fast-mode tBUF. That is a real specification on a real block, and it is why the bridge is normally integrated into an Hs-mode master rather than built discretely.

13. Debugging — The Slow Slave That Corrupts Only In Hs-Mode

Symptom

An Hs-mode bus works perfectly with three of four slaves. The fourth — an EEPROM with an internal write cycle — returns corrupt data when read immediately after a write, but only in Hs-mode. The same sequence at 400 kbit/s is flawless. A scope shows a clean 3.4 MHz clock with no missing pulses and every acknowledge present.

Root Cause

The master never disables its current source. §5.3.2 requires it off after every acknowledge, and this master leaves it enabled for the whole burst. The EEPROM does try to stretch — it pulls SCLH low with 3 mA — but it is fighting a 12 mA current source and cannot drag the line below VIL. The master sees a high clock, clocks on, and reads the EEPROM during its internal write cycle. The identical trCL before and after acknowledges was the direct evidence: a compliant master would show a resistor-only trCL1 there, roughly twice as slow.

Fix
Disable the current source after every acknowledge and every repeated START, and re-enable only once SCLH is observed high. The stretch then works, trCL1 appears in the capture as a visibly slower edge after each acknowledge, and the read returns correct data. Until the master is fixed, the workaround is to run this slave's transactions in F/S-mode.

Three things about that investigation generalise.

The decisive evidence was an edge rate, not a protocol event. trCL and trCL1 being identical proves the current source never switched off, and it is visible on any scope with enough bandwidth. No protocol analyser would have shown it, because nothing at the protocol level was wrong.

The absence of stretching was the symptom, not the cause. "SCLH shows no stretched low periods" reads like the slave failing to stretch. It was the slave stretching and being overpowered — and the two look identical on a logic analyser, which sees only that the line stayed high.

It works with three of four slaves because three of them never needed to stretch. The defect is invisible until a slave with real internal latency appears, which is often after the board ships. A master that counts its current-source disables, as §9's design does, would have failed its own self-check on the first burst.

14. Common Misconceptions

"Hs-mode is just a faster Fast-mode." It adds a mandatory preamble, a current-source pull-up, a bridged bus topology, and the removal of arbitration and synchronization during the transfer. It is the one mode that changes the protocol.

"Arbitration doesn't work in Hs-mode." Arbitration happens, at F/S speed, during the master code and the not-acknowledge — and it must complete there, because during the fast phase there is only one master. §1.

"The not-acknowledge after the master code means something went wrong." It is mandatory. No device may acknowledge the master code, so the ninth bit must read high. §2.

"An Hs-mode master always wins the bus." A master sending a reserved code 0000 0XXX beats 0000 1XXX at bit b3, and then the bridge stays closed and the transfer proceeds in F/S-mode. §3.

"Eight Hs-mode masters are supported." Eight codes exist and 0000 1000 is reserved for test and diagnostics, leaving seven for use. §2.

"The current source is on throughout the high-speed transfer." It is off during the preamble, off after every repeated START, and off after every acknowledge — which is what makes clock stretching physically possible. §5.

"Clock stretching is not allowed in Hs-mode." It is allowed, at one point: after the acknowledge bit. And the current source is disabled there precisely so that it can work. §5.

"trCL1 is a safety margin." It is the rise time of a resistor-only pull-up, because the current source is switched off in those intervals. Exactly double trCL, at both capacitance corners. §5a.

"A repeated START returns the bus to F/S-mode." Only a STOP does. Repeated STARTs are how several Hs-mode transfers share one master code. §8.

"3.4 Mbit/s is where the timing budget closes." The budget closes at 3⅓ MHz. 3.4 is that number rounded up, and at a true 3.4 MHz the edges must beat their own maxima by about 7 %. §6.

15. Reason It Through

A master sends 0000 1010 as its master code. Another master simultaneously sends the general call address 0000 0000. Who wins, and what happens next?

Compare MSB first. Both send 0 for b7, b6, b5, b4. At b3 the Hs-mode master releases (its bit is 1) and the general-call master pulls low (its bit is 0). The line reads 0, the Hs-mode master reads back a mismatch and loses. The general call proceeds in F/S-mode, the bridge stays closed, and the Hs-mode master waits for a STOP before retrying. The high-speed phase never happens.

Why can there not be two current sources enabled at once, given that a stronger pull-up would only make the edge faster?

Because the rise time is specified from both ends. Table 12 gives trCL as 10…40 ns at 100 pF — a minimum of 10 ns as well as a maximum of 40. Two sources at up to 12 mA each could produce an edge below 10 ns, which is a violation in the same way Fast-mode's 20 ns tr floor is (Chapter 14.1 §6). "Faster is better" is not true on a transmission line.

An Hs-mode master performs one 2-byte write per master code. Is this faster than Fast-mode?

No. The preamble is a START, 8 bits and a not-acknowledge at 400 kbit/s — about 25 µs. The Hs-mode payload, 2 bytes plus addressing at 3.4 Mbit/s, is roughly 6 µs. Total about 31 µs. The same transaction entirely in Fast-mode is about 75 µs, so this particular case is faster — but only 2.4×, not 8.5×, and the advantage vanishes for a single-byte transfer. Linking several transfers behind one master code is what recovers the ratio, which is why §5.3.2 recommends it.

During an Hs-mode burst, what do the Standard-mode devices on the other side of the bridge believe is happening?

That SDA has been low for a long time with SCL high, and then a STOP occurred. §8b: the bridge parks the F/S segment in the set-up state for a STOP for the whole burst. They never observe anything at 3.4 Mbit/s, and from their point of view one ordinary transaction ended normally.

A slave holds SCLH low for 500 ns after an acknowledge. The master's current source is off, as required. What does Table 12 say about the resulting rising edge?

That it must complete within trCL1, not trCL — 80 ns at 100 pF rather than 40 ns — because the current source is still off at the moment of release. The master re-enables it "when all devices have released and the SCLH signal reaches a HIGH level", so the source contributes only to the last part of the edge, which is what §5.3.2's phrase "speeds up the last part of the rise time" means literally.

Why does a losing master adapt its input filters to Hs-mode when it will not be transmitting?

Because it has to keep watching the bus to detect the STOP that tells it the bus is free (Chapter 13.4). Watching a 3.4 Mbit/s bus with a Fast-mode filter that swallows pulses up to 50 ns would mean missing edges — including, possibly, the STOP itself. Adapting to listen is a different obligation from adapting to drive, and §5.3.3 lists only the first for losing masters.

16. Understanding Check

17. Summary

Hs-mode is the one speed category that changes the protocol. It adds a mandatory preamble, an electrical regime only one device may activate, a bridged topology, and the removal of arbitration and synchronization during the fast phase.

The preamble exists to pick a winner. START, an 8-bit master code 0000 1XXX, and a mandatory not-acknowledge — all at F/S speed. Arbitration completes there, which is why it need not happen afterwards.

One bit decides whether Hs-mode happens. A reserved 0000 0XXX beats the master code at b3, the fifth bit sent, and the transfer then proceeds in F/S-mode with the bridge closed.

Seven usable master codes, not eight. 0000 1000 is reserved for test and diagnostics.

The current source is on the clock only, one master only, Hs-mode only — and it is off after every repeated START and every acknowledge, which is what makes clock stretching possible at the one point the specification permits it.

trCL1 is that rule, expressed as a rise time. Twice trCL at both corners, because the current source is switched off and the resistor is doing the work alone. A timing parameter that exists because of a protocol permission.

3.4 Mbit/s is a rounded number. The Hs-mode budget is exact at 3⅓ MHz and over by 2.00 % at both capacitance corners — which means at the headline rate the edges must come in about 7 % under their own maxima, and that is what the current source is for.

Only a STOP leaves Hs-mode, within Fast-mode tBUF. Repeated STARTs link transfers behind one master code, and the amortisation matters: one short transfer per preamble is slower than Fast-mode.

The bridge earns the speed by isolation. Table 8's Hs → Hs row holds at 3.4 Mbit/s in a mixed system only because the slow devices have been disconnected; every non-isolated pairing collapses to the slowest device present.

The F/S segment spends the burst parked in a pre-STOP state — SCL high, SDA low — and sees one ordinary transaction ending, never anything fast.

18. What Comes Next

Every mode so far has been open-drain and bidirectional. Chapter 14.3 takes the one that is neither.

Ultra Fast-mode reaches 5 Mbit/s by replacing the open-drain output with a push-pull driver and making both lines unidirectional. Those two changes are simple to state and they take away, in one go, almost everything the previous twelve modules were built on: the acknowledge, arbitration, clock synchronization, clock stretching, multi-master operation, the device ID, and the ability to read anything back at all.

Table 6 lists five of those features as "n/p" — not possible. Not optional, not unimplemented: structurally impossible on a bus where no slave may ever drive a line. The chapter takes the mode seriously on its own terms, works out what a transfer looks like when the ninth bit is always a master-driven one, and asks the question the specification itself answers with unusual candour: if nothing can be read back, how does a master ever learn that anything arrived?

Continue learning