I²C · Module 12
Why I²C Clock Stretching Exists
The one mechanism that lets a target push back on a clock it does not own. Covers the mismatch it solves, the specification's two stretching levels, and why 'optional' makes a non-stretching bus an electrical contract.
Module 11 closed on a promise. Chapter 11.9 §10 found that of the five ways to fix a low-phase deficit, exactly one costs nothing but speed: lengthen the low phase, which is always legal because tLOW has no maximum. This module is that remedy turned into a protocol feature — and handed to the device that needs it most.
The master owns the clock. The slave has the data. When those two disagree about how fast a byte can move, somebody has to give.
Clock stretching is how the slave gets to be the one who decides. It is the only mechanism in I²C that lets a device with no control over the clock stop it.
1. The Mismatch, Before Any Mechanism
Every chapter so far has assumed the master sets the pace and the slave keeps up. That assumption is wrong in an entirely ordinary case, and the specification names two versions of it.
A slave that cannot store a byte fast enough. An EEPROM writing a received byte into its array has a programming time measured in milliseconds, not microseconds. At Fast-mode a byte takes 22.5 µs. The arithmetic does not work by three orders of magnitude.
A slave that cannot produce the next byte fast enough. A sensor asked for a reading may have to start a conversion. An ADC's conversion time is not related to the bus rate in any way.
A slave whose whole existence is slower than the bus. A microcontroller emulating I²C in software has a service latency set by its interrupt path, not by its pins.
Without stretching, each of these forces the master to know something about the slave: a per-device delay baked into a driver, discovered empirically, and wrong the moment a part is substituted. With stretching, none of that is needed — the slave says "wait" and the master waits, and neither has to know the other's internals.
Four things in that passage are worth pulling out before going further, and each one becomes a section of this module.
There are TWO features described, not one. Byte level is a single pause at a known point; bit level extends every low period. They solve different problems and cost different amounts. §3 separates them and Chapter 12.3 prices them.
Byte-level stretching happens after the ACKNOWLEDGMENT, not after the eighth data bit. The slave acknowledges first — committing to the byte — and then stops the clock. That ordering is the whole handshake and §3 is about why it has to be that way round.
It is optional, and most slaves cannot do it. That single word governs an enormous amount of real design, and §4 is about it.
Hs-mode restricts it to byte level. A normative constraint that Chapter 12.3 turns into a hardware check.
2. What a Stretch Means
There is a sentence elsewhere in the specification that states the semantics better than §3.1.9 does, and it is easy to miss because it appears in the SMBus comparison:
3. The Two Levels, and Why the Acknowledge Comes First
The two levels of §3.1.9 differ in where the pause goes, and the byte-level position is the one that carries design content.
| byte level | bit level | |
|---|---|---|
| where | the low phase after the acknowledge | every low phase |
| how often | once per byte, when needed | continuously |
| what it solves | store this byte / prepare the next | the device is slower than the bus |
| typical device | EEPROM, sensor, any state machine with work to do | a microcontroller bit-banging in software |
| Hs-mode | permitted | not permitted |
| cost | one pause per byte | the bus runs at the device's rate |
Why the acknowledge comes first is the part worth reasoning through. The slave could in principle stop the clock before the ninth bit — after the eighth data bit, while it decides. It does not, and the specification is explicit that the handshake sits "after reception and acknowledgment of a byte".
The reason is that the acknowledge is a decision, and the slave must make it before it is allowed to ask for time. Acknowledging says "I have this byte and I will keep it". Only then does stretching mean "and I need a moment to put it away". Reversing the order would produce a slave that holds the bus while deciding whether to accept — which is indistinguishable, from the master's side, from a slave that has hung.
That is also why byte-level stretching is safe in a way a pre-acknowledge pause would not be: at the moment the clock stops, the byte's fate is already settled and recorded.
4. Optional — and What That Costs Everyone Else
Which leads to the rule Chapter 12.4 is built around: never assume a target will not stretch unless the device or system contract guarantees it. The cost of assuming wrongly is not a slow bus — it is a bus that does not work, and whose failure mode is electrical.
And note the asymmetry in who bears the cost. A slave that stretches needs an SCL output driver it would not otherwise have — which is why the specification says most slaves cannot do it. A master that tolerates stretching needs an SCL input and the logic to use it. The slave pays in silicon; the master pays in design discipline.
5. A Stretch, Drawn
The master releases; the line does not rise
10 cyclesThe figure is drawn with two separate rows for intent and observation, and that is the chapter's central point rather than a drafting convenience. A stretch is not visible on one wire. Looking only at the bottom row, all you can say is that SCL was low for six intervals — which is also what a slow master looks like. The event only exists in the difference between the two rows, and §6's design takes both as inputs for exactly that reason.
Both rows use kind: "signal" rather than kind: "clock", for the reason Chapter 11.1 §3 gives: the claim here is about the specific levels of specific intervals, and a clock row renders a generic square wave that would destroy the comparison.
6. Detecting a Stretch in Three Languages
// CLOCK STRETCHING, OBSERVED. Before anything can be said about why stretching exists or what
// it costs, a block has to be able to SEE it -- and seeing it is less obvious than it looks.
//
// UM10204 section 3.1.9: "Clock stretching pauses a transaction by holding the SCL line LOW.
// The transaction cannot continue until the line is released HIGH again."
//
// The trap is that "SCL is LOW" is not the observation. SCL is low for half of every bit on a
// healthy bus, because the master drives it low. What distinguishes a stretch is a DISAGREEMENT:
// this device has released the line and the line is still low, so somebody else is holding it.
//
// That is why this block has TWO clock inputs and not one:
//
// scl_release -- this device's INTENT. 1 = I have let go, 0 = I am driving it low.
// scl_in -- the OBSERVED line, which is the wired-AND of every device's intent.
//
// A stretch is exactly (scl_release && !scl_in). With only scl_in there is no way to tell a
// stretch from an ordinary low phase, and a monitor built that way reports a stretch on every
// bit of every transfer. Mutation A2 is that monitor.
//
// The block also CLASSIFIES the stretch, because section 3.1.9 describes two different features
// under one name:
//
// BYTE LEVEL -- "Slaves can then hold the SCL line LOW after reception and acknowledgment of a
// byte to force the master into a wait state until the slave is ready for the
// next byte transfer in a type of handshake procedure."
// BIT LEVEL -- "a device such as a microcontroller ... can slow down the bus clock by
// extending each clock LOW period. The speed of any master is adapted to the
// internal operating rate of this device."
//
// These are not the same thing and a report that conflates them is much less useful: a byte-level
// stretch is a handshake at a known point and is what most real slaves do, while a bit-level
// stretch on every bit means the master is being rate-matched to something slow. Hs-mode permits
// byte level ONLY, so for an Hs design the distinction is a compliance question rather than a
// characterisation one.
//
// Classification needs the bit position, which the block derives from SCL falling edges since the
// last START: nine falls per byte, and the low phase following the ninth is the one section 3.1.9
// names. Everything else is bit level.
module i2c_stretch_detector #(
parameter int TICK_W = 24 // wide: section 4.2.2 says there is NO limit on a stretch,
// so a 16-bit counter at 100 MHz (655 us) can be overrun by
// a perfectly legal device. See section 10 of the chapter.
)(
input logic clk,
input logic rst_n,
// ---- what this device intends, and what the bus actually shows ----
input logic scl_release, // 1 = released (not driving low)
input logic scl_in, // the wired-AND result on the pin
input logic sda_in,
// ---- the event ----
output logic stretching, // level: released, and still held low
output logic stretch_begin, // pulse
output logic stretch_end, // pulse: the line finally went high
output logic [TICK_W-1:0] stretch_ticks, // live duration while stretching
output logic stretch_valid, // pulse with the completed measurement
output logic [TICK_W-1:0] t_stretch,
// ---- the classification, from section 3.1.9's two levels ----
output logic was_byte_level, // the low phase following an acknowledge
output logic was_bit_level,
output logic [3:0] stretch_bit, // which bit of the byte, 1..9
// ---- framing, so the block is self-contained on a capture ----
output logic start_det,
output logic stop_det,
output logic in_transfer,
// ---- totals and worst case. A duration is a MAXIMUM-style statistic here: the
// interesting stretch is the LONGEST one, so the tracker starts at zero.
output logic [TICK_W-1:0] n_stretch,
output logic [TICK_W-1:0] n_byte_level,
output logic [TICK_W-1:0] n_bit_level,
output logic [TICK_W-1:0] max_stretch_seen,
output logic [TICK_W-1:0] total_stretch_ticks
);
logic sda_q, scl_q, rel_q;
wire scl_rise = scl_in && !scl_q;
wire scl_fall = !scl_in && scl_q;
wire sda_rise = sda_in && !sda_q;
wire sda_fall = !sda_in && sda_q;
// Framing by definition: SDA moving while SCL is HIGH.
assign start_det = sda_fall && scl_in;
assign stop_det = sda_rise && scl_in;
// The stretch condition itself. Combinational on purpose: it is a statement about the
// present state of two wires, not an event to be latched.
wire stretch_now = scl_release && !scl_in;
// "Including this cycle" -- the same convention every measurement in Module 11 uses, so a
// stretch of exactly N sample periods reports N and not N-1.
logic [TICK_W-1:0] ticks_q;
wire [TICK_W-1:0] ticks_now = ticks_q + 1'b1;
// Bit position within the byte: nine SCL falls per byte. bit_of_byte is the bit whose LOW
// phase we are currently in, 1..9, and post_ack marks the one low phase that follows an
// acknowledgment -- section 3.1.9's byte-level handshake point.
logic [3:0] bit_of_byte;
logic post_ack;
always_ff @(posedge clk) begin
if (!rst_n) begin
sda_q <= 1'b1;
scl_q <= 1'b1;
rel_q <= 1'b1;
stretching <= 1'b0;
stretch_begin <= 1'b0;
stretch_end <= 1'b0;
ticks_q <= '0;
stretch_valid <= 1'b0;
t_stretch <= '0;
was_byte_level <= 1'b0;
was_bit_level <= 1'b0;
stretch_bit <= 4'd0;
in_transfer <= 1'b0;
bit_of_byte <= 4'd0;
post_ack <= 1'b0;
n_stretch <= '0;
n_byte_level <= '0;
n_bit_level <= '0;
// A worst-case MAXIMUM starts at zero so the first measurement replaces it. The
// minimum trackers of Chapters 11.2 and 11.5 start at all-ones for the mirrored
// reason, and swapping the two is a defect that reports a plausible number forever.
max_stretch_seen <= '0;
total_stretch_ticks <= '0;
end else begin
sda_q <= sda_in;
scl_q <= scl_in;
rel_q <= scl_release;
stretch_begin <= 1'b0;
stretch_end <= 1'b0;
stretch_valid <= 1'b0;
// ---- framing ----
if (start_det) begin
in_transfer <= 1'b1;
bit_of_byte <= 4'd0;
post_ack <= 1'b0;
end else if (stop_det) begin
in_transfer <= 1'b0;
bit_of_byte <= 4'd0;
post_ack <= 1'b0;
end else if (scl_fall && in_transfer) begin
// Nine falls per byte. The fall that leaves bit 9 enters the low phase that
// section 3.1.9 calls the byte-level handshake point, and it is simultaneously
// bit 1 of the next byte.
if (bit_of_byte == 4'd9) begin
bit_of_byte <= 4'd1;
post_ack <= 1'b1;
end else begin
bit_of_byte <= bit_of_byte + 4'd1;
post_ack <= 1'b0;
end
end
// ---- the stretch itself ----
if (stretch_now) begin
if (!stretching) begin
// Begins the cycle the disagreement first appears.
stretching <= 1'b1;
stretch_begin <= 1'b1;
ticks_q <= '0;
stretch_bit <= bit_of_byte;
end else begin
ticks_q <= ticks_now;
end
end else if (stretching) begin
// The line went high (or this device took it low again -- either way the
// disagreement is over and the measurement is complete).
stretching <= 1'b0;
stretch_end <= 1'b1;
stretch_valid <= 1'b1;
t_stretch <= ticks_now;
was_byte_level <= post_ack;
was_bit_level <= !post_ack;
n_stretch <= n_stretch + 1'b1;
total_stretch_ticks <= total_stretch_ticks + ticks_now;
if (post_ack) n_byte_level <= n_byte_level + 1'b1;
else n_bit_level <= n_bit_level + 1'b1;
if (ticks_now > max_stretch_seen) max_stretch_seen <= ticks_now;
end
end
end
assign stretch_ticks = ticks_q;
endmodule `timescale 1ns/1ps
// 100 MHz sample clock: one tick is 10 ns. The stimulus drives a master's INTENT (scl_release)
// and a slave's hold separately, and derives the observed line as the wired-AND of the two --
// which is the whole mechanism of Chapter 12.2 used here as a stimulus generator.
//
// Every expectation is derived from the drive parameters rather than written as a constant, so a
// test that passes is a test of the comparison and not of an arithmetic coincidence.
module i2c_stretch_detector_tb;
localparam int TICK_W = 24;
logic clk = 1'b0, rst_n = 1'b0;
always #5 clk = ~clk; // 10 ns period
// Master intent, slave hold, and the wired-AND the DUT sees.
logic m_scl_release = 1'b1;
logic s_scl_hold = 1'b0; // 1 = slave pulls SCL low
logic sda = 1'b1;
wire scl_line = m_scl_release && !s_scl_hold;
logic stretching, stretch_begin, stretch_end, stretch_valid;
logic [TICK_W-1:0] stretch_ticks, t_stretch;
logic was_byte_level, was_bit_level;
logic [3:0] stretch_bit;
logic start_det, stop_det, in_transfer;
logic [TICK_W-1:0] n_stretch, n_byte_level, n_bit_level;
logic [TICK_W-1:0] max_stretch_seen, total_stretch_ticks;
i2c_stretch_detector #(.TICK_W(TICK_W)) dut (
.clk(clk), .rst_n(rst_n),
.scl_release(m_scl_release), .scl_in(scl_line), .sda_in(sda),
.stretching(stretching), .stretch_begin(stretch_begin), .stretch_end(stretch_end),
.stretch_ticks(stretch_ticks), .stretch_valid(stretch_valid), .t_stretch(t_stretch),
.was_byte_level(was_byte_level), .was_bit_level(was_bit_level), .stretch_bit(stretch_bit),
.start_det(start_det), .stop_det(stop_det), .in_transfer(in_transfer),
.n_stretch(n_stretch), .n_byte_level(n_byte_level), .n_bit_level(n_bit_level),
.max_stretch_seen(max_stretch_seen), .total_stretch_ticks(total_stretch_ticks)
);
int errors = 0;
// ---- observation log. Captured on the completion pulse, which is the only cycle the
// measurement is meaningful -- Chapter 11.6's reporting-lag lesson applied up front.
int n_log;
int tlog [0:63];
bit bylog [0:63];
int bitlog[0:63];
always @(posedge clk) if (rst_n && stretch_valid && n_log < 64) begin
tlog[n_log] = t_stretch;
bylog[n_log] = was_byte_level;
bitlog[n_log] = stretch_bit;
n_log++;
end
task automatic tick(input int n);
begin repeat (n) @(negedge clk); end
endtask
// A bit cell: SCL low for `lo` ticks then high for `hi`, with the slave optionally holding
// SCL low for `hold` ticks after the master releases. Stimulus is driven on the NEGEDGE
// throughout -- Module 9's race lesson: never drive at the edge the DUT samples.
task automatic bit_cell(input int lo, input int hi, input int hold);
begin
m_scl_release = 1'b0; // master drives SCL low
tick(lo);
if (hold > 0) begin
s_scl_hold = 1'b1; // slave grabs it BEFORE the master lets go
m_scl_release = 1'b1; // master releases -- the disagreement starts here
tick(hold);
s_scl_hold = 1'b0; // slave lets go; the line rises
end else begin
m_scl_release = 1'b1;
end
tick(hi);
end
endtask
// One byte plus its acknowledge: nine bit cells. `hold_at` selects which bit's low phase is
// stretched (1..9), or 0 for none; `post_hold` stretches the low phase AFTER the ack, which
// is section 3.1.9's byte-level handshake point.
task automatic do_byte(input int hold_at, input int hold_len);
int b;
begin
for (b = 1; b <= 9; b++) bit_cell(20, 20, (b == hold_at) ? hold_len : 0);
end
endtask
task automatic do_start();
begin sda = 1'b1; m_scl_release = 1'b1; tick(10); sda = 1'b0; tick(10); end
endtask
task automatic do_stop();
begin m_scl_release = 1'b0; tick(5); sda = 1'b0; m_scl_release = 1'b1; tick(10);
sda = 1'b1; tick(10); end
endtask
initial begin
tick(4); rst_n = 1'b1; tick(4);
// ---- 1. reset state: a MAXIMUM tracker must start at zero ----------------------------
if (max_stretch_seen !== '0 || n_stretch !== '0 || total_stretch_ticks !== '0) begin
$display("FAIL: the stretch trackers did not start at zero"); errors++; end
if (stretching !== 1'b0) begin
$display("FAIL: stretching asserted out of reset"); errors++; end
// ---- 2. an unstretched byte: NOTHING reported ---------------------------------------
// This is the test that kills a monitor watching scl_in alone: on this stimulus SCL goes
// low nine times and not one of them is a stretch.
do_start();
do_byte(0, 0);
if (n_stretch !== '0) begin
$display("FAIL: an unstretched byte produced %0d stretches -- the block is watching the LINE, not the DISAGREEMENT", n_stretch);
errors++; end
if (max_stretch_seen !== '0) begin
$display("FAIL: max_stretch_seen moved with no stretch"); errors++; end
// ---- 3. a bit-level stretch, mid-byte ------------------------------------------------
begin
int before_n; before_n = n_stretch;
do_byte(4, 60); // 60 ticks on bit 4's low phase
if (n_stretch !== before_n + 1) begin
$display("FAIL: a 60-tick stretch on bit 4 produced %0d events", n_stretch - before_n);
errors++; end
else begin
if (tlog[n_log-1] != 60) begin
$display("FAIL: a 60-tick stretch measured %0d", tlog[n_log-1]); errors++; end
if (bylog[n_log-1] !== 1'b0) begin
$display("FAIL: a mid-byte stretch was classified BYTE level"); errors++; end
if (bitlog[n_log-1] != 4) begin
$display("FAIL: the stretch was reported at bit %0d, expected 4", bitlog[n_log-1]);
errors++; end
end
end
// ---- 4. a BYTE-level stretch: the low phase after the acknowledge --------------------
// Section 3.1.9's handshake point. The ninth bit is the ack, so the stretch goes on the
// FIRST low phase of the following byte -- which is why do_byte is called twice and the
// hold sits on bit 1 of the second call.
begin
int before_by; before_by = n_byte_level;
do_byte(0, 0); // a clean byte, ending with its ack
do_byte(1, 90); // stretch the low phase right after that ack
if (n_byte_level !== before_by + 1) begin
$display("FAIL: the post-acknowledge stretch was not classified BYTE level (%0d)",
n_byte_level - before_by); errors++; end
if (tlog[n_log-1] != 90) begin
$display("FAIL: a 90-tick byte-level stretch measured %0d", tlog[n_log-1]);
errors++; end
if (bylog[n_log-1] !== 1'b1) begin
$display("FAIL: was_byte_level not set for a post-acknowledge stretch"); errors++; end
end
// ---- 5. boundary: a one-tick stretch is still a stretch -------------------------------
// Section 4.2.2 puts no lower bound on a stretch either. One sample period of
// disagreement is an event, and a block that needed two would silently drop the shortest
// real handshakes.
begin
int before_n; before_n = n_stretch;
do_byte(3, 1);
if (n_stretch !== before_n + 1) begin
$display("FAIL: a 1-tick stretch was not detected"); errors++; end
else if (tlog[n_log-1] != 1) begin
$display("FAIL: a 1-tick stretch measured %0d", tlog[n_log-1]); errors++; end
end
// ---- 6. the worst case is the LARGEST, and it is not erased --------------------------
begin
int peak; peak = max_stretch_seen;
do_byte(2, 30); // shorter than the 90 already seen
if (max_stretch_seen != peak) begin
$display("FAIL: max_stretch_seen moved to %0d after a SHORTER stretch (peak %0d)",
max_stretch_seen, peak); errors++; end
do_byte(2, 200); // longer -- must advance
if (max_stretch_seen != 200) begin
$display("FAIL: max_stretch_seen = %0d after a 200-tick stretch", max_stretch_seen);
errors++; end
end
// ---- 7. a very long stretch: legal, and measured in full -----------------------------
// Section 4.2.2: "There is no limit in the I2C-bus protocol as to how long this delay
// can be." A saturating counter would report a SMALL number here, which is the worst
// possible failure because it appears on the most extreme legal input.
begin
do_byte(5, 5000);
if (tlog[n_log-1] != 5000) begin
$display("FAIL: a 5000-tick stretch measured %0d -- the counter saturated",
tlog[n_log-1]); errors++; end
if (max_stretch_seen != 5000) begin
$display("FAIL: max_stretch_seen = %0d after a 5000-tick stretch",
max_stretch_seen); errors++; end
end
// ---- 8. the total accumulates, it does not track the latest -------------------------
begin
int before_tot; before_tot = total_stretch_ticks;
do_byte(6, 40);
if (total_stretch_ticks != before_tot + 40) begin
$display("FAIL: total_stretch_ticks = %0d, expected %0d",
total_stretch_ticks, before_tot + 40); errors++; end
end
// ---- 9. this device driving SCL low is NOT a stretch ---------------------------------
// The mirror of test 2, and the reason the intent input exists. The line is low for a
// long time here and scl_release is 0 throughout, so there is no disagreement.
begin
int before_n; before_n = n_stretch;
m_scl_release = 1'b0;
tick(400);
m_scl_release = 1'b1;
tick(20);
if (n_stretch !== before_n) begin
$display("FAIL: this device holding SCL low for 400 ticks was reported as %0d stretch(es)",
n_stretch - before_n); errors++; end
end
// ---- 10. counts split correctly between the two levels ------------------------------
if (n_byte_level + n_bit_level !== n_stretch) begin
$display("FAIL: %0d byte-level + %0d bit-level != %0d total",
n_byte_level, n_bit_level, n_stretch); errors++; end
// ---- 11. an idle bus produces nothing ----------------------------------------------
do_stop();
begin
int before_n; before_n = n_stretch;
tick(500);
if (n_stretch !== before_n) begin
$display("FAIL: an idle bus produced stretches"); errors++; end
if (in_transfer !== 1'b0) begin
$display("FAIL: in_transfer still set after a STOP"); errors++; end
end
if (errors == 0)
$display("PASS: a stretch is a DISAGREEMENT not a low line, byte level and bit level are separated, an unbounded stretch is measured in full, the worst case is the largest");
else
$display("FAIL: %0d error(s)", errors);
$finish;
end
// Watchdog: generous, because test 7 deliberately runs a 5000-tick stretch.
initial begin
#4000000;
$display("FAIL: watchdog expired");
$finish;
end
endmodule // CLOCK STRETCHING, OBSERVED. Before anything can be said about why stretching exists or what
// it costs, a block has to be able to SEE it -- and seeing it is less obvious than it looks.
//
// UM10204 section 3.1.9: "Clock stretching pauses a transaction by holding the SCL line LOW.
// The transaction cannot continue until the line is released HIGH again."
//
// The trap is that "SCL is LOW" is not the observation. SCL is low for half of every bit on a
// healthy bus, because the master drives it low. What distinguishes a stretch is a DISAGREEMENT:
// this device has released the line and the line is still low, so somebody else is holding it.
//
// That is why this block has TWO clock inputs and not one:
//
// scl_release -- this device's INTENT. 1 = I have let go, 0 = I am driving it low.
// scl_in -- the OBSERVED line, which is the wired-AND of every device's intent.
//
// A stretch is exactly (scl_release && !scl_in). With only scl_in there is no way to tell a
// stretch from an ordinary low phase, and a monitor built that way reports a stretch on every
// bit of every transfer. Mutation A2 is that monitor.
//
// The block also CLASSIFIES the stretch, because section 3.1.9 describes two different features
// under one name:
//
// BYTE LEVEL -- "Slaves can then hold the SCL line LOW after reception and acknowledgment of a
// byte to force the master into a wait state until the slave is ready for the
// next byte transfer in a type of handshake procedure."
// BIT LEVEL -- "a device such as a microcontroller ... can slow down the bus clock by
// extending each clock LOW period. The speed of any master is adapted to the
// internal operating rate of this device."
//
// These are not the same thing and a report that conflates them is much less useful: a byte-level
// stretch is a handshake at a known point and is what most real slaves do, while a bit-level
// stretch on every bit means the master is being rate-matched to something slow. Hs-mode permits
// byte level ONLY, so for an Hs design the distinction is a compliance question rather than a
// characterisation one.
//
// Classification needs the bit position, which the block derives from SCL falling edges since the
// last START: nine falls per byte, and the low phase following the ninth is the one section 3.1.9
// names. Everything else is bit level.
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
module i2c_stretch_detector #(
parameter TICK_W = 24 // wide: section 4.2.2 says there is NO limit on a stretch,
// so a 16-bit counter at 100 MHz (655 us) can be overrun by
// a perfectly legal device. See section 10 of the chapter.
)(
input wire clk,
input wire rst_n,
// ---- what this device intends, and what the bus actually shows ----
input wire scl_release, // 1 = released (not driving low)
input wire scl_in, // the wired-AND result on the pin
input wire sda_in,
// ---- the event ----
output reg stretching, // level: released, and still held low
output reg stretch_begin, // pulse
output reg stretch_end, // pulse: the line finally went high
output wire [TICK_W-1:0] stretch_ticks, // live duration while stretching
output reg stretch_valid, // pulse with the completed measurement
output reg [TICK_W-1:0] t_stretch,
// ---- the classification, from section 3.1.9's two levels ----
output reg was_byte_level, // the low phase following an acknowledge
output reg was_bit_level,
output reg [3:0] stretch_bit, // which bit of the byte, 1..9
// ---- framing, so the block is self-contained on a capture ----
output wire start_det,
output wire stop_det,
output reg in_transfer,
// ---- totals and worst case. A duration is a MAXIMUM-style statistic here: the
// interesting stretch is the LONGEST one, so the tracker starts at zero.
output reg [TICK_W-1:0] n_stretch,
output reg [TICK_W-1:0] n_byte_level,
output reg [TICK_W-1:0] n_bit_level,
output reg [TICK_W-1:0] max_stretch_seen,
output reg [TICK_W-1:0] total_stretch_ticks
);
reg sda_q, scl_q, rel_q;
wire scl_rise = scl_in && !scl_q;
wire scl_fall = !scl_in && scl_q;
wire sda_rise = sda_in && !sda_q;
wire sda_fall = !sda_in && sda_q;
// Framing by definition: SDA moving while SCL is HIGH.
assign start_det = sda_fall && scl_in;
assign stop_det = sda_rise && scl_in;
// The stretch condition itself. Combinational on purpose: it is a statement about the
// present state of two wires, not an event to be latched.
wire stretch_now = scl_release && !scl_in;
// "Including this cycle" -- the same convention every measurement in Module 11 uses, so a
// stretch of exactly N sample periods reports N and not N-1.
reg [TICK_W-1:0] ticks_q;
wire [TICK_W-1:0] ticks_now = ticks_q + 1'b1;
// Bit position within the byte: nine SCL falls per byte. bit_of_byte is the bit whose LOW
// phase we are currently in, 1..9, and post_ack marks the one low phase that follows an
// acknowledgment -- section 3.1.9's byte-level handshake point.
reg [3:0] bit_of_byte;
reg post_ack;
always @(posedge clk) begin
if (!rst_n) begin
sda_q <= 1'b1;
scl_q <= 1'b1;
rel_q <= 1'b1;
stretching <= 1'b0;
stretch_begin <= 1'b0;
stretch_end <= 1'b0;
ticks_q <= {TICK_W{1'b0}};
stretch_valid <= 1'b0;
t_stretch <= {TICK_W{1'b0}};
was_byte_level <= 1'b0;
was_bit_level <= 1'b0;
stretch_bit <= 4'd0;
in_transfer <= 1'b0;
bit_of_byte <= 4'd0;
post_ack <= 1'b0;
n_stretch <= {TICK_W{1'b0}};
n_byte_level <= {TICK_W{1'b0}};
n_bit_level <= {TICK_W{1'b0}};
// A worst-case MAXIMUM starts at zero so the first measurement replaces it. The
// minimum trackers of Chapters 11.2 and 11.5 start at all-ones for the mirrored
// reason, and swapping the two is a defect that reports a plausible number forever.
max_stretch_seen <= {TICK_W{1'b0}};
total_stretch_ticks <= {TICK_W{1'b0}};
end else begin
sda_q <= sda_in;
scl_q <= scl_in;
rel_q <= scl_release;
stretch_begin <= 1'b0;
stretch_end <= 1'b0;
stretch_valid <= 1'b0;
// ---- framing ----
if (start_det) begin
in_transfer <= 1'b1;
bit_of_byte <= 4'd0;
post_ack <= 1'b0;
end else if (stop_det) begin
in_transfer <= 1'b0;
bit_of_byte <= 4'd0;
post_ack <= 1'b0;
end else if (scl_fall && in_transfer) begin
// Nine falls per byte. The fall that leaves bit 9 enters the low phase that
// section 3.1.9 calls the byte-level handshake point, and it is simultaneously
// bit 1 of the next byte.
if (bit_of_byte == 4'd9) begin
bit_of_byte <= 4'd1;
post_ack <= 1'b1;
end else begin
bit_of_byte <= bit_of_byte + 4'd1;
post_ack <= 1'b0;
end
end
// ---- the stretch itself ----
if (stretch_now) begin
if (!stretching) begin
// Begins the cycle the disagreement first appears.
stretching <= 1'b1;
stretch_begin <= 1'b1;
ticks_q <= {TICK_W{1'b0}};
stretch_bit <= bit_of_byte;
end else begin
ticks_q <= ticks_now;
end
end else if (stretching) begin
// The line went high (or this device took it low again -- either way the
// disagreement is over and the measurement is complete).
stretching <= 1'b0;
stretch_end <= 1'b1;
stretch_valid <= 1'b1;
t_stretch <= ticks_now;
was_byte_level <= post_ack;
was_bit_level <= !post_ack;
n_stretch <= n_stretch + 1'b1;
total_stretch_ticks <= total_stretch_ticks + ticks_now;
if (post_ack) n_byte_level <= n_byte_level + 1'b1;
else n_bit_level <= n_bit_level + 1'b1;
if (ticks_now > max_stretch_seen) max_stretch_seen <= ticks_now;
end
end
end
assign stretch_ticks = ticks_q;
endmodule `timescale 1ns/1ps
// 100 MHz sample clock: one tick is 10 ns. The stimulus drives a master's INTENT (scl_release)
// and a slave's hold separately, and derives the observed line as the wired-AND of the two --
// which is the whole mechanism of Chapter 12.2 used here as a stimulus generator.
//
// Every expectation is derived from the drive parameters rather than written as a constant, so a
// test that passes is a test of the comparison and not of an arithmetic coincidence.
// (Verilog-2001 testbench -- same stimulus, same checks.)
module i2c_stretch_detector_tb;
localparam TICK_W = 24;
reg clk = 1'b0, rst_n = 1'b0;
always #5 clk = ~clk; // 10 ns period
// Master intent, slave hold, and the wired-AND the DUT sees.
reg m_scl_release = 1'b1;
reg s_scl_hold = 1'b0; // 1 = slave pulls SCL low
reg sda = 1'b1;
wire scl_line = m_scl_release && !s_scl_hold;
wire stretching, stretch_begin, stretch_end, stretch_valid;
wire [TICK_W-1:0] stretch_ticks, t_stretch;
wire was_byte_level, was_bit_level;
wire [3:0] stretch_bit;
wire start_det, stop_det, in_transfer;
wire [TICK_W-1:0] n_byte_level;
wire [TICK_W-1:0] n_stretch, n_bit_level;
wire [TICK_W-1:0] max_stretch_seen, total_stretch_ticks;
i2c_stretch_detector #(.TICK_W(TICK_W)) dut (
.clk(clk), .rst_n(rst_n),
.scl_release(m_scl_release), .scl_in(scl_line), .sda_in(sda),
.stretching(stretching), .stretch_begin(stretch_begin), .stretch_end(stretch_end),
.stretch_ticks(stretch_ticks), .stretch_valid(stretch_valid), .t_stretch(t_stretch),
.was_byte_level(was_byte_level), .was_bit_level(was_bit_level), .stretch_bit(stretch_bit),
.start_det(start_det), .stop_det(stop_det), .in_transfer(in_transfer),
.n_stretch(n_stretch), .n_byte_level(n_byte_level), .n_bit_level(n_bit_level),
.max_stretch_seen(max_stretch_seen), .total_stretch_ticks(total_stretch_ticks)
);
integer errors = 0;
// Hoisted to module scope: Verilog-2001 permits a variable declaration only at
// module level or in a NAMED block, and every call site below is sequential.
integer before_n = 0;
integer before_by = 0;
integer peak = 0;
integer before_tot = 0;
integer b = 0;
// ---- observation log. Captured on the completion pulse, which is the only cycle the
// measurement is meaningful -- Chapter 11.6's reporting-lag lesson applied up front.
integer n_log = 0;
integer tlog[0:63];
reg bylog[0:63];
integer bitlog[0:63];
always @(posedge clk) if (rst_n && stretch_valid && n_log < 64) begin
tlog[n_log] = t_stretch;
bylog[n_log] = was_byte_level;
bitlog[n_log] = stretch_bit;
n_log = n_log + 1;
end
task tick(input integer n);
begin repeat (n) @(negedge clk); end
endtask
// A bit cell: SCL low for `lo` ticks then high for `hi`, with the slave optionally holding
// SCL low for `hold` ticks after the master releases. Stimulus is driven on the NEGEDGE
// throughout -- Module 9's race lesson: never drive at the edge the DUT samples.
task bit_cell(input integer lo, input integer hi, input integer hold);
begin
m_scl_release = 1'b0; // master drives SCL low
tick(lo);
if (hold > 0) begin
s_scl_hold = 1'b1; // slave grabs it BEFORE the master lets go
m_scl_release = 1'b1; // master releases -- the disagreement starts here
tick(hold);
s_scl_hold = 1'b0; // slave lets go; the line rises
end else begin
m_scl_release = 1'b1;
end
tick(hi);
end
endtask
// One byte plus its acknowledge: nine bit cells. `hold_at` selects which bit's low phase is
// stretched (1..9), or 0 for none; `post_hold` stretches the low phase AFTER the ack, which
// is section 3.1.9's byte-level handshake point.
task do_byte(input integer hold_at, input integer hold_len);
begin
for (b = 1; b <= 9; b = b + 1) bit_cell(20, 20, (b == hold_at) ? hold_len : 0);
end
endtask
task do_start();
begin sda = 1'b1; m_scl_release = 1'b1; tick(10); sda = 1'b0; tick(10); end
endtask
task do_stop();
begin m_scl_release = 1'b0; tick(5); sda = 1'b0; m_scl_release = 1'b1; tick(10);
sda = 1'b1; tick(10); end
endtask
initial begin
tick(4); rst_n = 1'b1; tick(4);
// ---- 1. reset state: a MAXIMUM tracker must start at zero ----------------------------
if (max_stretch_seen !== {TICK_W{1'b0}} || n_stretch !== {TICK_W{1'b0}} || total_stretch_ticks !== {TICK_W{1'b0}}) begin
$display("FAIL: the stretch trackers did not start at zero"); errors = errors + 1; end
if (stretching !== 1'b0) begin
$display("FAIL: stretching asserted out of reset"); errors = errors + 1; end
// ---- 2. an unstretched byte: NOTHING reported ---------------------------------------
// This is the test that kills a monitor watching scl_in alone: on this stimulus SCL goes
// low nine times and not one of them is a stretch.
do_start();
do_byte(0, 0);
if (n_stretch !== {TICK_W{1'b0}}) begin
$display("FAIL: an unstretched byte produced %0d stretches -- the block is watching the LINE, not the DISAGREEMENT", n_stretch);
errors = errors + 1; end
if (max_stretch_seen !== {TICK_W{1'b0}}) begin
$display("FAIL: max_stretch_seen moved with no stretch"); errors = errors + 1; end
// ---- 3. a bit-level stretch, mid-byte ------------------------------------------------
begin
before_n = n_stretch;
do_byte(4, 60); // 60 ticks on bit 4's low phase
if (n_stretch !== before_n + 1) begin
$display("FAIL: a 60-tick stretch on bit 4 produced %0d events", n_stretch - before_n);
errors = errors + 1; end
else begin
if (tlog[n_log-1] != 60) begin
$display("FAIL: a 60-tick stretch measured %0d", tlog[n_log-1]); errors = errors + 1; end
if (bylog[n_log-1] !== 1'b0) begin
$display("FAIL: a mid-byte stretch was classified BYTE level"); errors = errors + 1; end
if (bitlog[n_log-1] != 4) begin
$display("FAIL: the stretch was reported at bit %0d, expected 4", bitlog[n_log-1]);
errors = errors + 1; end
end
end
// ---- 4. a BYTE-level stretch: the low phase after the acknowledge --------------------
// Section 3.1.9's handshake point. The ninth bit is the ack, so the stretch goes on the
// FIRST low phase of the following byte -- which is why do_byte is called twice and the
// hold sits on bit 1 of the second call.
begin
before_by = n_byte_level;
do_byte(0, 0); // a clean byte, ending with its ack
do_byte(1, 90); // stretch the low phase right after that ack
if (n_byte_level !== before_by + 1) begin
$display("FAIL: the post-acknowledge stretch was not classified BYTE level (%0d)",
n_byte_level - before_by); errors = errors + 1; end
if (tlog[n_log-1] != 90) begin
$display("FAIL: a 90-tick byte-level stretch measured %0d", tlog[n_log-1]);
errors = errors + 1; end
if (bylog[n_log-1] !== 1'b1) begin
$display("FAIL: was_byte_level not set for a post-acknowledge stretch"); errors = errors + 1; end
end
// ---- 5. boundary: a one-tick stretch is still a stretch -------------------------------
// Section 4.2.2 puts no lower bound on a stretch either. One sample period of
// disagreement is an event, and a block that needed two would silently drop the shortest
// real handshakes.
begin
before_n = n_stretch;
do_byte(3, 1);
if (n_stretch !== before_n + 1) begin
$display("FAIL: a 1-tick stretch was not detected"); errors = errors + 1; end
else if (tlog[n_log-1] != 1) begin
$display("FAIL: a 1-tick stretch measured %0d", tlog[n_log-1]); errors = errors + 1; end
end
// ---- 6. the worst case is the LARGEST, and it is not erased --------------------------
begin
peak = max_stretch_seen;
do_byte(2, 30); // shorter than the 90 already seen
if (max_stretch_seen != peak) begin
$display("FAIL: max_stretch_seen moved to %0d after a SHORTER stretch (peak %0d)",
max_stretch_seen, peak); errors = errors + 1; end
do_byte(2, 200); // longer -- must advance
if (max_stretch_seen != 200) begin
$display("FAIL: max_stretch_seen = %0d after a 200-tick stretch", max_stretch_seen);
errors = errors + 1; end
end
// ---- 7. a very long stretch: legal, and measured in full -----------------------------
// Section 4.2.2: "There is no limit in the I2C-bus protocol as to how long this delay
// can be." A saturating counter would report a SMALL number here, which is the worst
// possible failure because it appears on the most extreme legal input.
begin
do_byte(5, 5000);
if (tlog[n_log-1] != 5000) begin
$display("FAIL: a 5000-tick stretch measured %0d -- the counter saturated",
tlog[n_log-1]); errors = errors + 1; end
if (max_stretch_seen != 5000) begin
$display("FAIL: max_stretch_seen = %0d after a 5000-tick stretch",
max_stretch_seen); errors = errors + 1; end
end
// ---- 8. the total accumulates, it does not track the latest -------------------------
begin
before_tot = total_stretch_ticks;
do_byte(6, 40);
if (total_stretch_ticks != before_tot + 40) begin
$display("FAIL: total_stretch_ticks = %0d, expected %0d",
total_stretch_ticks, before_tot + 40); errors = errors + 1; end
end
// ---- 9. this device driving SCL low is NOT a stretch ---------------------------------
// The mirror of test 2, and the reason the intent input exists. The line is low for a
// long time here and scl_release is 0 throughout, so there is no disagreement.
begin
before_n = n_stretch;
m_scl_release = 1'b0;
tick(400);
m_scl_release = 1'b1;
tick(20);
if (n_stretch !== before_n) begin
$display("FAIL: this device holding SCL low for 400 ticks was reported as %0d stretch(es)",
n_stretch - before_n); errors = errors + 1; end
end
// ---- 10. counts split correctly between the two levels ------------------------------
if (n_byte_level + n_bit_level !== n_stretch) begin
$display("FAIL: %0d byte-level + %0d bit-level != %0d total",
n_byte_level, n_bit_level, n_stretch); errors = errors + 1; end
// ---- 11. an idle bus produces nothing ----------------------------------------------
do_stop();
begin
before_n = n_stretch;
tick(500);
if (n_stretch !== before_n) begin
$display("FAIL: an idle bus produced stretches"); errors = errors + 1; end
if (in_transfer !== 1'b0) begin
$display("FAIL: in_transfer still set after a STOP"); errors = errors + 1; end
end
if (errors == 0)
$display("PASS: a stretch is a DISAGREEMENT not a low line, byte level and bit level are separated, an unbounded stretch is measured in full, the worst case is the largest");
else
$display("FAIL: %0d error(s)", errors);
$finish;
end
// Watchdog: generous, because test 7 deliberately runs a 5000-tick stretch.
initial begin
#4000000;
$display("FAIL: watchdog expired");
$finish;
end
endmodule -- CLOCK STRETCHING, OBSERVED -- the VHDL form of the same block. Structurally identical to the
-- SystemVerilog: one clocked process, the same "including this cycle" measurement convention, and
-- the same central idea that a stretch is a DISAGREEMENT between this device's intent and the
-- observed line rather than a property of the line alone.
--
-- UM10204 section 3.1.9: "Clock stretching pauses a transaction by holding the SCL line LOW. The
-- transaction cannot continue until the line is released HIGH again."
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_stretch_detector is
generic (
-- Wide on purpose: section 4.2.2 places NO limit on the length of a stretch, so a narrow
-- counter can be overrun by a perfectly legal device.
TICK_W : natural := 24
);
port (
clk : in std_logic;
rst_n : in std_logic;
-- intent, and observation
scl_release : in std_logic;
scl_in : in std_logic;
sda_in : in std_logic;
stretching : out std_logic;
stretch_begin : out std_logic;
stretch_end : out std_logic;
stretch_ticks : out unsigned(TICK_W-1 downto 0);
stretch_valid : out std_logic;
t_stretch : out unsigned(TICK_W-1 downto 0);
was_byte_level : out std_logic;
was_bit_level : out std_logic;
stretch_bit : out unsigned(3 downto 0);
start_det : out std_logic;
stop_det : out std_logic;
in_transfer : out std_logic;
n_stretch : out unsigned(TICK_W-1 downto 0);
n_byte_level : out unsigned(TICK_W-1 downto 0);
n_bit_level : out unsigned(TICK_W-1 downto 0);
max_stretch_seen : out unsigned(TICK_W-1 downto 0);
total_stretch_ticks : out unsigned(TICK_W-1 downto 0)
);
end entity;
architecture rtl of i2c_stretch_detector is
signal sda_q, scl_q : std_logic := '1';
signal s_stretching : std_logic := '0';
signal s_in_transfer : std_logic := '0';
signal ticks_q : unsigned(TICK_W-1 downto 0) := (others => '0');
signal bit_of_byte : unsigned(3 downto 0) := (others => '0');
signal post_ack : std_logic := '0';
signal r_n_stretch : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_n_byte : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_n_bit : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_max : unsigned(TICK_W-1 downto 0) := (others => '0');
signal r_total : unsigned(TICK_W-1 downto 0) := (others => '0');
-- Edge terms and the framing definitions, as concurrent expressions so they read the same way
-- as the Verilog wires.
signal scl_fall_s, sda_rise_s, sda_fall_s : std_logic;
signal start_s, stop_s, stretch_now_s : std_logic;
signal ticks_now : unsigned(TICK_W-1 downto 0);
begin
scl_fall_s <= '1' when (scl_in = '0' and scl_q = '1') else '0';
sda_rise_s <= '1' when (sda_in = '1' and sda_q = '0') else '0';
sda_fall_s <= '1' when (sda_in = '0' and sda_q = '1') else '0';
-- Framing by definition: SDA moving while SCL is HIGH.
start_s <= '1' when (sda_fall_s = '1' and scl_in = '1') else '0';
stop_s <= '1' when (sda_rise_s = '1' and scl_in = '1') else '0';
-- THE stretch condition: released, and still held low by somebody else.
stretch_now_s <= '1' when (scl_release = '1' and scl_in = '0') else '0';
ticks_now <= ticks_q + 1;
start_det <= start_s;
stop_det <= stop_s;
in_transfer <= s_in_transfer;
stretching <= s_stretching;
stretch_ticks <= ticks_q;
n_stretch <= r_n_stretch;
n_byte_level <= r_n_byte;
n_bit_level <= r_n_bit;
max_stretch_seen <= r_max;
total_stretch_ticks <= r_total;
process (clk) is
begin
if rising_edge(clk) then
if rst_n = '0' then
sda_q <= '1';
scl_q <= '1';
s_stretching <= '0';
stretch_begin <= '0';
stretch_end <= '0';
ticks_q <= (others => '0');
stretch_valid <= '0';
t_stretch <= (others => '0');
was_byte_level <= '0';
was_bit_level <= '0';
stretch_bit <= (others => '0');
s_in_transfer <= '0';
bit_of_byte <= (others => '0');
post_ack <= '0';
r_n_stretch <= (others => '0');
r_n_byte <= (others => '0');
r_n_bit <= (others => '0');
-- A worst-case MAXIMUM starts at zero so the first measurement replaces it.
r_max <= (others => '0');
r_total <= (others => '0');
else
sda_q <= sda_in;
scl_q <= scl_in;
stretch_begin <= '0';
stretch_end <= '0';
stretch_valid <= '0';
-- framing
if start_s = '1' then
s_in_transfer <= '1';
bit_of_byte <= (others => '0');
post_ack <= '0';
elsif stop_s = '1' then
s_in_transfer <= '0';
bit_of_byte <= (others => '0');
post_ack <= '0';
elsif scl_fall_s = '1' and s_in_transfer = '1' then
-- Nine falls per byte. The fall leaving bit 9 enters the low phase section
-- 3.1.9 calls the byte-level handshake point.
if bit_of_byte = to_unsigned(9, 4) then
bit_of_byte <= to_unsigned(1, 4);
post_ack <= '1';
else
bit_of_byte <= bit_of_byte + 1;
post_ack <= '0';
end if;
end if;
-- the stretch itself
if stretch_now_s = '1' then
if s_stretching = '0' then
s_stretching <= '1';
stretch_begin <= '1';
ticks_q <= (others => '0');
stretch_bit <= bit_of_byte;
else
ticks_q <= ticks_now;
end if;
elsif s_stretching = '1' then
s_stretching <= '0';
stretch_end <= '1';
stretch_valid <= '1';
t_stretch <= ticks_now;
was_byte_level <= post_ack;
was_bit_level <= not post_ack;
r_n_stretch <= r_n_stretch + 1;
r_total <= r_total + ticks_now;
if post_ack = '1' then
r_n_byte <= r_n_byte + 1;
else
r_n_bit <= r_n_bit + 1;
end if;
if ticks_now > r_max then
r_max <= ticks_now;
end if;
end if;
end if;
end if;
end process;
end architecture; -- The VHDL testbench for the stretch detector. Same eleven checks as the SystemVerilog, same
-- expectations derived from the driven lengths rather than written as constants.
--
-- VHDL allows exactly ONE driver per signal, which shapes the structure: the stimulus process owns
-- the master's intent and the slave's hold, and a separate clocked process owns the observation log.
-- The observed SCL line is a concurrent wired-AND of the two intents -- which is the mechanism of
-- Chapter 12.2 used here as a stimulus generator, exactly as in the other two languages.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_stretch_detector_tb is
end entity;
architecture tb of i2c_stretch_detector_tb is
constant TICK_W : natural := 24;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
-- master intent, slave hold, and the wired-AND the DUT sees
signal m_scl_release : std_logic := '1';
signal s_scl_hold : std_logic := '0';
signal sda : std_logic := '1';
signal scl_line : std_logic;
signal stretching, stretch_begin, stretch_end, stretch_valid : std_logic;
signal stretch_ticks, t_stretch : unsigned(TICK_W-1 downto 0);
signal was_byte_level, was_bit_level : std_logic;
signal stretch_bit : unsigned(3 downto 0);
signal start_det, stop_det, in_transfer : std_logic;
signal n_stretch, n_byte_level, n_bit_level : unsigned(TICK_W-1 downto 0);
signal max_stretch_seen, total_stretch_ticks : unsigned(TICK_W-1 downto 0);
signal done : boolean := false;
-- the observation log, written by exactly one process
type int_arr is array (0 to 63) of integer;
type bool_arr is array (0 to 63) of std_logic;
signal tlog : int_arr := (others => 0);
signal bylog : bool_arr := (others => '0');
signal bitlog : int_arr := (others => 0);
signal n_log : integer := 0;
signal errors : integer := 0;
begin
scl_line <= m_scl_release and (not s_scl_hold);
clk_gen : process is
begin
while not done loop
clk <= '0'; wait for 5 ns;
clk <= '1'; wait for 5 ns;
end loop;
wait;
end process;
dut : entity work.i2c_stretch_detector
generic map (TICK_W => TICK_W)
port map (
clk => clk, rst_n => rst_n,
scl_release => m_scl_release, scl_in => scl_line, sda_in => sda,
stretching => stretching, stretch_begin => stretch_begin,
stretch_end => stretch_end, stretch_ticks => stretch_ticks,
stretch_valid => stretch_valid, t_stretch => t_stretch,
was_byte_level => was_byte_level, was_bit_level => was_bit_level,
stretch_bit => stretch_bit,
start_det => start_det, stop_det => stop_det, in_transfer => in_transfer,
n_stretch => n_stretch, n_byte_level => n_byte_level, n_bit_level => n_bit_level,
max_stretch_seen => max_stretch_seen, total_stretch_ticks => total_stretch_ticks);
-- the log: one process, one driver
logger : process (clk) is
begin
if rising_edge(clk) then
if rst_n = '1' and stretch_valid = '1' and n_log < 64 then
tlog(n_log) <= to_integer(t_stretch);
bylog(n_log) <= was_byte_level;
bitlog(n_log) <= to_integer(stretch_bit);
n_log <= n_log + 1;
end if;
end if;
end process;
stim : process is
procedure tick(n : in integer) is
begin
for i in 1 to n loop
wait until falling_edge(clk);
end loop;
end procedure;
-- One bit cell: SCL low for `lo`, high for `hi`, with an optional slave hold of `hold`
-- ticks inserted after the master releases.
procedure bit_cell(lo, hi, hold : in integer) is
begin
m_scl_release <= '0';
tick(lo);
if hold > 0 then
s_scl_hold <= '1';
m_scl_release <= '1';
tick(hold);
s_scl_hold <= '0';
else
m_scl_release <= '1';
end if;
tick(hi);
end procedure;
procedure do_byte(hold_at, hold_len : in integer) is
begin
for b in 1 to 9 loop
if b = hold_at then bit_cell(20, 20, hold_len);
else bit_cell(20, 20, 0);
end if;
end loop;
end procedure;
procedure do_start is
begin
sda <= '1'; m_scl_release <= '1'; tick(10);
sda <= '0'; tick(10);
end procedure;
procedure do_stop is
begin
m_scl_release <= '0'; tick(5);
sda <= '0'; m_scl_release <= '1'; tick(10);
sda <= '1'; tick(10);
end procedure;
procedure chk(cond : in boolean; msg : in string) is
begin
if not cond then
report "FAIL: " & msg severity error;
errors <= errors + 1;
wait for 0 ns;
end if;
end procedure;
variable before_n, before_by, before_tot, peak : integer;
begin
tick(4); rst_n <= '1'; tick(4);
-- 1. reset: a MAXIMUM tracker starts at zero
chk(max_stretch_seen = 0 and n_stretch = 0 and total_stretch_ticks = 0,
"the stretch trackers did not start at zero");
chk(stretching = '0', "stretching asserted out of reset");
-- 2. an unstretched byte reports NOTHING -- the test that kills a monitor watching the
-- line instead of the disagreement
do_start;
do_byte(0, 0);
chk(n_stretch = 0,
"an unstretched byte produced stretches -- the block is watching the LINE, not the DISAGREEMENT");
chk(max_stretch_seen = 0, "max_stretch_seen moved with no stretch");
-- 3. a bit-level stretch, mid-byte
before_n := to_integer(n_stretch);
do_byte(4, 60);
chk(to_integer(n_stretch) = before_n + 1, "a 60-tick stretch on bit 4 was not detected");
chk(tlog(n_log-1) = 60, "a 60-tick stretch measured the wrong value");
chk(bylog(n_log-1) = '0', "a mid-byte stretch was classified BYTE level");
chk(bitlog(n_log-1) = 4, "the stretch was reported at the wrong bit");
-- 4. a BYTE-level stretch: the low phase following the acknowledge
before_by := to_integer(n_byte_level);
do_byte(0, 0);
do_byte(1, 90);
chk(to_integer(n_byte_level) = before_by + 1,
"the post-acknowledge stretch was not classified BYTE level");
chk(tlog(n_log-1) = 90, "a 90-tick byte-level stretch measured the wrong value");
chk(bylog(n_log-1) = '1', "was_byte_level not set for a post-acknowledge stretch");
-- 5. boundary: one tick of disagreement is a stretch
before_n := to_integer(n_stretch);
do_byte(3, 1);
chk(to_integer(n_stretch) = before_n + 1, "a 1-tick stretch was not detected");
chk(tlog(n_log-1) = 1, "a 1-tick stretch measured the wrong value");
-- 6. the worst case is the LARGEST, and it is not erased
peak := to_integer(max_stretch_seen);
do_byte(2, 30);
chk(to_integer(max_stretch_seen) = peak, "max_stretch_seen moved after a SHORTER stretch");
do_byte(2, 200);
chk(to_integer(max_stretch_seen) = 200, "max_stretch_seen wrong after a 200-tick stretch");
-- 7. a very long stretch: legal, and measured in full (section 4.2.2 -- no limit)
do_byte(5, 5000);
chk(tlog(n_log-1) = 5000, "a 5000-tick stretch was not measured in full -- the counter saturated");
chk(to_integer(max_stretch_seen) = 5000, "max_stretch_seen wrong after a 5000-tick stretch");
-- 8. the total accumulates
before_tot := to_integer(total_stretch_ticks);
do_byte(6, 40);
chk(to_integer(total_stretch_ticks) = before_tot + 40, "total_stretch_ticks did not accumulate");
-- 9. THIS device driving SCL low is not a stretch
before_n := to_integer(n_stretch);
m_scl_release <= '0';
tick(400);
m_scl_release <= '1';
tick(20);
chk(to_integer(n_stretch) = before_n,
"this device holding SCL low for 400 ticks was reported as a stretch");
-- 10. the counts split correctly between the two levels
chk(n_byte_level + n_bit_level = n_stretch,
"the byte-level and bit-level counts do not sum to the total");
-- 11. an idle bus produces nothing
do_stop;
before_n := to_integer(n_stretch);
tick(500);
chk(to_integer(n_stretch) = before_n, "an idle bus produced stretches");
chk(in_transfer = '0', "in_transfer still set after a STOP");
if errors = 0 then
report "i2c_stretch_detector self-check complete: a stretch is a DISAGREEMENT not a low line, byte level and bit level are separated, an unbounded stretch is measured in full, the worst case is the largest" severity note;
else
report "FAILURES in i2c_stretch_detector" severity error;
end if;
done <= true;
wait;
end process;
end architecture;6a. Five Decisions Worth Defending
The block takes the device's INTENT and the observed line, and a stretch is the disagreement between them. §5's argument. A detector given only scl_in reports a stretch on every low phase of every bit — mutation J1 is that detector, and §7's test 2 kills it with an entirely ordinary byte.
The classification is derived from the bit position, so byte level and bit level are separated. §3 establishes that these are two different features; a report that merges them tells a board engineer far less. The position comes from counting SCL falling edges since the START — nine per byte — and the low phase following the ninth is the one §3.1.9 names.
The measurement includes the cycle in which the stretch is first seen. The same "including this cycle" convention as every measurement in Module 11, so a stretch of exactly one sample period reports 1. §7's test 5 pins it, and mutation J2 is the off-by-one.
The worst case is a MAXIMUM, so the tracker starts at zero. The mirror of Chapter 11.2's minimum tracker, and Chapter 11.4 §1 is the table of what inverts. Mutation J4 initialises it to all-ones and is killed by the reset assertion.
TICK_W defaults to 24 bits, not 16. This is the one sizing decision in the module that is forced by the specification rather than by convenience: §4.2.2 places no limit on how long a stretch may last, so a 16-bit counter — 655 µs at 100 MHz — can be overrun by a device that is behaving perfectly legally. Chapter 12.4 is about that absence of a limit; here it is a port width.
6b. Verified Execution
$ iverilog -g2012 -o d1 i2c_stretch_detector.sv i2c_stretch_detector_tb.sv && ./d1
PASS: a stretch is a DISAGREEMENT not a low line, byte level and bit level are separated, an
unbounded stretch is measured in full, the worst case is the largest
i2c_stretch_detector_tb.sv:237: $finish called at 96340000 (1ps)
$ iverilog -g2005 -o v1 i2c_stretch_detector.v i2c_stretch_detector_tb.v && ./v1
PASS: a stretch is a DISAGREEMENT not a low line, byte level and bit level are separated, an
unbounded stretch is measured in full, the worst case is the largest
i2c_stretch_detector_tb.v:246: $finish called at 96340000 (1ps)
$ nvc -a i2c_stretch_detector.vhd i2c_stretch_detector_tb.vhd
$ nvc -e i2c_stretch_detector_tb && nvc -r i2c_stretch_detector_tb --stop-time=2000us
** Note: 96340ns+1: i2c_stretch_detector self-check complete: a stretch is a DISAGREEMENT
not a low line, byte level and bit level are separated, an unbounded stretch is measured in
full, the worst case is the largestAll three at 96340 ns.
7. What the Testbench Proves
The stimulus drives the master's intent and a slave's hold as separate signals and derives the observed line as the wired-AND of the two — which is Chapter 12.2's mechanism used here as a stimulus generator.
| # | stimulus | what it establishes |
|---|---|---|
| 1 | reset | the trackers read zero, and stretching is low |
| 2 | an ordinary byte, no stretching | nothing reported — nine low phases, zero stretches |
| 3 | a 60-tick hold on bit 4's low phase | one stretch, measured 60, classified bit level, at bit 4 |
| 4 | a 90-tick hold on the low phase after an acknowledge | classified byte level |
| 5 | a 1-tick hold | detected, and measured as 1 |
| 6 | a shorter stretch, then a longer one | the worst case does not fall, then advances |
| 7 | a 5000-tick hold | measured in full — the counter does not saturate |
| 8 | another stretch | the running total accumulates |
| 9 | this device holding SCL low for 400 ticks | not a stretch |
| 10 | the two classification counts | sum to the total |
| 11 | an idle bus | nothing reported |
Test 2 is the most important test in the chapter, and it is the one that would be missing from a suite written without §5's insight. An unstretched byte drives SCL low nine times. A detector watching the line alone reports nine stretches and looks plausible doing it — the durations would even be sensible. Only a test that asserts zero on clean traffic distinguishes a working detector from one that has misunderstood the event.
Test 9 is test 2's mirror and it is stronger. Here the line is low for 400 consecutive ticks — far longer than any bit — and it is still not a stretch, because this device is the one holding it. A detector using a duration threshold instead of the intent comparison passes test 2 and fails here, which is why both exist.
Test 7 is the no-limit case. §4.2.2 says there is no bound on a stretch, so a 5000-tick hold is legal and must be measured in full. A saturating counter reports a small number for a very long stretch, which is the worst possible direction for the error: it makes the most extreme legal input look like a short one.
Tests 3 and 4 are the pair that keeps §3's two levels apart. Each asserts both the measurement and the classification, and each asserts that the other classification did not also fire. Mutation J3 disables the byte-level marking and is caught by test 4 alone.
8. Mutation Testing
Six defects injected into the SystemVerilog detector.
| # | injected defect | outcome |
|---|---|---|
| J1 | the stretch condition ignores this device's intent | killed — test 2 |
| J2 | the measurement excludes the sampling cycle | killed — test 5 |
| J3 | the byte-level handshake point is never marked | killed — test 4 |
| J4 | the maximum tracker is initialised like a minimum tracker | killed — test 1 |
| J5 | the worst case tracks the latest stretch | killed — test 6 |
| J6 | the running total is assigned rather than accumulated | killed — test 8 |
Six injected, six killed. Two notes.
J1 is the mutation this chapter exists to make impossible. Removing scl_release from the condition leaves a block that watches the line, and the failure message it produces is worth quoting because it is so specific: an unstretched byte produced 9 stretches. Nine — one per bit. A detector that reported nine stretches per byte would be dismissed instantly by anyone reading its output, which is precisely why the mutation is valuable: it confirms the test catches the defect at the first opportunity rather than in some corner case.
J2 is caught only at the boundary. With the measurement one tick short, test 3's 60-tick stretch reports 59 — and every other test in the suite has margins of tens of ticks, so none of them notices. Only test 5, sitting on a single-tick stretch, and test 3's exact-value assertion make the difference. This is the same lesson Chapter 11.1 §8 draws from its own off-by-one: an off-by-one is invisible except where the expected value is asserted exactly.
9. Verification Connection — Asserting on an Event That Needs Two Signals
// The property is trivial once the EVENT is defined correctly, and the definition is the part
// that takes thought: a stretch is a disagreement between this agent's intent and the pin.
// Written against the pin alone, every property below would fire on every bit.
wire stretching_now = scl_release && !scl_in;
// A stretch must END. This is bounded liveness in the same shape as Chapter 11.4 section 9 --
// except that section 4.2.2 gives no bound, so the only honest form is UNBOUNDED liveness, and
// the bound has to come from a system policy rather than from the protocol.
//
// `s_eventually` is the right operator precisely because no N is defensible here. A
// ##[1:N] form would be asserting a limit the specification declines to state, which is the
// error Chapter 12.4's mutation M1 injects into hardware.
property p_stretch_ends;
@(posedge clk) $rose(stretching_now) |-> s_eventually (!stretching_now);
endproperty
assert property (p_stretch_ends)
else $error("a stretch never ended -- SCL is stuck low, and section 3.1.16's remedy for that is a hardware reset, not a protocol action");
// The NEGATIVE property, and for this chapter it is the one that matters: this agent driving
// SCL low is never a stretch. Without it, a monitor can be "correct" on stretched traffic and
// still report a stretch on every ordinary bit -- which is mutation J1.
property p_own_drive_is_not_a_stretch;
@(posedge clk) (!scl_release) |-> !stretch_detected;
endproperty
assert property (p_own_drive_is_not_a_stretch)
else $error("this agent's own SCL low phase was reported as a stretch");
// And the classification, which is a property about POSITION rather than duration. Section
// 3.1.9 places the byte-level handshake after the acknowledgment, so a byte-level stretch can
// only occur in the low phase that follows bit 9.
property p_byte_level_position;
@(posedge clk) (stretch_valid && was_byte_level) |-> (stretch_bit == 4'd1 && post_ack);
endproperty
assert property (p_byte_level_position)
else $error("a byte-level stretch was reported somewhere other than after an acknowledge"); covergroup i2c_stretch_cg with function sample(int ticks, bit byte_level, int bit_pos,
bit any_stretch, int bytes_in_txn);
// Duration bins are ORDERS OF MAGNITUDE apart, because that is how stretch lengths actually
// distribute: a state machine's handshake is tens of ticks, an EEPROM write is hundreds of
// thousands. Linear bins would put every real case in one bucket.
duration: coverpoint ticks {
bins none = {0};
bins single_tick = {1}; // the boundary -- a stretch can be one sample period
bins short = {[2:99]}; // a logic handshake
bins medium = {[100:9999]}; // a conversion, a FIFO drain
bins long = {[10000:$]}; // an EEPROM write: milliseconds
}
// Section 3.1.9's two levels, which is the axis a suite is most likely to leave half empty.
level: coverpoint byte_level {
bins byte_lvl = {1};
bins bit_lvl = {0};
}
duration_x_level: cross duration, level;
// WHERE in the byte. A byte-level stretch can only be at the post-acknowledge position, so
// this coverpoint is partly a check that the classification is consistent -- and partly the
// thing that shows a bit-level suite only ever stretched one bit position.
position: coverpoint bit_pos { bins b[] = {[1:9]}; }
position_x_level: cross position, level;
// THE coverpoint people forget. Stretching is OPTIONAL (Table 2), so a regression must
// include transactions with NO stretching at all -- otherwise a master that mishandles the
// unstretched case ships. This is the coverage form of section 7's test 2.
present: coverpoint any_stretch {
bins never = {0};
bins sometime = {1};
}
endgroup10. FPGA and ASIC Implications
The detector is two counters, a small bit-position counter and some edge logic — around 120 flops at TICK_W = 24. Nothing is on a critical path.
The 24-bit counter width is a specification consequence, not a margin choice. §4.2.2 gives no upper bound on a stretch, so there is no width that is provably sufficient — only widths that are provably insufficient for a known device class. At 100 MHz: 16 bits covers 655 µs, which an EEPROM page write (typically 5 ms) overruns; 24 bits covers 168 ms, which covers every device the author has met. A saturating counter is acceptable only if it reports that it saturated, because a silent wrap turns the longest stretch into an apparently short one.
A slave that stretches needs an SCL output driver, and most do not have one. §3.1.9 says so directly, and it is the reason stretching is rare in fixed-function parts: adding an SCL pad driver to a sensor is silicon spent on a feature many systems will never use. A slave built in an FPGA has the driver for free, which is why FPGA-based slaves stretch far more often than commercial parts do — and why an FPGA slave dropped into a system designed around non-stretching parts is a classic integration surprise.
On the master side, tolerating stretching costs an SCL input path. The master must sample SCL and route it into its clock generator's sequencing, which means a synchroniser and — if the design filters (Chapter 11.8) — the filter's latency in that path too. The next chapter is entirely about getting that sequencing right.
Report the maximum and the total, not just a count. A count of stretches says the feature is in use; the maximum says whether a timeout would have fired; the total says what it cost. Chapter 12.3 builds the accounting and Chapter 12.4 the policy, and both need numbers a bare count cannot supply.
11. Debugging — The Bus That Worked Until the FPGA Slave Arrived
Pitfall — a push-pull SCL driver meeting a slave that stretches
// A single-master sensor hub, Fast-mode, six fixed-function slaves: two temperature sensors, a
// pressure sensor, an EEPROM and two GPIO expanders. None of them stretches -- checked against
// every datasheet during the design review, and correctly.
//
// So the master used a PUSH-PULL SCL driver, which UM10204 section 3.1.1 explicitly permits:
//
// "For a single master application, the master's SCL output can be a push-pull driver design
// if there are no devices on the bus which would stretch the clock."
//
// // SCL: driven both ways, no pull-up fitted on that line
// assign scl_pad_o = scl_internal;
// assign scl_pad_oe = 1'b1; // always driving
//
// The benefits were real and were the reason for the choice: SCL's rise time stopped depending on
// the pull-up, Chapter 11.7's whole Rp-Cb window disappeared for that line, and the board saved
// the static current of one pull-up. SDA stayed open-drain, as it must.
//
// The review recorded the assumption. It did not record that the assumption was load-bearing.Two years later a revision added a small FPGA on the same bus as a slave, to expose some board telemetry. It was given an ordinary open-drain I2C slave core -- one that stretches after each byte while its register file is read, which is the normal thing for an FPGA slave to do.
The bus stopped working entirely. Not intermittently: no device on it could be reached once the FPGA was configured, including the five sensors that had worked for two years.
The first theory was addressing -- a clash with the new slave. Its address was changed twice and verified against every other device. No change.
The second theory was loading. The FPGA added capacitance, so the pull-up on SDA was reduced and the bus was checked against Chapter 11.7's window. SDA was comfortably in specification. SCL had no pull-up to check, which nobody found odd because it was driven push-pull by design.
What finally surfaced it was a scope on SCL showing a level around 1.6 V during the FPGA's stretch -- neither a logic high nor a logic low, and drawing far more current than anything on that board should. The FPGA was pulling SCL down while the master's push-pull driver pulled it up. Two drivers, one wire, opposite intents: the resistive divider between two output stages, which is exactly the contention Chapter 2.2 exists to rule out.
Every device on the bus lost its clock, which is why they all failed together. And the FPGA was behaving perfectly correctly -- it is the master that was electrically unable to be stretched.
The master's SCL output was push-pull. UM10204 section 3.1.1 permits that ONLY when no device on the bus will stretch the clock, and Table 2's footnote [2] says the same thing from the other side: "If no slaves in a system can stretch the clock (hold SCL LOW), the master need not be designed to handle this procedure."
Both sentences are conditional, and the condition was quietly invalidated by a board revision two years after it was recorded. Stretching is OPTIONAL, so the original review was right about the parts that were on the bus at the time -- and an optional feature nobody uses is indistinguishable from a feature nobody may use.
The deeper cause is that the assumption lived in a review document rather than in the schematic. Nothing on the board said "SCL is push-pull, therefore no stretching device may ever be added", so the revision that added one had no way to know it was breaking a contract.
12. Common Misconceptions
"Stretching is a fault condition." It is a normal, specified handshake. §4.2.2's wording is that the slave is "busy but does not want to lose the communication" — the opposite of an error.
"A slave stretches instead of acknowledging." It acknowledges and then stretches. §3 is why that order is forced: the acknowledge settles the byte's fate before the slave is allowed to ask for time.
"Stretching and NACKing are two ways to say 'not ready'." A NACK ends the transaction and discards its state; a stretch preserves everything. Stretching is the only backpressure on this bus that is not also an abort.
"SCL being low means somebody is stretching." SCL is low for half of every bit. A stretch is the disagreement between a device's released intent and a line that is still low — §5, and mutation J1 is the detector that misses it.
"Every slave can stretch." §3.1.9 says most cannot, because most do not include an SCL driver. It is optional in Table 2 and it costs the slave a pad driver.
"A master can ignore stretching if its slaves do not use it." Only if that is guaranteed, and then it may even use a push-pull SCL — which is a contract over every device that will ever be on the bus. §11 is that contract being broken by a later revision.
"Byte-level and bit-level stretching are the same feature." They differ in position, frequency, cost and — in Hs-mode — legality. §3's table is the comparison and Chapter 12.3 prices them separately.
"A 16-bit tick counter is plenty." At 100 MHz it covers 655 µs, and an EEPROM page write is milliseconds. §4.2.2 places no limit on a stretch, so no width is provably sufficient — only some are provably too small.
13. Reason It Through
Why must a slave acknowledge before it stretches, rather than pausing to decide?
Because the acknowledge is the decision, and the specification places the handshake "after reception and acknowledgment of a byte". A slave that held the clock while deciding whether to accept would be indistinguishable from one that had hung — and at the moment a compliant slave stops the clock, the byte's fate is already settled and recorded.
A monitor watching only SCL reports nine stretches on every byte. What has it misunderstood?
That a stretch is a relationship, not a level. SCL is low for half of every bit by design; what makes an interval a stretch is that this device released the line and the line stayed low. Without the intent signal the event cannot be expressed at all.
A design review confirms no device on the bus stretches, so the master uses a push-pull SCL. What has to be recorded, and where?
That SCL is push-pull and therefore no stretching device may ever be added — on the schematic, on that net. The specification permits push-pull SCL only under that condition, and a condition recorded in a review document is invisible to the revision that violates it two years later.
Why do FPGA-based slaves stretch far more often than commercial fixed-function parts?
Because stretching requires an SCL output driver, which is silicon a fixed-function part must spend on an optional feature. In an FPGA that driver is already there, so the cost is zero and the obvious implementation uses it. That asymmetry is why an FPGA slave added to a bus designed around non-stretching parts is a recurring integration surprise.
SCL measures 1.6 V on a 3.3 V bus during a transfer. What does that rule out, and what does it prove?
It rules out everything a wired-AND bus can do: open-drain devices only pull down, so the worst several of them together can produce is a solid low. A level between the rails proves something is driving the line high against something pulling it low — contention, and on I²C the only thing that legitimately drives high is a pull-up, which cannot hold mid-rail against a pull-down.
§4.2.2 places no limit on the length of a stretch. What does that do to a liveness assertion?
It makes the bounded form indefensible: any ##[1:N] asserts a limit the specification declines to state. The honest property is unbounded — s_eventually (!stretching) — and any numeric bound has to be introduced as an explicit system policy, which is Chapter 12.4's subject.
14. Understanding Check
15. Summary
Stretching exists because the master owns the clock and the slave owns the data. When a slave cannot store or produce a byte at the master's rate, stretching lets it say so without either device knowing the other's internals.
It is the only backpressure on this bus that is not an abort. A NACK ends the transaction; a stretch preserves every byte of accumulated state. §4.2.2: "busy but does not want to lose the communication".
There are two levels and they are different features. Byte level is one pause after the acknowledgment; bit level extends every low period. They differ in cost, in frequency, and — in Hs-mode — in legality.
The acknowledge comes first, and that ordering is forced. Acknowledging settles the byte's fate; only then may the slave ask for time. A pause taken before the decision is indistinguishable from a hang.
It is optional, and that makes "nothing here stretches" a contract. A master relying on it may use a push-pull SCL — and a later board revision adding a stretching slave then produces contention rather than a wait state.
A stretch is a disagreement, not a low line. It exists only in the difference between a device's released intent and the observed line, which is why a detector needs both and why a clean byte must report zero.
No width of counter is provably enough, because the protocol places no limit on a stretch. Size for the device classes you know about, and report saturation rather than wrapping silently.
16. What Comes Next
Chapter 12.2 asks how stretching actually works, and the answer is that it needed no new mechanism at all. §3.1.7 already describes the machinery — for multi-master clock synchronization — and replacing "the master with the longest LOW period" with "a slave that is not ready" turns that paragraph into clock stretching word for word.
Which means the design question is not how do I implement stretching but is my clock generator built the way §3.1.7 describes. One sentence decides it: the masters start counting their high periods when "the clock line is released and goes HIGH" — not when this master released it. A generator that counts from its own release collapses the high phase to nothing on the very bit a stretch ended, and the receiver never samples that bit.
The chapter builds that generator, shows the collapse as a mutation, and records a bug found not by the SystemVerilog or Verilog suites but by porting the design to a third language.
Continue learning
Related tutorials
- Related topic
The ACK/NACK Cycle — What the Ninth Clock Proves
An acknowledge is one device pulling SDA low for one clock pulse. A not-acknowledge is nobody doing anything. That asymmetry decides how the bus behaves when a device is absent, and it is why an ACK proves far less than engineers assume.
- Related topic
Bus Idle and the Framing Primitives
Module 4 proved that an SDA edge while SCL is high cannot be data, and that the bus reserves the pattern. This chapter collects that debt: what an idle bus is, why a shared bus must delimit its own transfers, and why exactly two reserved edges produce exactly three framing events.
- Related topic
Address Allocation, Strapping, Conflicts and Bus Switches
112 addresses are available and boards still collide constantly, because almost no part lets you choose freely. How real devices move their address, what a strap pin is in silicon, how strapping can land a device in reserved space, and how muxes make one address appear twice.
- Related topic
I²C Transaction Atomicity and Bus Ownership Across Phases
What is and is not atomic on an I²C bus, stated precisely. Three things end bus ownership and one that looks like it should does not — and telling them apart needs one input the wire cannot supply.
