Stuck Buses, Bus Clear and Resets
Explain how a reset leaves a target holding SDA, recover the bus with at most nine clocks and a STOP, report the cases nine clocks cannot fix, and say what SMBus adds.
- Lesson
- P8.8
- Time
- 30 min
- Runs on
- Your browser
- Check
- 4 questions
A host rebooted in the middle of an EEPROM read. The EEPROM was sending a 0 and kept SDA low, waiting for a clock. After the reboot the driver saw SDA low, decided the bus was busy, and waited forever, until it learned to send nine clocks and a STOP.
- Cycle 5
The target is sending 00h: it pulls SDA for every bit. The controller makes the clock.
- Cycle 45
SCL rises for the third data bit, and the controller resets. It lets go of both lines.
- Cycle 60
Nobody clocks any more. The target waits for an SCL fall that never comes and keeps pulling:
after the reset: scl 1, sda 0, tdl 1.
Measured. Nobody broke a rule; the bus is half a transaction, and only a controller with a clock can finish it. Every controller that wakes up now sees SDA low and thinks the bus is busy.
Nine Clocks
If SDA is stuck low, the controller sends up to nine clock pulses; the holding device should let go within them (§3.1.16). Each falling edge moves the target to its next bit, and within nine it reaches a 1 or its acknowledge slot.
ctrl.bus_clear(cleared) on the stuck bus above. 250 ns per column.- Cycle 4
The controller pulls SCL low: the falling edge the target was waiting for. It moves on to its next bit, another 0.
- Cycle 9
Each pulse lets the target shift out one more bit. The controller checks SDA while SCL is high.
- Cycle 55
After 5 pulses the byte is done and the target lets go for the acknowledge slot. SDA rises.
- Cycle 64
The controller stops pulsing and sends a STOP, so every device's bus logic is idle again.
Measured: I2C CLEAR PULSES 5 STOP 1 CLEARED 1 TIMEOUT 0, and the next register read works: I2C S 50 W A 3c A Sr 50 R A 3c NA P.
A good bus clear stops pulsing as soon as SDA reads high, then sends a STOP, then reports whether it worked. It never claims the bus because SDA is low "so someone must be talking".
Five Cases
| Stuck device | Observer line | Meaning |
|---|---|---|
| lets go after 1 SCL rise | I2C CLEAR PULSES 1 STOP 1 CLEARED 1 TIMEOUT 0 | one pulse, then STOP |
| lets go after 5 | I2C CLEAR PULSES 5 STOP 1 CLEARED 1 TIMEOUT 0 | the controller stops as soon as SDA reads high |
| lets go after 9 | I2C CLEAR PULSES 9 STOP 1 CLEARED 1 TIMEOUT 0 | the ninth pulse is the last allowed |
| never lets go | I2C CLEAR PULSES 9 STOP 0 CLEARED 0 TIMEOUT 0 | nine pulses, no STOP can form, cleared = 0: reset or power |
| holds SCL instead | I2C CLEAR PULSES 0 STOP 0 CLEARED 0 TIMEOUT 1 | no pulse is possible; the controller reports after 20000 ns instead of hanging |
Measured with a third device on the bench that holds SDA until a given number of SCL rising edges, or holds SCL. The observer counts the controller's pulses and whether a STOP formed.
| Controller switch | Observer line | What went wrong |
|---|---|---|
clear_eight_clocks (device needs 9) | I2C CLEAR PULSES 8 STOP 0 CLEARED 0 TIMEOUT 0 | one pulse short; the bus stays stuck |
no_stop_after_clear (device needs 5) | I2C CLEAR PULSES 6 STOP 0 CLEARED 1 TIMEOUT 0, mon.stops 0 | SDA is free but no STOP: the observer counts the final release of SCL as a sixth rise, and devices still think a transfer is open |
Measured. UM10204 asks for the nine pulses only; the STOP afterwards is this bench's practice and most drivers'.
What Nine Clocks Cannot Fix
SDA may still be low after nine pulses, or SCL itself may be held low. Then the remedies are a hardware reset input or a power cycle that triggers each device's mandatory power-on reset (§3.1.16). So a controller must report, not hang.
- SDA stuck
Clock the stuck target through the rest of its byte, then STOP (§3.1.16).
- Nothing helps
If SDA is still low after nine pulses, or SCL itself is held, only a hardware reset or a power cycle clears it.
Read the diagram as text
- SDA low, SCL high: a target mid-byte
- SCL low: a device holds the clock
- Up to nine clocks: stop when SDA reads high
- STOP: every device idle again
- HW reset or power cycle: the mandatory POR
- SDA low, SCL high → Up to nine clocks
- Up to nine clocks → STOP: SDA free
- Up to nine clocks → HW reset or power cycle: still low
- SCL low → HW reset or power cycle
Software reset (general call, then 06h) helps only devices that implement it (optional, §3.1.14), and only once the bus is free enough to send it.
SMBus and Its Relatives
SMBus uses I2C hardware with a software layer and tighter electrical rules. Its 35 ms time-out exists because it chose a bounded system; I2C allows any stretch, so it cannot define "too long" (§4.2.2).
| I2C | SMBus | |
|---|---|---|
| Clock | 0 Hz up to 100 kHz, 400 kHz, 1 MHz or 3.4 MHz by mode | 10 kHz to 100 kHz |
| Time-out | none: a target may stretch for as long as it needs | 35 ms; devices reset, and targets may not hold SCL longer |
| Logic levels | 30 % and 70 % of VDD | fixed: LOW 0.8 V, HIGH 2.1 V |
| Sink current at 0.4 V | 3 mA (20 mA for Fm+) | 350 µA low power, 4 mA high power |
| Addresses | fixed by the device and its pins | an Address Resolution Protocol can assign them dynamically |
UM10204 §4.2 to §4.2.3. PMBus is SMBus 1.1 plus a standard command set for power converters (§4.3); IPMI, ATCA and DDC also run over I2C (§4.4 to §4.6). Packet error checking and the SMBus time-out names are in the SMBus specification, not UM10204.
Try It
A third device, stuck_sda, holds SDA until it has seen release_after SCL rising edges, the way a target finishes the bits of an interrupted byte. bus_clear pulses SCL until SDA reads high, at most nine times, then sends STOP. The observer prints when done rises.
Set release_after to 9, then 100, then replace the stuck SDA with stuck_scl = 1; and compare the lines.
I2C CLEAR PULSES 5 STOP 1 CLEARED 1 TIMEOUT 0Read 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
- A controller reset mid-byte leaves a target holding SDA, waiting for a clock.
- Send up to nine SCL pulses, stop when SDA reads high, then send a STOP.
- Report when SDA stays low or SCL is held; the remedy then is a hardware reset or a power cycle.
- Software reset (general call, 06h) is optional and needs a free bus.
- SMBus adds a 35 ms time-out, fixed levels and dynamic addressing; I2C has no time-out.
Practice
- Recover a Stuck SDA with at Most Nine Clocks Implement · Hard
- Why Nine Recovery Clocks Never Arrived Debug · Hard