I²C · Module 11
tSU;DAT and tHD;DAT — The I²C Data Window
Two parameters every bit of every byte must satisfy, measured against two different edges. One has a specified minimum of zero and is in practice among the tightest constraints on the bus — this chapter resolves that contradiction.
Chapter 11.2 constrained the clock. This chapter constrains the data relative to the clock, which is question 3 of Chapter 11.1 applied to every bit of every byte on the bus — address bits, payload bits, and the acknowledge alike.
There are two parameters and the single most important thing about them is that they are measured against different edges:
tSU;DAT— SDA stable before the RISING edge of SCL ·tHD;DAT— SDA held after the FALLING edge
Getting that backwards is the most common error in a hand-built timing checker, and it produces a block that passes the case it was debugged against and misses the mirror case entirely.
The chapter also resolves something that reads as a contradiction in Table 10: tHD;DAT(min) is zero, and yet a device must provide 300 ns.
1. The Two Rows, and the Footnote That Matters
Four things in that block are worth extracting before going further.
Footnote [2] fixes the reference edge for the hold time, and it is the FALLING edge. This is the clause people skip. A hold time in most digital contexts is measured after the sampling edge; here it is measured after the edge that ends the sampling window. §2 explains why.
tHD;DAT applies to the acknowledge as well as to data. The ninth bit is subject to the same window as the other eight — which matters because the ninth bit is driven by the other device (Chapter 7.2), so the obligation changes hands within a byte.
Footnote [3] is where the real number lives. Table 10's minimum is zero; the footnote requires 300 ns. §3 is entirely about that.
Footnote [5] is the clause that bites a reused design. A Fast-mode device dropped into a Standard-mode system must meet the Standard-mode 250 ns setup, not its native 100 ns — and if it stretches the clock it must have the next bit out 1250 ns before releasing SCL. That is the largest single number in this chapter and it exists because the Standard-mode rise time is a full microsecond.
2. Why the Hold Time Is Referenced to the Falling Edge
The asymmetry looks arbitrary until you ask what each parameter protects.
tSU;DAT protects the receiver. The receiver samples on the rising edge, so the data must be settled before it — that is an ordinary setup time and the reference edge is the obvious one.
tHD;DAT protects the receiver too, but against a different hazard. SCL's falling edge is not instantaneous. It takes tf to traverse the undefined region between VIH and VIL, and during that traversal a receiver's input stage cannot say whether it has already sampled. If the transmitter changes SDA inside that window, some receivers will have latched the old bit and some the new one.
So the hold requirement is not "hold until the receiver samples" — the receiver sampled at the rising edge, long before. It is "hold until the clock has unambiguously fallen", and the reference edge for that is the falling edge itself.
That is what footnote [3] means by "to bridge the undefined region of the falling edge of SCL." The 300 ns is not a margin against a receiver's setup requirement. It is the width of the ambiguity that a finite fall time creates.
3. A Minimum of Zero That Is Not Zero
Table 10 says tHD;DAT(min) is 0 for I²C-bus devices. Footnote [3] says a device must provide at least 300 ns. Both are correct and they are about different things.
| who it binds | what it says | |
|---|---|---|
| Table 10's 0 | the bus | a transmitter may change SDA the instant SCL falls, as far as the protocol is concerned |
| Footnote [3]'s 300 ns | the device | but a compliant device must not actually do that |
The bus-level minimum is zero because a receiver has already sampled by the time SCL falls — nothing downstream needs the data held. The device-level obligation is 300 ns because the falling edge has finite width and a change inside it makes the sampling instant ambiguous.
So the effective constraint is 300 ns in every speed mode. It does not scale with the speed grade — Standard, Fast and Fast-mode Plus all carry the same 300 ns — which makes it proportionally tightest at Fast-mode Plus, where the whole low phase is only 500 ns. A Fm+ device must hold for 300 ns of its 500 ns low phase, leaving 200 ns for everything else.
4. The Window, Drawn
tHD;DAT after the fall, tSU;DAT before the rise
10 cyclesTwo things the figure makes concrete.
The two intervals do not overlap and do not touch. Between them is a region where SDA is free to move — and the width of that region is what a transmitter actually has to work in. Chapter 11.4 constrains how much of it the transmitter may consume, and Chapter 11.9 shows the whole low phase adding up.
Both intervals live inside one low phase. That is why Chapter 11.2 §3 could derive tLOW(min) from its contents: the hold, the transmitter's response, the rise, and the setup all fit between one falling edge and the next rising one.
5. Two Measurement Shapes
§2's callout gives the rule; here is what it looks like as hardware.
The setup measurement is a free-running timer, sampled. A counter tracks how long SDA has been stable, resetting on every change. At the rising edge, its value is tSU;DAT. The counter runs whether or not a measurement is wanted, because the alternative is to start it when the edge arrives — at which point the interval is already over.
The hold measurement is an armed timer, stopped. The falling edge arms a counter; the next SDA change stops it. The value at the stop is tHD;DAT.
And the hold measurement needs two things the setup measurement does not:
A repeated bit produces no hold measurement at all. If the transmitter sends the same value again, SDA never moves, so the interval has no end. That is not a violation — a bit that never changes trivially satisfies any hold requirement — and a checker that measured the whole low phase instead would report the low width as a hold time and never see a violation. §7's test 6 asserts nothing is reported.
The arm must be cancelled when the low phase ends. If SDA has not moved by the time SCL rises, the interval is abandoned. Leaving the timer armed means the next SDA change — possibly in a later high phase, possibly a framing event — gets reported as a hold time measured from a falling edge two phases ago. §7's tests 6b and 6c are about that, and §8 records that both were added after mutations survived.
6. The Data-Window Checker in Three Languages
// THE DATA WINDOW: tSU;DAT and tHD;DAT, the two parameters every bit of every byte must
// satisfy. They are measured against DIFFERENT edges, and getting that wrong is the most
// common error in a hand-built timing checker:
//
// tSU;DAT SDA stable BEFORE the RISING edge of SCL (a receiver's need)
// tHD;DAT SDA held AFTER the FALLING edge of SCL (a transmitter's duty)
//
// The specification is explicit about the second, because it is the counter-intuitive one:
//
// [2] "tHD;DAT is the data hold time that is measured from the falling edge of SCL,
// applies to data in transmission and the acknowledge."
//
// And then Table 10 says something that looks like a contradiction and is not. For
// I2C-bus devices tHD;DAT(min) is ZERO -- there is no bus-level hold requirement at all.
// But footnote [3] imposes a device-level one:
//
// [3] "A device must internally provide a hold time of at least 300 ns for the SDA
// signal (with respect to the VIH(min) of the SCL signal) to bridge the undefined
// region of the falling edge of SCL."
//
// Both are true and they are about different things. The BUS does not require a hold,
// because a receiver has already sampled by the time SCL falls. The DEVICE requires 300 ns
// so that its own SDA change does not land inside the window where SCL is neither clearly
// high nor clearly low -- during which a receiver's own input stage cannot say whether it
// sampled before or after the change. So 300 ns is not a bus timing margin; it is the
// width of the ambiguity that SCL's finite fall time creates.
//
// This block therefore checks the setup against a bus minimum and the hold against the
// DEVICE obligation, and reports them separately because they answer to different clauses.
//
// PASSIVE: observes the two wires and drives nothing.
module i2c_data_window_checker #(
parameter int TICK_W = 16,
// Fast-mode tSU;DAT = 100 ns = 10 ticks at 100 MHz.
parameter int T_SU_DAT_MIN = 10,
// Footnote [3]'s internal hold obligation, 300 ns = 30 ticks. NOT Table 10's
// tHD;DAT(min), which is zero -- see the header.
parameter int T_HD_DAT_MIN = 30
)(
input logic clk,
input logic rst_n,
input logic sda_in,
input logic scl_in,
// ---- measured per bit ----
output logic su_valid, // pulse: a setup time has been measured
output logic [TICK_W-1:0] t_su_dat,
output logic hd_valid, // pulse: a hold time has been measured
output logic [TICK_W-1:0] t_hd_dat,
// ---- verdicts ----
output logic viol_su,
output logic viol_hd,
// ---- totals and worst cases ----
output logic [TICK_W-1:0] n_su,
output logic [TICK_W-1:0] n_hd,
output logic [TICK_W-1:0] n_viol_su,
output logic [TICK_W-1:0] n_viol_hd,
output logic [TICK_W-1:0] min_su_seen,
output logic [TICK_W-1:0] min_hd_seen
);
logic sda_q, scl_q;
logic scl_rise, scl_fall, sda_changed;
assign scl_rise = !scl_q && scl_in;
assign scl_fall = scl_q && !scl_in;
assign sda_changed = sda_q != sda_in;
// SETUP is measured by a timer that runs ALWAYS and is sampled at the rising edge.
// A timer started by the edge would be too late -- the interval being measured has
// already finished by the time its end arrives, which is the defining property of a
// setup time and the reason it cannot be measured the way a hold time can.
logic [TICK_W-1:0] stable_ticks;
logic [TICK_W-1:0] stable_now;
assign stable_now = sda_changed ? '0 : (stable_ticks + 1'b1);
// HOLD is the opposite: the interval STARTS at the falling edge, so a timer armed by
// that edge and stopped by the next SDA change measures it directly. The asymmetry
// between the two measurements is forced by which end of the interval the reference
// edge sits at, and it is why one counter cannot serve both.
logic hd_arm;
logic [TICK_W-1:0] hd_ticks;
logic [TICK_W-1:0] hd_now;
assign hd_now = hd_ticks + 1'b1;
always_ff @(posedge clk) begin
if (!rst_n) begin
sda_q <= 1'b1;
scl_q <= 1'b1;
stable_ticks <= '0;
hd_arm <= 1'b0;
hd_ticks <= '0;
su_valid <= 1'b0;
t_su_dat <= '0;
hd_valid <= 1'b0;
t_hd_dat <= '0;
viol_su <= 1'b0;
viol_hd <= 1'b0;
n_su <= '0;
n_hd <= '0;
n_viol_su <= '0;
n_viol_hd <= '0;
min_su_seen <= {TICK_W{1'b1}};
min_hd_seen <= {TICK_W{1'b1}};
end else begin
sda_q <= sda_in;
scl_q <= scl_in;
su_valid <= 1'b0;
hd_valid <= 1'b0;
stable_ticks <= stable_now;
// ---- the setup half: sample the always-running timer at the rising edge ----
if (scl_rise) begin
su_valid <= 1'b1;
t_su_dat <= stable_now;
viol_su <= (stable_now < T_SU_DAT_MIN[TICK_W-1:0]);
n_su <= n_su + 1'b1;
if (stable_now < T_SU_DAT_MIN[TICK_W-1:0]) n_viol_su <= n_viol_su + 1'b1;
if (stable_now < min_su_seen) min_su_seen <= stable_now;
end
// ---- the hold half: arm at the falling edge, stop at the next SDA change ----
if (scl_fall) begin
hd_arm <= 1'b1;
hd_ticks <= '0;
end else if (hd_arm) begin
if (sda_changed) begin
// SDA has moved. The hold time is however long it waited. Note that a
// transmitter which does not change the bit at all holds it for the
// whole low phase, which trivially satisfies the obligation -- so only
// an ACTUAL change produces a measurement. A checker that measured
// every low phase would report the low phase width as a hold time and
// never see a violation.
hd_arm <= 1'b0;
hd_valid <= 1'b1;
t_hd_dat <= hd_now;
viol_hd <= (hd_now < T_HD_DAT_MIN[TICK_W-1:0]);
n_hd <= n_hd + 1'b1;
if (hd_now < T_HD_DAT_MIN[TICK_W-1:0]) n_viol_hd <= n_viol_hd + 1'b1;
if (hd_now < min_hd_seen) min_hd_seen <= hd_now;
end else if (scl_rise) begin
// The low phase ended with no SDA change: the bit was repeated, so
// there is nothing to measure and nothing to complain about.
hd_arm <= 1'b0;
end else begin
hd_ticks <= hd_now;
end
end
end
end
endmodule `timescale 1ns/1ps
// 100 MHz sample clock. Fast-mode tSU;DAT = 100 ns = 10 ticks; footnote [3]'s internal
// hold obligation is 300 ns = 30 ticks.
module i2c_data_window_checker_tb;
localparam int TICK_W = 16;
localparam int T_SU_DAT_MIN = 10;
localparam int T_HD_DAT_MIN = 30;
logic clk = 1'b0;
always #5 clk = ~clk;
logic rst_n = 1'b0;
logic sda_in = 1'b1, scl_in = 1'b1;
logic su_valid, hd_valid, viol_su, viol_hd;
logic [TICK_W-1:0] t_su_dat, t_hd_dat, n_su, n_hd, n_viol_su, n_viol_hd;
logic [TICK_W-1:0] min_su_seen, min_hd_seen;
int errors = 0;
int base_su, base_hd;
logic [TICK_W-1:0] vsu_before, vhd_before, nhd_before;
i2c_data_window_checker #(.TICK_W(TICK_W), .T_SU_DAT_MIN(T_SU_DAT_MIN),
.T_HD_DAT_MIN(T_HD_DAT_MIN)) dut (.*);
initial begin #1000000; $display("FAIL: watchdog expired"); $finish; end
logic [TICK_W-1:0] su_log [0:31];
logic vsu_log [0:31];
int n_sulog;
always @(posedge clk) if (rst_n && su_valid && n_sulog < 32) begin
su_log[n_sulog] = t_su_dat; vsu_log[n_sulog] = viol_su; n_sulog++;
end
logic [TICK_W-1:0] hd_log [0:31];
logic vhd_log [0:31];
int n_hdlog;
always @(posedge clk) if (rst_n && hd_valid && n_hdlog < 32) begin
hd_log[n_hdlog] = t_hd_dat; vhd_log[n_hdlog] = viol_hd; n_hdlog++;
end
task automatic tick(input int n);
begin repeat (n) @(negedge clk); end
endtask
// One bit, with the two intervals under test made independently settable:
// `hold` ticks after SCL falls before SDA changes -> tHD;DAT
// `setup` ticks after SDA changes before SCL rises -> tSU;DAT
task automatic bit_cell(input logic v, input int hold, input int setup, input int high);
begin
scl_in = 1'b0; tick(hold); // hold the PREVIOUS bit after the falling edge
sda_in = v; tick(setup); // present the new bit, then wait the setup
scl_in = 1'b1; tick(high);
end
endtask
initial begin
tick(3);
if (min_su_seen !== {TICK_W{1'b1}} || min_hd_seen !== {TICK_W{1'b1}}) begin
$display("FAIL: worst-case trackers did not start at their maximum"); errors++; end
rst_n = 1'b1; tick(2);
sda_in = 1'b1; scl_in = 1'b1; tick(10);
// ---- 1: four LEGAL bits, alternating so that every one produces a real hold
// measurement. Generous margins on both sides: nothing may be flagged.
scl_in = 1'b1; tick(5);
bit_cell(1'b0, 50, 40, 80);
bit_cell(1'b1, 50, 40, 80);
bit_cell(1'b0, 50, 40, 80);
bit_cell(1'b1, 50, 40, 80);
scl_in = 1'b0; tick(50);
if (n_viol_su !== '0 || n_viol_hd !== '0) begin
$display("FAIL: legal bits flagged -- %0d setup, %0d hold", n_viol_su, n_viol_hd);
errors++; end
if (n_sulog < 4) begin
$display("FAIL: %0d setup measurements, expected 4", n_sulog); errors++; end
if (n_hdlog < 4) begin
$display("FAIL: %0d hold measurements, expected 4", n_hdlog); errors++; end
if (su_log[1] < 16'd35 || su_log[1] > 16'd45) begin
$display("FAIL: a 40-tick setup measured %0d", su_log[1]); errors++; end
if (hd_log[1] < 16'd45 || hd_log[1] > 16'd55) begin
$display("FAIL: a 50-tick hold measured %0d", hd_log[1]); errors++; end
// ---- 2: SETUP too short, hold fine. Only the setup verdict may fail -- the two
// parameters are measured against different edges and are independent.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b0, 50, 4, 80);
if (vsu_log[base_su] !== 1'b1) begin
$display("FAIL: a 4-tick setup was not flagged (min %0d)", T_SU_DAT_MIN);
errors++; end
if (vhd_log[base_hd] !== 1'b0) begin
$display("FAIL: a short SETUP also flagged the HOLD"); errors++; end
end
// ---- 3: HOLD too short, setup fine. The mirror of test 2 -- and the one that
// proves the hold is measured from the FALLING edge rather than the rising
// one. A checker that referenced the wrong edge passes test 2 and fails here.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b1, 5, 60, 80);
if (vhd_log[base_hd] !== 1'b1) begin
$display("FAIL: a 5-tick hold was not flagged (min %0d)", T_HD_DAT_MIN);
errors++; end
if (vsu_log[base_su] !== 1'b0) begin
$display("FAIL: a short HOLD also flagged the SETUP"); errors++; end
if (hd_log[base_hd] > 16'd7) begin
$display("FAIL: a 5-tick hold measured %0d ticks", hd_log[base_hd]); errors++; end
end
// ---- 4: BOTH too short at once. Both verdicts must fire; a design that reported
// only the first failure it found would hide half the problem.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b0, 3, 3, 80);
if (!(vhd_log[base_hd] && vsu_log[base_su])) begin
$display("FAIL: both intervals short reported hold=%b setup=%b",
vhd_log[base_hd], vsu_log[base_su]); errors++; end
end
// ---- 5: BOUNDARIES, both parameters. Exactly at the minimum is legal because the
// table's values are minima.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b1, T_HD_DAT_MIN, T_SU_DAT_MIN, 80);
if (vhd_log[base_hd] !== 1'b0) begin
$display("FAIL: a hold of EXACTLY the minimum was rejected"); errors++; end
if (vsu_log[base_su] !== 1'b0) begin
$display("FAIL: a setup of EXACTLY the minimum was rejected"); errors++; end
end
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b0, T_HD_DAT_MIN - 1, T_SU_DAT_MIN - 1, 80);
if (vhd_log[base_hd] !== 1'b1) begin
$display("FAIL: a hold one tick below the minimum was accepted"); errors++; end
if (vsu_log[base_su] !== 1'b1) begin
$display("FAIL: a setup one tick below the minimum was accepted"); errors++; end
end
// ---- 6: A REPEATED BIT produces NO hold measurement. If the transmitter sends
// the same value again, SDA never moves, so there is no hold interval to
// measure -- and nothing to complain about, because a bit that never changes
// trivially satisfies any hold requirement. A checker that measured the low
// phase instead would report the low width as a hold time and could never
// see a violation at all.
begin
nhd_before = n_hd; vhd_before = n_viol_hd;
sda_in = 1'b0; scl_in = 1'b0; tick(60);
scl_in = 1'b1; tick(80);
scl_in = 1'b0; tick(60); // same value: SDA does not move
scl_in = 1'b1; tick(80);
scl_in = 1'b0; tick(20);
if (n_hd !== nhd_before) begin
$display("FAIL: a repeated bit produced %0d hold measurements",
n_hd - nhd_before); errors++; end
if (n_viol_hd !== vhd_before) begin
$display("FAIL: a repeated bit was flagged as a hold violation"); errors++; end
end
// ---- 6b: and the consequence of NOT cancelling the arm. Straight after the repeated
// bit above, a bit with a genuinely SHORT hold must still be flagged. If the arm
// had survived the repeated bit, the timer would have been running since the
// EARLIER falling edge, the measured hold would be enormous, and this real
// violation would be missed entirely.
begin
// Leave SCL HIGH first, so bit_cell supplies a FRESH falling edge. Without that
// the hold would be timed from the repeated-bit sequence's own earlier fall and
// would measure 24 ticks rather than 4 -- correct behaviour, wrong test.
scl_in = 1'b1; tick(40);
base_hd = n_hdlog;
bit_cell(1'b1, 4, 60, 80);
if (vhd_log[base_hd] !== 1'b1) begin
$display("FAIL: a 4-tick hold right after a repeated bit was not flagged -- a stale arm inflated the measurement");
errors++; end
if (hd_log[base_hd] > 16'd10) begin
$display("FAIL: the hold measured %0d ticks, so it was timed from an earlier falling edge",
hd_log[base_hd]); errors++; end
end
// ---- 6c: and the sharper consequence. After a repeated bit, an SDA change during a
// HIGH phase must produce NO hold measurement -- a hold is an interval that starts
// at a falling edge, and this change has no falling edge in front of it that has
// not already been closed out. With the arm left standing, that change is timed
// from the previous fall and reported as a hold that never happened.
begin
nhd_before = n_hd;
sda_in = 1'b0; scl_in = 1'b0; tick(60);
scl_in = 1'b1; tick(40); // the low phase ends, no SDA change
sda_in = 1'b1; tick(40); // SDA moves while SCL is HIGH
scl_in = 1'b0; tick(40);
if (n_hd !== nhd_before) begin
$display("FAIL: an SDA change during a HIGH phase produced %0d hold measurements -- the arm outlived its low phase",
n_hd - nhd_before); errors++; end
end
// ---- 7: STRETCHING. A very long low phase gives an enormous hold time, which is
// legal -- tHD;DAT has a minimum and this block checks a minimum, so more is
// always better. This confirms the comparison has the right sense.
begin
vhd_before = n_viol_hd;
bit_cell(1'b1, 2000, 40, 80);
if (n_viol_hd !== vhd_before) begin
$display("FAIL: a 2000-tick hold was flagged"); errors++; end
end
// ---- 8: the worst case is TRACKED, not the most recent. A clean run after a
// violation must not erase the record of it.
begin
repeat (4) bit_cell(1'b0, 90, 90, 80);
repeat (4) bit_cell(1'b1, 90, 90, 80);
if (min_su_seen > 16'd10 || min_hd_seen > 16'd30) begin
$display("FAIL: worst cases were erased -- min_su %0d, min_hd %0d",
min_su_seen, min_hd_seen); errors++; end
end
// ---- 9: an IDLE bus measures nothing. No SCL edges, no setup and no hold.
begin
vsu_before = n_su;
sda_in = 1'b1; scl_in = 1'b1; tick(300);
if (n_su !== vsu_before) begin
$display("FAIL: an idle bus produced %0d setup measurements",
n_su - vsu_before); errors++; end
end
if (errors == 0)
$display("PASS: setup referenced to the rising edge and hold to the falling one, independent verdicts, both boundaries exact, a repeated bit measures nothing");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule // THE DATA WINDOW: tSU;DAT and tHD;DAT, the two parameters every bit of every byte must
// satisfy. They are measured against DIFFERENT edges, and getting that wrong is the most
// common error in a hand-built timing checker:
//
// tSU;DAT SDA stable BEFORE the RISING edge of SCL (a receiver's need)
// tHD;DAT SDA held AFTER the FALLING edge of SCL (a transmitter's duty)
//
// The specification is explicit about the second, because it is the counter-intuitive one:
//
// [2] "tHD;DAT is the data hold time that is measured from the falling edge of SCL,
// applies to data in transmission and the acknowledge."
//
// And then Table 10 says something that looks like a contradiction and is not. For
// I2C-bus devices tHD;DAT(min) is ZERO -- there is no bus-level hold requirement at all.
// But footnote [3] imposes a device-level one:
//
// [3] "A device must internally provide a hold time of at least 300 ns for the SDA
// signal (with respect to the VIH(min) of the SCL signal) to bridge the undefined
// region of the falling edge of SCL."
//
// Both are true and they are about different things. The BUS does not require a hold,
// because a receiver has already sampled by the time SCL falls. The DEVICE requires 300 ns
// so that its own SDA change does not land inside the window where SCL is neither clearly
// high nor clearly low -- during which a receiver's own input stage cannot say whether it
// sampled before or after the change. So 300 ns is not a bus timing margin; it is the
// width of the ambiguity that SCL's finite fall time creates.
//
// This block therefore checks the setup against a bus minimum and the hold against the
// DEVICE obligation, and reports them separately because they answer to different clauses.
//
// PASSIVE: observes the two wires and drives nothing.
// (Verilog-2001)
module i2c_data_window_checker #(
parameter TICK_W = 16,
// Fast-mode tSU;DAT = 100 ns = 10 ticks at 100 MHz.
parameter T_SU_DAT_MIN = 10,
// Footnote [3]'s internal hold obligation, 300 ns = 30 ticks. NOT Table 10's
// tHD;DAT(min), which is zero -- see the header.
parameter T_HD_DAT_MIN = 30
)(
input wire clk,
input wire rst_n,
input wire sda_in,
input wire scl_in,
// ---- measured per bit ----
output reg su_valid, // pulse: a setup time has been measured
output reg [TICK_W-1:0] t_su_dat,
output reg hd_valid, // pulse: a hold time has been measured
output reg [TICK_W-1:0] t_hd_dat,
// ---- verdicts ----
output reg viol_su,
output reg viol_hd,
// ---- totals and worst cases ----
output reg [TICK_W-1:0] n_su,
output reg [TICK_W-1:0] n_hd,
output reg [TICK_W-1:0] n_viol_su,
output reg [TICK_W-1:0] n_viol_hd,
output reg [TICK_W-1:0] min_su_seen,
output reg [TICK_W-1:0] min_hd_seen
);
reg sda_q, scl_q;
wire scl_rise, scl_fall, sda_changed;
assign scl_rise = !scl_q && scl_in;
assign scl_fall = scl_q && !scl_in;
assign sda_changed = sda_q != sda_in;
// SETUP is measured by a timer that runs ALWAYS and is sampled at the rising edge.
// A timer started by the edge would be too late -- the interval being measured has
// already finished by the time its end arrives, which is the defining property of a
// setup time and the reason it cannot be measured the way a hold time can.
reg [TICK_W-1:0] stable_ticks;
wire [TICK_W-1:0] stable_now;
assign stable_now = sda_changed ? {TICK_W{1'b0}} : (stable_ticks + 1'b1);
// HOLD is the opposite: the interval STARTS at the falling edge, so a timer armed by
// that edge and stopped by the next SDA change measures it directly. The asymmetry
// between the two measurements is forced by which end of the interval the reference
// edge sits at, and it is why one counter cannot serve both.
reg hd_arm;
reg [TICK_W-1:0] hd_ticks;
wire [TICK_W-1:0] hd_now;
assign hd_now = hd_ticks + 1'b1;
always @(posedge clk) begin
if (!rst_n) begin
sda_q <= 1'b1;
scl_q <= 1'b1;
stable_ticks <= {TICK_W{1'b0}};
hd_arm <= 1'b0;
hd_ticks <= {TICK_W{1'b0}};
su_valid <= 1'b0;
t_su_dat <= {TICK_W{1'b0}};
hd_valid <= 1'b0;
t_hd_dat <= {TICK_W{1'b0}};
viol_su <= 1'b0;
viol_hd <= 1'b0;
n_su <= {TICK_W{1'b0}};
n_hd <= {TICK_W{1'b0}};
n_viol_su <= {TICK_W{1'b0}};
n_viol_hd <= {TICK_W{1'b0}};
min_su_seen <= {TICK_W{1'b1}};
min_hd_seen <= {TICK_W{1'b1}};
end else begin
sda_q <= sda_in;
scl_q <= scl_in;
su_valid <= 1'b0;
hd_valid <= 1'b0;
stable_ticks <= stable_now;
// ---- the setup half: sample the always-running timer at the rising edge ----
if (scl_rise) begin
su_valid <= 1'b1;
t_su_dat <= stable_now;
viol_su <= (stable_now < T_SU_DAT_MIN);
n_su <= n_su + 1'b1;
if (stable_now < T_SU_DAT_MIN) n_viol_su <= n_viol_su + 1'b1;
if (stable_now < min_su_seen) min_su_seen <= stable_now;
end
// ---- the hold half: arm at the falling edge, stop at the next SDA change ----
if (scl_fall) begin
hd_arm <= 1'b1;
hd_ticks <= {TICK_W{1'b0}};
end else if (hd_arm) begin
if (sda_changed) begin
// SDA has moved. The hold time is however long it waited. Note that a
// transmitter which does not change the bit at all holds it for the
// whole low phase, which trivially satisfies the obligation -- so only
// an ACTUAL change produces a measurement. A checker that measured
// every low phase would report the low phase width as a hold time and
// never see a violation.
hd_arm <= 1'b0;
hd_valid <= 1'b1;
t_hd_dat <= hd_now;
viol_hd <= (hd_now < T_HD_DAT_MIN);
n_hd <= n_hd + 1'b1;
if (hd_now < T_HD_DAT_MIN) n_viol_hd <= n_viol_hd + 1'b1;
if (hd_now < min_hd_seen) min_hd_seen <= hd_now;
end else if (scl_rise) begin
// The low phase ended with no SDA change: the bit was repeated, so
// there is nothing to measure and nothing to complain about.
hd_arm <= 1'b0;
end else begin
hd_ticks <= hd_now;
end
end
end
end
endmodule `timescale 1ns/1ps
// 100 MHz sample clock. Fast-mode tSU;DAT = 100 ns = 10 ticks; footnote [3]'s internal
// hold obligation is 300 ns = 30 ticks.
module i2c_data_window_checker_tb; // Verilog-2001
localparam TICK_W = 16;
localparam T_SU_DAT_MIN = 10;
localparam T_HD_DAT_MIN = 30;
reg clk = 1'b0;
always #5 clk = ~clk;
reg rst_n = 1'b0;
reg sda_in = 1'b1, scl_in = 1'b1;
wire su_valid, hd_valid, viol_su, viol_hd;
wire [TICK_W-1:0] t_su_dat, t_hd_dat, n_su, n_hd, n_viol_su, n_viol_hd;
wire [TICK_W-1:0] min_su_seen, min_hd_seen;
integer errors = 0;
integer base_su = 0, base_hd = 0;
reg [TICK_W-1:0] vsu_before, vhd_before, nhd_before;
i2c_data_window_checker #(.TICK_W(TICK_W), .T_SU_DAT_MIN(T_SU_DAT_MIN),
.T_HD_DAT_MIN(T_HD_DAT_MIN)) dut (
.clk(clk), .rst_n(rst_n), .sda_in(sda_in), .scl_in(scl_in), .su_valid(su_valid),
.t_su_dat(t_su_dat), .hd_valid(hd_valid), .t_hd_dat(t_hd_dat), .viol_su(viol_su),
.viol_hd(viol_hd), .n_su(n_su), .n_hd(n_hd), .n_viol_su(n_viol_su),
.n_viol_hd(n_viol_hd), .min_su_seen(min_su_seen), .min_hd_seen(min_hd_seen));
initial begin #1000000; $display("FAIL: watchdog expired"); $finish; end
reg [TICK_W-1:0] su_log [0:31];
reg vsu_log [0:31];
integer n_sulog = 0;
always @(posedge clk) if (rst_n && su_valid && n_sulog < 32) begin
su_log[n_sulog] = t_su_dat; vsu_log[n_sulog] = viol_su; n_sulog = n_sulog + 1;
end
reg [TICK_W-1:0] hd_log [0:31];
reg vhd_log [0:31];
integer n_hdlog = 0;
always @(posedge clk) if (rst_n && hd_valid && n_hdlog < 32) begin
hd_log[n_hdlog] = t_hd_dat; vhd_log[n_hdlog] = viol_hd; n_hdlog = n_hdlog + 1;
end
task tick;
input integer n;
begin repeat (n) @(negedge clk); end
endtask
// One bit, with the two intervals under test made independently settable:
// `hold` ticks after SCL falls before SDA changes -> tHD;DAT
// `setup` ticks after SDA changes before SCL rises -> tSU;DAT
task bit_cell;
input v;
input integer hold;
input integer setup;
input integer high;
begin
scl_in = 1'b0; tick(hold); // hold the PREVIOUS bit after the falling edge
sda_in = v; tick(setup); // present the new bit, then wait the setup
scl_in = 1'b1; tick(high);
end
endtask
initial begin
tick(3);
if (min_su_seen !== {TICK_W{1'b1}} || min_hd_seen !== {TICK_W{1'b1}}) begin
$display("FAIL: worst-case trackers did not start at their maximum"); errors = errors + 1; end
rst_n = 1'b1; tick(2);
sda_in = 1'b1; scl_in = 1'b1; tick(10);
// ---- 1: four LEGAL bits, alternating so that every one produces a real hold
// measurement. Generous margins on both sides: nothing may be flagged.
scl_in = 1'b1; tick(5);
bit_cell(1'b0, 50, 40, 80);
bit_cell(1'b1, 50, 40, 80);
bit_cell(1'b0, 50, 40, 80);
bit_cell(1'b1, 50, 40, 80);
scl_in = 1'b0; tick(50);
if (n_viol_su !== {TICK_W{1'b0}} || n_viol_hd !== {TICK_W{1'b0}}) begin
$display("FAIL: legal bits flagged -- %0d setup, %0d hold", n_viol_su, n_viol_hd);
errors = errors + 1; end
if (n_sulog < 4) begin
$display("FAIL: %0d setup measurements, expected 4", n_sulog); errors = errors + 1; end
if (n_hdlog < 4) begin
$display("FAIL: %0d hold measurements, expected 4", n_hdlog); errors = errors + 1; end
if (su_log[1] < 16'd35 || su_log[1] > 16'd45) begin
$display("FAIL: a 40-tick setup measured %0d", su_log[1]); errors = errors + 1; end
if (hd_log[1] < 16'd45 || hd_log[1] > 16'd55) begin
$display("FAIL: a 50-tick hold measured %0d", hd_log[1]); errors = errors + 1; end
// ---- 2: SETUP too short, hold fine. Only the setup verdict may fail -- the two
// parameters are measured against different edges and are independent.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b0, 50, 4, 80);
if (vsu_log[base_su] !== 1'b1) begin
$display("FAIL: a 4-tick setup was not flagged (min %0d)", T_SU_DAT_MIN);
errors = errors + 1; end
if (vhd_log[base_hd] !== 1'b0) begin
$display("FAIL: a short SETUP also flagged the HOLD"); errors = errors + 1; end
end
// ---- 3: HOLD too short, setup fine. The mirror of test 2 -- and the one that
// proves the hold is measured from the FALLING edge rather than the rising
// one. A checker that referenced the wrong edge passes test 2 and fails here.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b1, 5, 60, 80);
if (vhd_log[base_hd] !== 1'b1) begin
$display("FAIL: a 5-tick hold was not flagged (min %0d)", T_HD_DAT_MIN);
errors = errors + 1; end
if (vsu_log[base_su] !== 1'b0) begin
$display("FAIL: a short HOLD also flagged the SETUP"); errors = errors + 1; end
if (hd_log[base_hd] > 16'd7) begin
$display("FAIL: a 5-tick hold measured %0d ticks", hd_log[base_hd]); errors = errors + 1; end
end
// ---- 4: BOTH too short at once. Both verdicts must fire; a design that reported
// only the first failure it found would hide half the problem.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b0, 3, 3, 80);
if (!(vhd_log[base_hd] && vsu_log[base_su])) begin
$display("FAIL: both intervals short reported hold=%b setup=%b",
vhd_log[base_hd], vsu_log[base_su]); errors = errors + 1; end
end
// ---- 5: BOUNDARIES, both parameters. Exactly at the minimum is legal because the
// table's values are minima.
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b1, T_HD_DAT_MIN, T_SU_DAT_MIN, 80);
if (vhd_log[base_hd] !== 1'b0) begin
$display("FAIL: a hold of EXACTLY the minimum was rejected"); errors = errors + 1; end
if (vsu_log[base_su] !== 1'b0) begin
$display("FAIL: a setup of EXACTLY the minimum was rejected"); errors = errors + 1; end
end
begin
base_su = n_sulog; base_hd = n_hdlog;
bit_cell(1'b0, T_HD_DAT_MIN - 1, T_SU_DAT_MIN - 1, 80);
if (vhd_log[base_hd] !== 1'b1) begin
$display("FAIL: a hold one tick below the minimum was accepted"); errors = errors + 1; end
if (vsu_log[base_su] !== 1'b1) begin
$display("FAIL: a setup one tick below the minimum was accepted"); errors = errors + 1; end
end
// ---- 6: A REPEATED BIT produces NO hold measurement. If the transmitter sends
// the same value again, SDA never moves, so there is no hold interval to
// measure -- and nothing to complain about, because a bit that never changes
// trivially satisfies any hold requirement. A checker that measured the low
// phase instead would report the low width as a hold time and could never
// see a violation at all.
begin
nhd_before = n_hd; vhd_before = n_viol_hd;
sda_in = 1'b0; scl_in = 1'b0; tick(60);
scl_in = 1'b1; tick(80);
scl_in = 1'b0; tick(60); // same value: SDA does not move
scl_in = 1'b1; tick(80);
scl_in = 1'b0; tick(20);
if (n_hd !== nhd_before) begin
$display("FAIL: a repeated bit produced %0d hold measurements",
n_hd - nhd_before); errors = errors + 1; end
if (n_viol_hd !== vhd_before) begin
$display("FAIL: a repeated bit was flagged as a hold violation"); errors = errors + 1; end
end
// ---- 6b: and the consequence of NOT cancelling the arm. Straight after the repeated
// bit above, a bit with a genuinely SHORT hold must still be flagged. If the arm
// had survived the repeated bit, the timer would have been running since the
// EARLIER falling edge, the measured hold would be enormous, and this real
// violation would be missed entirely.
begin
// Leave SCL HIGH first, so bit_cell supplies a FRESH falling edge. Without that
// the hold would be timed from the repeated-bit sequence's own earlier fall and
// would measure 24 ticks rather than 4 -- correct behaviour, wrong test.
scl_in = 1'b1; tick(40);
base_hd = n_hdlog;
bit_cell(1'b1, 4, 60, 80);
if (vhd_log[base_hd] !== 1'b1) begin
$display("FAIL: a 4-tick hold right after a repeated bit was not flagged -- a stale arm inflated the measurement");
errors = errors + 1; end
if (hd_log[base_hd] > 16'd10) begin
$display("FAIL: the hold measured %0d ticks, so it was timed from an earlier falling edge",
hd_log[base_hd]); errors = errors + 1; end
end
// ---- 6c: and the sharper consequence. After a repeated bit, an SDA change during a
// HIGH phase must produce NO hold measurement -- a hold is an interval that starts
// at a falling edge, and this change has no falling edge in front of it that has
// not already been closed out. With the arm left standing, that change is timed
// from the previous fall and reported as a hold that never happened.
begin
nhd_before = n_hd;
sda_in = 1'b0; scl_in = 1'b0; tick(60);
scl_in = 1'b1; tick(40); // the low phase ends, no SDA change
sda_in = 1'b1; tick(40); // SDA moves while SCL is HIGH
scl_in = 1'b0; tick(40);
if (n_hd !== nhd_before) begin
$display("FAIL: an SDA change during a HIGH phase produced %0d hold measurements -- the arm outlived its low phase",
n_hd - nhd_before); errors = errors + 1; end
end
// ---- 7: STRETCHING. A very long low phase gives an enormous hold time, which is
// legal -- tHD;DAT has a minimum and this block checks a minimum, so more is
// always better. This confirms the comparison has the right sense.
begin
vhd_before = n_viol_hd;
bit_cell(1'b1, 2000, 40, 80);
if (n_viol_hd !== vhd_before) begin
$display("FAIL: a 2000-tick hold was flagged"); errors = errors + 1; end
end
// ---- 8: the worst case is TRACKED, not the most recent. A clean run after a
// violation must not erase the record of it.
begin
repeat (4) bit_cell(1'b0, 90, 90, 80);
repeat (4) bit_cell(1'b1, 90, 90, 80);
if (min_su_seen > 16'd10 || min_hd_seen > 16'd30) begin
$display("FAIL: worst cases were erased -- min_su %0d, min_hd %0d",
min_su_seen, min_hd_seen); errors = errors + 1; end
end
// ---- 9: an IDLE bus measures nothing. No SCL edges, no setup and no hold.
begin
vsu_before = n_su;
sda_in = 1'b1; scl_in = 1'b1; tick(300);
if (n_su !== vsu_before) begin
$display("FAIL: an idle bus produced %0d setup measurements",
n_su - vsu_before); errors = errors + 1; end
end
if (errors == 0)
$display("PASS: setup referenced to the rising edge and hold to the falling one, independent verdicts, both boundaries exact, a repeated bit measures nothing");
else $display("FAIL: %0d error(s)", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- THE DATA WINDOW: tSU;DAT and tHD;DAT, the two parameters every bit of every byte must
-- satisfy. They are measured against DIFFERENT edges, and getting that wrong is the most
-- common error in a hand-built timing checker:
--
-- tSU;DAT SDA stable BEFORE the RISING edge of SCL (a receiver's need)
-- tHD;DAT SDA held AFTER the FALLING edge of SCL (a transmitter's duty)
--
-- [2] "tHD;DAT is the data hold time that is measured from the falling edge of SCL, applies
-- to data in transmission and the acknowledge."
--
-- And then Table 10 says something that looks like a contradiction and is not. For I2C-bus
-- devices tHD;DAT(min) is ZERO -- there is no bus-level hold requirement at all. But footnote
-- [3] imposes a device-level one:
--
-- [3] "A device must internally provide a hold time of at least 300 ns for the SDA signal
-- (with respect to the VIH(min) of the SCL signal) to bridge the undefined region of
-- the falling edge of SCL."
--
-- Both are true and they are about different things. The BUS does not require a hold, because
-- a receiver has already sampled by the time SCL falls. The DEVICE requires 300 ns so that
-- its own SDA change does not land inside the window where SCL is neither clearly high nor
-- clearly low. So 300 ns is not a bus timing margin; it is the width of the ambiguity that
-- SCL's finite fall time creates.
--
-- PASSIVE: observes the two wires and drives nothing.
entity i2c_data_window_checker is
generic (
TICK_W : positive := 16;
-- Fast-mode tSU;DAT = 100 ns = 10 ticks at 100 MHz.
T_SU_DAT_MIN : natural := 10;
-- Footnote [3]'s internal hold obligation, 300 ns = 30 ticks. NOT Table 10's
-- tHD;DAT(min), which is zero -- see the header.
T_HD_DAT_MIN : natural := 30
);
port (
clk : in std_logic;
rst_n : in std_logic;
sda_in : in std_logic;
scl_in : in std_logic;
su_valid : out std_logic;
t_su_dat : out unsigned(TICK_W - 1 downto 0);
hd_valid : out std_logic;
t_hd_dat : out unsigned(TICK_W - 1 downto 0);
viol_su : out std_logic;
viol_hd : out std_logic;
n_su : out unsigned(TICK_W - 1 downto 0);
n_hd : out unsigned(TICK_W - 1 downto 0);
n_viol_su : out unsigned(TICK_W - 1 downto 0);
n_viol_hd : out unsigned(TICK_W - 1 downto 0);
min_su_seen : out unsigned(TICK_W - 1 downto 0);
min_hd_seen : out unsigned(TICK_W - 1 downto 0)
);
end entity;
architecture rtl of i2c_data_window_checker is
signal sda_q, scl_q : std_logic := '1';
signal scl_rise, scl_fall, sda_changed : std_logic;
-- SETUP is measured by a timer that runs ALWAYS and is sampled at the rising edge. A timer
-- started by the edge would be too late -- the interval being measured has already
-- finished by the time its end arrives, which is the defining property of a setup time and
-- the reason it cannot be measured the way a hold time can.
signal stable_ticks : unsigned(TICK_W - 1 downto 0) := (others => '0');
signal stable_now : unsigned(TICK_W - 1 downto 0);
-- HOLD is the opposite: the interval STARTS at the falling edge, so a timer armed by that
-- edge and stopped by the next SDA change measures it directly. The asymmetry is forced by
-- which end of the interval the reference edge sits at, and it is why one counter cannot
-- serve both.
signal hd_arm : std_logic := '0';
signal hd_ticks : unsigned(TICK_W - 1 downto 0) := (others => '0');
signal hd_now : unsigned(TICK_W - 1 downto 0);
begin
scl_rise <= (not scl_q) and scl_in;
scl_fall <= scl_q and (not scl_in);
sda_changed <= '1' when sda_q /= sda_in else '0';
stable_now <= (others => '0') when sda_changed = '1' else stable_ticks + 1;
hd_now <= hd_ticks + 1;
process (clk)
begin
if rising_edge(clk) then
if rst_n = '0' then
sda_q <= '1';
scl_q <= '1';
stable_ticks <= (others => '0');
hd_arm <= '0';
hd_ticks <= (others => '0');
su_valid <= '0';
t_su_dat <= (others => '0');
hd_valid <= '0';
t_hd_dat <= (others => '0');
viol_su <= '0';
viol_hd <= '0';
n_su <= (others => '0');
n_hd <= (others => '0');
n_viol_su <= (others => '0');
n_viol_hd <= (others => '0');
min_su_seen <= (others => '1');
min_hd_seen <= (others => '1');
else
sda_q <= sda_in;
scl_q <= scl_in;
su_valid <= '0';
hd_valid <= '0';
stable_ticks <= stable_now;
-- the setup half: sample the always-running timer at the rising edge
if scl_rise = '1' then
su_valid <= '1';
t_su_dat <= stable_now;
if stable_now < to_unsigned(T_SU_DAT_MIN, TICK_W) then
viol_su <= '1';
n_viol_su <= n_viol_su + 1;
else
viol_su <= '0';
end if;
n_su <= n_su + 1;
if stable_now < min_su_seen then min_su_seen <= stable_now; end if;
end if;
-- the hold half: arm at the falling edge, stop at the next SDA change
if scl_fall = '1' then
hd_arm <= '1';
hd_ticks <= (others => '0');
elsif hd_arm = '1' then
if sda_changed = '1' then
-- SDA has moved. The hold time is however long it waited. A
-- transmitter that does not change the bit at all holds it for the
-- whole low phase, which trivially satisfies the obligation -- so only
-- an ACTUAL change produces a measurement. A checker that measured
-- every low phase would report the low width as a hold time and never
-- see a violation.
hd_arm <= '0';
hd_valid <= '1';
t_hd_dat <= hd_now;
if hd_now < to_unsigned(T_HD_DAT_MIN, TICK_W) then
viol_hd <= '1';
n_viol_hd <= n_viol_hd + 1;
else
viol_hd <= '0';
end if;
n_hd <= n_hd + 1;
if hd_now < min_hd_seen then min_hd_seen <= hd_now; end if;
elsif scl_rise = '1' then
-- The low phase ended with no SDA change: the bit was repeated, so
-- there is nothing to measure and nothing to complain about.
hd_arm <= '0';
else
hd_ticks <= hd_now;
end if;
end if;
end if;
end if;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- 100 MHz sample clock. Fast-mode tSU;DAT = 100 ns = 10 ticks; footnote [3]'s internal hold
-- obligation is 300 ns = 30 ticks.
entity i2c_data_window_checker_tb is
end entity;
architecture sim of i2c_data_window_checker_tb is
constant TICK_W : positive := 16;
constant T_SU_DAT_MIN : natural := 10;
constant T_HD_DAT_MIN : natural := 30;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal sda_in, scl_in : std_logic := '1';
signal su_valid, hd_valid, viol_su, viol_hd : std_logic;
signal t_su_dat, t_hd_dat : unsigned(TICK_W - 1 downto 0);
signal n_su, n_hd, n_viol_su, n_viol_hd : unsigned(TICK_W - 1 downto 0);
signal min_su_seen, min_hd_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 su_log : tick_arr := (others => (others => '0'));
signal hd_log : tick_arr := (others => (others => '0'));
signal vsu_log, vhd_log : bit_arr := (others => '0');
signal n_sulog, n_hdlog : natural := 0;
signal test_done : std_logic := '0';
begin
dut : entity work.i2c_data_window_checker
generic map (TICK_W => TICK_W, T_SU_DAT_MIN => T_SU_DAT_MIN,
T_HD_DAT_MIN => T_HD_DAT_MIN)
port map (clk => clk, rst_n => rst_n, sda_in => sda_in, scl_in => scl_in,
su_valid => su_valid, t_su_dat => t_su_dat, hd_valid => hd_valid,
t_hd_dat => t_hd_dat, viol_su => viol_su, viol_hd => viol_hd,
n_su => n_su, n_hd => n_hd, n_viol_su => n_viol_su, n_viol_hd => n_viol_hd,
min_su_seen => min_su_seen, min_hd_seen => min_hd_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;
obs_su : process (clk)
begin
if rising_edge(clk) and rst_n = '1' and su_valid = '1' and n_sulog < 32 then
su_log(n_sulog) <= t_su_dat; vsu_log(n_sulog) <= viol_su; n_sulog <= n_sulog + 1;
end if;
end process;
obs_hd : process (clk)
begin
if rising_edge(clk) and rst_n = '1' and hd_valid = '1' and n_hdlog < 32 then
hd_log(n_hdlog) <= t_hd_dat; vhd_log(n_hdlog) <= viol_hd; n_hdlog <= n_hdlog + 1;
end if;
end process;
stim : process
variable errs : natural := 0;
variable base_su, base_hd : natural;
variable nhd_before, vhd_before, nsu_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, with the two intervals under test independently settable:
-- `hold` ticks after SCL falls before SDA changes -> tHD;DAT
-- `setup` ticks after SDA changes before SCL rises -> tSU;DAT
procedure bit_cell (v : in std_logic; hold, setup, high : in positive) is
begin
scl_in <= '0'; tick(hold); -- hold the PREVIOUS bit after the falling edge
sda_in <= v; tick(setup); -- present the new bit, then wait the setup
scl_in <= '1'; tick(high);
end procedure;
begin
tick(3);
if min_su_seen /= (min_su_seen'range => '1')
or min_hd_seen /= (min_hd_seen'range => '1') then
report "worst-case trackers did not start at their maximum" severity error;
errs := errs + 1; end if;
rst_n <= '1'; tick(2);
sda_in <= '1'; scl_in <= '1'; tick(10);
-- 1: four LEGAL bits, alternating so every one produces a real hold measurement.
scl_in <= '1'; tick(5);
bit_cell('0', 50, 40, 80);
bit_cell('1', 50, 40, 80);
bit_cell('0', 50, 40, 80);
bit_cell('1', 50, 40, 80);
scl_in <= '0'; tick(50);
if n_viol_su /= to_unsigned(0, TICK_W) or n_viol_hd /= to_unsigned(0, TICK_W) then
report "legal bits were flagged" severity error; errs := errs + 1; end if;
if n_sulog < 4 then
report "too few setup measurements, expected 4" severity error;
errs := errs + 1; end if;
if n_hdlog < 4 then
report "too few hold measurements, expected 4" severity error;
errs := errs + 1; end if;
if su_log(1) < to_unsigned(35, TICK_W) or su_log(1) > to_unsigned(45, TICK_W) then
report "a 40-tick setup measured out of range" severity error; errs := errs + 1; end if;
if hd_log(1) < to_unsigned(45, TICK_W) or hd_log(1) > to_unsigned(55, TICK_W) then
report "a 50-tick hold measured out of range" severity error; errs := errs + 1; end if;
-- 2: SETUP too short, hold fine. Only the setup verdict may fail -- the two parameters
-- are measured against different edges and are independent.
base_su := n_sulog; base_hd := n_hdlog;
bit_cell('0', 50, 4, 80);
if vsu_log(base_su) /= '1' then
report "a 4-tick setup was not flagged" severity error; errs := errs + 1; end if;
if vhd_log(base_hd) /= '0' then
report "a short SETUP also flagged the HOLD" severity error; errs := errs + 1; end if;
-- 3: HOLD too short, setup fine. The mirror -- and the test that proves the hold is
-- measured from the FALLING edge rather than the rising one. A checker that referenced
-- the wrong edge passes test 2 and fails here.
base_su := n_sulog; base_hd := n_hdlog;
bit_cell('1', 5, 60, 80);
if vhd_log(base_hd) /= '1' then
report "a 5-tick hold was not flagged" severity error; errs := errs + 1; end if;
if vsu_log(base_su) /= '0' then
report "a short HOLD also flagged the SETUP" severity error; errs := errs + 1; end if;
if hd_log(base_hd) > to_unsigned(7, TICK_W) then
report "a 5-tick hold measured too long" severity error; errs := errs + 1; end if;
-- 4: BOTH too short at once. Both verdicts must fire; a design reporting only the
-- first failure it found would hide half the problem.
base_su := n_sulog; base_hd := n_hdlog;
bit_cell('0', 3, 3, 80);
if not (vhd_log(base_hd) = '1' and vsu_log(base_su) = '1') then
report "both intervals short did not report both verdicts" severity error;
errs := errs + 1; end if;
-- 5: BOUNDARIES, both parameters. Exactly at the minimum is legal.
base_su := n_sulog; base_hd := n_hdlog;
bit_cell('1', T_HD_DAT_MIN, T_SU_DAT_MIN, 80);
if vhd_log(base_hd) /= '0' then
report "a hold of EXACTLY the minimum was rejected" severity error;
errs := errs + 1; end if;
if vsu_log(base_su) /= '0' then
report "a setup of EXACTLY the minimum was rejected" severity error;
errs := errs + 1; end if;
base_su := n_sulog; base_hd := n_hdlog;
bit_cell('0', T_HD_DAT_MIN - 1, T_SU_DAT_MIN - 1, 80);
if vhd_log(base_hd) /= '1' then
report "a hold one tick below the minimum was accepted" severity error;
errs := errs + 1; end if;
if vsu_log(base_su) /= '1' then
report "a setup one tick below the minimum was accepted" severity error;
errs := errs + 1; end if;
-- 6: A REPEATED BIT produces NO hold measurement. If the transmitter sends the same
-- value again, SDA never moves, so there is no hold interval -- and nothing to complain
-- about, because a bit that never changes trivially satisfies any hold requirement.
nhd_before := n_hd; vhd_before := n_viol_hd;
sda_in <= '0'; scl_in <= '0'; tick(60);
scl_in <= '1'; tick(80);
scl_in <= '0'; tick(60); -- same value: SDA does not move
scl_in <= '1'; tick(80);
scl_in <= '0'; tick(20);
if n_hd /= nhd_before then
report "a repeated bit produced hold measurements" severity error;
errs := errs + 1; end if;
if n_viol_hd /= vhd_before then
report "a repeated bit was flagged as a hold violation" severity error;
errs := errs + 1; end if;
-- 6b: and the consequence of NOT cancelling the arm. Straight after the repeated bit, a
-- bit with a genuinely SHORT hold must still be flagged. If the arm had survived the
-- repeated bit, the timer would have been running since the EARLIER falling edge, the
-- measured hold would be enormous, and this real violation would be missed.
--
-- SCL is left HIGH first so bit_cell supplies a FRESH falling edge: without that the
-- hold is timed from the repeated-bit sequence's own earlier fall, which is correct
-- behaviour but the wrong test.
scl_in <= '1'; tick(40);
base_hd := n_hdlog;
bit_cell('1', 4, 60, 80);
if vhd_log(base_hd) /= '1' then
report "a 4-tick hold right after a repeated bit was not flagged -- a stale arm "
& "inflated the measurement" severity error; errs := errs + 1; end if;
if hd_log(base_hd) > to_unsigned(10, TICK_W) then
report "the hold was timed from an earlier falling edge" severity error;
errs := errs + 1; end if;
-- 6c: and the sharper consequence. After a repeated bit, an SDA change during a HIGH
-- phase must produce NO hold measurement -- a hold is an interval that starts at a
-- falling edge, and this change has no unclosed falling edge in front of it. With the
-- arm left standing, that change is timed from the previous fall and reported as a hold
-- that never happened.
nhd_before := n_hd;
sda_in <= '0'; scl_in <= '0'; tick(60);
scl_in <= '1'; tick(40); -- the low phase ends, no SDA change
sda_in <= '1'; tick(40); -- SDA moves while SCL is HIGH
scl_in <= '0'; tick(40);
if n_hd /= nhd_before then
report "an SDA change during a HIGH phase produced a hold measurement -- the arm "
& "outlived its low phase" severity error; errs := errs + 1; end if;
-- 7: STRETCHING. A very long low phase gives an enormous hold time, which is legal --
-- tHD;DAT has a minimum, so more is always better. This confirms the comparison's sense.
vhd_before := n_viol_hd;
bit_cell('1', 2000, 40, 80);
if n_viol_hd /= vhd_before then
report "a 2000-tick hold was flagged" severity error; errs := errs + 1; end if;
-- 8: the worst case is TRACKED, not the most recent. A clean run after a violation must
-- not erase the record of it.
for i in 1 to 4 loop bit_cell('0', 90, 90, 80); end loop;
for i in 1 to 4 loop bit_cell('1', 90, 90, 80); end loop;
if min_su_seen > to_unsigned(10, TICK_W) or min_hd_seen > to_unsigned(30, TICK_W) then
report "worst cases were erased" severity error; errs := errs + 1; end if;
-- 9: an IDLE bus measures nothing. No SCL edges, no setup and no hold.
nsu_before := n_su;
sda_in <= '1'; scl_in <= '1'; tick(300);
if n_su /= nsu_before then
report "an idle bus produced setup measurements" severity error;
errs := errs + 1; end if;
if errs = 0 then
report "i2c_data_window_checker self-check complete: setup referenced to the rising "
& "edge and hold to the falling one, independent verdicts, both boundaries "
& "exact, a repeated bit measures nothing" severity note;
else
report "i2c_data_window_checker self-check FAILED" severity error;
end if;
test_done <= '1';
wait;
end process;
end architecture;6a. Five Decisions Worth Defending
Two counters with visibly different shapes, because the parameters have different reference-edge positions. §2's callout is the argument. A reviewer should be able to see from the code which parameter is which kind, and a single shared counter would hide exactly the distinction that matters.
The hold is compared against footnote [3]'s 300 ns, not against Table 10's zero. The parameter is named T_HD_DAT_MIN and its comment says which of the two numbers it carries. A checker that used the table row would pass every device §3's callout warns about.
A repeated bit produces no measurement. Only an actual SDA change closes a hold interval. §5 gives the reason and mutation C2 in §8 shows what happens without the corresponding disarm.
Every measurement includes the sampling cycle. Both counters are read through a combinational "including this cycle" value. Chapter 11.1 §6a records how the omission was found; the consequence here is that both boundary comparisons shift by one sample period.
The two verdicts are independent outputs. A bit can fail one and pass the other — they are measured against different edges — and §7's tests 2 and 3 are the two halves of that. Mutation C4, which judges the hold against the setup minimum, is killed because the two minima differ (30 ticks against 10).
6b. Verified Execution
$ iverilog -g2012 -o d3 i2c_data_window_checker.sv i2c_data_window_checker_tb.sv && ./d3
PASS: setup referenced to the rising edge and hold to the falling one, independent verdicts,
both boundaries exact, a repeated bit measures nothing
i2c_data_window_checker_tb.sv:228: $finish called at 65170000 (1ps)
$ iverilog -g2005 -o v3 i2c_data_window_checker.v i2c_data_window_checker_tb.v && ./v3
PASS: setup referenced to the rising edge and hold to the falling one, independent verdicts,
both boundaries exact, a repeated bit measures nothing
i2c_data_window_checker_tb.v:237: $finish called at 65170000 (1ps)
$ nvc -a i2c_data_window_checker.vhd i2c_data_window_checker_tb.vhd
$ nvc -e i2c_data_window_checker_tb && nvc -r i2c_data_window_checker_tb --stop-time=1200us
** Note: 65170ns+0: i2c_data_window_checker self-check complete: setup referenced to the
rising edge and hold to the falling one, independent verdicts, both boundaries exact, a
repeated bit measures nothingAll three at 65170 ns.
7. What the Testbench Proves
The stimulus drives one bit with the hold and setup intervals independently settable, so either can be made illegal alone.
| # | stimulus | what it establishes |
|---|---|---|
| 1 | four legal alternating bits | nothing flagged; both measurements land where intended |
| 2 | 50-tick hold, 4-tick setup | only the setup verdict fails |
| 3 | 5-tick hold, 60-tick setup | only the hold verdict fails — the mirror of test 2 |
| 4 | both short | both verdicts fire |
| 5 | exactly at each minimum, then one below | both boundaries pinned from both sides |
| 6 | a repeated bit | no hold measurement, and no violation |
| 6b | a short hold immediately after a repeated bit | still flagged, and measured from the fresh falling edge |
| 6c | SDA moving during a high phase after a repeated bit | no hold measurement at all |
| 7 | a 2000-tick hold | not a violation — tHD;DAT is a minimum |
| 8 | a clean run after violations | worst cases not erased |
| 9 | an idle bus | nothing measured |
Test 3 is the one that proves the reference edge. A checker that armed its hold timer on the rising edge passes test 2 — the setup half is unaffected — and fails here, because the interval it measures is not tHD;DAT at all. Mutation C1 is that single-token change, and test 3 is the only stimulus in the suite that separates them.
Tests 6b and 6c exist because mutations survived without them, and the two cover different consequences of the same defect. Without the disarm, the arm from an earlier falling edge is still standing: 6b shows a genuinely short hold being missed because the stale timer inflated the measurement to 24 ticks, and 6c shows a hold being invented for an SDA change during a high phase, where there is no unclosed falling edge in front of it. One is a false negative and the other a false positive, from one missing branch.
Test 6b also carries a stimulus lesson. Its first version left SCL low, so bit_cell supplied no fresh falling edge and the hold was timed from the repeated-bit sequence's own earlier fall — correct behaviour, wrong test, measuring 24 ticks instead of 4. Fixing it meant raising SCL first. A test for an interval must supply the edge that starts it.
8. Mutation Testing
Five defects injected into the SystemVerilog checker.
| # | injected defect | outcome |
|---|---|---|
| C1 | the hold is referenced to the rising edge | killed — test 3 |
| C2 | the hold arm is not cancelled when the bit does not change | killed — tests 6b and 6c |
| C3 | the setup is measured with a timer armed by the edge | killed |
| C4 | the hold is judged against the setup minimum | killed |
| C5 | setup violations are counted but not reported | killed |
Five injected, five killed — with C2 requiring two new tests, recorded here rather than smoothed over.
C2 survived the first suite and the reason is instructive. Test 6 checked that a repeated bit produces no hold measurement, and it passed even with the disarm removed — because within test 6 itself SDA never moves at all, so no measurement appears either way. The defect only manifests later, when the stale arm meets the next SDA change. A test that checks "nothing happened during this window" cannot catch a defect whose effect is deferred, and the fix was to check what happens next.
That generalises: when a mutation removes a cleanup action, the test has to run past the cleanup to the operation that depends on it.
C4 is only observable because the two minima differ. With T_SU_DAT_MIN at 10 ticks and T_HD_DAT_MIN at 30, judging a hold against the setup limit accepts holds between 10 and 29 ticks that should fail. Had the testbench set both parameters to the same value the mutation would have been unobservable — the same parameter-degeneracy trap Chapter 11.4 §8 hits with tVD;DAT and tVD;ACK, where Table 10 gives the two the same number and the testbench has to deliberately differ.
9. Verification Connection — The Window Belongs to the Interface
// A continuously running stability timer. A setup interval ends at its reference edge, so it
// cannot be timed by a counter armed there -- section 2's rule, as SVA.
int stable_ticks;
always_ff @(posedge clk)
stable_ticks <= $changed(sda) ? 0 : stable_ticks + 1;
property p_su_dat;
@(posedge clk) $rose(scl) |-> (stable_ticks >= T_SU_DAT_MIN);
endproperty
assert property (p_su_dat)
else $error("tSU;DAT violated: stable for %0d ticks, minimum %0d",
stable_ticks, T_SU_DAT_MIN);
// The HOLD property is written the other way round: from the FALLING edge, SDA must not
// change for T_HD_DAT_MIN cycles. Expressed as a bounded `throughout` rather than by timing
// an interval, because what must be shown is that no change occurred inside a window -- and
// a property about an interval cannot be checked at its endpoints (Chapter 10.1 section 9).
property p_hd_dat;
@(posedge clk) $fell(scl) |-> ($stable(sda) [*T_HD_DAT_MIN]);
endproperty
assert property (p_hd_dat)
else $error("tHD;DAT violated: SDA moved within %0d ticks of SCL falling", T_HD_DAT_MIN);
// And the NEGATIVE property. tHD;DAT is a MINIMUM, so a long hold is legal -- a design that
// added an upper bound would flag every stretched transfer. Chapter 10.3 section 8 argues for
// asserting what must NOT happen wherever the wrong belief is reasonable, and "a hold that
// long must be wrong" is a very reasonable wrong belief.
property p_hd_has_no_maximum;
@(posedge clk) (hold_ticks > T_HD_DAT_MIN) |-> !viol_hd;
endproperty
assert property (p_hd_has_no_maximum)
else $error("a long hold was reported as a violation -- tHD;DAT has no maximum"); covergroup i2c_window_cg with function sample(int hold, int setup, int t_low,
int hd_min, int su_min);
// WHERE in the low phase the bit changed, as a fraction. This is the coverpoint that
// matters: a suite whose transitions always land in the middle of the low phase has
// exercised neither parameter, however many bits it sent.
position: coverpoint ((hold * 100) / t_low) {
bins at_the_fall = {[0:5]}; // the tHD;DAT edge -- must be reachable, must stay
// empty in regression
bins early = {[6:25]};
bins middle = {[26:70]};
bins late = {[71:94]};
bins at_the_rise = {[95:100]}; // the tSU;DAT edge
}
// Margins RELATIVE to each minimum, so the bins are reusable across speed modes -- the
// same reasoning Chapter 11.1 section 9 applies. An absolute bin set written for Fast-mode
// says nothing about Fast-mode Plus, where footnote [3]'s 300 ns is a much larger share of
// a much shorter low phase.
hd_margin: coverpoint (hold - hd_min) {
bins violation = {[$:-1]};
bins exact = {0};
bins tight = {[1:10]};
bins ample = {[11:$]};
}
su_margin: coverpoint (setup - su_min) {
bins violation = {[$:-1]};
bins exact = {0};
bins tight = {[1:10]};
bins ample = {[11:$]};
}
// The cross is the point. The two parameters are measured against different edges, so a
// suite can have ample margin on one and none on the other -- and covering them
// separately cannot show that both were ever tight at once.
hd_x_su: cross hd_margin, su_margin;
// A repeated bit is a distinct case that produces NO hold measurement, and a suite that
// never sends one has not exercised the disarm path that mutation C2 attacks.
repeated_bit: coverpoint (hold == 0 && setup == 0);
endgroup10. FPGA and ASIC Implications
Two counters, two compares — around 55 flops at TICK_W = 16. No arithmetic on any critical path.
The 300 ns of footnote [3] does not scale with speed, and that is the sizing consequence. It is the same 300 ns in all three modes, so as a share of the low phase it goes from 6 % of Standard-mode's 4.7 µs to 60 % of Fast-mode Plus's 500 ns. An Fm+ transmitter has 200 ns of its low phase left for everything else, and Chapter 11.9 shows that budget failing to close on the table's own worst-case numbers.
On the generating side, the hold is a delay you must insert deliberately. The natural RTL is to change SDA in the same clocked process that sees SCL fall, which gives a hold of one system-clock period — 10 ns at 100 MHz, thirty times short of the 300 ns required. Meeting footnote [3] means a counted delay between observing the falling edge and driving the new bit, and it is the kind of requirement that is easy to omit because the code looks correct without it.
The spike filter of Chapter 11.8 interacts with this measurement in a way worth stating. The filter delays transitions on both lines equally, so the measured intervals here are unchanged — a delay common to both ends of an interval cancels. What does not cancel is the effect on a transmitter: a filtered SCL means the falling edge is observed late, so a transmitter that starts its hold delay from the filtered edge finishes late too, and the time comes out of the low phase.
11. Debugging — The Hold Time Nobody Inserted
Pitfall — reading a minimum of zero as no hold requirement
// A slave's byte-transmit path. On each falling edge of SCL, present the next bit:
//
// always_ff @(posedge clk) begin
// if (scl_fall) sda_drive_low <= ~next_bit; // drive the new bit immediately
// end
//
// The designer checked Table 10, found tHD;DAT with a minimum of 0 for I2C-bus devices, and
// concluded correctly-as-far-as-it-goes that no hold was required. The RTL changes SDA one
// system clock after observing SCL fall: 10 ns at 100 MHz.
//
// Footnote [3] requires 300 ns. The design provides 10.
//
// And on the bench it worked perfectly, because the bench board had one slave, a 2.2 kohm
// pull-up and about 40 pF of bus capacitance -- SCL's falling edge took maybe 20 ns, so the
// ambiguous window the 300 ns exists to bridge was barely there.The production board had six devices, longer traces and 10 kohm pull-ups fitted to save current. SCL's fall time went from about 20 ns to nearly 250 ns.
Reads from the new slave returned values with single-bit errors. Not every read -- perhaps one byte in two hundred -- and the corrupted bit was not always in the same position.
The single most misleading observation: a logic-analyser capture of a failing read looked completely clean. Every bit sat at a well-defined level for its whole high phase. The analyser thresholds the signal, so a bit that changed during SCL's slow fall still reads as one stable value per cell.
Worse, the OTHER master on the bus read the same slave correctly. Two devices reading the same byte off the same wires, one getting it right and one wrong -- which is not a symptom anybody expects from a protocol violation, and it sent the investigation towards the failing master's input stage.
That divergence was the clue, once someone recognised it for what it is. If two receivers disagree about the same bit, the bit is genuinely ambiguous -- and on this bus the only thing that makes a bit ambiguous is SDA moving while SCL is neither clearly high nor clearly low. A scope confirmed it immediately: SDA was changing partway down SCL's fall.
The slave changed SDA within 10 ns of SCL's falling edge, inside the roughly 250 ns during which SCL was traversing the undefined region between VIH and VIL. Receivers whose input threshold sat higher had already latched; receivers whose threshold sat lower had not. Both were behaving correctly; the bus was not presenting a single answer.
The design met Table 10's stated minimum of zero and violated footnote [3]'s 300 ns, and the two are about different things: the table row is what the BUS requires, the footnote is what a DEVICE must provide precisely so that a finite fall time cannot make a bit ambiguous.
12. Common Misconceptions
"tHD;DAT(min) is zero, so there is no hold requirement." Zero is what the bus requires. Footnote [3] requires a device to provide 300 ns, and §11 is that misreading in the field.
"A hold time is measured after the sampling edge." On this bus it is measured after the falling edge — footnote [2] says so — because its job is to bridge the ambiguity of that edge, not to give the receiver time to sample. The receiver sampled at the rising edge.
"The 300 ns scales with the speed grade." It does not. The same 300 ns applies in all three modes, so it is proportionally tightest at Fast-mode Plus, where it consumes 60 % of the low phase.
"Setup and hold can share one counter." Their reference edges sit at opposite ends of their intervals, so one needs a free-running timer sampled at the edge and the other an armed timer stopped by a change. §2's callout is the rule.
"A repeated bit violates the hold requirement, since SDA never moved." A bit that never changes trivially satisfies any hold requirement. There is no interval to measure and nothing to report — and a checker that measured the whole low phase instead would never see a real violation.
"A long hold is a violation." tHD;DAT is a minimum. Footnote [4] mentions a maximum, but note its condition: it applies only if the device does not stretch the clock, and it is bounded by tVD;DAT rather than being an independent limit.
"A clean logic-analyser capture rules out a hold violation." It rules out nothing about timing. The analyser thresholds the signal before recording it, so a bit changing inside SCL's fall still reads as one stable value.
13. Reason It Through
A design changes SDA one system clock after SCL falls, and meets Table 10's tHD;DAT(min) of zero. Is it compliant?
No. Footnote [3] requires the device to provide at least 300 ns, and one clock at 100 MHz is 10 ns. The table row states what the bus requires; the footnote states what a device must provide so that SCL's finite fall time cannot make a bit ambiguous. §11 is the consequence.
Two masters read the same byte from the same slave and get different values. What does that tell you, and where should you not look?
That the bit is genuinely ambiguous on the wire — two correct receivers cannot disagree about an unambiguous signal. Do not look at either receiver's input stage. On this bus the only thing that makes a bit ambiguous is an SDA edge inside a clock transition, which is a tHD;DAT violation.
Why does a checker that measures the whole low phase as its hold time never report a violation?
Because the low phase is always at least tLOW(min), which is far longer than the 300 ns hold requirement. The measurement would always pass. The hold interval must end at the SDA change, which is why a repeated bit produces no measurement at all rather than a very long one.
A mutation removes the code that cancels the hold arm at the end of a low phase, and the test checking "a repeated bit produces no measurement" still passes. Why, and what kind of test is needed?
Because within that window SDA never moves, so no measurement appears either way — the defect's effect is deferred to the next SDA change. A test has to run past the cleanup to the operation that depends on it: §7's tests 6b and 6c check the next bit's hold is measured from a fresh edge, and that a high-phase change produces nothing. When a mutation removes a cleanup, the test must reach the next user of the cleaned-up state.
Footnote [5] requires a Fast-mode device in a Standard-mode system to get the next bit out 1250 ns before releasing SCL. Where does 1250 come from, and why is it so much larger than the 100 ns Fast-mode setup?
It is tr(max) + tSU;DAT at Standard-mode values: 1000 + 250. The 1000 ns dominates, and it is Standard-mode's rise time — a full microsecond, because Standard-mode permits far slower edges. The setup requirement is not the large term; the settling is. It is the clearest single illustration of Chapter 11.7's point that the edges are inside the budget.
14. Understanding Check
15. Summary
Two parameters, two reference edges. tSU;DAT before the rising edge, tHD;DAT after the falling one — and footnote [2] states the second explicitly because it is the counter-intuitive one.
The hold is referenced to the falling edge because that is where the ambiguity is. SCL's fall has finite width, and during it a receiver cannot say whether it has sampled. The hold keeps SDA still until the clock has unambiguously fallen; it is not time for the receiver to sample, which already happened.
tHD;DAT(min) is zero for the bus and 300 ns for a device. Both are true and about different things. Reading the table row without footnote [3] produces a design that works on a lightly loaded board and fails as the fall time grows.
The 300 ns does not scale with speed, so it is proportionally tightest at Fast-mode Plus — 60 % of a 500 ns low phase, which is why Chapter 11.9's budget does not close there on worst-case numbers.
Which end of an interval its reference edge sits at decides how it can be measured. Setup needs a free-running timer sampled at the edge; hold needs an armed timer stopped by a change. One counter cannot serve both, and a checker whose shapes are visibly different is one a reviewer can check.
A repeated bit measures nothing, and the arm must be cancelled when its low phase ends. Omitting the cancellation both misses real violations and invents false ones — and the test for it has to run past the omission to the next operation that depends on it.
Two receivers disagreeing about one bit is a diagnosis, not a mystery. It means the bit is ambiguous on the wire, and on this bus that means an SDA edge inside a clock transition.
16. What Comes Next
Chapter 11.4 takes the other side of this bargain. tSU;DAT says what a receiver needs; tVD;DAT says how quickly a transmitter must produce the bit — and it is a maximum, which inverts every comparison and every worst-case tracker in the checker.
It also explains why the acknowledge gets its own parameter, tVD;ACK, when Table 10 gives it the identical number in all three speed modes: because a different device drives it, so it is a different device's promise. Two parameters with one value are still two obligations, and a testbench that set them equal could not tell whether a design applied them independently — which is exactly the parameter-degeneracy trap that let one of that chapter's mutations survive until the configured values were deliberately made to differ.
Continue learning
Related tutorials
- Related topic
The Data-Valid Rule — SDA Stable While SCL Is High
One sentence governs every bit on an I²C bus, and it is derived rather than decreed: the receiver needs a settled value at the instant it looks. What falls out is that an SDA edge while SCL is HIGH cannot be data — which is why the bus reserves it for framing.
- Related topic
Reading an I²C Timing Diagram
A specification timing diagram shows every relationship at once, which is why it looks impenetrable. Read it in passes instead: signals, edges, stable regions, framing, intervals, edge shapes, and finally who owes each requirement.
- Related topic
START/STOP Timing and Malformed Framing
Three framing margins, each with two anchor events, all of them minimums: the hold after a START, the setup before a repeated START, and the setup before a STOP. Build a sequencer that generates all three and refuses an illegal configuration, then catalogue the malformed framing the margins exist to prevent.
- Related topic
Address Decoding Inside a Slave
The first piece of slave hardware in the curriculum, and it is assembled rather than written: byte capture, reserved-map classification, one equality test, and a decision whose ordering matters — because a prohibition has to beat an address match.
