Fast-Mode Plus and Hs-Mode
Say what changes from Standard-mode to Fast-mode Plus and what does not, run a 1 Mbit/s bus under the right checker, and run a bus into and out of Hs-mode, alone and behind a bridge.
- Lesson
- P8.9
- Time
- 45 min
- Runs on
- Your browser
- Check
- 5 questions
A team moved a working Fast-mode design to Fm+ and kept its 2.2 kΩ pull-ups. Simulation was clean, because a digital bench cannot see a rise. On the board the 186.4 ns rise ate most of the 260 ns high time, and 1s were sampled as 0s.
- 1 kΩ
- 2.2 kΩ
- Time 120.4 ns1 kΩ = 2.31 V · 2.2 kΩ = 1.391 V
With 1 kΩ, SDA reaches 0.7 VDD after a 84.7 ns rise, inside Fm+'s 120 ns.
- Time 264.9 ns1 kΩ = 3.067 V · 2.2 kΩ = 2.31 V
With 2.2 kΩ it takes 186.4 ns: legal in Fast-mode, over the Fm+ limit.
Read the plot as a table
| Time | 1 kΩ | 2.2 kΩ |
|---|---|---|
| 50 ns | 1.298 V | 670.9 mV |
| 100 ns | 2.086 V | 1.205 V |
| 120.4 ns | 2.31 V | 1.391 V |
| 150 ns | 2.564 V | 1.631 V |
| 200 ns | 2.853 V | 1.97 V |
| 250 ns | 3.029 V | 2.241 V |
| 264.9 ns | 3.067 V | 2.31 V |
| 300 ns | 3.136 V | 2.456 V |
| 350 ns | 3.2 V | 2.628 V |
| 400 ns | 3.24 V | 2.764 V |
Simulated on the Analog engine (ngspice 42). At 20 mA, Rp(min) at 3.3 V is 145 Ω, so 1 kΩ is well inside the window.
One Protocol, Four Speeds
From Sm to Fm the protocol does not change: the timing shrinks, inputs gain spike filters and Schmitt triggers, outputs gain slope control (§5.1). Fm+ keeps the protocol again and raises the sink current to 20 mA, so a heavier bus or a 1 Mbit/s bus still meets its rise time (§5.2).
| Sm | Fm | Fm+ | Hs | |
|---|---|---|---|---|
| Bit rate | 100 kbit/s | 400 kbit/s | 1 Mbit/s | 3.4 Mbit/s (1.7 at 400 pF) |
| Sink current at 0.4 V | 3 mA | 3 mA | 20 mA | Table 12 |
| Bus capacitance | 400 pF | 400 pF | 550 pF | 100 pF (400 pF at 1.7 Mbit/s) |
| Rise time (max) | 1000 ns | 300 ns | 120 ns | SCLH 40 ns, 80 ns after Sr or ACK |
| Spike filter | – | 50 ns | 50 ns | 10 ns |
| Arbitration during data | yes | yes | yes | no: it ends with the controller code |
| Clock stretching | byte and bit level | byte and bit level | byte and bit level | after an ACK only |
| Pull-up | resistor | resistor; current source above 200 pF | resistor | current source on SCLH in the active controller |
| Entered by | reset | reset | reset | S + controller code 0000 1XXX + NACK |
UM10204 §5 to §6.2, Tables 10 to 13. Sm, Fm and Fm+ change only timing and electrics; each faster device works on a slower bus by following its clock. A Standard-mode device on a Fast-mode bus does not (§5.1).
Fm+ on the Bench
The Fm+ controller preset runs the same register read in 38831 ns instead of 96281 ns, and the Fm+ checker is clean.
| Controller | Checker parameters | Measured |
|---|---|---|
| Fm+ preset (500 / 500 ns) | Fm+ (I2C_FMPLUS) | value 3c, ck.broken 0, 38831 ns |
| Fm bench (1300 / 1200 ns) | Fm+ (I2C_FMPLUS) | value 3c, ck.broken 0 |
| Fm+ preset | Fm | thd_sta ×2, tlow ×38, thigh ×37, tsu_sta ×1, tsu_sto ×1 |
Measured. The same wire is legal or illegal depending on which table the bus has agreed to. The Fm+ preset also shortens tHD;STA, tSU;STA and tSU;STO to 260 ns, so the Fm checker flags those rows too.
The checker cannot see the rise. That is the part the SPICE figure above shows, and why the library's fm_plus_slow_rise twin is an analog run, not a digital switch.
Entering Hs-Mode
Hs-mode starts only after S, an 8-bit controller code 0000 1XXX and a NACK, all sent at F/S speed (§5.3.2). No device may acknowledge the code. It is the last point where controllers arbitrate, and it tells every device to switch to Hs timing.
A low lane of 1 pulls the net down. The pull-up raises it only when every device releases.
Read phases and sample points (quarter bit)
- START: columns 2 through 3.
- 0x0a + NACK: columns 4 through 39.
- START: columns 40 through 43.
- 0xa1 + ACK: columns 44 through 79.
- 0x3c + NACK: columns 80 through 115.
- STOP: columns 116 through 119.
- SDA sampled at 6, 10, 14, 18, 22, 26, 30, 34, 38, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114.
- Cycle 38
t1: the NACK after the controller code. Arbitration and synchronisation end here; every Hs device switches its filters and timing.
- Cycle 42
tH: once SCLH is high, the winning controller turns on its current-source pull-up. The Sr and everything after it run in Hs-mode.
- Cycle 118
tFS: the STOP. The bus returns to F/S-mode, within tBUF.
Generated and decoded back: the decoder reads the code as an ordinary NACKed byte, which is what it is on the wire. The bench prints I2C S 05 W NA P: nobody acknowledges.
Hs-mode continues across repeated STARTs and ends only at STOP. Each Hs controller has its own code; up to eight can share a bus, with 0000 1000 kept for test and diagnostics.
What Hs Changes
No arbitration or clock synchronisation during the Hs transfer (§5.3.1). SCLH runs HIGH : LOW = 1 : 2. The active controller's current source speeds up SCLH's rise; it is switched off after each Sr and each ACK so a target may stretch there, and only there. At 100 pF, Table 13 allows tLOW 160 ns, tHIGH 60 ns and tSU;DAT 10 ns.
Every Hs device carries a recogniser for S, 0000 1XXX and NACK (§5.3.3). Between t1 and tH the winner adapts its filters, set-up and hold times, slopes and bit rate. Losers adapt their filters and wait for the STOP. Targets adapt filters and timing. The bench models the switch and when the current source is on.
- Cycle 11
The ninth clock of the code. Nobody pulls SDAH, so it reads NACK.
- Cycle 35
At t1 SCLH falls after the NACK, and the target's recogniser switches to Hs settings.
- Cycle 61
At tH, 1321 ns later, SCLH is high again and the controller turns on its current source (mcs). This LOW still keeps F/S timing.
- Cycle 64
Sr: the current source goes off for the first Hs rise, so a target could stretch here.
- Cycle 76
Hs bits: 200 ns LOW and 100 ns HIGH each.
Measured. mcs is the enable of the SCLH current source; the bench models when it is on, not the faster rise it gives.
| Part of the read | SCLH pulses | Shortest LOW | Shortest HIGH | Minimum LOW / HIGH |
|---|---|---|---|---|
| S, code, NACK (F/S-mode) | 9 | 1300 ns | 1201 ns | Fm: 1300 / 600 ns (Table 11) |
| After the NACK (Hs-mode) | 39 | 200 ns | 101 ns | Hs at 100 pF: 160 / 60 ns (Table 13) |
Measured on one register read: I2C S 05 W NA Sr 50 W A 3c A Sr 50 R A 3c NA P, checker clean. The recogniser is in Hs-mode from 25809 to 39208 ns, the STOP. The read takes 37209 ns; the same controller without a code takes 95939 ns. Two reads after one code, linked by Sr: 49127 ns, against 75717 ns as two Hs transfers.
Hs Devices on Slower Buses
Hs devices start in Fast-mode after reset (§5.3.3). On a bus with no code they never leave it: they talk at F/S speed with the current source off, through SDAH and SCLH (§5.3.4). Through a closed bridge, everyone shares one F/S bus with open-drain outputs (§5.3.6).
| Bus | Transfer | Measured |
|---|---|---|
| Hs devices and an F/S target on one bus (Fig. 35) | Hs controller, no code | value 3c, 95939 ns | ckf.broken 0, Hs entries 0, mcs 0 |
| Bridged bus (Fig. 36), bridge closed | F/S controller reads the Hs target | F/S controller read 3c from the Hs target |
| Bridged bus (Fig. 36), bridge closed | Hs controller reads the F/S target, no code | Hs controller read 3c from the F/S target | ckh.broken 0, ckf.broken 0 |
| Hs devices and an F/S target on one bus, no bridge | Hs controller sends its code and goes fast | F/S checker: tsu_sta ×2, thigh ×38, thd_sta ×2, tlow ×38, tsu_sto ×1 |
Measured. Without a code nothing switches: no Hs entries, no current source (mcs 0), and the bridge did not move (0 bridge events). The last row is why F/S devices need the bridge.
An Hs Transfer on a Mixed-Speed Bus
The bridge is built into Hs controllers, or into any IC, and is driven only by the bus lines (§5.3.5). On the code it isolates the Hs section. The F/S devices see one slow transfer: S, a byte nobody owns, then SDA held low until a STOP (§5.3.7). Table 9 lists the rate each pair of devices can use.
- F/S-mode
The bridge is closed: everyone shares one bus at F/S speed.
- Hs-mode
Between t1 and tH the bridge opens. The Hs section runs at 3.4 Mbit/s; the F/S side waits with SDA low and SCL high, ready for the STOP.
Read the diagram as text
- Hs controller: current source on SCLH
- Hs targets: on SDAH, SCLH
- Bridge TR1 to TR3: open during Hs, closed in F/S
- F/S devices: on SDA, SCL
- Hs controller → Hs targets: SDAH, SCLH
- Hs controller → Bridge TR1 to TR3
- Bridge TR1 to TR3 → F/S devices: SDA, SCL
UM10204 §5.3.5 to §5.3.8, Fig. 36. Table 9: Hs to Hs at 3.4 Mbit/s; any transfer in a system that has a Standard-mode device runs at 100 kbit/s.
- Cycle 9
TR1 opens while SCL is low, then TR3 pulls SDA low. A falling SDA under a low SCL is not a START.
- Cycle 35
At tH both clocks are high and TR2 opens. SCL now stays high with SDA low.
- Cycle 38
The Hs section runs on alone. The F/S section waits, ready for a STOP.
Measured. At tFS the order reverses: TR2 closes, TR3 lets SDA rise under a high SCL, which is the STOP the F/S devices see, and TR1 closes once SDA is high.
| Event | TR1 TR2 TR3 | Time after t1 |
|---|---|---|
| t1: the code is NACKed; TR1 opens | 0 1 0 | 0 ns |
| TR3 pulls SDA low | 0 1 1 | 5 ns |
| tH: SCLH and SCL are high; TR2 opens | 0 0 1 | 1320 ns |
| tFS: STOP on SDAH; TR2 closes, TR3 lets SDA rise | 0 1 0 | 13399 ns |
| t2: SDA is high; TR1 closes | 1 1 0 | 13399 ns |
Measured (1 = transistor on). The F/S decoder logs I2C S 05 W NA P: one short transfer that ends at tFS. The Hs decoder logs the whole read. Bench flags: ckh.broken 0, ckf.broken 0, F/S parked while TR2 open 1.
The bridge must act fast enough not to disturb SDAH and SCLH, and meet Fast-mode timing on SDA and SCL (§5.3.8). TR3 must release SDA in time for tBUF. In a hang-up, an F/S device can close the bridge by holding SCL low for at least 1 µs; the bench does not model that recovery.
| Switch | Measured | What broke |
|---|---|---|
bridge.skips_tr3 = 1 | F/S section: starts 1, stops 0 | SDA was high before TR2 opened, so no STOP can follow: the F/S side stays busy |
ctrl.skip_code = 1 | F/S checker: thd_sta ×2, tlow ×38, thigh ×37, tsu_sta ×1, tsu_sto ×1 | no code, so the bridge never opened and Hs timing reached the F/S devices |
| target address 0x05 (0000 101) | I2C S 05 W A P, code_acked 1, hs 0, Hs entries 0 | an address in the reserved 0000 1XX range ACKs the code; the bench controller sends a STOP and stays in F/S-mode |
| second Hs controller, code 0000 1001 | ctrl: lost 1, back at 39209 | other: Hs from 25809 to 39209, value 10 | nothing broke: the lower code wins, and the loser waits for the STOP (§5.3.3) |
Measured. The arbitration run logs I2C S 04 R NA Sr 50 W A 10 A Sr 50 R A 10 NA P: the decoder reads code 0000 1001 as address 04 with R.
Try It
The controller takes the Fm+ preset: 500 ns LOW, 500 ns HIGH, 50 ns hold. The checker takes the Fm+ column of Table 11. The protocol, the target and the decoder are unchanged.
Delete the checker parameters to judge the same wire against Fast-mode and read the lines.
I2C S 50 W A 3c A Sr 50 R A 3c NA P value 3c, ck.broken 0, 38831 nsRead 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
- Sm, Fm and Fm+ share the protocol; each step shrinks Table 11 and a faster device follows a slower clock.
- Fm+ adds 20 mA drive for 550 pF or 1 Mbit/s; the checker sees its timing, SPICE sees its rise.
- Hs-mode is entered with S, a controller code 0000 1XXX and a NACK, and ends at STOP.
- In Hs-mode there is no arbitration, SCLH has a current-source pull-up, and stretching is allowed only after an ACK.
- Without a code Hs devices stay in F/S-mode; with one, a bridge isolates the F/S section and parks SDA low until the STOP.
Practice
- Respect Bus-Free and Fm-Plus Timing Fix · Medium