Timing: Table 11 and the Pull-Up
Read Table 11 as the bus timing, match each checker line to its row, explain why a hold time of zero still needs care, and size a pull-up from the rise time and the sink current.
- Lesson
- P8.7
- Time
- 40 min
- Runs on
- Your browser
- Check
- 4 questions
A board worked at 100 kHz and failed at 400 kHz with the same parts. Its 4.7 kΩ pull-ups and 150 pF of bus gave a rise of about 600 ns: legal in Standard-mode, double the Fast-mode limit. Everyone had copied the resistor from a 100 kHz reference design.
- 1 kΩ
- 2.2 kΩ
- 4.7 kΩ
Read the plot as a table
| Time | 1 kΩ | 2.2 kΩ | 4.7 kΩ |
|---|---|---|---|
| 500 ns | 2.355 V | 1.43 V | 770.6 mV |
| 1 µs | 3.029 V | 2.241 V | 1.361 V |
| 1.5 µs | 3.222 V | 2.7 V | 1.814 V |
| 2 µs | 3.278 V | 2.96 V | 2.161 V |
| 2.5 µs | 3.294 V | 3.107 V | 2.427 V |
| 3 µs | 3.298 V | 3.191 V | 2.631 V |
Simulated on the Analog engine (ngspice 42), three copies of the library's pull-up netlist at 3.3 V. Each rise time is the 0.7 VDD crossing minus the 0.3 VDD crossing, so read it between the two lines, not from zero.
The Table and the Checker
Every parameter has a definition in Fig. 38 and a value per mode. Eight are edge-to-edge times, and the bench checker measures them on the pins. Four are analog: rise, fall, spike width and bus capacitance. For those the page has SPICE and the pull-up arithmetic.
| Parameter | Sm | Fm | Fm+ | Bench checker rule | Measured on the bench |
|---|---|---|---|---|---|
| tHD;STA (after S or Sr) | 4.0 µs | 0.6 µs | 0.26 µs | thd_sta | 600 ns |
| tLOW | 4.7 µs | 1.3 µs | 0.5 µs | tlow | 1300 ns |
| tHIGH | 4.0 µs | 0.6 µs | 0.26 µs | thigh | 1210 ns |
| tSU;STA (before Sr) | 4.7 µs | 0.6 µs | 0.26 µs | tsu_sta | 610 ns |
| tHD;DAT (min) | 0 | 0 | 0 | thd_dat (minimum set by the bench) | controller 100 ns, target 0 ns |
| tSU;DAT | 250 ns | 100 ns | 50 ns | tsu_dat | 1200 ns |
| tSU;STO | 4.0 µs | 0.6 µs | 0.26 µs | tsu_sto | 610 ns |
| tBUF | 4.7 µs | 1.3 µs | 0.5 µs | tbuf | 2000 ns |
| tr (max) | 1000 ns | 300 ns | 120 ns | not measurable on digital pins | SPICE below |
| tf (max) | 300 ns | 300 ns | 120 ns | not measurable on digital pins | – |
| Cb (max) | 400 pF | 400 pF | 550 pF | not measurable on digital pins | – |
| tVD;DAT, tVD;ACK (max) | 3.45 µs | 0.9 µs | 0.45 µs | – | – |
UM10204 Table 11 minima (maxima where marked). The last column is the shortest of each time on the bench's Fast-mode controller, measured from its pins. All Table 11 values are referred to 0.3 VDD and 0.7 VDD.
A low lane of 1 pulls the net down. The pull-up raises it only when every device releases.
- Cycle 3
START: SDA falls while SCL is high.
- Cycle 9
SCL falls 600 ns later: that is tHD;STA, and Fast-mode needs 600.
- Cycle 10
The controller changes SDA 100 ns after the fall: its tHD;DAT.
- Cycle 22
SCL rises. The LOW lasted 1300 ns (tLOW) and SDA was ready 1200 ns before (tSU;DAT).
- Cycle 35
SCL falls again after 1210 ns of HIGH (tHIGH).
The other four parameters sit at the STOP and the next START: tSU;STO, tBUF, and before a repeated START tSU;STA. The table above lists all eight as measured.
Three Twins, Three Rows
| Switch | Checker lines | Table 11 row |
|---|---|---|
ctrl.fast_scl = 1 (100 ns phases) | tlow ×36, thigh ×36 | tLOW, tHIGH |
ctrl.short_tbuf = 1 | I2C CHECK tbuf at 51290 | tBUF: 800 ns from STOP to START |
ctrl.hold_zero_early = 1, minimum 50 ns | thd_dat ×18 | tHD;DAT (note 2) |
Measured over two write transactions. The bench controller is clean: ck.broken 0; under the Fm+ checker it is clean too (ck.broken 0), because slower is always legal.
A Hold Time of Zero
tHD;DAT may be 0 for I2C devices (Table 11). That is not a free pass. Note 2: the sender must make sure SCL has dropped below 0.3 VDD before SDA enters the 0.3 to 0.7 VDD band. A device provides its own internal hold, or measures its SCL fall and waits.
Measured. A receiver whose SCL input sees the fall a little late would sample the twin's new bit. With the checker's tHD;DAT minimum set to 50 ns: I2C CHECK thd_dat at 3301.
The bench target itself lets go of its ACK at the SCL fall: with the 50 ns minimum the checker flags it too (I2C CHECK thd_dat at 25890). The maximum, 0.9 µs in Fast-mode, binds only devices that do not stretch (note 3).
Sizing the Pull-Up
The rise from 0.3 to 0.7 VDD takes 0.8473 Rp × Cb (§7.1). At 400 pF, 1 kΩ rises in 339 ns and misses Fast-mode by 39 ns.
- 1 kΩ
- 2.2 kΩ
- 4.7 kΩ
Read the plot as a table
| Time | 1 kΩ | 2.2 kΩ | 4.7 kΩ |
|---|---|---|---|
| 100 ns | 2.086 V | 1.205 V | 632.5 mV |
| 200 ns | 2.853 V | 1.97 V | 1.144 V |
| 300 ns | 3.136 V | 2.456 V | 1.557 V |
| 400 ns | 3.24 V | 2.764 V | 1.891 V |
| 500 ns | 3.278 V | 2.96 V | 2.161 V |
| 600 ns | 3.292 V | 3.084 V | 2.379 V |
| 700 ns | 3.297 V | 3.163 V | 2.556 V |
| 800 ns | 3.299 V | 3.213 V | 2.698 V |
Simulated on the Analog engine (ngspice 42), three copies of the library's pull-up netlist at 3.3 V. Each rise time is the 0.7 VDD crossing minus the 0.3 VDD crossing, so read it between the two lines, not from zero.
| Rp | Cb | Predicted 0.8473 RC | Measured rise | Legal in |
|---|---|---|---|---|
| 1.00 kΩ | 400 pF | 339 ns | 338.9 ns | Sm only |
| 2.20 kΩ | 400 pF | 746 ns | 745.6 ns | Sm only |
| 4.70 kΩ | 400 pF | 1593 ns | 1592.9 ns | none |
| 1.00 kΩ | 100 pF | 85 ns | 84.7 ns | Fm+, Fm, Sm |
| 2.20 kΩ | 100 pF | 186 ns | 186.4 ns | Fm, Sm |
| 4.70 kΩ | 100 pF | 398 ns | 398.2 ns | Sm only |
Measured crossings from the two runs above beside the §7.1 prediction.
Rp(max) comes from the rise limit (Eq. 1, Fig. 41); Rp(min) from the 3 mA a device must sink at 0.4 V (Eq. 2, Fig. 42). At Fm and 400 pF the maximum is below the minimum: no resistor works. That is why §5.1 asks for a current source or switched pull-up above 200 pF in Fast-mode.
| Mode | Cb | Rp(min) = (VDD − 0.4 V) / IOL | Rp(max) = tr / (0.8473 Cb) | Window |
|---|---|---|---|---|
| Sm | 100 pF | 967 Ω (3 mA) | 11.80 kΩ (1000 ns) | 967 Ω to 11.80 kΩ |
| Sm | 400 pF | 967 Ω (3 mA) | 2.95 kΩ (1000 ns) | 967 Ω to 2.95 kΩ |
| Fm | 100 pF | 967 Ω (3 mA) | 3.54 kΩ (300 ns) | 967 Ω to 3.54 kΩ |
| Fm | 400 pF | 967 Ω (3 mA) | 885 Ω (300 ns) | empty: no resistor works |
| Fm+ | 100 pF | 145 Ω (20 mA) | 1.42 kΩ (120 ns) | 145 Ω to 1.42 kΩ |
| Fm+ | 550 pF | 145 Ω (20 mA) | 258 Ω (120 ns) | 145 Ω to 258 Ω |
VDD 3.3 V. Eq. 1 and Eq. 2 of §7.1, computed. The spec's own example (§7.2.4): 5.5 V and 3 mA give Rp(min) = 1.7 kΩ, which limits a 300 ns rise to about 200 pF.
Try It
Two write transactions back to back. With short_tbuf the controller waits only 100 ns after its STOP before starting the next START sequence; the probe measures the gap on the wire. The checker names the row it breaks.
Remove the switch and the gap becomes 2000 ns: the 1300 ns wait plus the controller's START setup.
I2C CHECK tbuf at 51290 1 800Read included file: i2c_ctrl.sv
`timescale 1ns/1ns
module i2c_ctrl(inout wire scl,sda,output reg scl_low=0,sda_low=0);
parameter time TLOW=1300,THIGH=1200,HOLD=100,TBUF=1300,TIMEOUT=20000,THD_STA=600,TSU_STA=600,TSU_STO=600;
bit ctrl_ignores_scl=0,ctrl_ignores_sda=0,eight_bit_addr=0,stop_between=0,data_change_scl_high=0,clear_eight_clocks=0,no_stop_after_clear=0,hold_zero_early=0,short_tbuf=0,fast_scl=0;
bit loser_keeps_clocking_past_byte=0,loser_no_target_mode=0,drops_on_identical=0,arb_on_scl_low=0;
bit lost=0,timed_out=0,active=0,target_mode=0;int clear_pulses=0;time stopped=0;
assign scl=scl_low?1'b0:1'bz;assign sda=sda_low?1'b0:1'bz;
task automatic wait_clock();time began;began=$time;
if(!ctrl_ignores_scl)while(scl!==1'b1&&!timed_out)begin #10;if($time-began>=TIMEOUT)begin timed_out=1;scl_low=0;sda_low=0;end end
endtask
task automatic high_time();time began;began=$time-1;while(scl===1'b1&&$time-began<(fast_scl?100:THIGH))#1;endtask
task automatic start();
if(!active)begin lost=0;timed_out=0;target_mode=0;if($time-stopped<(short_tbuf?100:TBUF))#((short_tbuf?100:TBUF)-($time-stopped));end
else begin scl_low=1;#(TLOW);end
sda_low=0;#(HOLD);scl_low=0;wait_clock();#(TSU_STA);
if(!timed_out)begin sda_low=1;#(THD_STA);scl_low=1;active=1;end
endtask
task automatic stop();
if(!lost&&!timed_out)begin scl_low=1;#(HOLD);sda_low=1;#(TLOW);scl_low=0;wait_clock();#(TSU_STO);sda_low=0;stopped=$time;#1;end
active=0;scl_low=0;sda_low=0;
endtask
task automatic bit_cycle(input bit value,input bit arbitrate,output bit sampled);
sampled=1;
if(!timed_out&&(!lost||loser_keeps_clocking_past_byte))begin
scl_low=1;#(hold_zero_early?1:HOLD);sda_low=!value;
if(arb_on_scl_low&&arbitrate&&value&&sda===0)lost=1;
#(fast_scl?100:TLOW-(hold_zero_early?1:HOLD));scl_low=0;wait_clock();#1;sampled=sda;
if(arbitrate&&!ctrl_ignores_sda&&((value&&!sampled)||drops_on_identical))begin
lost=1;sda_low=0;target_mode=!loser_no_target_mode;
end
if(data_change_scl_high)begin #100;sda_low=!sda_low;end
high_time();scl_low=1;
if(lost&&!loser_keeps_clocking_past_byte)begin scl_low=0;sda_low=0;end
end
endtask
task automatic write_byte(input reg [7:0] value,output bit ack);bit sampled;
for(int b=7;b>=0;b--)bit_cycle(value[b],1,sampled);
bit_cycle(1,0,sampled);ack=!sampled;sda_low=0;
endtask
task automatic read_byte(input bit ack,output reg [7:0] value);bit sampled;
for(int b=7;b>=0;b--)begin bit_cycle(1,0,sampled);value[b]=sampled;end
bit_cycle(!ack,0,sampled);sda_low=0;
endtask
task automatic address7(input reg [6:0] addr,input bit rd,output bit ack);
write_byte(eight_bit_addr?{1'b0,addr}:{addr,rd},ack);
endtask
task automatic combined_read(input reg [6:0] addr,input reg [7:0] pointer,output reg [7:0] value);
bit ack;start();address7(addr,0,ack);write_byte(pointer,ack);if(stop_between)stop();start();address7(addr,1,ack);read_byte(0,value);stop();
endtask
task automatic bus_clear(output bit cleared);
bit sampled;clear_pulses=0;lost=0;timed_out=0;active=1;sda_low=0;scl_low=0;wait_clock();
while(sda===0&&clear_pulses<(clear_eight_clocks?8:9)&&!timed_out)begin bit_cycle(1,0,sampled);#(HOLD);if(!timed_out)clear_pulses++;end
cleared=sda===1&&!timed_out;
if(!no_stop_after_clear)stop();else begin active=0;scl_low=0;sda_low=0;end
endtask
endmodule
Read included file: i2c_target.sv
`timescale 1ns/1ns
module i2c_target #(parameter [9:0] ADDRESS=10'h050,parameter TEN_BIT=0)
(inout wire scl,sda,output reg scl_low=0,sda_low=0,output wire high_drive);
bit ack_not_released=0,ack_wrong_clock=0,stretch_never_released=0,wrong_addr_ack=0,no_stop_detect=0,autoinc_wrap_bug=0,push_pull_target=0,ignores_sr=0;
// Legal receiver responses, not faults. rx_limit counts post-address bytes,
// including the pointer byte; -1 is unlimited. A busy address is declined once.
bit busy_nack=0;int rx_limit=-1,rx_count=0;
// Optional general-call software reset: UM10204 §§3.1.13–14. This model
// restores mem[i]=i and ptr=0; ADDRESS is fixed, not a programmable register.
// Fault switches and bench configuration are not device reset state.
time stretch_ns=0;bit general_call=1,software_reset=1,general_selected=0,reset_pending=0;int resets=0;
reg [7:0] mem[0:255];reg [7:0] ptr=0,shift=0,tx=0;
int state=0,count=0,bytes=0;bit active=0,selected=0,reading=0,need_ptr=1,ack_value=0,ack_active=0,low_addr_next=0,addressed10=0,reading_ack=0;
// state: 0 receive, 1 receiver ACK, 2 transmit, 3 controller ACK, 4 ignore.
assign high_drive=push_pull_target&&!sda_low;
assign sda=sda_low?1'b0:high_drive?1'b1:1'bz;assign scl=scl_low?1'b0:1'bz;
initial for(int i=0;i<256;i++)mem[i]=i[7:0];
function automatic [7:0] increment(input reg [7:0] p);increment=autoinc_wrap_bug?((p+1)&8'h0f):p+1'b1;endfunction
always @(negedge sda)if(scl===1)begin
if(!(ignores_sr&&active))begin active=1;selected=0;state=0;count=0;bytes=0;rx_count=0;shift=0;reading=0;ack_active=0;sda_low=0;low_addr_next=0;general_selected=0;reset_pending=0;end
end
always @(posedge sda)if(scl===1&&active&&!no_stop_detect)begin active=0;selected=0;state=0;count=0;sda_low=0;addressed10=0;end
always @(posedge scl)if(active)begin
if(state==0)begin
shift={shift[6:0],sda};count++;
if(count==8)begin
ack_value=0;
if(bytes==0)begin
reading=shift[0];
general_selected=general_call&&shift==0;
if(general_selected)begin selected=1;low_addr_next=0;end
else if(TEN_BIT)begin selected=(shift[7:3]==5'b11110&&shift[2:1]==ADDRESS[9:8]&&(!reading||addressed10));low_addr_next=!reading;end
else selected=(shift[7:1]==ADDRESS[6:0]||(wrong_addr_ack&&shift[7:1]==ADDRESS[6:0]+1'b1));
if(selected&&busy_nack)begin selected=0;busy_nack=0;end
ack_value=selected;need_ptr=1;
end else if(low_addr_next)begin selected=selected&&shift==ADDRESS[7:0];ack_value=selected;addressed10=selected;low_addr_next=0;end
else if(selected)begin
ack_value=rx_limit<0||rx_count<rx_limit;
if(general_selected)begin
// Unsupported general-call commands are NACKed, never register writes.
ack_value=ack_value&&bytes==1&&software_reset&&shift==8'h06;
reset_pending=ack_value;
end else if(ack_value)begin rx_count++;if(need_ptr)begin ptr=shift;need_ptr=0;end else begin mem[ptr]=shift;ptr=increment(ptr);end end
if(!ack_value)selected=0;
end
bytes++;state=1;ack_active=0;count=0;
end
end else if(state==2)begin count++;if(count==8)begin state=3;count=0;reading_ack=0;end end
else if(state==3&&reading_ack)begin if(sda===0)begin ptr=increment(ptr);tx=mem[ptr];state=2;count=0;end else state=4;end
end
always @(negedge scl)if(active)begin
// Deliberate early-ACK fault: after seven samples, pull for clock 8.
// The address prefix is already known; its R/W bit has not arrived yet.
if(state==0&&count==7&&ack_wrong_clock)begin
if(bytes==0)sda_low=!busy_nack&&((general_call&&shift[6:0]==0)||(TEN_BIT?(shift[6:2]==5'b11110&&shift[1:0]==ADDRESS[9:8]):(shift[6:0]==ADDRESS[6:0]||(wrong_addr_ack&&shift[6:0]==ADDRESS[6:0]+1'b1))));
else sda_low=selected&&(rx_limit<0||rx_count<rx_limit);
end else if(state==1)begin
if(!ack_active)begin
sda_low=ack_value&&!ack_wrong_clock;ack_active=1;
end else begin
sda_low=ack_not_released&&ack_value;ack_active=0;
if(reset_pending)begin
// Finish the ninth ACK clock before resetting and releasing the bus.
for(int i=0;i<256;i++)mem[i]=i[7:0];
ptr=0;shift=0;tx=0;count=0;bytes=0;rx_count=0;need_ptr=1;
selected=0;reading=0;reading_ack=0;low_addr_next=0;addressed10=0;
general_selected=0;reset_pending=0;resets++;sda_low=0;scl_low=0;state=4;
end else if(!selected)state=4;
else if(reading)begin state=2;tx=mem[ptr];count=0;sda_low=!tx[7];end
else state=0;
// Byte-level stretch after the ACK, as in UM10204 §3.1.9. Prepare
// outgoing data before releasing SCL; overlap the controller's LOW.
if(selected&&ack_value&&(stretch_ns>0||stretch_never_released))begin scl_low=1;if(!stretch_never_released)begin #(stretch_ns);scl_low=0;end end
end
end else if(state==2)sda_low=!tx[7-count];
else if(state==3)begin sda_low=0;reading_ack=1;end
else if(state==4)sda_low=0;
end
endmodule
Read included file: i2c_checker.sv
`timescale 1ns/1ns
// Logical Fm timing only. Instantaneous digital pins cannot measure RC rise/fall or
// establish which high-SCL SDA edge was intended as data; driver intent is separate.
module i2c_checker #(parameter time TLOW=1300,THIGH=600,TBUF=1300,THD_STA=600,TSU_STA=600,TSU_DAT=100,THD_DAT=0,TSU_STO=600)
(input wire scl,sda,input wire [2:0] sda_lows,scl_lows,input wire high_drive);
int broken=0,starts=0,stops=0,pulses=0;bit active=0,seen_rise=0,seen_fall=0,start_pending=0;
time rise=0,fall=0,changed=0,stop_time=0,start_time=0;
task automatic bad(input string rule);broken++;$display("I2C CHECK %s at %0t",rule,$time);endtask
always @(posedge scl)begin
if(seen_fall&&$time-fall<TLOW)bad("tlow");
if(active&&$time-changed<TSU_DAT)bad("tsu_dat");rise=$time;seen_rise=1;if(active)pulses++;
end
always @(negedge scl)begin
if(active&&seen_rise&&$time-rise<THIGH)bad("thigh");
if(start_pending&&$time-start_time<THD_STA)bad("thd_sta");start_pending=0;fall=$time;seen_fall=1;
end
always @(sda)begin
if(scl===1)begin
if(sda===0)begin
if(!active&&stops>0&&$time-stop_time<TBUF)bad("tbuf");
if(active&&$time-rise<TSU_STA)bad("tsu_sta");starts++;active=1;start_pending=1;start_time=$time;
end else if(sda===1&&active)begin if($time-rise<TSU_STO)bad("tsu_sto");stops++;active=0;stop_time=$time;end
end else if(seen_fall&&$time-fall<THD_DAT)bad("thd_dat");
changed=$time;
end
always @(high_drive or sda_lows)begin #1;if(high_drive&&|sda_lows)bad("push_pull_contention");end
// A separate intent-aware bench checks ACK ownership/ninth slot, lost-controller
// withdrawal, timeouts and accidental START/STOP. Those are not inferable from SDA alone.
endmodule
Read included file: i2c_mon.sv
`timescale 1ns/1ns
module i2c_mon(input wire scl,sda);
bit active=0,header=1,trace=0;int count=0,frames=0,starts=0,stops=0;reg [7:0] value=0;
// Assemble the whole S ... Sr ... P transaction before one atomic report.
// Incomplete/stuck transactions produce no line; counters remain live.
string text="";
always @(negedge sda)if(scl===1)begin if(active)text={text," Sr"};else text="I2C S";active=1;header=1;count=0;starts++;end
always @(posedge sda)if(scl===1&&active)begin if(trace)$display("%s P",text);text="";active=0;count=0;frames++;stops++;end
always @(posedge scl)if(active)begin
if(count<8)begin value={value[6:0],sda};count++;end
else begin
if(header)text=$sformatf("%s %02h %s",text,value[7:1],value[0]?"R":"W");
else text=$sformatf("%s %02h",text,value);
text={text,sda?" NA":" A"};header=0;count=0;
end
end
endmodule
// Trusted recovery observer. Driver intent distinguishes the STOP setup edge
// from recovery pulses, including when a stuck target prevents an actual STOP.
module i2c_clear_mon(input wire scl,sda,ctrl_sda_low,enabled,done,timed_out);
int pulses=0;bit stopped=0;
always @(posedge enabled)begin pulses=0;stopped=0;end
always @(posedge scl)if(enabled&&!ctrl_sda_low)pulses++;
always @(posedge sda)if(enabled&&scl===1)stopped=1;
always @(posedge done)$display("I2C CLEAR PULSES %0d STOP %0d CLEARED %0d TIMEOUT %0d",pulses,stopped,(sda===1&&!timed_out),timed_out);
endmodule
Check Yourself
Remember
- Table 11 is the bus timing, referred to 0.3 and 0.7 VDD.
- Eight parameters are edge-to-edge and a digital checker measures them; rise, fall, spikes and Cb need analog.
- tHD;DAT may be 0, but the sender still owes an internal hold until SCL is below 0.3 VDD.
- The rise is 0.8473 Rp × Cb; Rp(max) comes from tr, Rp(min) from the 3 mA sink.
- The Rp window can be empty, which is what current sources and Fm+ drive fix.
Practice
- Measure Logical I2C Timing Without Rejecting a Stretch Verify · Hard
- Respect Bus-Free and Fm-Plus Timing Fix · Medium