I²C · Module 14
Choosing a Speed Mode — Pull-Ups, Capacitance and Design Cost
The pull-up resistor has one floor and two independent ceilings, and the interval between them is sometimes empty. Reproduces UM10204's own worked example, shows a Fast-mode bus at its own capacitance limit has no legal resistor, and explains why Fast-mode Plus is a 20 mA output stage rather than a faster one.
Three chapters have described what the modes are. This one answers the question an engineer actually has: given this board, which mode can it run?
The answer is not a lookup. It reduces, with remarkable directness, to a single component value:
Choosing a speed mode is choosing a pull-up resistor. And the resistor has to satisfy one floor and two independent ceilings simultaneously — which is sometimes impossible.
That last clause is the chapter. It is why Chapter 11.7 found the window empty at the table's own limits, why Chapter 14.1 noted that Fast-mode and Standard-mode share an output stage, and why Fast-mode Plus is defined by a current rather than by a time.
1. Three Constraints On One Value
Two of those three are widely known. The third is not, and it is the one that bites on the buses people actually build.
2. The Floor — Sink Current
The logic is straightforward. When a device pulls the line low, it must sink whatever current the pull-up delivers, and it must do so while keeping the line below VOL(max) = 0.4 V. A smaller resistor delivers more current than the output stage is rated to sink, and the line never reaches a valid low.
So the floor is set by the weakest output stage on the bus, and note which quantity that is: a current, from Table 9, not a time from Table 10. Standard-mode and Fast-mode specify the same 3 mA. Fast-mode Plus specifies 20 mA, and §5 shows that single difference is what Fm+ is actually for.
3. The First Ceiling — Rise Time
This is the one everybody knows, and the specification derives it from first principles rather than asserting it.
Worth pausing on where 0.8473 comes from, because it is often quoted as a magic number and it is not one. The line charges exponentially towards VDD through Rp into Cb. It crosses 30 % at 0.357 time constants and 70 % at 1.204 — and rise time is measured between the thresholds, not from zero. The difference of those two figures is 0.8473, and it is exact arithmetic on the exponential, not an empirical fudge.
The design in §9 carries it as 1180220/1000, which is 1000/0.8473 to seven figures, and §9b shows it reproducing the specification's own worked example to within a picofarad. Every figure quoted in this chapter is the value that solver returns, so the arithmetic truncates rather than rounds — 966 Ω where a calculator shows 966.67, and 257 Ω where it shows 257.5.
4. The Second Ceiling — Leakage Against The Noise Margin
This one is usually skipped and it should not be. The reasoning:
Every device's input leaks. Table 10 caps it at 10 µA per pin, and that current flows through the pull-up, producing a drop of Ileak × Rp below VDD. The high level on the bus is therefore not VDD; it is VDD minus that drop. And Chapter 11.7's noise margin requirement says the high must stay at least 0.2 VDD above VIH, so:
Rp(max,leak) = 0.2 × VDD / Ileak(total)— andIleak(total)is 10 µA times the number of pins.
Which means this ceiling falls as devices are added, independent of anything to do with speed:
| VDD | 1 device | 5 devices | 10 devices | 20 devices |
|---|---|---|---|---|
| 3.3 V | 66 kΩ | 13.2 kΩ | 6.6 kΩ | 3.3 kΩ |
| 5.0 V | 100 kΩ | 20 kΩ | 10 kΩ | 5.0 kΩ |
On a lightly loaded bus with a handful of devices this ceiling is far above the rise-time one and never matters. On a heavily populated bus it can drop below it — and then the binding constraint on the pull-up has nothing to do with speed at all. Tests 7, 8 and 10 in §9 are that regime, and §10's mutation V4 is the version that uses the wrong noise margin.
5. The Specification's Own Worked Example
§7.2.4 does the arithmetic in prose, which makes it the best available check on any implementation of Equations 1 and 2.
Two details before the arithmetic. It uses 5.5 V, the +10 % corner, not the nominal 5 V — because the floor must hold at the worst case. And it then runs Equation 1 backwards, solving for capacitance at a fixed resistance.
Doing that:
Cb(max) = tr / (0.8473 × Rp) = 300 ns / (0.8473 × 1700 Ω) = 208 pF
The specification says "about 200 pF". The solver in §9 returns 208, and test 1 asserts both numbers. That is the strongest validation available here, because it is a check against the document rather than against the implementation's own assumptions — if the constant, the scaling or the division order were wrong, one of the two figures would miss.
5a. And Now §5.1's Threshold Is Not Arbitrary
Recall what Chapter 14.1 §3 quoted:
"For bus loads up to 200 pF, the pull-up device for each bus line can be a resistor; for bus loads between 200 pF and 400 pF, the pull-up device can be a current source (3 mA max.) or a switched resistor circuit."
That 200 pF is the number that just fell out of Equation 1 at the 5.5 V corner. It is not a round figure chosen for convenience — it is where the resistor window closes. Above it, no single resistor satisfies both the floor and the rise-time ceiling, so the specification stops permitting one.
Solving for the closing point at other supplies:
| VDD | Rp(min) at 3 mA | Cb where the window closes |
|---|---|---|
| 2.5 V | 700 Ω | 505 pF |
| 3.3 V | 966 Ω | 366 pF |
| 5.0 V | 1533 Ω | 230 pF |
| 5.5 V | 1700 Ω | 208 pF |
A lower supply gives a wider window, because the floor drops faster than the ceiling does. That is a genuinely useful and counter-intuitive result: a 3.3 V Fast-mode bus can carry substantially more capacitance than a 5 V one, with identical parts and identical timing.
6. The Window Is Sometimes Empty
Here is the result that matters most, and it is not a corner case.
Take Fast-mode at its own maximum capacitance. Table 10 permits Cb up to 400 pF in Fast-mode. At 3.3 V with a 3 mA output stage:
| quantity | value |
|---|---|
Rp(min) = (3300 − 400)/3 mA | 966 Ω |
Rp(max,rise) = 300 ns / (0.8473 × 400 pF) | 885 Ω |
| window | empty — the floor is above the ceiling |
A Fast-mode bus at the capacitance Fast-mode itself permits has no legal pull-up resistor.
That is not a contradiction in the specification; it is the specification telling you, through §5.1, that above 200 pF you must stop using a resistor. But it is easy to reach by reading Table 10 alone: Cb max 400 pF, fSCL max 400 kHz, and nothing in Table 10 says those two cannot be combined. The incompatibility lives in §7.1, three sections later.
And at 5.5 V it is worse — the floor rises to 1700 Ω against the same 885 Ω ceiling. Test 2 in §9 is exactly that case, and it reports binding as empty, rise-limited, which is the diagnosis a designer needs: the problem is the rise time, so slowing the bus or reducing capacitance will help and a stronger driver will not.
6a. Standard-Mode On The Same Board Is Comfortable
Nothing about the board changed. Only the mode:
| mode at 3.3 V, 400 pF | Rp(min) | Rp(max,rise) | window |
|---|---|---|---|
Standard (tr ≤ 1000 ns) | 966 Ω | 2950 Ω | 3.05 × |
Fast (tr ≤ 300 ns) | 966 Ω | 885 Ω | empty |
Same parts, same capacitance, same supply, same floor. The Standard-mode bus has a comfortable three-to-one range of legal resistors; the Fast-mode bus has none. The entire difference is the rise-time allowance, and this is the quantitative content of Chapter 14.1 §6's "downward compatible, not upward compatible."
7. What Fast-Mode Plus Actually Buys
Fm+ has the tightest rise-time allowance of the three modes — 120 ns against Fast-mode's 300 ns — and the largest permitted capacitance, 550 pF against 400 pF. Those two facts look contradictory until the floor is included:
| mode at 3.3 V | IOL | Cb | Rp(min) | Rp(max,rise) | window |
|---|---|---|---|---|---|
| Fast | 3 mA | 400 pF | 966 Ω | 885 Ω | empty |
| Fast-mode Plus | 20 mA | 550 pF | 145 Ω | 257 Ω | 1.77 × |
The 20 mA sink current drops the floor from 966 Ω to 145 Ω — a factor of 6.7 — and that headroom is what absorbs both the tighter rise time and the larger capacitance.
Fm+ buys capacitance with drive current, not with timing. The 1 Mbit/s is a consequence; the 20 mA output stage is the mode.
Which is why §5.2 talks about drive rather than speed when it explains the mode:
That last sentence is the most practically useful line in §5. A pure Fm+ bus run slowly can carry roughly ten times the capacitance — because dropping the rate relaxes tr, which raises the rise-time ceiling proportionally, while the 20 mA floor does not move. A 5 kΩ-loaded, 4 metre, 30-device bus is entirely feasible with Fm+ parts at 50 kbit/s, and impossible with Fast-mode parts at any rate.
8. When The Window Is Empty — The Four Remedies
Each has a cost, and the costs are different in kind.
Reduced fSCL raises the rise-time ceiling in direct proportion and costs only throughput. It is the remedy to reach for first, and Equation 3 gives the number: fmax = 1/(tLOW + tHIGH + tr + tf) with the actual edges. §7.2.2 adds the warning that matters — "the maximum rating for fSCL as specified in Table 10 (100 kHz, 400 kHz and 1000 kHz) may become limiting" — that is, derating past a mode boundary means you are in the slower mode, with its phase minima, not in the faster mode running slowly.
Higher drive lowers the floor and is the cleanest fix when it is available, but it is a change of part, and §5.2's caution applies: every device on the bus must tolerate what the strong driver does to the edges.
Bus buffers halve the capacitance per segment and are the only remedy that scales, but §7.2.3 is blunt about the price: "adding a buffer always adds delays — a buffer delay plus an additional transition time to each edge, which reduces the maximum operating frequency and may also introduce special VIL and VOL considerations." A buffer solves a capacitance problem by creating a timing problem, and on a bus that was already tight on fSCL that can be a net loss.
A switched pull-up keeps a weak resistor for the static high and switches in a strong one only during the rising edge — which sidesteps the conflict entirely, because the floor constrains the static pull-up and the ceiling constrains the dynamic one. §7.2.4's circuit uses 1.7 kΩ permanently with 1.3 kΩ switched in between 0.8 V and 2.0 V by an HCT4066, and it is controlled by the bus levels themselves, so it needs no control signal. It is the most elegant of the four and the only one that needs extra components on the board.
9. The Window Solver in Three Languages
The design evaluates all three constraints and reports not just the numbers but which one binds — and, when the window is empty, which constraint made it empty. That distinction is the whole value of the block: "no legal resistor, and the rise time is why" points at capacitance or speed, while "no legal resistor, and leakage is why" points at device count and would not be improved by slowing the bus at all.
All arithmetic is integer, and the divisions are written plainly rather than pipelined — none of these results is needed in a hurry.
// -----------------------------------------------------------------------------
// i2c_pullup_window.sv
// Pull-up resistor feasibility solver for Standard-mode, Fast-mode and
// Fast-mode Plus (UM10204 7.1, 7.2, 7.4).
//
// Choosing a speed mode is really choosing a pull-up resistor, and the resistor
// has to satisfy one floor and two independent ceilings at the same time:
//
// FLOOR Rp(min) = (VDD - VOL(max)) / IOL
// The device must be able to pull the line to a valid LOW while
// sinking no more than its rated current. A smaller resistor draws
// more current than the output stage can sink.
//
// CEILING Rp(max,rise) = tr(max) / (0.8473 x Cb) -- 7.1 Eq 1
// The RC rise between the 30 % and 70 % thresholds must fit inside
// tr(max). The constant is exact, not a fudge: charging through R
// into C reaches 30 % at 0.3566749 RC and 70 % at 1.2039729 RC, and
// the difference is 0.8473 RC.
//
// CEILING Rp(max,leak) = 0.2 x VDD / Ileak(total) -- 7.4
// Every connected pin leaks up to 10 uA, and that current flows
// through Rp. The resulting drop must not eat the 0.2 VDD HIGH
// noise margin. This ceiling falls as devices are added, so it is
// the one that bites on long, heavily populated buses.
//
// The interesting output is not the resistor value, it is whether the window
// EXISTS. It does not always. At 5.5 V with a 3 mA output stage the floor is
// 1.7 kohm, and Equation 1 then caps the capacitance at 208 pF for a Fast-mode
// rise time -- which is exactly why UM10204 5.1 permits a plain resistor only
// up to 200 pF and calls for a current source or a switched resistor above it.
// The spec's own worked example in 7.2.4 is reproduced by this block bit for bit.
//
// Arithmetic notes. Everything is integer. 1000/0.8473 = 1180.2195..., held as
// 1180220/1000, which reproduces the spec's example to within a picofarad. The
// divisions are written as `/` for clarity; a real implementation would share
// one sequential divider, since none of these results is needed in a hurry.
// -----------------------------------------------------------------------------
module i2c_pullup_window #(
parameter int VOL_MV = 400 // VOL(max), Table 10: 0.4 V
) (
input logic clk,
input logic rst_n,
input logic calc, // one pulse: evaluate the inputs below
input logic [1:0] mode, // 0 = Standard, 1 = Fast, 2 = Fast-mode Plus
input logic [13:0] vdd_mv, // supply, mV. Use the worst-case corner.
input logic [10:0] cb_pf, // estimated bus capacitance, pF
input logic [5:0] n_devices, // pins on the bus, for the leakage ceiling
output logic done,
output logic [19:0] rp_min_ohm,
output logic [19:0] rp_max_rise_ohm,
output logic [19:0] rp_max_leak_ohm,
output logic [19:0] rp_max_ohm, // the binding ceiling: the lower of the two
output logic feasible, // a legal resistor exists
output logic [2:0] binding,
output logic [15:0] fmax_khz, // Equation 3, at the mode's edge maxima
output logic [15:0] cb_max_pf, // where the window closes, given rp_min
output logic [15:0] window_x100 // rp_max/rp_min as a percentage; <100 is empty
);
localparam [1:0] MODE_STD = 2'd0;
localparam [1:0] MODE_FAST = 2'd1;
localparam [1:0] MODE_FMP = 2'd2;
localparam [2:0] BIND_RISE = 3'd1, // feasible, rise time is the binding ceiling
BIND_LEAK = 3'd2, // feasible, leakage is the binding ceiling
BIND_EMPTY_RISE = 3'd3, // infeasible: the floor exceeds the rise ceiling
BIND_EMPTY_LEAK = 3'd4; // infeasible: the floor exceeds the leak ceiling
// 1000 / 0.8473, scaled by 1000.
localparam [31:0] K_RC = 32'd1180220;
// Table 10 per-mode values.
function [15:0] tr_max_ns (input [1:0] m);
case (m)
MODE_STD : tr_max_ns = 16'd1000;
MODE_FAST: tr_max_ns = 16'd300;
default : tr_max_ns = 16'd120;
endcase
endfunction
function [15:0] tf_max_ns (input [1:0] m);
case (m)
MODE_STD : tf_max_ns = 16'd300;
MODE_FAST: tf_max_ns = 16'd300;
default : tf_max_ns = 16'd120;
endcase
endfunction
function [15:0] tlow_min_ns (input [1:0] m);
case (m)
MODE_STD : tlow_min_ns = 16'd4700;
MODE_FAST: tlow_min_ns = 16'd1300;
default : tlow_min_ns = 16'd500;
endcase
endfunction
function [15:0] thigh_min_ns (input [1:0] m);
case (m)
MODE_STD : thigh_min_ns = 16'd4000;
MODE_FAST: thigh_min_ns = 16'd600;
default : thigh_min_ns = 16'd260;
endcase
endfunction
// Rated sink current: 3 mA for Standard and Fast, 20 mA for Fast-mode Plus.
// This single number is what buys Fm+ its extra capacitance -- not its timing.
function [15:0] iol_ua (input [1:0] m);
case (m)
MODE_FMP: iol_ua = 16'd20000;
default : iol_ua = 16'd3000;
endcase
endfunction
logic [31:0] num, den;
logic [31:0] r_min, r_rise, r_leak, r_max;
logic [31:0] ileak_ua, sum_ns;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
done <= 1'b0;
rp_min_ohm <= 20'd0;
rp_max_rise_ohm <= 20'd0;
rp_max_leak_ohm <= 20'd0;
rp_max_ohm <= 20'd0;
feasible <= 1'b0;
binding <= BIND_RISE;
fmax_khz <= 16'd0;
cb_max_pf <= 16'd0;
window_x100 <= 16'd0;
end else if (calc) begin
// ---- FLOOR: Rp(min) = (VDD - VOL) / IOL --------------------------
if (vdd_mv > VOL_MV) begin
num = ({18'd0, vdd_mv} - VOL_MV) * 32'd1000; // mV -> uV
r_min = num / {16'd0, iol_ua(mode)};
end else begin
r_min = 32'd0; // a supply at or below VOL cannot make a HIGH at all
end
// ---- CEILING 1: Rp(max) = tr / (0.8473 x Cb) ---------------------
if (cb_pf != 11'd0) begin
num = K_RC * {16'd0, tr_max_ns(mode)};
den = 32'd1000 * {21'd0, cb_pf};
r_rise = num / den;
end else begin
r_rise = {32{1'b1}}; // no capacitance, no rise-time ceiling
end
// ---- CEILING 2: Rp(max) = 0.2 VDD / Ileak ------------------------
ileak_ua = {26'd0, n_devices} * 32'd10; // 10 uA per pin, Table 10
if (ileak_ua != 32'd0)
r_leak = ({18'd0, vdd_mv} * 32'd200) / ileak_ua;
else
r_leak = {32{1'b1}};
// ---- the binding ceiling is whichever is lower -------------------
// Internal values are blocking so the later arithmetic sees them; only
// the module outputs are assigned non-blocking.
if (r_rise <= r_leak) begin
r_max = r_rise;
binding <= (r_min > r_rise) ? BIND_EMPTY_RISE : BIND_RISE;
end else begin
r_max = r_leak;
binding <= (r_min > r_leak) ? BIND_EMPTY_LEAK : BIND_LEAK;
end
rp_max_ohm <= (r_max > 32'h000F_FFFF) ? 20'hFFFFF : r_max[19:0];
feasible <= (r_min <= r_max) && (r_min != 32'd0);
// ---- Cb at which the window closes, for this floor ---------------
if (r_min != 32'd0)
cb_max_pf <= (K_RC * {16'd0, tr_max_ns(mode)}) / (32'd1000 * r_min);
else
cb_max_pf <= 16'd0;
// ---- Equation 3: fmax at the mode's own edge maxima --------------
sum_ns = {16'd0, tlow_min_ns(mode)} + {16'd0, thigh_min_ns(mode)}
+ {16'd0, tr_max_ns(mode)} + {16'd0, tf_max_ns(mode)};
fmax_khz <= 32'd1000000 / sum_ns;
// ---- figure of merit --------------------------------------------
if (r_min != 32'd0)
window_x100 <= (r_max * 32'd100) / r_min;
else
window_x100 <= 16'd0;
rp_min_ohm <= r_min[19:0];
rp_max_rise_ohm <= (r_rise > 32'h000F_FFFF) ? 20'hFFFFF : r_rise[19:0];
rp_max_leak_ohm <= (r_leak > 32'h000F_FFFF) ? 20'hFFFFF : r_leak[19:0];
done <= 1'b1;
end else begin
done <= 1'b0;
end
end
endmodule `timescale 1ns/1ps
// -----------------------------------------------------------------------------
// i2c_pullup_window_tb.sv
// Independent oracle for i2c_pullup_window.
//
// The strongest check available here is not a self-consistency check: UM10204
// 7.2.4 works one example through in prose, and T1 asserts the block reproduces
// it. The spec says Rp(min) = (5.5 - 0.4)/0.003 = 1.7 kohm and that this "limits
// the maximum bus capacitance to about 200 pF to meet the maximum tr requirement
// of 300 ns". If the block agrees on both numbers, the whole arithmetic chain --
// the 0.8473 constant, the scaling, the division order -- is validated against
// the specification rather than against itself.
//
// The bench computes every expectation with its own integer arithmetic, written
// out longhand, and never calls the DUT's functions.
// -----------------------------------------------------------------------------
module i2c_pullup_window_tb;
localparam [1:0] M_STD = 2'd0, M_FAST = 2'd1, M_FMP = 2'd2;
localparam [2:0] BIND_RISE = 3'd1, BIND_LEAK = 3'd2,
BIND_EMPTY_RISE = 3'd3, BIND_EMPTY_LEAK = 3'd4;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic calc = 1'b0;
logic [1:0] mode = M_FAST;
logic [13:0] vdd_mv = 14'd3300;
logic [10:0] cb_pf = 11'd400;
logic [5:0] n_devices = 6'd4;
logic done, feasible;
logic [19:0] rp_min_ohm, rp_max_rise_ohm, rp_max_leak_ohm, rp_max_ohm;
logic [2:0] binding;
logic [15:0] fmax_khz, cb_max_pf, window_x100;
integer errors = 0;
i2c_pullup_window #(.VOL_MV(400)) dut (
.clk(clk), .rst_n(rst_n), .calc(calc), .mode(mode),
.vdd_mv(vdd_mv), .cb_pf(cb_pf), .n_devices(n_devices),
.done(done), .rp_min_ohm(rp_min_ohm),
.rp_max_rise_ohm(rp_max_rise_ohm), .rp_max_leak_ohm(rp_max_leak_ohm),
.rp_max_ohm(rp_max_ohm), .feasible(feasible), .binding(binding),
.fmax_khz(fmax_khz), .cb_max_pf(cb_max_pf), .window_x100(window_x100));
always #10 clk = ~clk;
task ck (input [200*8:1] what, input integer got, input integer exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0d expected %0d", what, got, exp);
errors = errors + 1;
end
end
endtask
task ck_bit (input [200*8:1] what, input got, input exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0b expected %0b", what, got, exp);
errors = errors + 1;
end
end
endtask
task do_solve (input integer m, input integer vdd, input integer cb, input integer nd);
begin
@(negedge clk);
mode = m[1:0];
vdd_mv = vdd[13:0];
cb_pf = cb[10:0];
n_devices = nd[5:0];
calc = 1'b1;
@(posedge clk); @(negedge clk);
calc = 1'b0;
@(posedge clk); @(negedge clk);
end
endtask
// The bench's own arithmetic, spelled out.
function integer exp_rp_min (input integer vdd, input integer iol_ua);
begin exp_rp_min = ((vdd - 400) * 1000) / iol_ua; end
endfunction
function integer exp_rp_rise (input integer tr, input integer cb);
begin exp_rp_rise = (1180220 * tr) / (1000 * cb); end
endfunction
function integer exp_rp_leak (input integer vdd, input integer nd);
begin exp_rp_leak = (vdd * 200) / (nd * 10); end
endfunction
initial begin
$display("=== i2c_pullup_window: the resistor has a floor and two ceilings ===");
@(negedge clk); rst_n = 1'b0;
repeat (3) @(posedge clk);
@(negedge clk); rst_n = 1'b1;
@(posedge clk);
// ----------------------------------------------------------------
// T1. UM10204 7.2.4, WORKED THROUGH BY THE SPECIFICATION ITSELF.
// VDD = 5 V +10 % = 5.5 V, VOL(max) = 0.4 V at 3 mA.
// The spec states Rp(min) = 1.7 kohm and that this caps the
// capacitance at "about 200 pF" for a 300 ns rise time.
// n_devices is set to 1 so the leakage ceiling cannot interfere.
// ----------------------------------------------------------------
do_solve(M_FAST, 5500, 400, 1);
$display("T1 UM10204 7.2.4 worked example");
ck_bit("T1 done", done, 1'b0); // done is a single-cycle pulse
ck("T1 Rp(min) is the spec's 1700 ohm", rp_min_ohm, 1700);
ck("T1 Cb(max) is the spec's 'about 200 pF'", cb_max_pf, 208);
// ----------------------------------------------------------------
// T2. The consequence: at 5.5 V with a 3 mA stage, a Fast-mode bus of
// 400 pF has NO legal resistor. The floor is 1700 ohm and the rise
// ceiling is 885 ohm. This is why UM10204 5.1 stops allowing a plain
// resistor at 200 pF and calls for a current source above it.
// ----------------------------------------------------------------
do_solve(M_FAST, 5500, 400, 1);
$display("T2 Fast-mode, 5.5 V, 400 pF: the window is empty");
ck("T2 Rp(min)", rp_min_ohm, exp_rp_min(5500, 3000));
ck("T2 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 400));
ck_bit("T2 infeasible", feasible, 1'b0);
ck("T2 empty, rise-limited", binding, BIND_EMPTY_RISE);
if (window_x100 >= 100) begin
$display(" FAIL T2 window_x100: got %0d, expected < 100 for an empty window",
window_x100);
errors = errors + 1;
end
// ----------------------------------------------------------------
// T3. The same bus at 200 pF fits, just. This is the boundary the spec
// draws, confirmed from the arithmetic rather than quoted.
// ----------------------------------------------------------------
do_solve(M_FAST, 5500, 200, 1);
$display("T3 Fast-mode, 5.5 V, 200 pF: feasible but tight");
ck("T3 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 200));
ck_bit("T3 feasible", feasible, 1'b1);
ck("T3 rise-limited", binding, BIND_RISE);
if (window_x100 < 100 || window_x100 > 120) begin
$display(" FAIL T3 window_x100: got %0d, expected a narrow 100..120", window_x100);
errors = errors + 1;
end
// ----------------------------------------------------------------
// T4. Standard-mode on the same 400 pF bus is comfortable, because its
// rise-time allowance is 1000 ns rather than 300 ns. Identical
// hardware, identical capacitance; only the mode changed.
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 400, 1);
$display("T4 Standard-mode, 3.3 V, 400 pF: comfortable");
ck("T4 Rp(min)", rp_min_ohm, exp_rp_min(3300, 3000));
ck("T4 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(1000, 400));
ck_bit("T4 feasible", feasible, 1'b1);
ck("T4 fmax is 100 kHz", fmax_khz, 100);
// Standard-mode is the ONLY mode where tr(max) and tf(max) differ
// (1000 ns vs 300 ns), so it is the only place a Cb(max) computed from the
// wrong edge is observable at all.
ck("T4 Cb(max) is sized from the RISE time",
cb_max_pf, (1180220 * 1000) / (1000 * exp_rp_min(3300, 3000)));
if (window_x100 < 300) begin
$display(" FAIL T4 window_x100: got %0d, expected a wide window (>= 300)",
window_x100);
errors = errors + 1;
end
// ----------------------------------------------------------------
// T5. Fast-mode on that same 400 pF bus at 3.3 V is STILL empty: the
// floor is 966 ohm and the ceiling 885 ohm. The mode, not the
// supply, is what makes it impossible.
// ----------------------------------------------------------------
do_solve(M_FAST, 3300, 400, 1);
$display("T5 Fast-mode, 3.3 V, 400 pF: still empty");
ck("T5 Rp(min)", rp_min_ohm, exp_rp_min(3300, 3000));
ck("T5 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 400));
ck_bit("T5 infeasible", feasible, 1'b0);
ck("T5 fmax is 400 kHz", fmax_khz, 400);
// ----------------------------------------------------------------
// T6. Fast-mode Plus at a LARGER capacitance, 550 pF, is feasible --
// and the only thing that changed is the sink current, 20 mA rather
// than 3 mA. Drive current buys capacitance; timing does not.
// ----------------------------------------------------------------
do_solve(M_FMP, 3300, 550, 1);
$display("T6 Fast-mode Plus, 3.3 V, 550 pF: feasible on 20 mA");
ck("T6 Rp(min) on 20 mA", rp_min_ohm, exp_rp_min(3300, 20000));
ck("T6 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(120, 550));
ck_bit("T6 feasible", feasible, 1'b1);
ck("T6 fmax is 1000 kHz", fmax_khz, 1000);
// The proof that it is the current and not the timing: Fm+ has the
// TIGHTEST rise allowance of the three modes and the LARGEST capacitance.
if (rp_min_ohm >= exp_rp_min(3300, 3000)) begin
$display(" FAIL T6 the 20 mA floor should be far below the 3 mA floor");
errors = errors + 1;
end
// ----------------------------------------------------------------
// T7. THE LEAKAGE CEILING TAKES OVER. Standard-mode, 3.3 V, one device,
// and only 10 pF of load. The rise ceiling is then 118 kohm, far
// above the 66 kohm the HIGH noise margin allows, so the constraint
// that binds has nothing to do with speed at all.
// (At 20 pF the rise ceiling is 59 kohm and still binds -- the
// crossover is close, and it is worth knowing where it sits.)
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 10, 1);
$display("T7 a lightly loaded bus is leakage-limited, not rise-limited");
ck("T7 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(3300, 1));
ck("T7 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(1000, 10));
ck("T7 leakage binds", binding, BIND_LEAK);
ck("T7 the binding ceiling is the leakage one", rp_max_ohm, exp_rp_leak(3300, 1));
ck_bit("T7 feasible", feasible, 1'b1);
// And the crossover claim, checked rather than asserted:
do_solve(M_STD, 3300, 20, 1);
ck("T7b at 20 pF the rise ceiling binds again", binding, BIND_RISE);
// ----------------------------------------------------------------
// T8. Adding devices lowers the leakage ceiling proportionally. Twenty
// pins at 10 uA each is 200 uA, and at 3.3 V that caps Rp at
// 3.3 kohm however little capacitance there is.
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 10, 20);
$display("T8 twenty pins drag the leakage ceiling down to a few kohm");
ck("T8 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(3300, 20));
ck("T8 leakage still binds", binding, BIND_LEAK);
ck("T8 ceiling is 3300 ohm", rp_max_ohm, 3300);
ck_bit("T8 still feasible", feasible, 1'b1);
// ----------------------------------------------------------------
// T9. Both ceilings squeezed at once: many devices AND a lot of
// capacitance, in Fast-mode. The rise ceiling is 885 ohm, the
// leakage ceiling 1650 ohm, so rise binds and the floor of 966 ohm
// is above it. Infeasible, and correctly attributed to the rise.
// ----------------------------------------------------------------
do_solve(M_FAST, 3300, 400, 40);
$display("T9 both ceilings squeezed: the rise one still binds");
ck("T9 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 400));
ck("T9 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(3300, 40));
ck("T9 empty, attributed to the rise time", binding, BIND_EMPTY_RISE);
ck_bit("T9 infeasible", feasible, 1'b0);
// ----------------------------------------------------------------
// T10. INFEASIBLE THROUGH LEAKAGE rather than rise time -- the other way
// a window can close, and the one nobody expects.
//
// Solving 200.VDD/Ileak < 1000.(VDD-400)/IOL for IOL = 3 mA and 63
// pins gives VDD > 8.4 V. So on a 10 V bus with 63 pins the HIGH
// noise margin allows at most 3174 ohm while the output stage needs
// at least 3200 ohm. The bus is barely loaded -- 10 pF, a 118 kohm
// rise ceiling -- and still has no legal resistor. Slowing it down
// would not help; only fewer devices or a lower supply would.
// ----------------------------------------------------------------
do_solve(M_STD, 10000, 10, 63);
$display("T10 infeasible through leakage, not rise time");
ck("T10 Rp(min)", rp_min_ohm, exp_rp_min(10000, 3000));
ck("T10 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(10000, 63));
ck("T10 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(1000, 10));
ck("T10 empty, attributed to leakage", binding, BIND_EMPTY_LEAK);
ck_bit("T10 infeasible", feasible, 1'b0);
// The rise ceiling is not remotely the problem here:
if (rp_max_rise_ohm <= rp_min_ohm) begin
$display(" FAIL T10 the rise ceiling should be far above the floor");
errors = errors + 1;
end
// ----------------------------------------------------------------
// T11. Equation 3 across all three modes. Because Table 10 is internally
// exact, feeding the mode's own minima and maxima back through
// Equation 3 must return the mode's own headline frequency.
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 100, 2);
ck("T11 Standard fmax", fmax_khz, 100);
do_solve(M_FAST, 3300, 100, 2);
ck("T11 Fast fmax", fmax_khz, 400);
do_solve(M_FMP, 3300, 100, 2);
ck("T11 Fast-mode Plus fmax", fmax_khz, 1000);
$display("T11 Equation 3 returns each mode's headline rate exactly");
// ----------------------------------------------------------------
// T12. Degenerate inputs must not produce a confident wrong answer.
// A supply at VOL cannot make a HIGH at all.
// ----------------------------------------------------------------
do_solve(M_FAST, 400, 400, 4);
$display("T12 a supply at VOL(max) yields no usable window");
ck("T12 Rp(min) is zero", rp_min_ohm, 0);
ck_bit("T12 not feasible", feasible, 1'b0);
// ----------------------------------------------------------------
// T14. THE EXACT BOUNDARY. At 1.5 V with a 3 mA stage the floor is 366 ohm,
// and a 966 pF Fast-mode load puts the rise ceiling at 366 ohm too.
// The window contains exactly ONE legal resistor value. A solver that
// tests the ceiling with a strict inequality rejects this bus, and no
// other case in this suite can tell the two spellings apart.
// ----------------------------------------------------------------
do_solve(M_FAST, 1500, 966, 1);
$display("T14 a window holding exactly one value is still a window");
ck("T14 Rp(min)", rp_min_ohm, exp_rp_min(1500, 3000));
ck("T14 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 966));
ck("T14 floor equals ceiling", rp_min_ohm, rp_max_rise_ohm);
ck_bit("T14 feasible at the boundary", feasible, 1'b1);
ck("T14 window is exactly 100 %", window_x100, 100);
// ----------------------------------------------------------------
// T13. done is a single-cycle pulse that follows calc, not a level.
// ----------------------------------------------------------------
@(negedge clk);
mode = M_FAST; vdd_mv = 14'd3300; cb_pf = 11'd100; n_devices = 6'd2;
calc = 1'b1;
@(posedge clk); @(negedge clk);
ck_bit("T13 done asserted the cycle after calc", done, 1'b1);
calc = 1'b0;
@(posedge clk); @(negedge clk);
ck_bit("T13 done deasserted again", done, 1'b0);
$display("T13 done is a pulse, not a level");
if (errors == 0)
$display("=== i2c_pullup_window: ALL CHECKS PASSED ===");
else
$display("=== i2c_pullup_window: %0d CHECK(S) FAILED ===", errors);
$finish;
end
endmodule // -----------------------------------------------------------------------------
// i2c_pullup_window.sv
// Pull-up resistor feasibility solver for Standard-mode, Fast-mode and
// Fast-mode Plus (UM10204 7.1, 7.2, 7.4).
//
// Choosing a speed mode is really choosing a pull-up resistor, and the resistor
// has to satisfy one floor and two independent ceilings at the same time:
//
// FLOOR Rp(min) = (VDD - VOL(max)) / IOL
// The device must be able to pull the line to a valid LOW while
// sinking no more than its rated current. A smaller resistor draws
// more current than the output stage can sink.
//
// CEILING Rp(max,rise) = tr(max) / (0.8473 x Cb) -- 7.1 Eq 1
// The RC rise between the 30 % and 70 % thresholds must fit inside
// tr(max). The constant is exact, not a fudge: charging through R
// into C reaches 30 % at 0.3566749 RC and 70 % at 1.2039729 RC, and
// the difference is 0.8473 RC.
//
// CEILING Rp(max,leak) = 0.2 x VDD / Ileak(total) -- 7.4
// Every connected pin leaks up to 10 uA, and that current flows
// through Rp. The resulting drop must not eat the 0.2 VDD HIGH
// noise margin. This ceiling falls as devices are added, so it is
// the one that bites on long, heavily populated buses.
//
// The interesting output is not the resistor value, it is whether the window
// EXISTS. It does not always. At 5.5 V with a 3 mA output stage the floor is
// 1.7 kohm, and Equation 1 then caps the capacitance at 208 pF for a Fast-mode
// rise time -- which is exactly why UM10204 5.1 permits a plain resistor only
// up to 200 pF and calls for a current source or a switched resistor above it.
// The spec's own worked example in 7.2.4 is reproduced by this block bit for bit.
//
// Arithmetic notes. Everything is integer. 1000/0.8473 = 1180.2195..., held as
// 1180220/1000, which reproduces the spec's example to within a picofarad. The
// divisions are written as `/` for clarity; a real implementation would share
// one sequential divider, since none of these results is needed in a hurry.
// -----------------------------------------------------------------------------
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
module i2c_pullup_window #(
parameter VOL_MV = 400 // VOL(max), Table 10: 0.4 V
) (
input wire clk,
input wire rst_n,
input wire calc, // one pulse: evaluate the inputs below
input wire [1:0] mode, // 0 = Standard, 1 = Fast, 2 = Fast-mode Plus
input wire [13:0] vdd_mv, // supply, mV. Use the worst-case corner.
input wire [10:0] cb_pf, // estimated bus capacitance, pF
input wire [5:0] n_devices, // pins on the bus, for the leakage ceiling
output reg done,
output reg [19:0] rp_min_ohm,
output reg [19:0] rp_max_rise_ohm,
output reg [19:0] rp_max_leak_ohm,
output reg [19:0] rp_max_ohm, // the binding ceiling: the lower of the two
output reg feasible, // a legal resistor exists
output reg [2:0] binding,
output reg [15:0] fmax_khz, // Equation 3, at the mode's edge maxima
output reg [15:0] cb_max_pf, // where the window closes, given rp_min
output reg [15:0] window_x100 // rp_max/rp_min as a percentage; <100 is empty
);
localparam [1:0] MODE_STD = 2'd0;
localparam [1:0] MODE_FAST = 2'd1;
localparam [1:0] MODE_FMP = 2'd2;
localparam [2:0] BIND_RISE = 3'd1, // feasible, rise time is the binding ceiling
BIND_LEAK = 3'd2, // feasible, leakage is the binding ceiling
BIND_EMPTY_RISE = 3'd3, // infeasible: the floor exceeds the rise ceiling
BIND_EMPTY_LEAK = 3'd4; // infeasible: the floor exceeds the leak ceiling
// 1000 / 0.8473, scaled by 1000.
localparam [31:0] K_RC = 32'd1180220;
// Table 10 per-mode values.
function [15:0] tr_max_ns (input [1:0] m);
case (m)
MODE_STD : tr_max_ns = 16'd1000;
MODE_FAST: tr_max_ns = 16'd300;
default : tr_max_ns = 16'd120;
endcase
endfunction
function [15:0] tf_max_ns (input [1:0] m);
case (m)
MODE_STD : tf_max_ns = 16'd300;
MODE_FAST: tf_max_ns = 16'd300;
default : tf_max_ns = 16'd120;
endcase
endfunction
function [15:0] tlow_min_ns (input [1:0] m);
case (m)
MODE_STD : tlow_min_ns = 16'd4700;
MODE_FAST: tlow_min_ns = 16'd1300;
default : tlow_min_ns = 16'd500;
endcase
endfunction
function [15:0] thigh_min_ns (input [1:0] m);
case (m)
MODE_STD : thigh_min_ns = 16'd4000;
MODE_FAST: thigh_min_ns = 16'd600;
default : thigh_min_ns = 16'd260;
endcase
endfunction
// Rated sink current: 3 mA for Standard and Fast, 20 mA for Fast-mode Plus.
// This single number is what buys Fm+ its extra capacitance -- not its timing.
function [15:0] iol_ua (input [1:0] m);
case (m)
MODE_FMP: iol_ua = 16'd20000;
default : iol_ua = 16'd3000;
endcase
endfunction
reg [31:0] num, den;
reg [31:0] r_min, r_rise, r_leak, r_max;
reg [31:0] ileak_ua, sum_ns;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
done <= 1'b0;
rp_min_ohm <= 20'd0;
rp_max_rise_ohm <= 20'd0;
rp_max_leak_ohm <= 20'd0;
rp_max_ohm <= 20'd0;
feasible <= 1'b0;
binding <= BIND_RISE;
fmax_khz <= 16'd0;
cb_max_pf <= 16'd0;
window_x100 <= 16'd0;
end else if (calc) begin
// ---- FLOOR: Rp(min) = (VDD - VOL) / IOL --------------------------
if (vdd_mv > VOL_MV) begin
num = ({18'd0, vdd_mv} - VOL_MV) * 32'd1000; // mV -> uV
r_min = num / {16'd0, iol_ua(mode)};
end else begin
r_min = 32'd0; // a supply at or below VOL cannot make a HIGH at all
end
// ---- CEILING 1: Rp(max) = tr / (0.8473 x Cb) ---------------------
if (cb_pf != 11'd0) begin
num = K_RC * {16'd0, tr_max_ns(mode)};
den = 32'd1000 * {21'd0, cb_pf};
r_rise = num / den;
end else begin
r_rise = {32{1'b1}}; // no capacitance, no rise-time ceiling
end
// ---- CEILING 2: Rp(max) = 0.2 VDD / Ileak ------------------------
ileak_ua = {26'd0, n_devices} * 32'd10; // 10 uA per pin, Table 10
if (ileak_ua != 32'd0)
r_leak = ({18'd0, vdd_mv} * 32'd200) / ileak_ua;
else
r_leak = {32{1'b1}};
// ---- the binding ceiling is whichever is lower -------------------
// Internal values are blocking so the later arithmetic sees them; only
// the module outputs are assigned non-blocking.
if (r_rise <= r_leak) begin
r_max = r_rise;
binding <= (r_min > r_rise) ? BIND_EMPTY_RISE : BIND_RISE;
end else begin
r_max = r_leak;
binding <= (r_min > r_leak) ? BIND_EMPTY_LEAK : BIND_LEAK;
end
rp_max_ohm <= (r_max > 32'h000F_FFFF) ? 20'hFFFFF : r_max[19:0];
feasible <= (r_min <= r_max) && (r_min != 32'd0);
// ---- Cb at which the window closes, for this floor ---------------
if (r_min != 32'd0)
cb_max_pf <= (K_RC * {16'd0, tr_max_ns(mode)}) / (32'd1000 * r_min);
else
cb_max_pf <= 16'd0;
// ---- Equation 3: fmax at the mode's own edge maxima --------------
sum_ns = {16'd0, tlow_min_ns(mode)} + {16'd0, thigh_min_ns(mode)}
+ {16'd0, tr_max_ns(mode)} + {16'd0, tf_max_ns(mode)};
fmax_khz <= 32'd1000000 / sum_ns;
// ---- figure of merit --------------------------------------------
if (r_min != 32'd0)
window_x100 <= (r_max * 32'd100) / r_min;
else
window_x100 <= 16'd0;
rp_min_ohm <= r_min[19:0];
rp_max_rise_ohm <= (r_rise > 32'h000F_FFFF) ? 20'hFFFFF : r_rise[19:0];
rp_max_leak_ohm <= (r_leak > 32'h000F_FFFF) ? 20'hFFFFF : r_leak[19:0];
done <= 1'b1;
end else begin
done <= 1'b0;
end
end
endmodule `timescale 1ns/1ps
// -----------------------------------------------------------------------------
// i2c_pullup_window_tb.sv
// Independent oracle for i2c_pullup_window.
//
// The strongest check available here is not a self-consistency check: UM10204
// 7.2.4 works one example through in prose, and T1 asserts the block reproduces
// it. The spec says Rp(min) = (5.5 - 0.4)/0.003 = 1.7 kohm and that this "limits
// the maximum bus capacitance to about 200 pF to meet the maximum tr requirement
// of 300 ns". If the block agrees on both numbers, the whole arithmetic chain --
// the 0.8473 constant, the scaling, the division order -- is validated against
// the specification rather than against itself.
//
// The bench computes every expectation with its own integer arithmetic, written
// out longhand, and never calls the DUT's functions.
// -----------------------------------------------------------------------------
// (Verilog-2001 -- structurally identical to the SystemVerilog above.)
module i2c_pullup_window_tb;
localparam [1:0] M_STD = 2'd0, M_FAST = 2'd1, M_FMP = 2'd2;
localparam [2:0] BIND_RISE = 3'd1, BIND_LEAK = 3'd2,
BIND_EMPTY_RISE = 3'd3, BIND_EMPTY_LEAK = 3'd4;
reg clk = 1'b0;
reg rst_n = 1'b0;
reg calc = 1'b0;
reg [1:0] mode = M_FAST;
reg [13:0] vdd_mv = 14'd3300;
reg [10:0] cb_pf = 11'd400;
reg [5:0] n_devices = 6'd4;
wire done, feasible;
wire [19:0] rp_min_ohm, rp_max_rise_ohm, rp_max_leak_ohm, rp_max_ohm;
wire [2:0] binding;
wire [15:0] fmax_khz, cb_max_pf, window_x100;
integer errors = 0;
i2c_pullup_window #(.VOL_MV(400)) dut (
.clk(clk), .rst_n(rst_n), .calc(calc), .mode(mode),
.vdd_mv(vdd_mv), .cb_pf(cb_pf), .n_devices(n_devices),
.done(done), .rp_min_ohm(rp_min_ohm),
.rp_max_rise_ohm(rp_max_rise_ohm), .rp_max_leak_ohm(rp_max_leak_ohm),
.rp_max_ohm(rp_max_ohm), .feasible(feasible), .binding(binding),
.fmax_khz(fmax_khz), .cb_max_pf(cb_max_pf), .window_x100(window_x100));
always #10 clk = ~clk;
task ck (input [200*8:1] what, input integer got, input integer exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0d expected %0d", what, got, exp);
errors = errors + 1;
end
end
endtask
task ck_bit (input [200*8:1] what, input got, input exp);
begin
if (got !== exp) begin
$display(" FAIL %0s: got %0b expected %0b", what, got, exp);
errors = errors + 1;
end
end
endtask
task do_solve (input integer m, input integer vdd, input integer cb, input integer nd);
begin
@(negedge clk);
mode = m[1:0];
vdd_mv = vdd[13:0];
cb_pf = cb[10:0];
n_devices = nd[5:0];
calc = 1'b1;
@(posedge clk); @(negedge clk);
calc = 1'b0;
@(posedge clk); @(negedge clk);
end
endtask
// The bench's own arithmetic, spelled out.
function integer exp_rp_min (input integer vdd, input integer iol_ua);
begin exp_rp_min = ((vdd - 400) * 1000) / iol_ua; end
endfunction
function integer exp_rp_rise (input integer tr, input integer cb);
begin exp_rp_rise = (1180220 * tr) / (1000 * cb); end
endfunction
function integer exp_rp_leak (input integer vdd, input integer nd);
begin exp_rp_leak = (vdd * 200) / (nd * 10); end
endfunction
initial begin
$display("=== i2c_pullup_window: the resistor has a floor and two ceilings ===");
@(negedge clk); rst_n = 1'b0;
repeat (3) @(posedge clk);
@(negedge clk); rst_n = 1'b1;
@(posedge clk);
// ----------------------------------------------------------------
// T1. UM10204 7.2.4, WORKED THROUGH BY THE SPECIFICATION ITSELF.
// VDD = 5 V +10 % = 5.5 V, VOL(max) = 0.4 V at 3 mA.
// The spec states Rp(min) = 1.7 kohm and that this caps the
// capacitance at "about 200 pF" for a 300 ns rise time.
// n_devices is set to 1 so the leakage ceiling cannot interfere.
// ----------------------------------------------------------------
do_solve(M_FAST, 5500, 400, 1);
$display("T1 UM10204 7.2.4 worked example");
ck_bit("T1 done", done, 1'b0); // done is a single-cycle pulse
ck("T1 Rp(min) is the spec's 1700 ohm", rp_min_ohm, 1700);
ck("T1 Cb(max) is the spec's 'about 200 pF'", cb_max_pf, 208);
// ----------------------------------------------------------------
// T2. The consequence: at 5.5 V with a 3 mA stage, a Fast-mode bus of
// 400 pF has NO legal resistor. The floor is 1700 ohm and the rise
// ceiling is 885 ohm. This is why UM10204 5.1 stops allowing a plain
// resistor at 200 pF and calls for a current source above it.
// ----------------------------------------------------------------
do_solve(M_FAST, 5500, 400, 1);
$display("T2 Fast-mode, 5.5 V, 400 pF: the window is empty");
ck("T2 Rp(min)", rp_min_ohm, exp_rp_min(5500, 3000));
ck("T2 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 400));
ck_bit("T2 infeasible", feasible, 1'b0);
ck("T2 empty, rise-limited", binding, BIND_EMPTY_RISE);
if (window_x100 >= 100) begin
$display(" FAIL T2 window_x100: got %0d, expected < 100 for an empty window",
window_x100);
errors = errors + 1;
end
// ----------------------------------------------------------------
// T3. The same bus at 200 pF fits, just. This is the boundary the spec
// draws, confirmed from the arithmetic rather than quoted.
// ----------------------------------------------------------------
do_solve(M_FAST, 5500, 200, 1);
$display("T3 Fast-mode, 5.5 V, 200 pF: feasible but tight");
ck("T3 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 200));
ck_bit("T3 feasible", feasible, 1'b1);
ck("T3 rise-limited", binding, BIND_RISE);
if (window_x100 < 100 || window_x100 > 120) begin
$display(" FAIL T3 window_x100: got %0d, expected a narrow 100..120", window_x100);
errors = errors + 1;
end
// ----------------------------------------------------------------
// T4. Standard-mode on the same 400 pF bus is comfortable, because its
// rise-time allowance is 1000 ns rather than 300 ns. Identical
// hardware, identical capacitance; only the mode changed.
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 400, 1);
$display("T4 Standard-mode, 3.3 V, 400 pF: comfortable");
ck("T4 Rp(min)", rp_min_ohm, exp_rp_min(3300, 3000));
ck("T4 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(1000, 400));
ck_bit("T4 feasible", feasible, 1'b1);
ck("T4 fmax is 100 kHz", fmax_khz, 100);
// Standard-mode is the ONLY mode where tr(max) and tf(max) differ
// (1000 ns vs 300 ns), so it is the only place a Cb(max) computed from the
// wrong edge is observable at all.
ck("T4 Cb(max) is sized from the RISE time",
cb_max_pf, (1180220 * 1000) / (1000 * exp_rp_min(3300, 3000)));
if (window_x100 < 300) begin
$display(" FAIL T4 window_x100: got %0d, expected a wide window (>= 300)",
window_x100);
errors = errors + 1;
end
// ----------------------------------------------------------------
// T5. Fast-mode on that same 400 pF bus at 3.3 V is STILL empty: the
// floor is 966 ohm and the ceiling 885 ohm. The mode, not the
// supply, is what makes it impossible.
// ----------------------------------------------------------------
do_solve(M_FAST, 3300, 400, 1);
$display("T5 Fast-mode, 3.3 V, 400 pF: still empty");
ck("T5 Rp(min)", rp_min_ohm, exp_rp_min(3300, 3000));
ck("T5 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 400));
ck_bit("T5 infeasible", feasible, 1'b0);
ck("T5 fmax is 400 kHz", fmax_khz, 400);
// ----------------------------------------------------------------
// T6. Fast-mode Plus at a LARGER capacitance, 550 pF, is feasible --
// and the only thing that changed is the sink current, 20 mA rather
// than 3 mA. Drive current buys capacitance; timing does not.
// ----------------------------------------------------------------
do_solve(M_FMP, 3300, 550, 1);
$display("T6 Fast-mode Plus, 3.3 V, 550 pF: feasible on 20 mA");
ck("T6 Rp(min) on 20 mA", rp_min_ohm, exp_rp_min(3300, 20000));
ck("T6 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(120, 550));
ck_bit("T6 feasible", feasible, 1'b1);
ck("T6 fmax is 1000 kHz", fmax_khz, 1000);
// The proof that it is the current and not the timing: Fm+ has the
// TIGHTEST rise allowance of the three modes and the LARGEST capacitance.
if (rp_min_ohm >= exp_rp_min(3300, 3000)) begin
$display(" FAIL T6 the 20 mA floor should be far below the 3 mA floor");
errors = errors + 1;
end
// ----------------------------------------------------------------
// T7. THE LEAKAGE CEILING TAKES OVER. Standard-mode, 3.3 V, one device,
// and only 10 pF of load. The rise ceiling is then 118 kohm, far
// above the 66 kohm the HIGH noise margin allows, so the constraint
// that binds has nothing to do with speed at all.
// (At 20 pF the rise ceiling is 59 kohm and still binds -- the
// crossover is close, and it is worth knowing where it sits.)
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 10, 1);
$display("T7 a lightly loaded bus is leakage-limited, not rise-limited");
ck("T7 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(3300, 1));
ck("T7 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(1000, 10));
ck("T7 leakage binds", binding, BIND_LEAK);
ck("T7 the binding ceiling is the leakage one", rp_max_ohm, exp_rp_leak(3300, 1));
ck_bit("T7 feasible", feasible, 1'b1);
// And the crossover claim, checked rather than asserted:
do_solve(M_STD, 3300, 20, 1);
ck("T7b at 20 pF the rise ceiling binds again", binding, BIND_RISE);
// ----------------------------------------------------------------
// T8. Adding devices lowers the leakage ceiling proportionally. Twenty
// pins at 10 uA each is 200 uA, and at 3.3 V that caps Rp at
// 3.3 kohm however little capacitance there is.
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 10, 20);
$display("T8 twenty pins drag the leakage ceiling down to a few kohm");
ck("T8 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(3300, 20));
ck("T8 leakage still binds", binding, BIND_LEAK);
ck("T8 ceiling is 3300 ohm", rp_max_ohm, 3300);
ck_bit("T8 still feasible", feasible, 1'b1);
// ----------------------------------------------------------------
// T9. Both ceilings squeezed at once: many devices AND a lot of
// capacitance, in Fast-mode. The rise ceiling is 885 ohm, the
// leakage ceiling 1650 ohm, so rise binds and the floor of 966 ohm
// is above it. Infeasible, and correctly attributed to the rise.
// ----------------------------------------------------------------
do_solve(M_FAST, 3300, 400, 40);
$display("T9 both ceilings squeezed: the rise one still binds");
ck("T9 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 400));
ck("T9 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(3300, 40));
ck("T9 empty, attributed to the rise time", binding, BIND_EMPTY_RISE);
ck_bit("T9 infeasible", feasible, 1'b0);
// ----------------------------------------------------------------
// T10. INFEASIBLE THROUGH LEAKAGE rather than rise time -- the other way
// a window can close, and the one nobody expects.
//
// Solving 200.VDD/Ileak < 1000.(VDD-400)/IOL for IOL = 3 mA and 63
// pins gives VDD > 8.4 V. So on a 10 V bus with 63 pins the HIGH
// noise margin allows at most 3174 ohm while the output stage needs
// at least 3200 ohm. The bus is barely loaded -- 10 pF, a 118 kohm
// rise ceiling -- and still has no legal resistor. Slowing it down
// would not help; only fewer devices or a lower supply would.
// ----------------------------------------------------------------
do_solve(M_STD, 10000, 10, 63);
$display("T10 infeasible through leakage, not rise time");
ck("T10 Rp(min)", rp_min_ohm, exp_rp_min(10000, 3000));
ck("T10 Rp(max,leak)", rp_max_leak_ohm, exp_rp_leak(10000, 63));
ck("T10 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(1000, 10));
ck("T10 empty, attributed to leakage", binding, BIND_EMPTY_LEAK);
ck_bit("T10 infeasible", feasible, 1'b0);
// The rise ceiling is not remotely the problem here:
if (rp_max_rise_ohm <= rp_min_ohm) begin
$display(" FAIL T10 the rise ceiling should be far above the floor");
errors = errors + 1;
end
// ----------------------------------------------------------------
// T11. Equation 3 across all three modes. Because Table 10 is internally
// exact, feeding the mode's own minima and maxima back through
// Equation 3 must return the mode's own headline frequency.
// ----------------------------------------------------------------
do_solve(M_STD, 3300, 100, 2);
ck("T11 Standard fmax", fmax_khz, 100);
do_solve(M_FAST, 3300, 100, 2);
ck("T11 Fast fmax", fmax_khz, 400);
do_solve(M_FMP, 3300, 100, 2);
ck("T11 Fast-mode Plus fmax", fmax_khz, 1000);
$display("T11 Equation 3 returns each mode's headline rate exactly");
// ----------------------------------------------------------------
// T12. Degenerate inputs must not produce a confident wrong answer.
// A supply at VOL cannot make a HIGH at all.
// ----------------------------------------------------------------
do_solve(M_FAST, 400, 400, 4);
$display("T12 a supply at VOL(max) yields no usable window");
ck("T12 Rp(min) is zero", rp_min_ohm, 0);
ck_bit("T12 not feasible", feasible, 1'b0);
// ----------------------------------------------------------------
// T14. THE EXACT BOUNDARY. At 1.5 V with a 3 mA stage the floor is 366 ohm,
// and a 966 pF Fast-mode load puts the rise ceiling at 366 ohm too.
// The window contains exactly ONE legal resistor value. A solver that
// tests the ceiling with a strict inequality rejects this bus, and no
// other case in this suite can tell the two spellings apart.
// ----------------------------------------------------------------
do_solve(M_FAST, 1500, 966, 1);
$display("T14 a window holding exactly one value is still a window");
ck("T14 Rp(min)", rp_min_ohm, exp_rp_min(1500, 3000));
ck("T14 Rp(max,rise)", rp_max_rise_ohm, exp_rp_rise(300, 966));
ck("T14 floor equals ceiling", rp_min_ohm, rp_max_rise_ohm);
ck_bit("T14 feasible at the boundary", feasible, 1'b1);
ck("T14 window is exactly 100 %", window_x100, 100);
// ----------------------------------------------------------------
// T13. done is a single-cycle pulse that follows calc, not a level.
// ----------------------------------------------------------------
@(negedge clk);
mode = M_FAST; vdd_mv = 14'd3300; cb_pf = 11'd100; n_devices = 6'd2;
calc = 1'b1;
@(posedge clk); @(negedge clk);
ck_bit("T13 done asserted the cycle after calc", done, 1'b1);
calc = 1'b0;
@(posedge clk); @(negedge clk);
ck_bit("T13 done deasserted again", done, 1'b0);
$display("T13 done is a pulse, not a level");
if (errors == 0)
$display("=== i2c_pullup_window: ALL CHECKS PASSED ===");
else
$display("=== i2c_pullup_window: %0d CHECK(S) FAILED ===", errors);
$finish;
end
endmodule -- ---------------------------------------------------------------------------
-- i2c_pullup_window.vhd
-- Pull-up resistor feasibility solver for Standard-mode, Fast-mode and
-- Fast-mode Plus (UM10204 7.1, 7.2, 7.4). Behavioural twin of
-- i2c_pullup_window.sv / .v.
--
-- Choosing a speed mode is really choosing a pull-up resistor, and the resistor
-- has to satisfy one floor and two independent ceilings at the same time:
--
-- FLOOR Rp(min) = (VDD - VOL(max)) / IOL
-- The device must pull the line to a valid LOW while sinking no
-- more than its rated current.
--
-- CEILING Rp(max,rise) = tr(max) / (0.8473 x Cb) -- 7.1 Eq 1
-- The RC rise between the 30 % and 70 % thresholds must fit inside
-- tr(max). The constant is exact: charging through R into C reaches
-- 30 % at 0.3566749 RC and 70 % at 1.2039729 RC, and the difference
-- is 0.8473 RC.
--
-- CEILING Rp(max,leak) = 0.2 x VDD / Ileak(total) -- 7.4
-- Every connected pin leaks up to 10 uA through Rp, and the drop
-- must not eat the 0.2 VDD HIGH noise margin. This ceiling falls as
-- devices are added, so it bites on heavily populated buses.
--
-- The interesting output is not the resistor value, it is whether the window
-- EXISTS. It does not always. At 5.5 V with a 3 mA output stage the floor is
-- 1.7 kohm, and Equation 1 then caps the capacitance at 208 pF for a Fast-mode
-- rise time -- which is exactly why UM10204 5.1 permits a plain resistor only up
-- to 200 pF and calls for a current source or a switched resistor above it.
--
-- All arithmetic is integer. 1000/0.8473 = 1180.2195..., held as 1180220/1000.
-- ---------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_pullup_window is
generic (
VOL_MV : integer := 400 -- VOL(max), Table 10: 0.4 V
);
port (
clk : in std_logic;
rst_n : in std_logic;
calc : in std_logic; -- one pulse: evaluate the inputs
mode : in std_logic_vector(1 downto 0); -- 0 = Std, 1 = Fast, 2 = Fm+
vdd_mv : in unsigned(13 downto 0); -- supply, mV, worst-case corner
cb_pf : in unsigned(10 downto 0); -- estimated bus capacitance, pF
n_devices : in unsigned(5 downto 0); -- pins on the bus, for leakage
done : out std_logic;
rp_min_ohm : out unsigned(19 downto 0);
rp_max_rise_ohm : out unsigned(19 downto 0);
rp_max_leak_ohm : out unsigned(19 downto 0);
rp_max_ohm : out unsigned(19 downto 0); -- the lower of the two ceilings
feasible : out std_logic;
binding : out unsigned(2 downto 0);
fmax_khz : out unsigned(15 downto 0); -- Equation 3
cb_max_pf : out unsigned(15 downto 0); -- where the window closes
window_x100 : out unsigned(15 downto 0) -- rp_max/rp_min as a percentage
);
end entity i2c_pullup_window;
architecture rtl of i2c_pullup_window is
constant MODE_STD : std_logic_vector(1 downto 0) := "00";
constant MODE_FAST : std_logic_vector(1 downto 0) := "01";
constant MODE_FMP : std_logic_vector(1 downto 0) := "10";
constant BIND_RISE : integer := 1; -- feasible, rise time is the binding ceiling
constant BIND_LEAK : integer := 2; -- feasible, leakage is the binding ceiling
constant BIND_EMPTY_RISE : integer := 3; -- infeasible: floor above the rise ceiling
constant BIND_EMPTY_LEAK : integer := 4; -- infeasible: floor above the leak ceiling
-- 1000 / 0.8473, scaled by 1000.
constant K_RC : integer := 1180220;
-- A value that stands in for "no ceiling", clamped by the 20-bit outputs.
constant HUGE : integer := 1048575; -- 2**20 - 1
function tr_max_ns (m : std_logic_vector(1 downto 0)) return integer is
begin
if m = MODE_STD then return 1000;
elsif m = MODE_FAST then return 300;
else return 120;
end if;
end function;
function tf_max_ns (m : std_logic_vector(1 downto 0)) return integer is
begin
if m = MODE_STD then return 300;
elsif m = MODE_FAST then return 300;
else return 120;
end if;
end function;
function tlow_min_ns (m : std_logic_vector(1 downto 0)) return integer is
begin
if m = MODE_STD then return 4700;
elsif m = MODE_FAST then return 1300;
else return 500;
end if;
end function;
function thigh_min_ns (m : std_logic_vector(1 downto 0)) return integer is
begin
if m = MODE_STD then return 4000;
elsif m = MODE_FAST then return 600;
else return 260;
end if;
end function;
-- Rated sink current: 3 mA for Standard and Fast, 20 mA for Fast-mode Plus.
-- This single number is what buys Fm+ its extra capacitance -- not its timing.
function iol_ua (m : std_logic_vector(1 downto 0)) return integer is
begin
if m = MODE_FMP then return 20000; else return 3000; end if;
end function;
-- Saturating conversions. These matter for mutation testing as much as for
-- correctness: without them a mutated expression can drive an out-of-range
-- value into to_unsigned and CRASH the simulation, which is indistinguishable
-- from a tool problem. Clamping makes a bad mutant produce a wrong NUMBER,
-- which the bench then reports as a failed check.
function clamp16 (v : integer) return unsigned is
begin
if v > 65535 then
return to_unsigned(65535, 16);
elsif v < 0 then
return to_unsigned(0, 16);
else
return to_unsigned(v, 16);
end if;
end function;
function clamp20 (v : integer) return unsigned is
begin
if v > HUGE then
return to_unsigned(HUGE, 20);
elsif v < 0 then
return to_unsigned(0, 20);
else
return to_unsigned(v, 20);
end if;
end function;
begin
process (clk, rst_n)
variable r_min, r_rise, r_leak, r_max : integer;
variable ileak, sum_ns, vdd_i, cb_i, nd_i : integer;
variable bind_v : integer;
begin
if rst_n = '0' then
done <= '0';
rp_min_ohm <= (others => '0');
rp_max_rise_ohm <= (others => '0');
rp_max_leak_ohm <= (others => '0');
rp_max_ohm <= (others => '0');
feasible <= '0';
binding <= to_unsigned(BIND_RISE, 3);
fmax_khz <= (others => '0');
cb_max_pf <= (others => '0');
window_x100 <= (others => '0');
elsif rising_edge(clk) then
if calc = '1' then
vdd_i := to_integer(vdd_mv);
cb_i := to_integer(cb_pf);
nd_i := to_integer(n_devices);
-- FLOOR: Rp(min) = (VDD - VOL) / IOL
if vdd_i > VOL_MV then
r_min := ((vdd_i - VOL_MV) * 1000) / iol_ua(mode); -- mV -> uV
else
r_min := 0; -- a supply at or below VOL cannot make a HIGH at all
end if;
-- CEILING 1: Rp(max) = tr / (0.8473 x Cb)
if cb_i /= 0 then
r_rise := (K_RC * tr_max_ns(mode)) / (1000 * cb_i);
else
r_rise := HUGE; -- no capacitance, no rise-time ceiling
end if;
-- CEILING 2: Rp(max) = 0.2 VDD / Ileak
ileak := nd_i * 10; -- 10 uA per pin, Table 10
if ileak /= 0 then
r_leak := (vdd_i * 200) / ileak;
else
r_leak := HUGE;
end if;
-- The binding ceiling is whichever is lower.
if r_rise <= r_leak then
r_max := r_rise;
if r_min > r_rise then
bind_v := BIND_EMPTY_RISE;
else
bind_v := BIND_RISE;
end if;
else
r_max := r_leak;
if r_min > r_leak then
bind_v := BIND_EMPTY_LEAK;
else
bind_v := BIND_LEAK;
end if;
end if;
binding <= to_unsigned(bind_v, 3);
rp_max_ohm <= clamp20(r_max);
if r_min <= r_max and r_min /= 0 then
feasible <= '1';
else
feasible <= '0';
end if;
-- Cb at which the window closes, for this floor.
if r_min /= 0 then
cb_max_pf <= clamp16((K_RC * tr_max_ns(mode)) / (1000 * r_min));
else
cb_max_pf <= (others => '0');
end if;
-- Equation 3: fmax at the mode's own edge maxima.
sum_ns := tlow_min_ns(mode) + thigh_min_ns(mode)
+ tr_max_ns(mode) + tf_max_ns(mode);
fmax_khz <= clamp16(1000000 / sum_ns);
-- Figure of merit.
if r_min /= 0 then
window_x100 <= clamp16((r_max * 100) / r_min);
else
window_x100 <= (others => '0');
end if;
rp_min_ohm <= clamp20(r_min);
rp_max_rise_ohm <= clamp20(r_rise);
rp_max_leak_ohm <= clamp20(r_leak);
done <= '1';
else
done <= '0';
end if;
end if;
end process;
end architecture rtl; -- ---------------------------------------------------------------------------
-- i2c_pullup_window_tb.vhd
-- Independent oracle for i2c_pullup_window. Behavioural twin of the
-- SystemVerilog and Verilog benches.
--
-- The strongest check available here is not a self-consistency check: UM10204
-- 7.2.4 works one example through in prose, and T1 asserts the block reproduces
-- it. The spec says Rp(min) = (5.5 - 0.4)/0.003 = 1.7 kohm and that this "limits
-- the maximum bus capacitance to about 200 pF to meet the maximum tr requirement
-- of 300 ns". If the block agrees on both numbers, the whole arithmetic chain --
-- the 0.8473 constant, the scaling, the division order -- is validated against
-- the specification rather than against itself.
--
-- The bench computes every expectation with its own integer arithmetic and never
-- calls the DUT's functions.
-- ---------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_pullup_window_tb is
end entity i2c_pullup_window_tb;
architecture sim of i2c_pullup_window_tb is
constant TCLK : time := 20 ns;
constant M_STD : std_logic_vector(1 downto 0) := "00";
constant M_FAST : std_logic_vector(1 downto 0) := "01";
constant M_FMP : std_logic_vector(1 downto 0) := "10";
constant BIND_RISE : integer := 1;
constant BIND_LEAK : integer := 2;
constant BIND_EMPTY_RISE : integer := 3;
constant BIND_EMPTY_LEAK : integer := 4;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal calc : std_logic := '0';
signal mode : std_logic_vector(1 downto 0) := M_FAST;
signal vdd_mv : unsigned(13 downto 0) := to_unsigned(3300, 14);
signal cb_pf : unsigned(10 downto 0) := to_unsigned(400, 11);
signal n_devices : unsigned(5 downto 0) := to_unsigned(4, 6);
signal done, feasible : std_logic;
signal rp_min_ohm, rp_max_rise_ohm, rp_max_leak_ohm, rp_max_ohm : unsigned(19 downto 0);
signal binding : unsigned(2 downto 0);
signal fmax_khz, cb_max_pf, window_x100 : unsigned(15 downto 0);
signal halt : boolean := false;
begin
dut : entity work.i2c_pullup_window
generic map (VOL_MV => 400)
port map (
clk => clk, rst_n => rst_n, calc => calc, mode => mode,
vdd_mv => vdd_mv, cb_pf => cb_pf, n_devices => n_devices,
done => done, rp_min_ohm => rp_min_ohm,
rp_max_rise_ohm => rp_max_rise_ohm, rp_max_leak_ohm => rp_max_leak_ohm,
rp_max_ohm => rp_max_ohm, feasible => feasible, binding => binding,
fmax_khz => fmax_khz, cb_max_pf => cb_max_pf, window_x100 => window_x100);
clkgen : process
begin
while not halt loop
clk <= '0'; wait for TCLK/2;
clk <= '1'; wait for TCLK/2;
end loop;
wait;
end process;
stim : process
variable err : integer := 0;
procedure ck (what : string; got : integer; exp : integer) is
begin
if got /= exp then
report " FAIL " & what & ": got " & integer'image(got)
& " expected " & integer'image(exp) severity note;
err := err + 1;
end if;
end procedure;
procedure ck_bit (what : string; got : std_logic; exp : std_logic) is
begin
if got /= exp then
report " FAIL " & what & ": got " & std_logic'image(got)
& " expected " & std_logic'image(exp) severity note;
err := err + 1;
end if;
end procedure;
procedure do_solve (m : std_logic_vector(1 downto 0);
vdd : integer; cb : integer; nd : integer) is
begin
wait until falling_edge(clk);
mode <= m;
vdd_mv <= to_unsigned(vdd, 14);
cb_pf <= to_unsigned(cb, 11);
n_devices <= to_unsigned(nd, 6);
calc <= '1';
wait until rising_edge(clk);
wait until falling_edge(clk);
calc <= '0';
wait until rising_edge(clk);
wait until falling_edge(clk);
end procedure;
-- The bench's own arithmetic, spelled out.
function exp_rp_min (vdd : integer; iol : integer) return integer is
begin
return ((vdd - 400) * 1000) / iol;
end function;
function exp_rp_rise (tr : integer; cb : integer) return integer is
begin
return (1180220 * tr) / (1000 * cb);
end function;
function exp_rp_leak (vdd : integer; nd : integer) return integer is
begin
return (vdd * 200) / (nd * 10);
end function;
begin
report "=== i2c_pullup_window: the resistor has a floor and two ceilings ==="
severity note;
wait until falling_edge(clk);
rst_n <= '0';
for k in 0 to 2 loop wait until rising_edge(clk); end loop;
wait until falling_edge(clk);
rst_n <= '1';
wait until rising_edge(clk);
-- T1. UM10204 7.2.4, WORKED THROUGH BY THE SPECIFICATION ITSELF.
-- VDD = 5 V +10 % = 5.5 V, VOL(max) = 0.4 V at 3 mA. The spec states
-- Rp(min) = 1.7 kohm and that this caps the capacitance at "about
-- 200 pF" for a 300 ns rise time. n_devices = 1 so the leakage ceiling
-- cannot interfere.
do_solve(M_FAST, 5500, 400, 1);
report "T1 UM10204 7.2.4 worked example" severity note;
ck_bit("T1 done", done, '0'); -- done is a single-cycle pulse
ck("T1 Rp(min) is the spec's 1700 ohm", to_integer(rp_min_ohm), 1700);
ck("T1 Cb(max) is the spec's 'about 200 pF'", to_integer(cb_max_pf), 208);
-- T2. The consequence: at 5.5 V with a 3 mA stage, a Fast-mode bus of
-- 400 pF has NO legal resistor. The floor is 1700 ohm and the rise
-- ceiling is 885 ohm.
do_solve(M_FAST, 5500, 400, 1);
report "T2 Fast-mode, 5.5 V, 400 pF: the window is empty" severity note;
ck("T2 Rp(min)", to_integer(rp_min_ohm), exp_rp_min(5500, 3000));
ck("T2 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(300, 400));
ck_bit("T2 infeasible", feasible, '0');
ck("T2 empty, rise-limited", to_integer(binding), BIND_EMPTY_RISE);
if to_integer(window_x100) >= 100 then
report " FAIL T2 window_x100: expected < 100 for an empty window"
severity note;
err := err + 1;
end if;
-- T3. The same bus at 200 pF fits, just. This is the boundary the spec
-- draws, confirmed from the arithmetic rather than quoted.
do_solve(M_FAST, 5500, 200, 1);
report "T3 Fast-mode, 5.5 V, 200 pF: feasible but tight" severity note;
ck("T3 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(300, 200));
ck_bit("T3 feasible", feasible, '1');
ck("T3 rise-limited", to_integer(binding), BIND_RISE);
if to_integer(window_x100) < 100 or to_integer(window_x100) > 120 then
report " FAIL T3 window_x100: expected a narrow 100..120" severity note;
err := err + 1;
end if;
-- T4. Standard-mode on the same 400 pF bus is comfortable, because its
-- rise-time allowance is 1000 ns rather than 300 ns.
do_solve(M_STD, 3300, 400, 1);
report "T4 Standard-mode, 3.3 V, 400 pF: comfortable" severity note;
ck("T4 Rp(min)", to_integer(rp_min_ohm), exp_rp_min(3300, 3000));
ck("T4 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(1000, 400));
ck_bit("T4 feasible", feasible, '1');
ck("T4 fmax is 100 kHz", to_integer(fmax_khz), 100);
-- Standard-mode is the ONLY mode where tr(max) and tf(max) differ
-- (1000 ns vs 300 ns), so it is the only place a Cb(max) computed from the
-- wrong edge is observable at all.
ck("T4 Cb(max) is sized from the RISE time", to_integer(cb_max_pf),
(1180220 * 1000) / (1000 * exp_rp_min(3300, 3000)));
if to_integer(window_x100) < 300 then
report " FAIL T4 window_x100: expected a wide window" severity note;
err := err + 1;
end if;
-- T5. Fast-mode on that same 400 pF bus at 3.3 V is STILL empty: the floor
-- is 966 ohm and the ceiling 885 ohm. The mode, not the supply, is what
-- makes it impossible.
do_solve(M_FAST, 3300, 400, 1);
report "T5 Fast-mode, 3.3 V, 400 pF: still empty" severity note;
ck("T5 Rp(min)", to_integer(rp_min_ohm), exp_rp_min(3300, 3000));
ck("T5 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(300, 400));
ck_bit("T5 infeasible", feasible, '0');
ck("T5 fmax is 400 kHz", to_integer(fmax_khz), 400);
-- T6. Fast-mode Plus at a LARGER capacitance, 550 pF, is feasible -- and the
-- only thing that changed is the sink current, 20 mA rather than 3 mA.
do_solve(M_FMP, 3300, 550, 1);
report "T6 Fast-mode Plus, 3.3 V, 550 pF: feasible on 20 mA" severity note;
ck("T6 Rp(min) on 20 mA", to_integer(rp_min_ohm), exp_rp_min(3300, 20000));
ck("T6 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(120, 550));
ck_bit("T6 feasible", feasible, '1');
ck("T6 fmax is 1000 kHz", to_integer(fmax_khz), 1000);
-- The proof that it is the current and not the timing: Fm+ has the TIGHTEST
-- rise allowance of the three modes and the LARGEST capacitance.
if to_integer(rp_min_ohm) >= exp_rp_min(3300, 3000) then
report " FAIL T6 the 20 mA floor should be far below the 3 mA floor"
severity note;
err := err + 1;
end if;
-- T7. THE LEAKAGE CEILING TAKES OVER. Standard-mode, 3.3 V, one device, and
-- only 10 pF of load. The rise ceiling is then 118 kohm, far above the
-- 66 kohm the HIGH noise margin allows, so the constraint that binds has
-- nothing to do with speed at all.
do_solve(M_STD, 3300, 10, 1);
report "T7 a lightly loaded bus is leakage-limited, not rise-limited"
severity note;
ck("T7 Rp(max,leak)", to_integer(rp_max_leak_ohm), exp_rp_leak(3300, 1));
ck("T7 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(1000, 10));
ck("T7 leakage binds", to_integer(binding), BIND_LEAK);
ck("T7 the binding ceiling is the leakage one",
to_integer(rp_max_ohm), exp_rp_leak(3300, 1));
ck_bit("T7 feasible", feasible, '1');
-- And the crossover claim, checked rather than asserted:
do_solve(M_STD, 3300, 20, 1);
ck("T7b at 20 pF the rise ceiling binds again", to_integer(binding), BIND_RISE);
-- T8. Adding devices lowers the leakage ceiling proportionally. Twenty pins
-- at 10 uA each is 200 uA, and at 3.3 V that caps Rp at 3.3 kohm however
-- little capacitance there is.
do_solve(M_STD, 3300, 10, 20);
report "T8 twenty pins drag the leakage ceiling down to a few kohm"
severity note;
ck("T8 Rp(max,leak)", to_integer(rp_max_leak_ohm), exp_rp_leak(3300, 20));
ck("T8 leakage still binds", to_integer(binding), BIND_LEAK);
ck("T8 ceiling is 3300 ohm", to_integer(rp_max_ohm), 3300);
ck_bit("T8 still feasible", feasible, '1');
-- T9. Both ceilings squeezed at once: many devices AND a lot of capacitance,
-- in Fast-mode. The rise ceiling is 885 ohm, the leakage ceiling
-- 1650 ohm, so rise binds and the 966 ohm floor is above it.
do_solve(M_FAST, 3300, 400, 40);
report "T9 both ceilings squeezed: the rise one still binds" severity note;
ck("T9 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(300, 400));
ck("T9 Rp(max,leak)", to_integer(rp_max_leak_ohm), exp_rp_leak(3300, 40));
ck("T9 empty, attributed to the rise time", to_integer(binding), BIND_EMPTY_RISE);
ck_bit("T9 infeasible", feasible, '0');
-- T10. INFEASIBLE THROUGH LEAKAGE rather than rise time -- the other way a
-- window can close, and the one nobody expects.
--
-- Solving 200.VDD/Ileak < 1000.(VDD-400)/IOL for IOL = 3 mA and 63 pins
-- gives VDD > 8.4 V. So on a 10 V bus with 63 pins the HIGH noise margin
-- allows at most 3174 ohm while the output stage needs at least
-- 3200 ohm. The bus is barely loaded -- 10 pF, a 118 kohm rise ceiling --
-- and still has no legal resistor. Slowing it down would not help.
do_solve(M_STD, 10000, 10, 63);
report "T10 infeasible through leakage, not rise time" severity note;
ck("T10 Rp(min)", to_integer(rp_min_ohm), exp_rp_min(10000, 3000));
ck("T10 Rp(max,leak)", to_integer(rp_max_leak_ohm), exp_rp_leak(10000, 63));
ck("T10 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(1000, 10));
ck("T10 empty, attributed to leakage", to_integer(binding), BIND_EMPTY_LEAK);
ck_bit("T10 infeasible", feasible, '0');
if to_integer(rp_max_rise_ohm) <= to_integer(rp_min_ohm) then
report " FAIL T10 the rise ceiling should be far above the floor"
severity note;
err := err + 1;
end if;
-- T11. Equation 3 across all three modes. Because Table 10 is internally
-- exact, feeding the mode's own minima and maxima back through
-- Equation 3 must return the mode's own headline frequency.
do_solve(M_STD, 3300, 100, 2);
ck("T11 Standard fmax", to_integer(fmax_khz), 100);
do_solve(M_FAST, 3300, 100, 2);
ck("T11 Fast fmax", to_integer(fmax_khz), 400);
do_solve(M_FMP, 3300, 100, 2);
ck("T11 Fast-mode Plus fmax", to_integer(fmax_khz), 1000);
report "T11 Equation 3 returns each mode's headline rate exactly" severity note;
-- T12. Degenerate inputs must not produce a confident wrong answer.
do_solve(M_FAST, 400, 400, 4);
report "T12 a supply at VOL(max) yields no usable window" severity note;
ck("T12 Rp(min) is zero", to_integer(rp_min_ohm), 0);
ck_bit("T12 not feasible", feasible, '0');
-- T14. THE EXACT BOUNDARY. At 1.5 V with a 3 mA stage the floor is 366 ohm,
-- and a 966 pF Fast-mode load puts the rise ceiling at 366 ohm too. The
-- window contains exactly ONE legal resistor value. A solver that tests
-- the ceiling with a strict inequality rejects this bus, and no other
-- case in this suite can tell the two spellings apart.
do_solve(M_FAST, 1500, 966, 1);
report "T14 a window holding exactly one value is still a window" severity note;
ck("T14 Rp(min)", to_integer(rp_min_ohm), exp_rp_min(1500, 3000));
ck("T14 Rp(max,rise)", to_integer(rp_max_rise_ohm), exp_rp_rise(300, 966));
ck("T14 floor equals ceiling",
to_integer(rp_min_ohm), to_integer(rp_max_rise_ohm));
ck_bit("T14 feasible at the boundary", feasible, '1');
ck("T14 window is exactly 100 %", to_integer(window_x100), 100);
-- T13. done is a single-cycle pulse that follows calc, not a level.
wait until falling_edge(clk);
mode <= M_FAST;
vdd_mv <= to_unsigned(3300, 14);
cb_pf <= to_unsigned(100, 11);
n_devices <= to_unsigned(2, 6);
calc <= '1';
wait until rising_edge(clk);
wait until falling_edge(clk);
ck_bit("T13 done asserted the cycle after calc", done, '1');
calc <= '0';
wait until rising_edge(clk);
wait until falling_edge(clk);
ck_bit("T13 done deasserted again", done, '0');
report "T13 done is a pulse, not a level" severity note;
if err = 0 then
report "=== i2c_pullup_window: ALL CHECKS PASSED ===" severity note;
else
report "=== i2c_pullup_window: " & integer'image(err)
& " CHECK(S) FAILED ===" severity note;
end if;
halt <= true;
wait;
end process;
end architecture sim;9a. Six Decisions Worth Defending
The RC constant is held as 1180220/1000, and the comment says where it comes from. That is 1000/0.8473 to seven figures. Rounding it to 1180 would introduce a 0.02 % error that happens to be enough to turn the specification's 208 pF into 207, and test 1 would then fail against the document. Mutation V1 replaces it with 1 000 000 — dropping the 0.8473 entirely, which is the commonest error in hand calculations — and 18 checks fail.
Both ceilings are computed always, and the lower one is selected. It would be cheaper to compute the rise ceiling and treat leakage as a design rule checked elsewhere. But then the block would confidently report a feasible window on a 63-device bus where none exists, and tests 7, 8 and 10 are the cases that catch it. Mutation V5 selects the higher of the two and 16 checks fail.
The empty-window verdict is attributed. BIND_EMPTY_RISE and BIND_EMPTY_LEAK are separate codes because they call for opposite remedies. A single "infeasible" flag would be correct and useless. Mutation V9 swaps the two attributions and two checks catch it, which is the minimum a suite should have for a two-valued diagnosis.
Feasibility uses <=, not <. A window containing exactly one legal value is a window. This is a one-character decision and it survived the first mutation run, because no test exercised exact equality — which made it a coverage gap rather than an equivalence. Test 14 now hits it: at 1.5 V with a 966 pF Fast-mode load the floor and the ceiling are both 366 Ω, and the window is exactly 100 % wide.
Degenerate inputs are handled explicitly rather than by luck. A supply at or below VOL(max) cannot produce a high at all, so Rp(min) is defined as zero and the window is reported infeasible. Zero capacitance means no rise-time ceiling. Both are guarded because an unguarded division by zero in a solver is a crash where a diagnosis was wanted.
The VHDL version uses saturating conversions, and the reason is mutation testing. An out-of-range value passed to to_unsigned can abort the simulation, which is indistinguishable from a tool problem. Clamping makes a bad mutant produce a wrong number that the bench reports as a failed check. That is a lesson from earlier modules in this curriculum: range-constrained VHDL integers make mutants crash rather than fail, and a crash teaches you nothing about your testbench.
9b. Verified Execution
$ iverilog -g2012 -o d i2c_pullup_window.sv i2c_pullup_window_tb.sv && ./d
=== i2c_pullup_window: the resistor has a floor and two ceilings ===
T1 UM10204 7.2.4 worked example
T2 Fast-mode, 5.5 V, 400 pF: the window is empty
T3 Fast-mode, 5.5 V, 200 pF: feasible but tight
T4 Standard-mode, 3.3 V, 400 pF: comfortable
T5 Fast-mode, 3.3 V, 400 pF: still empty
T6 Fast-mode Plus, 3.3 V, 550 pF: feasible on 20 mA
T7 a lightly loaded bus is leakage-limited, not rise-limited
T8 twenty pins drag the leakage ceiling down to a few kohm
T9 both ceilings squeezed: the rise one still binds
T10 infeasible through leakage, not rise time
T11 Equation 3 returns each mode's headline rate exactly
T12 a supply at VOL(max) yields no usable window
T14 a window holding exactly one value is still a window
T13 done is a pulse, not a level
=== i2c_pullup_window: ALL CHECKS PASSED ===
i2c_pullup_window_tb.sv:317: $finish called at 1100000 (1ps)
$ iverilog -g2005 -o v i2c_pullup_window.v i2c_pullup_window_tb.v && ./v
=== i2c_pullup_window: the resistor has a floor and two ceilings ===
T1 UM10204 7.2.4 worked example
T2 Fast-mode, 5.5 V, 400 pF: the window is empty
T3 Fast-mode, 5.5 V, 200 pF: feasible but tight
T4 Standard-mode, 3.3 V, 400 pF: comfortable
T5 Fast-mode, 3.3 V, 400 pF: still empty
T6 Fast-mode Plus, 3.3 V, 550 pF: feasible on 20 mA
T7 a lightly loaded bus is leakage-limited, not rise-limited
T8 twenty pins drag the leakage ceiling down to a few kohm
T9 both ceilings squeezed: the rise one still binds
T10 infeasible through leakage, not rise time
T11 Equation 3 returns each mode's headline rate exactly
T12 a supply at VOL(max) yields no usable window
T14 a window holding exactly one value is still a window
T13 done is a pulse, not a level
=== i2c_pullup_window: ALL CHECKS PASSED ===
i2c_pullup_window_tb.v:318: $finish called at 1100000 (1ps)
$ nvc --std=2008 -a i2c_pullup_window.vhd i2c_pullup_window_tb.vhd
$ nvc --std=2008 -e i2c_pullup_window_tb && nvc --std=2008 -r i2c_pullup_window_tb --stop-time=500us
** Note: 0ms+0: === i2c_pullup_window: the resistor has a floor and two ceilings ===
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 140ns+1: T1 UM10204 7.2.4 worked example
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 200ns+1: T2 Fast-mode, 5.5 V, 400 pF: the window is empty
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 260ns+1: T3 Fast-mode, 5.5 V, 200 pF: feasible but tight
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 320ns+1: T4 Standard-mode, 3.3 V, 400 pF: comfortable
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 380ns+1: T5 Fast-mode, 3.3 V, 400 pF: still empty
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 440ns+1: T6 Fast-mode Plus, 3.3 V, 550 pF: feasible on 20 mA
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 500ns+1: T7 a lightly loaded bus is leakage-limited, not rise-limited
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 620ns+1: T8 twenty pins drag the leakage ceiling down to a few kohm
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 680ns+1: T9 both ceilings squeezed: the rise one still binds
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 740ns+1: T10 infeasible through leakage, not rise time
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 920ns+1: T11 Equation 3 returns each mode's headline rate exactly
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 980ns+1: T12 a supply at VOL(max) yields no usable window
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 1040ns+1: T14 a window holding exactly one value is still a window
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 1100ns+1: T13 done is a pulse, not a level
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74
** Note: 1100ns+1: === i2c_pullup_window: ALL CHECKS PASSED ===
Process :i2c_pullup_window_tb:stim at i2c_pullup_window_tb.vhd:74All three at 1100 ns — the shortest run in the module, because the solver is arithmetic with no protocol to sequence. Test 1's two assertions are the ones to read: Rp(min) = 1700 Ω and Cb(max) = 208 pF, both against §7.2.4's prose. Three independent implementations agreeing with a document written in 2014 is a stronger statement than three implementations agreeing with each other.
9c. What The Testbench Proves
| # | case | what it establishes |
|---|---|---|
| 1 | §7.2.4's example: Fast, 5.5 V, 400 pF, 1 device | Rp(min) = 1700 Ω and Cb(max) = 208 pF, per the specification |
| 2 | the same at 400 pF | empty, attributed to the rise time |
| 3 | the same at 200 pF | feasible but narrow — the boundary §5.1 draws |
| 4 | Standard, 3.3 V, 400 pF | comfortable; Cb(max) sized from the rise time; fmax 100 kHz |
| 5 | Fast, 3.3 V, 400 pF | still empty — the mode, not the supply |
| 6 | Fm+, 3.3 V, 550 pF | feasible, on 20 mA; floor far below the 3 mA floor |
| 7 | Standard, 3.3 V, 10 pF, 1 device | leakage binds, not the rise time |
| 7b | the same at 20 pF | the rise ceiling binds again — the crossover, checked |
| 8 | Standard, 3.3 V, 10 pF, 20 devices | ceiling dragged to 3.3 kΩ by leakage alone |
| 9 | Fast, 3.3 V, 400 pF, 40 devices | both ceilings squeezed; rise still binds |
| 10 | Standard, 10 V, 10 pF, 63 devices | empty through leakage, with a 118 kΩ rise ceiling |
| 11 | all three modes | Equation 3 returns each mode's headline rate exactly |
| 12 | a supply at VOL(max) | no usable window, no division by zero |
| 13 | back-to-back evaluations | done is a pulse, not a level |
| 14 | Fast, 1.5 V, 966 pF | floor equals ceiling: a window of exactly one value |
Test 1 is the only test in this module that checks against a human-written document. Everything else in Module 14 checks a design against a bench. This one checks both against §7.2.4's prose, and it is the reason the arithmetic can be trusted at all.
Test 7b exists because test 7's claim was initially wrong. The first version used 20 pF and asserted that leakage binds. It does not: at 20 pF the rise ceiling is 59 kΩ against a 66 kΩ leakage ceiling, so the rise still binds. The crossover is between 10 pF and 20 pF, and once that was established the honest thing was to test both sides of it rather than to move the number and quietly assert the conclusion.
Test 10 required solving an inequality to construct. Making leakage close the window needs 200·VDD/Ileak < 1000·(VDD−400)/IOL, which for 3 mA and 63 pins gives VDD > 8.4 V. So a 10 V bus with 63 pins has a 3174 Ω ceiling against a 3200 Ω floor — infeasible, on a bus carrying 10 pF with a 118 kΩ rise ceiling. Slowing that bus down would not help at all, which is exactly what the attributed verdict is for.
Test 11 closes the loop with Chapter 14.1. Feeding each mode's own phase minima and edge maxima through Equation 3 returns 100, 400 and 1000 kHz. The budget identity, evaluated in hardware.
Test 14 was added because a mutation survived. See §10.
10. Mutation Testing
Ten defects injected into the SystemVerilog solver.
| # | injected defect | outcome |
|---|---|---|
| V1 | the 0.8473 RC constant dropped | killed — 18 checks |
| V2 | VOL(max) ignored in the floor | killed — 16 checks |
| V3 | leakage 10 µA per pin → 1 µA | killed — 9 checks |
| V4 | HIGH noise margin 0.2 VDD → 0.1 VDD | killed — 8 checks |
| V5 | the higher of the two ceilings taken | killed — 16 checks |
| V6 | the boundary excluded from feasibility | killed — test 14 |
| V7 | Fm+ sinks 3 mA instead of 20 mA | killed — 3 checks |
| V8 | Equation 3 omits the edge times | killed — 6 checks |
| V9 | the two empty-window attributions swapped | killed — 2 checks |
| V10 | Cb(max) sized from the fall time | killed — test 4 |
Ten of ten, and two of them required new tests. That is the useful part of this run.
V6 survived the first pass. Changing r_min <= r_max to r_min < r_max differs only when the two are exactly equal, and no case in the original thirteen produced equality. That is not an equivalence by construction — the two spellings genuinely disagree about a real, meaningful bus, the one where exactly one resistor value is legal. It was a coverage gap, and closing it meant solving for an exact-equality point: at 1.5 V with a 3 mA stage the floor is 366 Ω, and a 966 pF Fast-mode load puts the rise ceiling at 366 Ω too. Test 14 is that bus.
V10 also survived the first pass, for a different reason. Sizing Cb(max) from tf instead of tr is a no-op in two of the three modes, because Fast-mode and Fm+ have identical rise and fall maxima — 300/300 and 120/120. Only Standard-mode distinguishes them, at 1000 ns against 300 ns, and no test had checked cb_max_pf in Standard-mode. Adding that one assertion to test 4 kills it: 1221 pF correct against 366 pF mutated.
Both survivors were the same shape: a defect that is invisible because the configuration used in testing happens to make two quantities equal. Neither was a subtle design question; both were the suite failing to vary something.
That shape is worth naming, because it is different from the equivalence-by-construction survivor in Chapter 14.2 §10. There, no test could ever have distinguished the mutant. Here, the distinguishing test simply had not been written — and the way to tell the two apart is to try to construct the distinguishing case. If you can, it was a gap; if you can prove you cannot, it was an equivalence.
V2's 16 checks are worth a note. Ignoring VOL(max) changes the floor by only 400 mV out of several volts — under 8 % at 5.5 V — and it fails 16 checks. Small absolute errors in the floor propagate into the feasibility verdict, the window ratio and Cb(max), and the verdict is a boolean that flips. A suite that only compared resistances to within a tolerance would have let this through.
11. Verification Connection — Constraining A Randomiser To The Physically Possible
The natural way to verify a multi-mode I²C controller is to randomise mode, supply, capacitance and device count. Done naively, most of the random space is physically impossible, and the regression spends its time on buses that cannot exist.
// Randomising mode, supply, capacitance and device count independently produces a
// majority of configurations with NO legal pull-up resistor. Those runs are not
// "hard cases" -- they are buses that cannot be built, and the failures they
// produce are the constraint solver's, not the DUT's.
//
// The fix is to make the feasibility condition a CONSTRAINT, and then to cover
// the interesting regions of what remains.
class i2c_board_cfg extends uvm_object;
`uvm_object_utils(i2c_board_cfg)
typedef enum { SM, FM, FMP } mode_e;
rand mode_e mode;
rand int unsigned vdd_mv; // the WORST-CASE corner, not nominal
rand int unsigned cb_pf;
rand int unsigned n_devices;
rand int unsigned rp_ohm;
// UM10204 Table 10 and Table 9, as functions rather than scattered literals.
function int unsigned tr_max_ns();
case (mode) SM: return 1000; FM: return 300; default: return 120; endcase
endfunction
function int unsigned iol_ua();
return (mode == FMP) ? 20000 : 3000;
endfunction
function int unsigned cb_limit_pf();
return (mode == FMP) ? 550 : 400;
endfunction
constraint c_supply { vdd_mv inside {[1800:5500]}; }
constraint c_devices { n_devices inside {[2:32]}; }
constraint c_cb { cb_pf inside {[10:cb_limit_pf()]}; }
// THE FLOOR. Equation 2, with VOL(max) = 400 mV.
constraint c_floor { rp_ohm * iol_ua() >= (vdd_mv - 400) * 1000; }
// THE RISE CEILING. Equation 1, rearranged to avoid a division in a
// constraint: rp <= 1180220*tr / (1000*cb) becomes rp*1000*cb <= 1180220*tr.
constraint c_ceil_rise { rp_ohm * 1000 * cb_pf <= 1180220 * tr_max_ns(); }
// THE LEAKAGE CEILING. Section 7.4: Ileak*Rp must not eat the 0.2 VDD margin.
constraint c_ceil_leak { rp_ohm * n_devices * 10 <= vdd_mv * 200; }
// Bias towards the cases that actually stress a controller: buses near the
// capacitance limit, where the rise time is the binding constraint.
constraint c_interesting {
cb_pf dist { [10:100] :/ 1, [101:300] :/ 2, [301:cb_limit_pf()] :/ 4 };
}
function string convert2string();
return $sformatf("%s VDD=%0d mV Cb=%0d pF n=%0d Rp=%0d ohm",
mode.name(), vdd_mv, cb_pf, n_devices, rp_ohm);
endfunction
endclassWhat to cover. binding from §9's solver, crossed with mode: it answers whether the regression ever exercised a leakage-limited bus, or only rise-limited ones. And cross cb_pf bucketed against mode, because a Fast-mode bus at 380 pF and one at 40 pF are electrically different animals and a suite that only ever ran the second has not tested the envelope.
12. FPGA and ASIC Implications
Measure Cb, do not estimate it. Every quantity in Equations 1 and 2 except capacitance is on a datasheet. Cb is "the total capacitance of wire, connections and pins" — 10 pF per pin from Table 10, plus roughly 1 pF per cm of PCB trace, plus connectors, plus any cable. It is the only input a designer can be badly wrong about, and it is the one the rise-time ceiling is inversely proportional to.
A lower supply widens the window. §5a's table: the floor falls faster with VDD than the ceiling does, so a 3.3 V Fast-mode bus tolerates 366 pF where a 5.5 V one tolerates 208. If the bus is marginal and the supply is a free choice, lowering it is the cheapest fix available.
FPGA pads are the wrong shape for I²C, in both directions. They usually sink far more than 3 mA, which is harmless — it only lowers the floor. But they have no slope control, which §5.1 requires for Fast-mode, and no open-drain mode on some families, which means emulating it with a tristate buffer driving only low. Check both.
Count the pins for the leakage ceiling, including the ones you forgot. Every device, every unused input on a mux, every test point with a scope probe attached. §7.4's 10 µA is per input/output connection, not per device, and a 20-connection bus at 3.3 V is capped at 3.3 kΩ regardless of how little capacitance it carries.
A buffer is not free, and §7.2.3 says so. It halves the capacitance per segment and adds a propagation delay plus an extra transition to every edge, which lowers fmax. On a bus that was capacitance-limited it is a clear win; on one that was already frequency-limited it may not be.
Follow §7.5's wiring pattern on anything long. "The bus lines are most susceptible to crosstalk and interference at the HIGH level because of the relatively high impedance of the pull-up devices. If the length of the bus lines on a PCB or ribbon cable exceeds 10 cm and includes the VDD and VSS lines, the wiring pattern should be: SDA / VDD / VSS / SCL." Interleaving the supplies between the signals is a free guard trace, and the reason it is needed is the same high impedance that makes the rise-time ceiling exist at all.
13. Debugging — The Bus That Failed Only When Fully Populated
A 3.3 V Standard-mode bus works with eight devices fitted. On the fully populated variant with twenty-four devices it fails intermittently: random NACKs from random addresses, worse when warm. Total capacitance measures 180 pF, well under the 400 pF limit, and the rise time measures 620 ns against Standard-mode's 1000 ns allowance. Both are comfortably legal.
The leakage ceiling, which nobody checked. Section 7.4 caps Rp at 0.2 x VDD / Ileak(total): at 3.3 V with 26 connections that is 660 mV / 260 uA = 2.54 kOhm. The fitted 4.7 kOhm is nearly twice that. The rise time and the sink current were both fine because neither has anything to do with this failure: the high level was simply being pulled too far below VDD by the summed input leakage, leaving less than the required noise margin, and any small disturbance then read as a low.
Fit 1.5 kOhm, which sits above the 966 Ohm floor and below both the 2.54 kOhm leakage ceiling and the 6.6 kOhm rise ceiling. The high level returns to 3.24 V, the noise margin is restored, and the temperature sensitivity disappears. Then add the leakage ceiling to the design rules, because this bus had twenty-six connections and the rule that failed scales with connection count.Three points from that investigation.
Both of the famous constraints were satisfied. The rise time was inside its limit and the sink current was inside its limit. The failing constraint was the third one, which does not appear in Table 10 and is easy never to have read.
The failure scales with device count, not with capacitance. Adding devices adds both, but at 180 pF the capacitance was irrelevant while the leakage had tripled. Any design rule that only tracks Cb misses this entirely.
The temperature dependence was the clue. Leakage roughly doubles every 10 °C; capacitance and sink current barely move. A bus that degrades when warm and has a high level measurably below VDD is a leakage problem, and measuring the static high level — not the edge — is the one-probe diagnostic.
14. Common Misconceptions
"The pull-up only has a maximum." It has a floor from the rated sink current and VOL(max), and two independent ceilings from the rise time and from leakage against the HIGH noise margin. §1.
"0.8473 is an empirical constant." It is 1.2039729 − 0.3566749, the exponential's 70 % and 30 % crossing times in units of RC. Exact arithmetic. §3.
"If the capacitance is under the limit, any reasonable resistor works." A Fast-mode bus at its own 400 pF maximum has no legal resistor with a 3 mA output stage. §6.
"Fast-mode Plus is Fast-mode at 1 MHz." It is a 20 mA output stage against 3 mA, which lowers the floor by a factor of 6.7 and is what allows 550 pF with a tighter rise time. §7.
"A higher supply gives more margin." The floor rises with VDD faster than the ceiling does, so a lower supply widens the resistor window. §5a.
"Leakage is negligible." Twenty-six connections at 10 µA is 260 µA, which through 4.7 kΩ is more than a volt. §13.
"The 200 pF figure in §5.1 is a rule of thumb." It is where the resistor window closes at the 5 V +10 % corner with a 3 mA stage, and §7.2.4 derives it. §5a.
"Adding a bus buffer solves the problem." It halves the capacitance per segment and adds delay plus an extra transition to every edge, lowering fmax. §7.2.3 states this outright. §8.
"Series protection resistors are free." They come out of the LOW noise margin, 0.1 VDD, and they slow the fall time. §4.
"Derating to a lower frequency keeps you in the same mode." Below a mode boundary you are in the slower mode and its phase minima apply. §7.2.2 warns that the Table 10 frequency rating "may become limiting". §8.
15. Reason It Through
A 5 V Fast-mode bus carries 300 pF. Is a resistor possible?
Floor at the +10 % corner: (5500 − 400)/3 mA = 1700 Ω. Rise ceiling: 300 ns / (0.8473 × 300 pF) = 1180 Ω. Empty — the floor is above the ceiling. Either drop to 200 pF, move to Fm+, or run the bus at Standard-mode speed where the ceiling rises to 3934 Ω.
The same bus at 3.3 V?
Floor: (3300 − 400)/3 mA = 966 Ω. Ceiling unchanged at 1180 Ω. Feasible, though only 1.22 × wide — pick something near 1.1 kΩ. The only change was the supply, and it turned an impossible bus into a workable one.
A 3.3 V bus has 40 connections and 50 pF. What limits the pull-up?
Rise ceiling in Fast-mode: 300 ns / (0.8473 × 50 pF) = 7081 Ω. Leakage ceiling: 0.2 × 3.3 V / 400 µA = 1650 Ω. Leakage binds, by a factor of more than four, on a bus carrying almost no capacitance. The floor is 966 Ω, so the window is 966 Ω to 1650 Ω — narrow, and nothing about it involves speed.
Why does a pure Fm+ bus tolerate roughly ten times the capacitance when slowed down?
Because dropping the rate relaxes the tr requirement, and the rise ceiling is proportional to tr, while the 20 mA floor does not move at all. Fm+ starts with a floor 6.7 × lower than Fast-mode's, so there is room for the ceiling to fall a long way before the window closes. §5.2's "factor of ten" is that headroom spent on capacitance instead of speed.
A design review proposes solving a capacitance problem with a bus buffer on a bus already running at Fast-mode's maximum 400 kHz. What is the objection?
The buffer adds a propagation delay and an extra transition time to every edge, which by Equation 3 lowers fmax. A bus at its frequency limit has no margin to give, so the buffer would fix the capacitance and break the timing. §7.2.3 says so explicitly, and the right remedy here is higher-drive parts or a lower rate.
Why is the specification's worked example the strongest possible test of a pull-up calculator?
Because it is a check against a document rather than against the implementation. Any single error in the constant, the scaling, the division order, or the choice of supply corner shifts either the 1.7 kΩ or the 208 pF. Agreement on both figures constrains the whole chain, which is why test 1 asserts both and why three independent implementations were run against it.
16. Understanding Check
17. Summary
Choosing a speed mode is choosing a pull-up resistor, and the resistor must satisfy one floor and two ceilings at once.
The floor is a current, not a time: (VDD − VOL)/IOL, with 3 mA for Standard and Fast-mode and 20 mA for Fm+. It is set by the weakest output stage on the bus.
The first ceiling is the RC rise, tr/(0.8473 × Cb), and 0.8473 is exact — the exponential's 70 % and 30 % crossings differ by that many time constants.
The second ceiling is leakage against the 0.2 VDD HIGH margin, and it falls as connections are added. On a heavily populated bus it binds regardless of speed, and it is the constraint nobody checks. §13 is that failure.
A series resistor comes out of the other margin — 0.1 VDD at the LOW level — and slows the fall time.
The specification's own example reproduces exactly: 1.7 kΩ at the 5.5 V corner, and 208 pF against its "about 200 pF". Three independent implementations agree with the 2014 document.
So §5.1's 200 pF threshold is derived, not chosen. It is where the resistor window closes.
The window is sometimes empty. A Fast-mode bus at Fast-mode's own 400 pF limit has no legal resistor with a 3 mA stage — and a Standard-mode bus on the identical board has a comfortable three-to-one range.
A lower supply widens the window, because the floor falls with VDD and the ceiling does not.
Fm+ buys capacitance with current. 20 mA lowers the floor by 6.7 ×, which is what pays for both the tighter rise time and the larger load. Run a pure Fm+ bus slowly and §5.2 promises roughly ten times the capacitance.
When the window is empty there are four remedies, each with a different cost: derate the frequency, use stronger drivers, segment with buffers at the price of delay, or switch a second pull-up in only during the edge.
And an unsatisfiable constraint is a result, not a bug. It means the board cannot exist.
18. What Comes Next
Module 14 is complete, and with it the question of magnitude.
Four chapters have turned the five speed categories into a small number of facts that actually matter. The protocol is unchanged across the three mainstream modes. Their four timing limits are one budget, exact in four of the five categories and rounded in the fifth. High-speed mode is the only one that changes the protocol, and it does so to buy an electrical regime that must be switched off at every acknowledge so that a slave can still ask for time. Ultra Fast-mode buys 5 Mbit/s by giving up the wired-AND, and with it five features that Table 6 marks not merely optional but impossible. And the whole of it reduces, at the board, to one resistor with a floor and two ceilings between which there is sometimes nothing.
The through-line is the same one Chapter 13.5 named. Every mechanism in this protocol comes from one electrical rule, and every limit on it comes from the physics of a resistor charging a capacitance. Speed is where those two meet: the protocol does not care how fast the bus runs, and the resistor does.
Module 15 turns to the parts of the specification that sit outside an ordinary transfer — the reserved-address services, the stricter profiles built on the same two wires, and what to do when the bus stops working altogether.
Its through-line is the one this module kept running into from the electrical side: what a master can and cannot know. A general call reaches every device and tells the master only that somebody was listening. A Device ID confirms an identity you already expected and cannot enumerate a bus. An SMBus limit can be violated by a perfectly conforming I²C part, because the protocols are identical and only the numbers differ — and one of those numbers turns this chapter's pull-up arithmetic into something sharper, because a 350 µA output stage forces a floor nearly nine times I²C's. And a stuck clock admits no protocol remedy at all, because the one instrument the master has is the thing being held.
Continue learning
Related tutorials
- Related topic
tr, tf and Cb — The I²C Electrical Envelope
The only parameters an RTL designer cannot control, and the only edge with a minimum as well as a maximum. Derives the pull-up sizing window and finds that Fast-mode at the maximum bus capacitance does not close with a resistor at all.
- Related topic
External Pull-Ups vs Internal Weak Pull-Ups
Every FPGA pin can enable an internal pull-up from a constraints file, and it is the wrong device for a bus. Works the arithmetic both ways: what resistance each speed mode allows at a given bus capacitance, what rise time a weak internal pull-up actually produces, and why a value that costs nothing is five times too large for Fast mode.
- Related topic
Why Push-Pull Fails on a Shared Bus
An ordinary digital output drives both levels actively, which assumes it owns the node. Attach two of them to one conductor, let them disagree, and the result is not a confused message but a low-impedance path from supply to ground through two output stages — an electrical fault no protocol discipline can prevent.
- Related topic
Open-Drain Outputs — Drive Low, Release High
The architectural move the whole bus rests on, and it is a subtraction: delete every device's ability to drive HIGH. What remains is one switch to ground, so the two states are pull LOW and release — and two devices can never impose opposite levels because only one level can be imposed at all.
