Skip to content
VLSI Mentor

I²C · Module 11

tVD;DAT and tVD;ACK — I²C Data Valid Time

The module's first maximum, and it inverts everything: the comparison, the worst-case tracker, and what a passing measurement means. Plus why the acknowledge gets its own parameter when Table 10 gives it an identical number.

Every parameter so far has been a minimum. tLOW, tHIGH, tSU;DAT, tHD;DAT — all of them say wait at least this long, and for all of them waiting longer is always compliant. That is why clock stretching is legal and why Chapter 11.2 could note that a 30 µs low phase raises nothing.

tVD;DAT is the module's first maximum, and a maximum inverts everything about how a parameter is checked:

a minimum (tSU;DAT)a maximum (tVD;DAT)
the comparisonmeasured < limit failsmeasured > limit fails
the worst case is thesmallest value seenlargest value seen
the tracker is initialised toits maximumzero
waiting longer isalways safethe violation itself

Every one of those four lines is a place a checker written by analogy with the previous chapter is wrong. §6 keeps a minimum-tracker and a maximum-tracker side by side in one design so the difference is visible rather than remembered.

1. The Two Rows

Three things to extract.

Both are referenced to SCL going LOW — the same edge tHD;DAT is referenced to. So one falling edge starts two intervals with opposite senses: a minimum before SDA may move, and a maximum by which it must have finished moving. §3 draws that.

The two parameters carry identical numbers in all three modes. 3.45, 0.9, 0.45 — no difference anywhere. That invites the question §2 answers, and it creates the testing hazard §8 records.

The footnote wording says "minimum time … to be valid", which reads backwards for a maximum. It is not a contradiction: the parameter is a maximum on the delay, and the footnote is describing when the data becomes valid — the latest instant at which validity may begin. The table's column heading is authoritative, and the column is max.

2. Why the Acknowledge Gets Its Own Parameter

If tVD;ACK has the same value as tVD;DAT in every speed mode, why is it a separate row?

Because a different device drives it. During data bits 1 through 8 of a write, the master drives SDA. During bit 9 — the acknowledge — the slave drives it (Chapter 7.2). The obligation changes hands inside a single byte, and a specification that binds devices rather than wires has to name both obligations even when the numbers agree.

That is not pedantry, and it has two concrete consequences.

A device may satisfy one and violate the other. A master with a fast data path and a slow acknowledge path — quite normal, because the acknowledge often depends on an address decode or a FIFO's fullness rather than on a shift register — meets tVD;DAT comfortably and can miss tVD;ACK. A checker collapsing the two would report a tVD;DAT violation for a defect in the acknowledge path, sending the investigation to the wrong logic.

The numbers may diverge in a future revision or a vendor's part. The specification's structure allows it; a design that hard-wires them together does not.

3. One Edge, Two Opposite Obligations

tHD;DAT is a floor and tVD;DAT is a ceiling, from the same edge

10 cycles
Ten intervals. SCL is high for the first, low for the next eight, and high for the last. SDA holds its old value for three intervals after SCL falls, then changes. A region row marks the hold floor immediately after the falling edge, the window in which the transition must occur, and the late region beyond the data-valid ceiling in which a transition would be a violation.tHD;DAT floortHD;DAT floorlegal windowlegal windowtoo latetoo lateSCL LOW: both start hereSCL LOW: both start heretransition inside the windowtransition inside thewindowbeyond tVD;DAT: violationbeyond tVD;DAT: violationsclsdaregion0floorfloorfloorokoklatelatelatelatet0t1t2t3t4t5t6t7t8t9
SCL's falling edge starts two intervals with opposite senses. The hold is a floor on when SDA may begin to move; the data-valid time is a ceiling on when it must have finished. The gap between them is the transmitter's actual working room.

The figure makes the geometry explicit: the transmitter's working room is the gap between a floor and a ceiling, both measured from the same edge. Too early violates tHD;DAT; too late violates tVD;DAT. Neither parameter alone describes the window, which is why Chapter 11.9 adds them rather than choosing between them.

And note what the ceiling does not cover. It bounds how late a new value may become valid, so it says nothing about a bit whose value does not change — that bit was valid before the low phase began. §5 works that case through, and it is the one most people get wrong.

4. The Relationship That Runs Into the Next Chapters

tVD;DAT does not stand alone. What has to fit inside one low phase is the transmitter's response, the line's settling, and the receiver's setup:

tVD;DAT(max) + tSU;DAT(min) + tr(max) ≤ tLOW(min)

modetVD;DATtSU;DATtrsumtLOW(min)slack
Standard3.450.2501.0004.700 µs4.700 µs0
Fast0.90.1000.3001.300 µs1.300 µs0
Fm+0.450.0500.1200.620 µs0.500 µs−0.120 µs

Two results, and the second is the more interesting.

In Standard-mode and Fast-mode the inequality closes exactly. Not with margin — exactly. This is the same internal consistency Chapter 11.2 §4 found in the clock envelope, and it confirms that tLOW(min) is the sum of its contents rather than a chosen round number.

At Fast-mode Plus it does not close. 0.62 µs of obligation into a 0.5 µs low phase — a 120 ns deficit, which is exactly tr(max). So at Fm+ a device cannot simultaneously take its full tVD;DAT, suffer the maximum rise time, and still deliver tSU;DAT inside the minimum low phase. Something must give, and in practice it is the rise time: Fm+ is specified with the expectation of a current-source pull-up rather than a resistor, which is Chapter 11.7's subject.

5. The Bit That Does Not Change

A maximum on a delay raises a question a minimum never does: what happens when the transition the parameter bounds never occurs?

The transmitter is supposed to produce a bit. Suppose SDA does not move at all through the whole low phase. The armed counter runs and is never stopped, so no comparison is ever made — and the instinct is that this must be the worst possible violation, reported the moment the counter passes the limit.

It is not a violation, and the reason is worth working through carefully.

tVD;DAT bounds the time from SCL going low until SDA is valid. If the next bit has the same value as the last, SDA was already valid when the low phase began — validity was never lost, so there is no interval during which the line was not carrying the right value. The transmitter kept the bit it already had, and that trivially satisfies any bound on how long it may take to establish one.

So a repeated bit produces no measurement and no violation, and the arm must be cancelled when the low phase ends:

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker.sv — a repeated bit has no interval to measure
   end else if (scl_rise) begin
      // The low phase ended with SDA never moving, so this interval has no end and there is
      // nothing to measure: the transmitter simply kept the bit it already had, which
      // trivially meets any maximum.
      armed <= 1'b0;
   end

But there is a subtlety that cost a real bug, recorded in §6a: the arm must be cancelled at the rising edge specifically. If it survives past the rise, a legal SDA change in some later phase is measured against a falling edge that is long gone.

6. The Data-Valid Checker in Three Languages

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker.sv — two maxima, two drivers, one measurement shape
   // DATA VALID TIME: tVD;DAT and tVD;ACK. These are the transmitter's side of the bargain
   // whose receiver side is tSU;DAT, and they differ from every parameter checked so far in
   // one structural way: they are MAXIMA, not minima.
   //
   //   [11] "tVD;DAT = time for data signal from SCL LOW to SDA output (HIGH or LOW,
   //         depending on which one is worse)."
   //   [12] "tVD;ACK = time for Acknowledgement signal from SCL LOW to SDA output (HIGH or
   //         LOW, depending on which one is worse)."
   //
   // So the interval starts when SCL goes LOW and ends when SDA is valid, and it must not
   // EXCEED the table's value. That inverts the comparison, and with it the meaning of every
   // margin: for a minimum, more measured time is safer; for a maximum, more is a violation.
   // A checker that got the sense wrong would pass a broken bus and fail a good one.
   //
   // WHY THE ACKNOWLEDGE GETS ITS OWN PARAMETER, when the numbers are identical in every
   // speed mode: because a different DEVICE drives it. The data bits of a byte come from one
   // device and the ninth bit comes from the other, so tVD;ACK is a promise made by the
   // device that is answering rather than by the device that is sending. Two parameters with
   // the same number are still two obligations, and a capture that violates one of them tells
   // you WHICH device is slow. Collapsing them loses exactly that.
   //
   // And the reason the limit exists at all: tVD;DAT(max) and tSU;DAT(min) must both fit
   // inside tLOW, because the transmitter has to get the bit out and the receiver has to see
   // it settle, both before the next rising edge. Adding the two maxima from Table 10 gives
   // 1.0 us against a 1.3 us tLOW in Fast-mode -- 0.3 us of slack, which is exactly tr(max).
   // In Fast-mode Plus the same sum is 0.5 us against a tLOW(min) of 0.5 us: EXACTLY tight,
   // with no room for the rise time at all. So an Fm+ transmitter cannot actually take its
   // full tVD;DAT on a bus with any rise time worth measuring.
   //
   // PASSIVE: observes the wires plus one input saying which slot this is.
   module i2c_data_valid_checker #(
       parameter int TICK_W = 16,
       // Fast-mode maxima, in ticks of a 100 MHz sample clock: 0.9 us = 90 ticks for both.
       parameter int T_VD_DAT_MAX = 90,
       parameter int T_VD_ACK_MAX = 90
   )(
       input  logic clk,
       input  logic rst_n,
       input  logic sda_in,
       input  logic scl_in,
       // High during the ninth clock slot of a byte, from Chapter 7.2's acknowledge engine.
       // The parameter that applies depends on WHOSE promise this slot is, and only a
       // byte-level block knows that.
       input  logic ack_slot,

       // ---- measured ----
       output logic              vd_valid,       // pulse: a data-valid time has been measured
       output logic [TICK_W-1:0] t_vd,
       output logic              vd_was_ack,     // which parameter this measurement is judged by

       // ---- verdicts: MAXIMA, so the comparison is GREATER THAN ----
       output logic viol_vd_dat,
       output logic viol_vd_ack,

       // ---- totals and worst cases. For a MAXIMUM the worst case is the LARGEST value,
       //      which is the opposite of every other tracker in this module -- and a tracker
       //      copied from a minimum checker would silently report the wrong extreme.
       output logic [TICK_W-1:0] n_vd_dat,
       output logic [TICK_W-1:0] n_vd_ack,
       output logic [TICK_W-1:0] n_viol,
       output logic [TICK_W-1:0] max_vd_dat_seen,
       output logic [TICK_W-1:0] max_vd_ack_seen
   );
       logic sda_q, scl_q;
       logic scl_fall, scl_rise, sda_changed;
       assign scl_fall    =  scl_q && !scl_in;
       assign scl_rise    = !scl_q &&  scl_in;
       assign sda_changed = sda_q != sda_in;

       // The interval STARTS at the falling edge of SCL, so a timer armed by that edge
       // measures it directly -- the same shape as the hold time in Chapter 11.3 and the
       // opposite of a setup time, which has already finished by the time its reference edge
       // arrives.
       logic              armed;
       logic              armed_is_ack;   // latched at the arming edge, not read at the end
       logic [TICK_W-1:0] vd_ticks;
       logic [TICK_W-1:0] vd_now;
       assign vd_now = vd_ticks + 1'b1;

       always_ff @(posedge clk) begin
           if (!rst_n) begin
               sda_q           <= 1'b1;
               scl_q           <= 1'b1;
               armed           <= 1'b0;
               armed_is_ack    <= 1'b0;
               vd_ticks        <= '0;
               vd_valid        <= 1'b0;
               t_vd            <= '0;
               vd_was_ack      <= 1'b0;
               viol_vd_dat     <= 1'b0;
               viol_vd_ack     <= 1'b0;
               n_vd_dat        <= '0;
               n_vd_ack        <= '0;
               n_viol          <= '0;
               // A MAXIMUM tracker starts at ZERO so the first measurement replaces it. The
               // minimum trackers elsewhere in this module start at all-ones for the same
               // reason, inverted -- and swapping the two initialisations is a defect that
               // reports a plausible number forever.
               max_vd_dat_seen <= '0;
               max_vd_ack_seen <= '0;
           end else begin
               sda_q <= sda_in;
               scl_q <= scl_in;

               vd_valid <= 1'b0;

               if (scl_fall) begin
                   // Arm, and latch WHICH promise is being measured. Reading ack_slot at the
                   // end of the interval would be wrong: the slot boundary can move between
                   // the falling edge and the SDA transition, and the obligation belongs to
                   // whoever owned the slot when the clock fell.
                   armed        <= 1'b1;
                   armed_is_ack <= ack_slot;
                   vd_ticks     <= '0;
               end else if (armed) begin
                   if (sda_changed) begin
                       armed      <= 1'b0;
                       vd_valid   <= 1'b1;
                       t_vd       <= vd_now;
                       vd_was_ack <= armed_is_ack;

                       if (armed_is_ack) begin
                           viol_vd_ack <= (vd_now > T_VD_ACK_MAX[TICK_W-1:0]);
                           viol_vd_dat <= 1'b0;
                           n_vd_ack    <= n_vd_ack + 1'b1;
                           if (vd_now > T_VD_ACK_MAX[TICK_W-1:0]) n_viol <= n_viol + 1'b1;
                           if (vd_now > max_vd_ack_seen) max_vd_ack_seen <= vd_now;
                       end else begin
                           viol_vd_dat <= (vd_now > T_VD_DAT_MAX[TICK_W-1:0]);
                           viol_vd_ack <= 1'b0;
                           n_vd_dat    <= n_vd_dat + 1'b1;
                           if (vd_now > T_VD_DAT_MAX[TICK_W-1:0]) n_viol <= n_viol + 1'b1;
                           if (vd_now > max_vd_dat_seen) max_vd_dat_seen <= vd_now;
                       end
                   end else if (scl_rise) begin
                       // The low phase ended with SDA never moving, so this interval has no
                       // end and there is nothing to measure: the transmitter simply kept
                       // the bit it already had, which trivially meets any maximum.
                       //
                       // Disarming here is NOT optional. Without it the timer keeps running
                       // across the high phase and the next SDA change -- possibly a framing
                       // event, possibly the next byte -- is reported as a data-valid time,
                       // producing a huge measurement and a violation on entirely legal
                       // traffic. A checker that raises false alarms is worse than none.
                       armed <= 1'b0;
                   end else begin
                       vd_ticks <= vd_now;
                   end
               end
           end
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker_tb.sv — ten scenarios, the two limits deliberately different
   `timescale 1ns/1ps
   // 100 MHz sample clock. Fast-mode tVD;DAT(max) = tVD;ACK(max) = 0.9 us = 90 ticks.
   // These are MAXIMA, so every boundary test here runs the opposite way round from the
   // minimum checkers of Chapters 11.1 to 11.3.
   module i2c_data_valid_checker_tb;
       localparam int TICK_W       = 16;
       localparam int T_VD_DAT_MAX = 90;
       // Deliberately DIFFERENT from T_VD_DAT_MAX. Table 10 gives the two the same number in
       // every speed mode, and a testbench that copied that could not tell whether the design
       // applies them independently -- the mutation that judges an acknowledge by the data limit
       // is unobservable while the two are equal. Two parameters with one value are still two
       // obligations, so the test uses two values.
       localparam int T_VD_ACK_MAX = 60;

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

       logic rst_n = 1'b0;
       logic sda_in = 1'b1, scl_in = 1'b1, ack_slot = 1'b0;

       logic vd_valid, vd_was_ack, viol_vd_dat, viol_vd_ack;
       logic [TICK_W-1:0] t_vd, n_vd_dat, n_vd_ack, n_viol;
       logic [TICK_W-1:0] max_vd_dat_seen, max_vd_ack_seen;

       int errors = 0;
       int base;
       logic [TICK_W-1:0] viol_before, ndat_before;

       i2c_data_valid_checker #(.TICK_W(TICK_W), .T_VD_DAT_MAX(T_VD_DAT_MAX),
           .T_VD_ACK_MAX(T_VD_ACK_MAX)) dut (.*);

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

       logic [TICK_W-1:0] vd_log [0:31];
       logic ack_log [0:31];
       logic vdat_log [0:31];
       logic vack_log [0:31];
       int n_log;
       always @(posedge clk) if (rst_n && vd_valid && n_log < 32) begin
           vd_log[n_log]   = t_vd;        ack_log[n_log]  = vd_was_ack;
           vdat_log[n_log] = viol_vd_dat; vack_log[n_log] = viol_vd_ack;
           n_log++;
       end

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

       // One bit whose DATA VALID time is settable: SCL falls, then `vd` ticks pass, then the
       // transmitter gets SDA to its new value. `is_ack` says whose promise this slot is.
       task automatic vd_bit(input logic v, input int vd, input int rest, input logic is_ack);
           begin
               ack_slot = is_ack;
               scl_in   = 1'b0;  tick(vd);      // the transmitter's response time
               sda_in   = v;     tick(rest);    // settled, waiting for the rising edge
               scl_in   = 1'b1;  tick(40);
           end
       endtask

       initial begin
           tick(3);
           // A MAXIMUM tracker must start at ZERO, the opposite of a minimum tracker.
           if (max_vd_dat_seen !== '0 || max_vd_ack_seen !== '0) begin
               $display("FAIL: the maximum trackers did not start at zero"); errors++; end
           rst_n = 1'b1; tick(2);
           sda_in = 1'b1; scl_in = 1'b1; tick(10);

           // ---- 1: four LEGAL data bits. 40 ticks of response where 90 is the limit --
           //      well inside, so nothing may be flagged. For a maximum, SMALLER is safer.
           vd_bit(1'b0, 40, 60, 1'b0);
           vd_bit(1'b1, 40, 60, 1'b0);
           vd_bit(1'b0, 40, 60, 1'b0);
           vd_bit(1'b1, 40, 60, 1'b0);
           if (n_viol !== '0) begin
               $display("FAIL: %0d legal bits flagged", n_viol); errors++; end
           if (n_vd_dat < 16'd4) begin
               $display("FAIL: %0d data measurements, expected 4", n_vd_dat); errors++; end
           if (vd_log[1] < 16'd35 || vd_log[1] > 16'd45) begin
               $display("FAIL: a 40-tick data-valid time measured %0d", vd_log[1]); errors++; end
           if (ack_log[1] !== 1'b0) begin
               $display("FAIL: a data slot was judged as an acknowledge"); errors++; end

           // ---- 2: a SLOW transmitter. 130 ticks of response where the limit is 90. For a
           //      MAXIMUM this is the violation direction -- and a checker whose comparison
           //      had the wrong sense would have passed this and failed test 1.
           begin
               base = n_log;
               vd_bit(1'b0, 130, 40, 1'b0);
               if (vdat_log[base] !== 1'b1) begin
                   $display("FAIL: a 130-tick response was not flagged (max %0d)", T_VD_DAT_MAX);
                   errors++; end
               if (vack_log[base] !== 1'b0) begin
                   $display("FAIL: a slow DATA slot also flagged the ACKNOWLEDGE parameter");
                   errors++; end
           end

           // ---- 3: BOUNDARY. Exactly AT the maximum is legal, because the table's value is
           //      a limit that may be reached. One tick above is not.
           begin
               base = n_log;
               vd_bit(1'b1, T_VD_DAT_MAX, 40, 1'b0);
               if (vdat_log[base] !== 1'b0) begin
                   $display("FAIL: a response of EXACTLY the maximum (%0d) was rejected",
                            T_VD_DAT_MAX); errors++; end
           end
           begin
               base = n_log;
               vd_bit(1'b0, T_VD_DAT_MAX + 1, 40, 1'b0);
               if (vdat_log[base] !== 1'b1) begin
                   $display("FAIL: a response one tick ABOVE the maximum was accepted"); errors++; end
           end

           // ---- 4: the ACKNOWLEDGE slot is judged by its OWN parameter and counted
           //      separately, even though the numbers are identical. The point is to know
           //      WHICH DEVICE was slow, and that is lost if the two are merged.
           //      The value alternates from the previous bit: a response-time measurement
           //      needs an actual SDA TRANSITION, and repeating a value produces none.
           begin
               base = n_log;
               vd_bit(1'b1, 40, 60, 1'b1);
               if (ack_log[base] !== 1'b1) begin
                   $display("FAIL: an acknowledge slot was judged as data"); errors++; end
               if (n_vd_ack !== 16'd1) begin
                   $display("FAIL: n_vd_ack = %0d after one acknowledge slot", n_vd_ack);
                   errors++; end
           end

           // ---- 5: a SLOW ACKNOWLEDGE. Only the acknowledge verdict may fire, and the
           //      acknowledge worst-case tracker must move while the data one does not.
           begin
               base = n_log;
               viol_before = n_viol;
               vd_bit(1'b0, 140, 40, 1'b1);
               if (vack_log[base] !== 1'b1) begin
                   $display("FAIL: a 140-tick acknowledge response was not flagged"); errors++; end
               if (vdat_log[base] !== 1'b0) begin
                   $display("FAIL: a slow ACKNOWLEDGE also flagged the DATA parameter"); errors++; end
               if (n_viol !== viol_before + 16'd1) begin
                   $display("FAIL: the violation total did not advance"); errors++; end
               if (max_vd_ack_seen < 16'd135) begin
                   $display("FAIL: max_vd_ack_seen = %0d after a 140-tick acknowledge",
                            max_vd_ack_seen); errors++; end
           end

           // ---- 6: the slot identity is latched at the ARMING edge, not read at the end.
           //      Here ack_slot drops midway through the interval; the measurement must
           //      still be judged as an ACKNOWLEDGE, because that is whose promise it was
           //      when the clock fell.
           begin
               base = n_log;
               ack_slot = 1'b1;
               scl_in = 1'b0;  tick(30);
               ack_slot = 1'b0;                 // the slot boundary moves mid-interval
               tick(30);
               sda_in = 1'b1;  tick(40);        // alternated, so a transition occurs
               scl_in = 1'b1;  tick(40);
               if (ack_log[base] !== 1'b1) begin
                   $display("FAIL: the slot identity was read at the END of the interval, not latched at its start");
                   errors++; end
           end

           // ---- 6b: THE SEPARATION TEST. A 70-tick response is LEGAL for data (max 90) and
           //      ILLEGAL for an acknowledge (max 60). One stimulus, two verdicts, decided only
           //      by which parameter the slot is judged against.
           begin
               base = n_log;
               vd_bit(1'b0, 70, 40, 1'b0);          // a DATA slot: legal
               if (vdat_log[base] !== 1'b0) begin
                   $display("FAIL: a 70-tick DATA response was flagged against a 90-tick limit");
                   errors++; end
               base = n_log;
               vd_bit(1'b1, 70, 40, 1'b1);          // an ACKNOWLEDGE slot: illegal
               if (vack_log[base] !== 1'b1) begin
                   $display("FAIL: a 70-tick ACKNOWLEDGE response was not flagged against its own 60-tick limit -- the slot is being judged by the DATA parameter");
                   errors++; end
           end

           // ---- 7: the worst case for a MAXIMUM is the LARGEST value seen, and a run of
           //      fast bits must not erase it. A tracker copied from a minimum checker would
           //      move the wrong way here.
           begin
               ack_slot = 1'b0;
               repeat (4) begin
                   vd_bit(1'b0, 20, 60, 1'b0);
                   vd_bit(1'b1, 20, 60, 1'b0);
               end
               if (max_vd_dat_seen < 16'd90) begin
                   $display("FAIL: max_vd_dat_seen fell to %0d after fast bits -- a maximum tracker must not decrease",
                            max_vd_dat_seen); errors++; end
           end

           // ---- 8: a REPEATED bit produces no measurement. If SDA never moves there is no
           //      response time to measure, and a transmitter that keeps the line where it
           //      already is has trivially met any maximum.
           begin
               ndat_before = n_vd_dat;
               sda_in = 1'b0; scl_in = 1'b0; tick(120);   // already 1 from test 7's last bit
               scl_in = 1'b1; tick(60);
               scl_in = 1'b0; tick(120);                  // SDA does not move: no measurement
               scl_in = 1'b1; tick(60);
               if (n_vd_dat !== ndat_before) begin
                   $display("FAIL: a repeated bit produced %0d measurements",
                            n_vd_dat - ndat_before); errors++; end
           end

           // ---- 9: an IDLE bus measures nothing.
           begin
               ndat_before = n_vd_dat;
               sda_in = 1'b1; scl_in = 1'b1; tick(400);
               if (n_vd_dat !== ndat_before) begin
                   $display("FAIL: an idle bus produced measurements"); errors++; end
           end

           if (errors == 0)
               $display("PASS: a MAXIMUM is checked the other way round, the acknowledge is judged by its own parameter, the slot identity is latched at the arming edge, the worst case is the largest");
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker.v — the same checker in Verilog-2001
   // DATA VALID TIME: tVD;DAT and tVD;ACK. These are the transmitter's side of the bargain
   // whose receiver side is tSU;DAT, and they differ from every parameter checked so far in
   // one structural way: they are MAXIMA, not minima.
   //
   //   [11] "tVD;DAT = time for data signal from SCL LOW to SDA output (HIGH or LOW,
   //         depending on which one is worse)."
   //   [12] "tVD;ACK = time for Acknowledgement signal from SCL LOW to SDA output (HIGH or
   //         LOW, depending on which one is worse)."
   //
   // So the interval starts when SCL goes LOW and ends when SDA is valid, and it must not
   // EXCEED the table's value. That inverts the comparison, and with it the meaning of every
   // margin: for a minimum, more measured time is safer; for a maximum, more is a violation.
   // A checker that got the sense wrong would pass a broken bus and fail a good one.
   //
   // WHY THE ACKNOWLEDGE GETS ITS OWN PARAMETER, when the numbers are identical in every
   // speed mode: because a different DEVICE drives it. The data bits of a byte come from one
   // device and the ninth bit comes from the other, so tVD;ACK is a promise made by the
   // device that is answering rather than by the device that is sending. Two parameters with
   // the same number are still two obligations, and a capture that violates one of them tells
   // you WHICH device is slow. Collapsing them loses exactly that.
   //
   // And the reason the limit exists at all: tVD;DAT(max) and tSU;DAT(min) must both fit
   // inside tLOW, because the transmitter has to get the bit out and the receiver has to see
   // it settle, both before the next rising edge. Adding the two maxima from Table 10 gives
   // 1.0 us against a 1.3 us tLOW in Fast-mode -- 0.3 us of slack, which is exactly tr(max).
   // In Fast-mode Plus the same sum is 0.5 us against a tLOW(min) of 0.5 us: EXACTLY tight,
   // with no room for the rise time at all. So an Fm+ transmitter cannot actually take its
   // full tVD;DAT on a bus with any rise time worth measuring.
   //
   // PASSIVE: observes the wires plus one input saying which slot this is.
   // (Verilog-2001)
   module i2c_data_valid_checker #(
       parameter TICK_W = 16,
       // Fast-mode maxima, in ticks of a 100 MHz sample clock: 0.9 us = 90 ticks for both.
       parameter T_VD_DAT_MAX = 90,
       parameter T_VD_ACK_MAX = 90
   )(
       input  wire  clk,
       input  wire  rst_n,
       input  wire  sda_in,
       input  wire  scl_in,
       // High during the ninth clock slot of a byte, from Chapter 7.2's acknowledge engine.
       // The parameter that applies depends on WHOSE promise this slot is, and only a
       // byte-level block knows that.
       input  wire  ack_slot,

       // ---- measured ----
       output reg                vd_valid,       // pulse: a data-valid time has been measured
       output reg   [TICK_W-1:0] t_vd,
       output reg                vd_was_ack,     // which parameter this measurement is judged by

       // ---- verdicts: MAXIMA, so the comparison is GREATER THAN ----
       output reg   viol_vd_dat,
       output reg   viol_vd_ack,

       // ---- totals and worst cases. For a MAXIMUM the worst case is the LARGEST value,
       //      which is the opposite of every other tracker in this module -- and a tracker
       //      copied from a minimum checker would silently report the wrong extreme.
       output reg   [TICK_W-1:0] n_vd_dat,
       output reg   [TICK_W-1:0] n_vd_ack,
       output reg   [TICK_W-1:0] n_viol,
       output reg   [TICK_W-1:0] max_vd_dat_seen,
       output reg   [TICK_W-1:0] max_vd_ack_seen
   );
       reg sda_q, scl_q;
       wire scl_fall, scl_rise, sda_changed;
       assign scl_fall    =  scl_q && !scl_in;
       assign scl_rise    = !scl_q &&  scl_in;
       assign sda_changed = sda_q != sda_in;

       // The interval STARTS at the falling edge of SCL, so a timer armed by that edge
       // measures it directly -- the same shape as the hold time in Chapter 11.3 and the
       // opposite of a setup time, which has already finished by the time its reference edge
       // arrives.
       reg              armed;
       reg              armed_is_ack;   // latched at the arming edge, not read at the end
       reg [TICK_W-1:0] vd_ticks;
       wire [TICK_W-1:0] vd_now;
       assign vd_now = vd_ticks + 1'b1;

       always @(posedge clk) begin
           if (!rst_n) begin
               sda_q           <= 1'b1;
               scl_q           <= 1'b1;
               armed           <= 1'b0;
               armed_is_ack    <= 1'b0;
               vd_ticks        <= {TICK_W{1'b0}};
               vd_valid        <= 1'b0;
               t_vd            <= {TICK_W{1'b0}};
               vd_was_ack      <= 1'b0;
               viol_vd_dat     <= 1'b0;
               viol_vd_ack     <= 1'b0;
               n_vd_dat        <= {TICK_W{1'b0}};
               n_vd_ack        <= {TICK_W{1'b0}};
               n_viol          <= {TICK_W{1'b0}};
               // A MAXIMUM tracker starts at ZERO so the first measurement replaces it. The
               // minimum trackers elsewhere in this module start at all-ones for the same
               // reason, inverted -- and swapping the two initialisations is a defect that
               // reports a plausible number forever.
               max_vd_dat_seen <= {TICK_W{1'b0}};
               max_vd_ack_seen <= {TICK_W{1'b0}};
           end else begin
               sda_q <= sda_in;
               scl_q <= scl_in;

               vd_valid <= 1'b0;

               if (scl_fall) begin
                   // Arm, and latch WHICH promise is being measured. Reading ack_slot at the
                   // end of the interval would be wrong: the slot boundary can move between
                   // the falling edge and the SDA transition, and the obligation belongs to
                   // whoever owned the slot when the clock fell.
                   armed        <= 1'b1;
                   armed_is_ack <= ack_slot;
                   vd_ticks     <= {TICK_W{1'b0}};
               end else if (armed) begin
                   if (sda_changed) begin
                       armed      <= 1'b0;
                       vd_valid   <= 1'b1;
                       t_vd       <= vd_now;
                       vd_was_ack <= armed_is_ack;

                       if (armed_is_ack) begin
                           viol_vd_ack <= (vd_now > T_VD_ACK_MAX);
                           viol_vd_dat <= 1'b0;
                           n_vd_ack    <= n_vd_ack + 1'b1;
                           if (vd_now > T_VD_ACK_MAX) n_viol <= n_viol + 1'b1;
                           if (vd_now > max_vd_ack_seen) max_vd_ack_seen <= vd_now;
                       end else begin
                           viol_vd_dat <= (vd_now > T_VD_DAT_MAX);
                           viol_vd_ack <= 1'b0;
                           n_vd_dat    <= n_vd_dat + 1'b1;
                           if (vd_now > T_VD_DAT_MAX) n_viol <= n_viol + 1'b1;
                           if (vd_now > max_vd_dat_seen) max_vd_dat_seen <= vd_now;
                       end
                   end else if (scl_rise) begin
                       // The low phase ended with SDA never moving, so this interval has no
                       // end and there is nothing to measure: the transmitter simply kept
                       // the bit it already had, which trivially meets any maximum.
                       //
                       // Disarming here is NOT optional. Without it the timer keeps running
                       // across the high phase and the next SDA change -- possibly a framing
                       // event, possibly the next byte -- is reported as a data-valid time,
                       // producing a huge measurement and a violation on entirely legal
                       // traffic. A checker that raises false alarms is worse than none.
                       armed <= 1'b0;
                   end else begin
                       vd_ticks <= vd_now;
                   end
               end
           end
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker_tb.v — the Verilog testbench, structurally identical
   `timescale 1ns/1ps
   // 100 MHz sample clock. Fast-mode tVD;DAT(max) = tVD;ACK(max) = 0.9 us = 90 ticks.
   // These are MAXIMA, so every boundary test here runs the opposite way round from the
   // minimum checkers of Chapters 11.1 to 11.3.
   module i2c_data_valid_checker_tb;   // Verilog-2001
       localparam TICK_W       = 16;
       localparam T_VD_DAT_MAX = 90;
       // Deliberately DIFFERENT from T_VD_DAT_MAX. Table 10 gives the two the same number in
       // every speed mode, and a testbench that copied that could not tell whether the design
       // applies them independently -- the mutation that judges an acknowledge by the data limit
       // is unobservable while the two are equal. Two parameters with one value are still two
       // obligations, so the test uses two values.
       localparam T_VD_ACK_MAX = 60;

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

       reg rst_n = 1'b0;
       reg sda_in = 1'b1, scl_in = 1'b1, ack_slot = 1'b0;

       wire vd_valid, vd_was_ack, viol_vd_dat, viol_vd_ack;
       wire [TICK_W-1:0] t_vd, n_vd_dat, n_vd_ack, n_viol;
       wire [TICK_W-1:0] max_vd_dat_seen, max_vd_ack_seen;

       integer errors = 0;
       integer base = 0;
       reg [TICK_W-1:0] viol_before, ndat_before;

       i2c_data_valid_checker #(.TICK_W(TICK_W), .T_VD_DAT_MAX(T_VD_DAT_MAX),
           .T_VD_ACK_MAX(T_VD_ACK_MAX)) dut (
           .clk(clk), .rst_n(rst_n), .sda_in(sda_in), .scl_in(scl_in), .ack_slot(ack_slot),
           .vd_valid(vd_valid), .t_vd(t_vd), .vd_was_ack(vd_was_ack), .viol_vd_dat(viol_vd_dat),
           .viol_vd_ack(viol_vd_ack), .n_vd_dat(n_vd_dat), .n_vd_ack(n_vd_ack), .n_viol(n_viol),
           .max_vd_dat_seen(max_vd_dat_seen), .max_vd_ack_seen(max_vd_ack_seen));

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

       reg [TICK_W-1:0] vd_log [0:31];
       reg ack_log [0:31];
       reg vdat_log [0:31];
       reg vack_log [0:31];
       integer n_log = 0;
       always @(posedge clk) if (rst_n && vd_valid && n_log < 32) begin
           vd_log[n_log]   = t_vd;        ack_log[n_log]  = vd_was_ack;
           vdat_log[n_log] = viol_vd_dat; vack_log[n_log] = viol_vd_ack;
           n_log = n_log + 1;
       end

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

       // One bit whose DATA VALID time is settable: SCL falls, then `vd` ticks pass, then the
       // transmitter gets SDA to its new value. `is_ack` says whose promise this slot is.
       task vd_bit;
           input v;
           input integer vd;
           input integer rest;
           input is_ack;
       begin
               ack_slot = is_ack;
               scl_in   = 1'b0;  tick(vd);      // the transmitter's response time
               sda_in   = v;     tick(rest);    // settled, waiting for the rising edge
               scl_in   = 1'b1;  tick(40);
               end
       endtask

       initial begin
           tick(3);
           // A MAXIMUM tracker must start at ZERO, the opposite of a minimum tracker.
           if (max_vd_dat_seen !== {TICK_W{1'b0}} || max_vd_ack_seen !== {TICK_W{1'b0}}) begin
               $display("FAIL: the maximum trackers did not start at zero"); errors = errors + 1; end
           rst_n = 1'b1; tick(2);
           sda_in = 1'b1; scl_in = 1'b1; tick(10);

           // ---- 1: four LEGAL data bits. 40 ticks of response where 90 is the limit --
           //      well inside, so nothing may be flagged. For a maximum, SMALLER is safer.
           vd_bit(1'b0, 40, 60, 1'b0);
           vd_bit(1'b1, 40, 60, 1'b0);
           vd_bit(1'b0, 40, 60, 1'b0);
           vd_bit(1'b1, 40, 60, 1'b0);
           if (n_viol !== {TICK_W{1'b0}}) begin
               $display("FAIL: %0d legal bits flagged", n_viol); errors = errors + 1; end
           if (n_vd_dat < 16'd4) begin
               $display("FAIL: %0d data measurements, expected 4", n_vd_dat); errors = errors + 1; end
           if (vd_log[1] < 16'd35 || vd_log[1] > 16'd45) begin
               $display("FAIL: a 40-tick data-valid time measured %0d", vd_log[1]); errors = errors + 1; end
           if (ack_log[1] !== 1'b0) begin
               $display("FAIL: a data slot was judged as an acknowledge"); errors = errors + 1; end

           // ---- 2: a SLOW transmitter. 130 ticks of response where the limit is 90. For a
           //      MAXIMUM this is the violation direction -- and a checker whose comparison
           //      had the wrong sense would have passed this and failed test 1.
           begin
               base = n_log;
               vd_bit(1'b0, 130, 40, 1'b0);
               if (vdat_log[base] !== 1'b1) begin
                   $display("FAIL: a 130-tick response was not flagged (max %0d)", T_VD_DAT_MAX);
                   errors = errors + 1; end
               if (vack_log[base] !== 1'b0) begin
                   $display("FAIL: a slow DATA slot also flagged the ACKNOWLEDGE parameter");
                   errors = errors + 1; end
           end

           // ---- 3: BOUNDARY. Exactly AT the maximum is legal, because the table's value is
           //      a limit that may be reached. One tick above is not.
           begin
               base = n_log;
               vd_bit(1'b1, T_VD_DAT_MAX, 40, 1'b0);
               if (vdat_log[base] !== 1'b0) begin
                   $display("FAIL: a response of EXACTLY the maximum (%0d) was rejected",
                            T_VD_DAT_MAX); errors = errors + 1; end
           end
           begin
               base = n_log;
               vd_bit(1'b0, T_VD_DAT_MAX + 1, 40, 1'b0);
               if (vdat_log[base] !== 1'b1) begin
                   $display("FAIL: a response one tick ABOVE the maximum was accepted"); errors = errors + 1; end
           end

           // ---- 4: the ACKNOWLEDGE slot is judged by its OWN parameter and counted
           //      separately, even though the numbers are identical. The point is to know
           //      WHICH DEVICE was slow, and that is lost if the two are merged.
           //      The value alternates from the previous bit: a response-time measurement
           //      needs an actual SDA TRANSITION, and repeating a value produces none.
           begin
               base = n_log;
               vd_bit(1'b1, 40, 60, 1'b1);
               if (ack_log[base] !== 1'b1) begin
                   $display("FAIL: an acknowledge slot was judged as data"); errors = errors + 1; end
               if (n_vd_ack !== 16'd1) begin
                   $display("FAIL: n_vd_ack = %0d after one acknowledge slot", n_vd_ack);
                   errors = errors + 1; end
           end

           // ---- 5: a SLOW ACKNOWLEDGE. Only the acknowledge verdict may fire, and the
           //      acknowledge worst-case tracker must move while the data one does not.
           begin
               base = n_log;
               viol_before = n_viol;
               vd_bit(1'b0, 140, 40, 1'b1);
               if (vack_log[base] !== 1'b1) begin
                   $display("FAIL: a 140-tick acknowledge response was not flagged"); errors = errors + 1; end
               if (vdat_log[base] !== 1'b0) begin
                   $display("FAIL: a slow ACKNOWLEDGE also flagged the DATA parameter"); errors = errors + 1; end
               if (n_viol !== viol_before + 16'd1) begin
                   $display("FAIL: the violation total did not advance"); errors = errors + 1; end
               if (max_vd_ack_seen < 16'd135) begin
                   $display("FAIL: max_vd_ack_seen = %0d after a 140-tick acknowledge",
                            max_vd_ack_seen); errors = errors + 1; end
           end

           // ---- 6: the slot identity is latched at the ARMING edge, not read at the end.
           //      Here ack_slot drops midway through the interval; the measurement must
           //      still be judged as an ACKNOWLEDGE, because that is whose promise it was
           //      when the clock fell.
           begin
               base = n_log;
               ack_slot = 1'b1;
               scl_in = 1'b0;  tick(30);
               ack_slot = 1'b0;                 // the slot boundary moves mid-interval
               tick(30);
               sda_in = 1'b1;  tick(40);        // alternated, so a transition occurs
               scl_in = 1'b1;  tick(40);
               if (ack_log[base] !== 1'b1) begin
                   $display("FAIL: the slot identity was read at the END of the interval, not latched at its start");
                   errors = errors + 1; end
           end

           // ---- 6b: THE SEPARATION TEST. A 70-tick response is LEGAL for data (max 90) and
           //      ILLEGAL for an acknowledge (max 60). One stimulus, two verdicts, decided only
           //      by which parameter the slot is judged against.
           begin
               base = n_log;
               vd_bit(1'b0, 70, 40, 1'b0);          // a DATA slot: legal
               if (vdat_log[base] !== 1'b0) begin
                   $display("FAIL: a 70-tick DATA response was flagged against a 90-tick limit");
                   errors = errors + 1; end
               base = n_log;
               vd_bit(1'b1, 70, 40, 1'b1);          // an ACKNOWLEDGE slot: illegal
               if (vack_log[base] !== 1'b1) begin
                   $display("FAIL: a 70-tick ACKNOWLEDGE response was not flagged against its own 60-tick limit -- the slot is being judged by the DATA parameter");
                   errors = errors + 1; end
           end

           // ---- 7: the worst case for a MAXIMUM is the LARGEST value seen, and a run of
           //      fast bits must not erase it. A tracker copied from a minimum checker would
           //      move the wrong way here.
           begin
               ack_slot = 1'b0;
               repeat (4) begin
                   vd_bit(1'b0, 20, 60, 1'b0);
                   vd_bit(1'b1, 20, 60, 1'b0);
               end
               if (max_vd_dat_seen < 16'd90) begin
                   $display("FAIL: max_vd_dat_seen fell to %0d after fast bits -- a maximum tracker must not decrease",
                            max_vd_dat_seen); errors = errors + 1; end
           end

           // ---- 8: a REPEATED bit produces no measurement. If SDA never moves there is no
           //      response time to measure, and a transmitter that keeps the line where it
           //      already is has trivially met any maximum.
           begin
               ndat_before = n_vd_dat;
               sda_in = 1'b0; scl_in = 1'b0; tick(120);   // already 1 from test 7's last bit
               scl_in = 1'b1; tick(60);
               scl_in = 1'b0; tick(120);                  // SDA does not move: no measurement
               scl_in = 1'b1; tick(60);
               if (n_vd_dat !== ndat_before) begin
                   $display("FAIL: a repeated bit produced %0d measurements",
                            n_vd_dat - ndat_before); errors = errors + 1; end
           end

           // ---- 9: an IDLE bus measures nothing.
           begin
               ndat_before = n_vd_dat;
               sda_in = 1'b1; scl_in = 1'b1; tick(400);
               if (n_vd_dat !== ndat_before) begin
                   $display("FAIL: an idle bus produced measurements"); errors = errors + 1; end
           end

           if (errors == 0)
               $display("PASS: a MAXIMUM is checked the other way round, the acknowledge is judged by its own parameter, the slot identity is latched at the arming edge, the worst case is the largest");
           else $display("FAIL: %0d error(s)", errors);
           $finish;
       end
   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker.vhd — the same checker in VHDL
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   -- DATA VALID TIME: tVD;DAT and tVD;ACK. The transmitter's side of the bargain whose receiver
   -- side is tSU;DAT, and they differ from every parameter checked so far in one structural way:
   -- they are MAXIMA, not minima.
   --
   --   [11] "tVD;DAT = time for data signal from SCL LOW to SDA output (HIGH or LOW, depending on
   --         which one is worse)."
   --   [12] "tVD;ACK = time for Acknowledgement signal from SCL LOW to SDA output (HIGH or LOW,
   --         depending on which one is worse)."
   --
   -- The interval starts when SCL goes LOW and ends when SDA is valid, and it must not EXCEED the
   -- table's value. That inverts the comparison, and with it the meaning of every margin: for a
   -- minimum, more measured time is safer; for a maximum, more is a violation. A checker with the
   -- sense wrong would pass a broken bus and fail a good one.
   --
   -- WHY THE ACKNOWLEDGE GETS ITS OWN PARAMETER, when the numbers are identical in every speed
   -- mode: because a different DEVICE drives it. The data bits of a byte come from one device and
   -- the ninth bit comes from the other, so tVD;ACK is a promise made by the device that is
   -- answering rather than by the device that is sending. Two parameters with the same number are
   -- still two obligations, and a capture that violates one tells you WHICH device is slow.
   --
   -- And the reason the limit exists: tVD;DAT(max) and tSU;DAT(min) must both fit inside tLOW.
   -- In Fast-mode that sum is 1.0 us against a 1.3 us tLOW -- 0.3 us of slack, exactly tr(max).
   -- In Fast-mode Plus the same sum is 0.5 us against a tLOW(min) of 0.5 us: EXACTLY tight, with
   -- no room for the rise time at all.
   entity i2c_data_valid_checker is
       generic (
           TICK_W : positive := 16;
           -- Fast-mode maxima, in ticks of a 100 MHz sample clock: 0.9 us = 90 ticks for both.
           T_VD_DAT_MAX : natural := 90;
           T_VD_ACK_MAX : natural := 90
       );
       port (
           clk    : in std_logic;
           rst_n  : in std_logic;
           sda_in : in std_logic;
           scl_in : in std_logic;
           -- High during the ninth clock slot, from Chapter 7.2's acknowledge engine. Which
           -- parameter applies depends on WHOSE promise this slot is, and only a byte-level block
           -- knows that.
           ack_slot : in std_logic;

           vd_valid   : out std_logic;
           t_vd       : out unsigned(TICK_W - 1 downto 0);
           vd_was_ack : out std_logic;

           -- MAXIMA, so the comparison is GREATER THAN
           viol_vd_dat : out std_logic;
           viol_vd_ack : out std_logic;

           n_vd_dat : out unsigned(TICK_W - 1 downto 0);
           n_vd_ack : out unsigned(TICK_W - 1 downto 0);
           n_viol   : out unsigned(TICK_W - 1 downto 0);
           -- For a MAXIMUM the worst case is the LARGEST value, the opposite of every other
           -- tracker in this module -- and a tracker copied from a minimum checker would silently
           -- report the wrong extreme.
           max_vd_dat_seen : out unsigned(TICK_W - 1 downto 0);
           max_vd_ack_seen : out unsigned(TICK_W - 1 downto 0)
       );
   end entity;

   architecture rtl of i2c_data_valid_checker is
       signal sda_q, scl_q : std_logic := '1';
       signal scl_fall, scl_rise, sda_changed : std_logic;

       -- The interval STARTS at the falling edge of SCL, so a timer armed by that edge measures it
       -- directly -- the same shape as the hold time in Chapter 11.3 and the opposite of a setup
       -- time, which has already finished by the time its reference edge arrives.
       signal armed, armed_is_ack : std_logic := '0';
       signal vd_ticks : unsigned(TICK_W - 1 downto 0) := (others => '0');
       signal vd_now   : unsigned(TICK_W - 1 downto 0);
   begin
       scl_fall    <= scl_q and (not scl_in);
       scl_rise    <= (not scl_q) and scl_in;
       sda_changed <= '1' when sda_q /= sda_in else '0';
       vd_now      <= vd_ticks + 1;

       process (clk)
       begin
           if rising_edge(clk) then
               if rst_n = '0' then
                   sda_q        <= '1';
                   scl_q        <= '1';
                   armed        <= '0';
                   armed_is_ack <= '0';
                   vd_ticks     <= (others => '0');
                   vd_valid     <= '0';
                   t_vd         <= (others => '0');
                   vd_was_ack   <= '0';
                   viol_vd_dat  <= '0';
                   viol_vd_ack  <= '0';
                   n_vd_dat     <= (others => '0');
                   n_vd_ack     <= (others => '0');
                   n_viol       <= (others => '0');
                   -- A MAXIMUM tracker starts at ZERO so the first measurement replaces it. The
                   -- minimum trackers elsewhere start at all-ones for the same reason, inverted --
                   -- and swapping the two initialisations reports a plausible number forever.
                   max_vd_dat_seen <= (others => '0');
                   max_vd_ack_seen <= (others => '0');
               else
                   sda_q <= sda_in;
                   scl_q <= scl_in;

                   vd_valid <= '0';

                   if scl_fall = '1' then
                       -- Arm, and latch WHICH promise is being measured. Reading ack_slot at the
                       -- END of the interval would be wrong: the slot boundary can move between
                       -- the falling edge and the SDA transition, and the obligation belongs to
                       -- whoever owned the slot when the clock fell.
                       armed        <= '1';
                       armed_is_ack <= ack_slot;
                       vd_ticks     <= (others => '0');
                   elsif armed = '1' then
                       if sda_changed = '1' then
                           armed      <= '0';
                           vd_valid   <= '1';
                           t_vd       <= vd_now;
                           vd_was_ack <= armed_is_ack;

                           if armed_is_ack = '1' then
                               viol_vd_dat <= '0';
                               if vd_now > to_unsigned(T_VD_ACK_MAX, TICK_W) then
                                   viol_vd_ack <= '1';
                                   n_viol      <= n_viol + 1;
                               else
                                   viol_vd_ack <= '0';
                               end if;
                               n_vd_ack <= n_vd_ack + 1;
                               if vd_now > max_vd_ack_seen then max_vd_ack_seen <= vd_now; end if;
                           else
                               viol_vd_ack <= '0';
                               if vd_now > to_unsigned(T_VD_DAT_MAX, TICK_W) then
                                   viol_vd_dat <= '1';
                                   n_viol      <= n_viol + 1;
                               else
                                   viol_vd_dat <= '0';
                               end if;
                               n_vd_dat <= n_vd_dat + 1;
                               if vd_now > max_vd_dat_seen then max_vd_dat_seen <= vd_now; end if;
                           end if;
                       elsif scl_rise = '1' then
                           -- The low phase ended with SDA never moving, so this interval has no
                           -- end and there is nothing to measure: the transmitter kept the bit it
                           -- already had, which trivially meets any maximum.
                           --
                           -- Disarming here is NOT optional. Without it the timer keeps running
                           -- across the high phase and the next SDA change -- possibly a framing
                           -- event -- is reported as a data-valid time, producing a huge
                           -- measurement and a violation on entirely legal traffic.
                           armed <= '0';
                       else
                           vd_ticks <= vd_now;
                       end if;
                   end if;
               end if;
           end if;
       end process;
   end architecture;
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker_tb.vhd — the VHDL testbench, single-writer throughout
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   -- 100 MHz sample clock. Fast-mode tVD;DAT(max) = tVD;ACK(max) = 0.9 us = 90 ticks. These are
   -- MAXIMA, so every boundary test runs the opposite way round from the minimum checkers of
   -- Chapters 11.1 to 11.3.
   entity i2c_data_valid_checker_tb is
   end entity;

   architecture sim of i2c_data_valid_checker_tb is
       constant TICK_W       : positive := 16;
       constant T_VD_DAT_MAX : natural  := 90;
       -- Deliberately DIFFERENT from T_VD_DAT_MAX. Table 10 gives the two the same number in every
       -- speed mode, and a testbench that copied that could not tell whether the design applies them
       -- independently -- the mutation that judges an acknowledge by the data limit is unobservable
       -- while the two are equal. Two parameters with one value are still two obligations.
       constant T_VD_ACK_MAX : natural  := 60;

       signal clk   : std_logic := '0';
       signal rst_n : std_logic := '0';
       signal sda_in, scl_in : std_logic := '1';
       signal ack_slot : std_logic := '0';

       signal vd_valid, vd_was_ack, viol_vd_dat, viol_vd_ack : std_logic;
       signal t_vd, n_vd_dat, n_vd_ack, n_viol : unsigned(TICK_W - 1 downto 0);
       signal max_vd_dat_seen, max_vd_ack_seen : unsigned(TICK_W - 1 downto 0);

       type tick_arr is array (0 to 31) of unsigned(TICK_W - 1 downto 0);
       type bit_arr  is array (0 to 31) of std_logic;
       signal vd_log : tick_arr := (others => (others => '0'));
       signal ack_log, vdat_log, vack_log : bit_arr := (others => '0');
       signal n_log : natural := 0;

       signal test_done : std_logic := '0';
   begin
       dut : entity work.i2c_data_valid_checker
           generic map (TICK_W => TICK_W, T_VD_DAT_MAX => T_VD_DAT_MAX,
                        T_VD_ACK_MAX => T_VD_ACK_MAX)
           port map (clk => clk, rst_n => rst_n, sda_in => sda_in, scl_in => scl_in,
                     ack_slot => ack_slot, vd_valid => vd_valid, t_vd => t_vd,
                     vd_was_ack => vd_was_ack, viol_vd_dat => viol_vd_dat,
                     viol_vd_ack => viol_vd_ack, n_vd_dat => n_vd_dat, n_vd_ack => n_vd_ack,
                     n_viol => n_viol, max_vd_dat_seen => max_vd_dat_seen,
                     max_vd_ack_seen => max_vd_ack_seen);

       clk <= not clk after 5 ns;

       watchdog : process
       begin
           wait for 1 ms;
           if test_done = '0' then report "watchdog expired" severity failure; end if;
           wait;
       end process;

       observe : process (clk)
       begin
           if rising_edge(clk) and rst_n = '1' and vd_valid = '1' and n_log < 32 then
               vd_log(n_log)   <= t_vd;        ack_log(n_log)  <= vd_was_ack;
               vdat_log(n_log) <= viol_vd_dat; vack_log(n_log) <= viol_vd_ack;
               n_log <= n_log + 1;
           end if;
       end process;

       stim : process
           variable errs : natural := 0;
           variable base : natural;
           variable viol_before, ndat_before : unsigned(TICK_W - 1 downto 0);

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

           -- One bit whose DATA VALID time is settable: SCL falls, then `vd` ticks pass, then the
           -- transmitter gets SDA to its new value. `is_ack` says whose promise this slot is.
           procedure vd_bit (v : in std_logic; vd, rest : in positive; is_ack : in std_logic) is
           begin
               ack_slot <= is_ack;
               scl_in   <= '0'; tick(vd);     -- the transmitter's response time
               sda_in   <= v;   tick(rest);   -- settled, waiting for the rising edge
               scl_in   <= '1'; tick(40);
           end procedure;
       begin
           tick(3);
           -- A MAXIMUM tracker must start at ZERO, the opposite of a minimum tracker.
           if max_vd_dat_seen /= to_unsigned(0, TICK_W)
              or max_vd_ack_seen /= to_unsigned(0, TICK_W) then
               report "the maximum trackers did not start at zero" severity error;
               errs := errs + 1; end if;
           rst_n <= '1'; tick(2);
           sda_in <= '1'; scl_in <= '1'; tick(10);

           -- 1: four LEGAL data bits. 40 ticks of response where 90 is the limit -- well inside,
           -- so nothing may be flagged. For a maximum, SMALLER is safer.
           vd_bit('0', 40, 60, '0');
           vd_bit('1', 40, 60, '0');
           vd_bit('0', 40, 60, '0');
           vd_bit('1', 40, 60, '0');
           if n_viol /= to_unsigned(0, TICK_W) then
               report "legal bits were flagged" severity error; errs := errs + 1; end if;
           if n_vd_dat < to_unsigned(4, TICK_W) then
               report "too few data measurements, expected 4" severity error; errs := errs + 1; end if;
           if vd_log(1) < to_unsigned(35, TICK_W) or vd_log(1) > to_unsigned(45, TICK_W) then
               report "a 40-tick data-valid time measured out of range" severity error;
               errs := errs + 1; end if;
           if ack_log(1) /= '0' then
               report "a data slot was judged as an acknowledge" severity error;
               errs := errs + 1; end if;

           -- 2: a SLOW transmitter. 130 ticks where the limit is 90. For a MAXIMUM this is the
           -- violation direction -- a checker with the wrong sense would have passed this and
           -- failed test 1.
           base := n_log;
           vd_bit('0', 130, 40, '0');
           if vdat_log(base) /= '1' then
               report "a 130-tick response was not flagged" severity error; errs := errs + 1; end if;
           if vack_log(base) /= '0' then
               report "a slow DATA slot also flagged the ACKNOWLEDGE parameter" severity error;
               errs := errs + 1; end if;

           -- 3: BOUNDARY. Exactly AT the maximum is legal, because the table's value is a limit
           -- that may be reached. One tick above is not.
           base := n_log;
           vd_bit('1', T_VD_DAT_MAX, 40, '0');
           if vdat_log(base) /= '0' then
               report "a response of EXACTLY the maximum was rejected" severity error;
               errs := errs + 1; end if;

           base := n_log;
           vd_bit('0', T_VD_DAT_MAX + 1, 40, '0');
           if vdat_log(base) /= '1' then
               report "a response one tick ABOVE the maximum was accepted" severity error;
               errs := errs + 1; end if;

           -- 4: the ACKNOWLEDGE slot is judged by its OWN parameter and counted separately, even
           -- though the numbers are identical. The point is to know WHICH DEVICE was slow.
           -- The value alternates from the previous bit: a response-time measurement needs an
           -- actual SDA TRANSITION, and repeating a value produces none.
           base := n_log;
           vd_bit('1', 40, 60, '1');
           if ack_log(base) /= '1' then
               report "an acknowledge slot was judged as data" severity error; errs := errs + 1; end if;
           if n_vd_ack /= to_unsigned(1, TICK_W) then
               report "wrong acknowledge count after one acknowledge slot" severity error;
               errs := errs + 1; end if;

           -- 5: a SLOW ACKNOWLEDGE. Only the acknowledge verdict may fire, and the acknowledge
           -- worst-case tracker must move while the data one does not.
           base := n_log;
           viol_before := n_viol;
           vd_bit('0', 140, 40, '1');
           if vack_log(base) /= '1' then
               report "a 140-tick acknowledge response was not flagged" severity error;
               errs := errs + 1; end if;
           if vdat_log(base) /= '0' then
               report "a slow ACKNOWLEDGE also flagged the DATA parameter" severity error;
               errs := errs + 1; end if;
           if n_viol /= viol_before + 1 then
               report "the violation total did not advance" severity error; errs := errs + 1; end if;
           if max_vd_ack_seen < to_unsigned(135, TICK_W) then
               report "the acknowledge worst case did not move" severity error;
               errs := errs + 1; end if;

           -- 6: the slot identity is latched at the ARMING edge, not read at the end. ack_slot
           -- drops midway through the interval; the measurement must still be judged as an
           -- ACKNOWLEDGE, because that is whose promise it was when the clock fell.
           base := n_log;
           ack_slot <= '1';
           scl_in <= '0'; tick(30);
           ack_slot <= '0';                 -- the slot boundary moves mid-interval
           tick(30);
           sda_in <= '1'; tick(40);         -- alternated, so a transition occurs
           scl_in <= '1'; tick(40);
           if ack_log(base) /= '1' then
               report "the slot identity was read at the END of the interval, not latched at its start"
                   severity error; errs := errs + 1; end if;

           -- 6b: THE SEPARATION TEST. A 70-tick response is LEGAL for data (max 90) and ILLEGAL for
           -- an acknowledge (max 60). One stimulus, two verdicts, decided only by which parameter
           -- the slot is judged against.
           base := n_log;
           vd_bit('0', 70, 40, '0');          -- a DATA slot: legal
           if vdat_log(base) /= '0' then
               report "a 70-tick DATA response was flagged against a 90-tick limit" severity error;
               errs := errs + 1; end if;
           base := n_log;
           vd_bit('1', 70, 40, '1');          -- an ACKNOWLEDGE slot: illegal
           if vack_log(base) /= '1' then
               report "a 70-tick ACKNOWLEDGE response was not flagged against its own 60-tick limit "
                    & "-- the slot is being judged by the DATA parameter" severity error;
               errs := errs + 1; end if;

           -- 7: the worst case for a MAXIMUM is the LARGEST value seen, and a run of fast bits
           -- must not erase it. A tracker copied from a minimum checker would move the wrong way.
           ack_slot <= '0';
           for i in 1 to 4 loop
               vd_bit('0', 20, 60, '0');
               vd_bit('1', 20, 60, '0');
           end loop;
           if max_vd_dat_seen < to_unsigned(90, TICK_W) then
               report "the data worst case fell after fast bits -- a maximum tracker must not decrease"
                   severity error; errs := errs + 1; end if;

           -- 8: a REPEATED bit produces no measurement. If SDA never moves there is no response
           -- time to measure, and a transmitter keeping the line where it already is has trivially
           -- met any maximum.
           ndat_before := n_vd_dat;
           sda_in <= '0'; scl_in <= '0'; tick(120);   -- already 1 from test 7's last bit
           scl_in <= '1'; tick(60);
           scl_in <= '0'; tick(120);                  -- SDA does not move: no measurement
           scl_in <= '1'; tick(60);
           -- No measurement is produced: the SDA change coincides with the arming falling edge,
           -- so the arm is set in the same cycle and the change is not seen as the interval's
           -- END. That is correct -- an interval cannot both start and finish on one edge.
           if n_vd_dat /= ndat_before then
               report "a repeated bit produced measurements" severity error;
               errs := errs + 1; end if;

           -- 9: an IDLE bus measures nothing.
           ndat_before := n_vd_dat;
           sda_in <= '1'; scl_in <= '1'; tick(400);
           if n_vd_dat /= ndat_before then
               report "an idle bus produced measurements" severity error; errs := errs + 1; end if;

           if errs = 0 then
               report "i2c_data_valid_checker self-check complete: a MAXIMUM is checked the other "
                    & "way round, the acknowledge is judged by its own parameter, the slot identity "
                    & "is latched at the arming edge, the worst case is the largest" severity note;
           else
               report "i2c_data_valid_checker self-check FAILED" severity error;
           end if;
           test_done <= '1';
           wait;
       end process;
   end architecture;

6a. Five Decisions Worth Defending

A maximum-tracker starts at ZERO and keeps the largest value. The mirror of Chapter 11.2's minimum-tracker, which starts at its maximum and keeps the smallest. Both mistakes report a value that never changes, and the design keeps one of each so a reviewer can compare them in place. Mutation D3 initialises the maximum-tracker to its maximum, and §7 asserts both reset values explicitly.

The two limits are separate parameters even though Table 10 gives them one value. §2's callout is the argument; §8 is the evidence that it matters.

A repeated bit produces no measurement, and the arm is cancelled at the rising edge. §5 is the argument — SDA was already valid, so nothing was late — and the cancellation is what mutation D6 removes.

The arm is cleared at the rising edge, and this closed a real bug. The first version left armed standing across the rise. On entirely legal traffic — a bit driven promptly in one low phase and SDA next changing in a subsequent phase — the checker measured an interval spanning the whole high phase and reported a violation that had not occurred. A transmitter's obligation exists only within the low phase where the bit is due, so the arm must end with that phase:

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_checker.sv — the one-line fix for a false positive on legal traffic
   // Without the second branch the arm survives the high phase, and the next SDA change --
   // however legal -- is measured from a falling edge two phases back. Found by running the
   // checker against a multi-byte write that had no violations in it at all.
   if (scl_fall)       armed <= 1'b1;
   else if (sda_moved) armed <= 1'b0;
   else if (scl_rise)  armed <= 1'b0;

The measurement includes the cycle in which it is read. The same combinational "including this cycle" form as the rest of the module, so that a delay of exactly the limit is judged correctly rather than one tick generously.

6b. Verified Execution

Azvya Education Pvt. Ltd.VLSI Mentor
terminal — three simulators, one result, one finish time
   $ iverilog -g2012 -o d4 i2c_data_valid_checker.sv i2c_data_valid_checker_tb.sv && ./d4
   PASS: a MAXIMUM is checked the other way round, the acknowledge is judged by its own
   parameter, the slot identity is latched at the arming edge, the worst case is the largest
   i2c_data_valid_checker_tb.sv:217: $finish called at 36460000 (1ps)

   $ iverilog -g2005 -o v4 i2c_data_valid_checker.v i2c_data_valid_checker_tb.v && ./v4
   PASS: a MAXIMUM is checked the other way round, the acknowledge is judged by its own
   parameter, the slot identity is latched at the arming edge, the worst case is the largest
   i2c_data_valid_checker_tb.v:226: $finish called at 36460000 (1ps)

   $ nvc -a i2c_data_valid_checker.vhd i2c_data_valid_checker_tb.vhd
   $ nvc -e i2c_data_valid_checker_tb && nvc -r i2c_data_valid_checker_tb --stop-time=800us
   ** Note: 36460ns+0: i2c_data_valid_checker self-check complete: a MAXIMUM is checked the
      other way round, the acknowledge is judged by its own parameter, the slot identity is
      latched at the arming edge, the worst case is the largest

All three at 36460 ns.

7. What the Testbench Proves

The two limits are configured differently on purpose — T_VD_DAT_MAX = 90 (Fast-mode's 0.9 µs at a 100 MHz sample clock) and T_VD_ACK_MAX = 60 — for the reason §2's callout gives. Table 10 gives them the same number; the testbench does not.

#stimuluswhat it establishes
1resetboth maximum-trackers read zero
2four legal bits, 40-tick responsesnothing flagged; four data measurements, each 40
3a data slotis not judged as an acknowledge
4a 130-tick data responseviolates tVD;DAT (90), and does not flag tVD;ACK
5exactly at the maximum, then one tick aboveboth sides of the boundary pinned
6an acknowledge slotis not judged as data; n_vd_ack advances by one
7a 140-tick acknowledgeviolates tVD;ACK, without flagging tVD;DAT; max_vd_ack_seen records it
8a 70-tick responselegal as data (90), illegal as an acknowledge (60)
9fast bits after a slow onemax_vd_dat_seen does not fall — a maximum tracker never decreases
10a repeated bitproduces no measurement
11an idle busproduces no measurements

Test 8 is the test §2's callout exists for, and it is the reason the limits are configured apart. A 70-tick response is inside the 90-tick data limit and outside the 60-tick acknowledge limit, so it is the single stimulus that distinguishes a checker applying each limit to its own slot from one that applies T_VD_DAT_MAX to both. Configured with Table 10's own equal values, that stimulus cannot exist — and §8 records that the corresponding mutation survived until the values were separated.

Test 9 is the maximum-tracker direction check. After a 140-tick acknowledge has been recorded, a run of fast bits must not pull the recorded maximum back down. A tracker that assigned rather than compared would report the most recent measurement, which on an intermittently marginal bus hides every violation but the last.

Test 3 and test 6 are a matched pair about slot identity. Each asserts that a slot is judged by its own parameter — and mutation D3 shows why the identity must be latched at the arming edge rather than read when the interval closes: the acknowledge bit's slot flag can change before the measurement completes, and a checker reading it late attributes the measurement to the wrong parameter.

Test 10 is the case that produces nothing. A repeated bit never changes SDA, so no interval closes. Together with test 11 it fixes that the block reports measurements only for real transitions in real low phases — which is what mutation D6 removes.

8. Mutation Testing

Six defects injected into the SystemVerilog checker, each verified to change behaviour and then verified to be caught.

#injected defectoutcome
D1the comparison sense is inverted — a maximum checked as a minimumkilled — test 4
D2the acknowledge slot is judged by the data parameterkilled — test 8, after the limits were separated
D3the slot identity is read at the end of the interval, not latched at its startkilled — test 3 / test 6
D4the maximum tracker is initialised like a minimum trackerkilled — test 1
D5the worst case moves the wrong way — smallest, not largestkilled — test 9
D6the arm is not cancelled when the low phase ends with no transitionkilled — test 11

Six injected, six killed. Two are worth recording in detail.

D1 is the mutation that exists because this is the module's first maximum. Flipping > to < turns the check into a minimum, which passes every slow response and flags every fast one — the exact inversion the chapter opened with. It is a one-character change and it is caught immediately by a 130-tick response against a 90-tick limit, but only because a test exists that exceeds the limit. A suite carried over from the minimum checkers of Chapters 11.1 to 11.3, whose violations are all short intervals, contains no such stimulus.

D2 survived the first run, and the fix was to the testbench rather than to the design. With both limits set to Table 10's shared value, judging the acknowledge against the data limit is behaviourally identical to judging it correctly, on every possible stimulus. The mutant was unobservable, and the tempting conclusion was "equivalent mutant, no action" — the same conclusion Chapter 11.2 §8 reached legitimately for its B5.

It was the wrong conclusion here, and the difference is worth being precise about. B5's guard was unreachable by construction: no configuration could reach it, so the code was genuinely dead. D2's defect is unreachable by configuration: a different and entirely legal parameterisation exposes it immediately. A defect hidden by the values you happened to choose is a live defect.

So the three outcomes for a surviving mutant, refined from Chapter 9.1 §7:

outcomeevidenceresponse
a real test gapa stimulus exists that distinguishes themwrite it
equivalent by constructionno configuration can distinguish themdelete the dead code
equivalent by configurationa different parameterisation distinguishes themchange the configuration

The third is the one that masquerades as the second, and wherever a specification gives two distinct parameters the same number, it is waiting. This module hit it three times — here, in Chapter 11.5 where the two START margins coincide in two speed modes, and in Chapter 11.7 where tr(max) and tf(max) coincide in Fast-mode.

9. Verification Connection — Asserting a Maximum

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_data_valid_props.sv — a maximum needs a bounded liveness property
   // A minimum is a safety property: "nothing bad happened in this window", checkable by looking
   // at the window. A maximum is BOUNDED LIVENESS: "something must happen within N cycles" -- so
   // it needs the ##[1:N] form rather than a window, and it needs an explicit escape for the case
   // where the obligation does not arise at all. Section 5 is why that escape is `scl_rise`: a
   // repeated bit was already valid, so there is nothing for the property to demand.
   property p_vd_dat;
      @(posedge clk) (scl_fall && data_phase) |-> ##[1:T_VD_DAT_MAX] $changed(sda) or scl_rise;
   endproperty
   assert property (p_vd_dat)
      else $error("tVD;DAT violated: SDA not valid within %0d cycles of SCL LOW", T_VD_DAT_MAX);

   // The acknowledge is its own property with its own limit, for the reason section 2 gives --
   // and note the two limits are separate localparams even where the spec gives one number, so
   // that a future divergence is a one-line change rather than a hunt.
   property p_vd_ack;
      @(posedge clk) (scl_fall && ack_phase) |-> ##[1:T_VD_ACK_MAX] $changed(sda) or scl_rise;
   endproperty
   assert property (p_vd_ack)
      else $error("tVD;ACK violated: ack not valid within %0d cycles of SCL LOW", T_VD_ACK_MAX);

   // The `or scl_rise` disjunct is section 5 expressed as a property: the obligation lives only
   // inside the low phase where a NEW value is due. Without it, the property fails on entirely
   // legal traffic whenever a bit repeats -- a false positive, which in an assertion is worse than
   // a missed check because it trains reviewers to waive it.

   // And the NEGATIVE property. A maximum is violated by lateness, so an EARLY transition must
   // not be flagged here -- that is tHD;DAT's business, and a checker conflating them reports
   // the wrong parameter for a real fault.
   property p_early_is_not_this_violation;
      @(posedge clk) (vd_ticks < T_VD_DAT_MAX) |-> !viol_vd_dat;
   endproperty
   assert property (p_early_is_not_this_violation)
      else $error("a transition inside the limit was reported as a tVD;DAT violation");
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_valid_cov.sv — proximity to a ceiling, and the case with no transition
   covergroup i2c_valid_cg with function sample(int vd, int limit, bit is_ack, bit no_transition);
      // Distance BELOW the ceiling, which is the mirror of Chapter 11.1's margin bins. For a
      // maximum the interesting region is just under the limit, so the fine bins go there.
      headroom: coverpoint (limit - vd) {
         bins over       = {[$:-1]};      // a violation
         bins exact      = {0};           // exactly at the ceiling -- legal, and the boundary
         bins within_5   = {[1:5]};
         bins within_20  = {[6:20]};
         bins comfortable = {[21:$]};
      }

      // Crossed with WHO was driving, because section 2's whole argument is that the data path
      // and the acknowledge path are different logic with different delays. A suite that only
      // ever exercised prompt acknowledges has not tested tVD;ACK, however many bytes it sent.
      driver: coverpoint is_ack { bins data = {0}; bins ack = {1}; }
      headroom_x_driver: cross headroom, driver;

      // The repeated-bit case is a bin of its own, because it produces no measurement at all and
      // a suite can therefore have full headroom coverage without ever entering it -- which
      // leaves mutation D6's cancellation path unexercised.
      repeated_bit: coverpoint no_transition { bins value_unchanged = {1}; }
   endgroup

10. FPGA and ASIC Implications

One armed counter, a latched slot flag and two compares — around 50 flops at TICK_W = 16. The comparison happens only when a transition closes the interval, so nothing here is on a critical path. The latched flag is the part worth attention: it costs one bit and it is what mutation D3 attacks.

§4's Fm+ deficit is the sizing consequence that reaches the schematic. 0.45 + 0.05 + 0.12 = 0.62 µs into a 0.5 µs low phase means a Fast-mode-Plus device cannot take its full data-valid time on a bus with a worst-case rise. Either the transmitter is faster than the specification's ceiling or the rise is faster than its ceiling, and the second is the one a board designer controls. This is the concrete reason Fm+ designs use current-source pull-ups.

On the generating side, tVD;DAT is a budget for the whole response path, not for the output register. It covers the time from observing SCL low to SDA being valid on the wire — so it contains the input synchroniser on SCL, any spike filter (Chapter 11.8 adds T_SP + 1 cycles), the state machine's decision, the output register, and the pad. A design that measures only from its internal scl_fall pulse to its output flop is measuring a fraction of the parameter, and typically the smallest fraction.

Two limits, two localparams, even where the specification gives one number. Table 10's values are equal today. The cost of separating them is nothing; the cost of a merged constant when a part's datasheet diverges is a hunt through the RTL.

11. Debugging — The Acknowledge That Was Always a Little Late

Pitfall — one limit for two obligations, hidden by identical specification values
Buggy Code
// An I2C slave's timing monitor, written carefully and reviewed. One parameter, used twice,
// because Table 10 gives tVD;DAT and tVD;ACK the same value in every speed mode:
//
//     localparam T_VD_MAX = 90;            // 0.9 us at 100 MHz -- Fast-mode
//
//     always_ff @(posedge clk) begin
//        if (armed && (vd_ticks > T_VD_MAX)) viol_vd <= 1'b1;
//     end
//
// The reasoning in the review was explicit and looked sound: the two parameters have the same
// number, so a single constant is simpler and cannot be wrong.
//
// The monitor also reported one violation flag, for the same reason.
Symptom

The design passed its own regression for eight months. Then a customer integrated it with a master that had a tighter acknowledge requirement than the specification's -- 600 ns rather than 900, documented in that master's datasheet -- and reported intermittent NACK misdetection on the first byte of a write.

The slave's monitor reported nothing. Not a marginal count, not a warning: zero violations across a capture containing thousands of transfers, including transfers the master had rejected.

The investigation went to the master. Its acknowledge sampling window was measured and found to be within its own datasheet. It was then pointed at the board -- pull-ups, capacitance, trace length -- and a smaller pull-up did reduce the failure rate, which read as confirmation that the problem was electrical.

What broke it open was instrumenting the slave's acknowledge path separately from its data path, which nobody had done because the monitor reported them together. The data path drove SDA about 180 ns after SCL fell. The acknowledge path took 740 ns -- four times longer, because the acknowledge decision waited on an address comparator and a FIFO-space check that the data path did not involve.

740 ns is inside the specification's 900 ns ceiling, so the slave was compliant and its monitor was right to stay quiet. It was outside the master's 600 ns requirement. And because the monitor could not distinguish the two paths, nothing in eight months of regression had ever reported that the acknowledge path was four times slower than the data path.

Root Cause

Two distinct obligations were checked with one limit and reported through one flag, on the grounds that the specification gives them the same number.

The numbers being equal does not make the obligations equal. They are met by different logic with different delays -- here a shift register against an address comparator plus a FIFO check -- and merging them removed the only measurement that would have shown the acknowledge path was an outlier.

The smaller pull-up appeared to help because it reduced the rise time, which bought back some of the margin the slow acknowledge path had consumed. That made an electrical explanation look right and delayed the real diagnosis.

12. Common Misconceptions

"tVD;DAT is like tSU;DAT with a different name." It is a maximum. The comparison, the worst-case tracker's initial value, the direction of the worst case, and the meaning of a longer delay all invert.

"A worst-case register for a maximum starts at its maximum." That is a minimum-tracker. A maximum-tracker starts at zero. Both mistakes produce a register that reports the same value forever, and §7's test 10 asserts the reset value.

"tVD;ACK is redundant, since Table 10 gives it the same number." A different device drives the acknowledge, so it is a different device's promise — and it is met by different logic with different delays. §11 is eight months of that.

"A bit that never changes must violate a data-valid time, since nothing arrived." It does not. tVD;DAT bounds how soon a new value becomes valid, and a repeated bit was already valid when the low phase began — nothing was late. It produces no measurement and no violation.

"Waiting longer is always safe." True of every minimum in the module and false of every maximum. For tVD;DAT waiting longer is the violation.

"The transmitter has the whole low phase to produce the bit." It has tVD;DAT, and after that the line still has to rise and the receiver still needs its setup. §4 shows those three exactly filling tLOW(min) in Standard-mode and Fast-mode, and overflowing it at Fm+.

"A mutation nothing can detect is an equivalent mutant." Only if nothing can detect it in any configuration. If a different parameterisation exposes it, the defect is live and the configuration is hiding it — §8's D2 against Chapter 11.2's B5.

13. Reason It Through

A checker's max_vd_seen reads 0 after an hour of clean traffic. Compare with the same reading on a min_low_seen register.

Opposite diagnoses. For a maximum-tracker, 0 after real traffic means the register is never being updated — probably initialised to its maximum, so no measurement is ever larger. For a minimum-tracker, 0 means it was initialised to zero and nothing is ever smaller. Same symptom, mirrored cause, and knowing which kind of tracker you are reading is the whole diagnosis.

A bit repeats, so SDA never moves through the whole low phase. Violation or not?

Not a violation. tVD;DAT bounds the time until SDA is valid, and a repeated bit was already valid when the low phase began — validity was never lost, so nothing was late. The armed interval has no end, so it must be cancelled at the rising edge rather than reported. Note this is the same behaviour as Chapter 11.3's hold time for a repeated bit, reached by a different argument.

§4's Fm+ row overruns by 120 ns, which is exactly tr(max). What is that telling you?

That the specification does not expect all three worst cases at once, and that the term you are expected to beat is the rise time. A deficit equal to one of the terms identifies which term the specification assumes is not at maximum — which at Fm+ means a current-source pull-up rather than a resistor.

A mutation swapping T_VD_ACK_MAX for T_VD_DAT_MAX survives. Is it an equivalent mutant?

No — it is equivalent only under this configuration. Set the two limits to different values and a stimulus between them distinguishes them immediately. Equivalence by construction means no configuration can distinguish the mutant, and that justifies deleting dead code; equivalence by configuration means the values you chose are hiding a live defect, and the fix is to the testbench.

A slave reports zero tVD violations, and its acknowledge path is four times slower than its data path. Are both facts consistent, and which one should appear in a report?

Both are consistent: 740 ns and 180 ns are inside a 900 ns ceiling, so there is no violation to report. The report should carry the measurements, because the asymmetry is the engineering finding — it says the acknowledge path is the one that will fail first against any tighter-than-specification requirement, and a verdict-only report hides it completely.

14. Understanding Check

15. Summary

A maximum inverts four things at once: the comparison, the worst-case tracker's initial value, which extreme the worst case is, and whether waiting longer is safe. A checker written by analogy with a minimum is wrong in all four places.

Both parameters are referenced to SCL going low, the same edge as tHD;DAT — so one falling edge starts a floor and a ceiling, and the transmitter's working room is the gap between them.

A repeated bit is a non-event for this parameter, not a violation. It was already valid, so nothing was late — the same silence Chapter 11.3 requires for the hold time, justified by a different argument. The arm is cancelled at the rising edge in both cases.

The acknowledge is a separate obligation because a separate device drives it, and it is met by different logic with different delays. Keep two constants and two reported maxima even though Table 10 gives one number.

tVD;DAT(max) + tSU;DAT(min) + tr(max) = tLOW(min) exactly at Standard-mode and Fast-mode, and overruns by 120 ns at Fm+. A deficit equal to one of its terms names the term the specification does not expect at maximum — here the rise time.

An arm must not outlive the phase that created it. The obligation is bounded by the low phase, and a surviving arm turns legal traffic into a false positive.

A mutation nothing can detect under your configuration may still be a live defect. Equivalence by construction justifies deleting code; equivalence by configuration means the testbench is hiding something, and wherever a specification repeats a number it is waiting to.

16. What Comes Next

The four chapters so far have all been about bits inside a transfer. Chapter 11.5 moves to the framing: tSU;STA and tHD;STA, the margins around START and repeated-START.

They introduce something none of the bit-level parameters have — the two are referenced to different signals. tSU;STA is measured to SDA's falling edge from SCL's rise; tHD;STA from SDA's fall to SCL's fall. And the parameter that at first looks like a duplicate, the repeated-START's setup, turns out to be the reason a repeated START is timed differently from a first START even though the waveform is the same shape — which is the timing-level answer to the question Chapter 10.2 answered at the protocol level.

Continue learning