Skip to content

Commit 1687117

Browse files
committed
I2CBitController: only detect SCL stretching while in a transaction
1 parent e5f3844 commit 1687117

File tree

4 files changed

+101
-32
lines changed

4 files changed

+101
-32
lines changed

hdl/ip/bsv/I2C/I2CBitController.bsv

+2-2
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,14 @@ module mkI2CBitController #(
155155
// After the delay we know SCL is being stretched if we aren't the ones
156156
// holding it low.
157157
(* fire_when_enabled *)
158-
rule do_sample_scl_stretch(scl_stretch_sample_strobe && scl_in == 0);
158+
rule do_sample_scl_stretch(scl_stretch_sample_strobe && scl_in == 0 && state != AwaitStart);
159159
scl_stretching <= scl_out_en == 0;
160160
scl_stretch_sample_delay <= False;
161161
endrule
162162

163163
// If SCL is high then no one is holding it
164164
(* fire_when_enabled *)
165-
rule do_clear_scl_stretch(scl_in == 1);
165+
rule do_clear_scl_stretch(scl_in == 1 || state == AwaitStart);
166166
scl_stretching <= False;
167167
endrule
168168

hdl/ip/bsv/I2C/test/I2CPeripheralModel.bsv

+15-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ interface I2CPeripheralModel;
3333
interface Get#(ModelEvent) receive;
3434
method Action nack_response(Bool ack);
3535
method Action stretch_next(Bool timeout);
36+
method Action bus_pullups(Bool present);
3637
endinterface
3738

3839
typedef union tagged {
@@ -110,6 +111,8 @@ module mkI2CPeripheralModel #(Bit#(7) i2c_address,
110111
Reg#(Bool) countdown_reset <- mkReg(False);
111112
Reg#(Bool) back_to_rx <- mkReg(False);
112113

114+
ConfigReg#(Bool) pullups_lost <- mkConfigReg(False);
115+
113116
(* fire_when_enabled *)
114117
rule do_detect_scl_fedge;
115118
scl_in_prev <= scl_in;
@@ -319,13 +322,21 @@ module mkI2CPeripheralModel #(Bit#(7) i2c_address,
319322
endmethod
320323

321324
method Bit#(1) scl_o();
322-
return scl_out;
325+
if (pullups_lost) begin
326+
return 0;
327+
end else begin
328+
return scl_out;
329+
end
323330
endmethod
324331

325332
method Action sda_i(Bit#(1) sda_i_next) = sda_in._write(sda_i_next);
326333

327334
method Bit#(1) sda_o();
328-
return sda_out;
335+
if (pullups_lost) begin
336+
return 0;
337+
end else begin
338+
return sda_out;
339+
end
329340
endmethod
330341

331342
method Action nack_response(Bool ack) = nack_response_._write(ack);
@@ -341,6 +352,8 @@ module mkI2CPeripheralModel #(Bit#(7) i2c_address,
341352
end
342353
endmethod
343354

355+
method Action bus_pullups(Bool present) = pullups_lost._write(!present);
356+
344357
interface Get receive = toGet(outgoing_events);
345358
endmodule
346359

hdl/projects/sidecar/qsfp_x32/QSFPModule/test/QsfpModuleControllerTests.bsv

+57-8
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ QsfpModuleController::Parameters qsfp_test_params =
3333
core_clk_period_ns: i2c_test_params.core_clk_period_ns,
3434
i2c_frequency_hz: i2c_test_params.scl_freq_hz,
3535
power_good_timeout_ms: 10,
36-
t_init_ms: 20, // normally 2000, but sped up for simulation
36+
t_init_ms: 5, // normally 2000, but sped up for simulation
3737
t_clock_hold_us: i2c_test_params.max_scl_stretch_us
3838
};
3939

@@ -134,6 +134,14 @@ module mkBench (Bench);
134134
mkConnection(controller.pins.sda.out, periph.sda_i);
135135
mkConnection(controller.pins.sda.in, periph.sda_o);
136136

137+
// We need the ability to simulate the bus losing its pull-ups when a module has not been
138+
// inserted since that is how the design behaves. We only apply power to the module (and by the
139+
// board design, it's bus) when a module is present. This is kind of janky given we can't
140+
// properly simulate tristate logic in bluesim.
141+
rule do_pullup_simulation;
142+
periph.bus_pullups(controller.pg);
143+
endrule
144+
137145
// Used to make dummy data for the DUT to pull from
138146
Reg#(UInt#(8)) fifo_idx <- mkReg(0);
139147

@@ -289,6 +297,21 @@ function Stmt insert_and_power_module(Bench bench);
289297
endseq);
290298
endfunction
291299

300+
function Stmt remove_and_power_down_module(Bench bench);
301+
return (seq
302+
bench.set_modprsl(1);
303+
// modprsl is debounced, so wait for it to transition
304+
await(bench.modprsl == 1);
305+
delay(5);
306+
assert_false(bench.hsc_en(),
307+
"Hot swap should be disabled when module is missing");
308+
// after some delay, remove power good
309+
bench.set_hsc_pg(0);
310+
// power good is debounced, so wait for it to transition
311+
await(!bench.hsc_pg);
312+
endseq);
313+
endfunction
314+
292315
function Stmt deassert_reset_and_await_init(Bench bench);
293316
return (seq
294317
// release reset
@@ -326,6 +349,9 @@ module mkNoModuleTest (Empty);
326349
assert_eq(unpack(bench.registers.port_status.error[2:0]),
327350
NoModule,
328351
"NoModule error should be present when attempting to communicate with a device which is not present.");
352+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
353+
False,
354+
"Should not have observed and SCL stretching.");
329355
delay(5);
330356
endseq);
331357
endmodule
@@ -352,6 +378,9 @@ module mkNoPowerTest (Empty);
352378
assert_eq(unpack(bench.registers.port_status.error[2:0]),
353379
NoPower,
354380
"NoPower error should be present when attempting to communicate before the hot swap is stable.");
381+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
382+
False,
383+
"Should not have observed and SCL stretching.");
355384
delay(5);
356385
endseq);
357386
endmodule
@@ -385,6 +414,9 @@ module mkRemovePowerEnableTest (Empty);
385414
await(!bench.hsc_pg);
386415
delay(3);
387416
assert_eq(bench.hsc_en(), False, "Expect hot swap to no longer be enabled.");
417+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
418+
False,
419+
"Should not have observed and SCL stretching.");
388420
delay(5);
389421
endseq);
390422
endmodule
@@ -410,6 +442,9 @@ module mkPowerGoodTimeoutTest (Empty);
410442
assert_eq(unpack(bench.registers.port_status.error[2:0]),
411443
PowerFault,
412444
"PowerFault error should be present when attempting to communicate after the hot swap has timed out");
445+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
446+
False,
447+
"Should not have observed and SCL stretching.");
413448
delay(5);
414449
endseq);
415450
endmodule
@@ -437,6 +472,9 @@ module mkPowerGoodLostTest (Empty);
437472
assert_eq(unpack(bench.registers.port_status.error[2:0]),
438473
PowerFault,
439474
"PowerFault error should be present when attempting to communicate after the hot swap has aborted");
475+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
476+
False,
477+
"Should not have observed and SCL stretching.");
440478
delay(5);
441479
endseq);
442480
endmodule
@@ -535,6 +573,9 @@ module mkInitializationTest (Empty);
535573
assert_eq(unpack(bench.registers.port_status.error[2:0]),
536574
NotInitialized,
537575
"NotInitialized error should be present when resetl is asserted.");
576+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
577+
False,
578+
"Should not have observed and SCL stretching.");
538579
delay(5);
539580
endseq);
540581
endmodule
@@ -562,19 +603,18 @@ module mkUninitializationAfterRemovalTest (Empty);
562603
NoError,
563604
"NoError should be present when attempting to communicate after t_init has elapsed.");
564605

565-
bench.set_modprsl(1);
566-
// ModPrsL is debounced and thus won't transition immediately
567-
await(bench.modprsl == 1);
568-
bench.set_modprsl(0);
569-
await(bench.modprsl == 0);
570-
delay(3); // wait a few cycles for power to re-enable
571-
bench.command(read_cmd, False, False);
606+
remove_and_power_down_module(bench);
607+
insert_and_power_module(bench);
572608

609+
bench.command(read_cmd, False, False);
573610
await(!bench.i2c_busy());
574611
delay(3);
575612
assert_eq(unpack(bench.registers.port_status.error[2:0]),
576613
NotInitialized,
577614
"NotInitialized error should be present when a module has been reseated but not initialized.");
615+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
616+
False,
617+
"Should not have observed and SCL stretching.");
578618
endseq);
579619
endmodule
580620

@@ -605,6 +645,9 @@ module mkNoLPModeWhenModuleIsUnpoweredTest (Empty);
605645

606646
await(bench.hsc_pg); // wait out debounce
607647
assert_set(bench.lpmode, "LpMode should be asserted now that 3.3V is up.");
648+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
649+
False,
650+
"Should not have observed and SCL stretching.");
608651
endseq);
609652
endmodule
610653

@@ -620,6 +663,9 @@ module mkIntLTest (Empty);
620663
bench.set_intl(0);
621664
await(bench.intl == 0);
622665
assert_not_set(bench.intl, "IntL should be low after debounce");
666+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
667+
False,
668+
"Should not have observed and SCL stretching.");
623669
endseq);
624670
endmodule
625671

@@ -635,6 +681,9 @@ module mkModPrsLTest (Empty);
635681
bench.set_modprsl(0);
636682
await(bench.modprsl == 0);
637683
assert_not_set(bench.modprsl, "ModPrsL should be low after debounce");
684+
assert_eq(unpack(bench.registers.port_status.stretching_seen),
685+
False,
686+
"Should not have observed and SCL stretching.");
638687
endseq);
639688
endmodule
640689

hdl/projects/sidecar/qsfp_x32/QSFPModule/test/QsfpModuleControllerTests.gtkw

+27-20
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
[*]
2-
[*] GTKWave Analyzer v3.3.100 (w)1999-2019 BSI
3-
[*] Mon Sep 16 21:06:14 2024
2+
[*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3+
[*] Thu Dec 5 13:08:47 2024
44
[*]
5-
[dumpfile] "\\wsl$\Ubuntu-20.04\home\aaron\Oxide\git\quartz\build\vcd\QsfpModuleControllerTests_mkI2CSclStretchTimeoutTest.vcd"
6-
[dumpfile_mtime] "Mon Sep 16 17:50:10 2024"
7-
[dumpfile_size] 71544432
8-
[savefile] "\\wsl$\Ubuntu-20.04\home\aaron\Oxide\git\quartz\hdl\projects\sidecar\qsfp_x32\QSFPModule\test\QsfpModuleControllerTests.gtkw"
9-
[timestart] 15988190
10-
[size] 2558 1360
5+
[dumpfile] "/home/aaron/Oxide/git/quartz/build/vcd/QsfpModuleControllerTests_mkUninitializationAfterRemovalTest.vcd"
6+
[dumpfile_mtime] "Thu Dec 5 13:00:44 2024"
7+
[dumpfile_size] 93767403
8+
[savefile] "/home/aaron/Oxide/git/quartz/hdl/projects/sidecar/qsfp_x32/QSFPModule/test/QsfpModuleControllerTests.gtkw"
9+
[timestart] 13499672
10+
[size] 2592 1283
1111
[pos] -1 -1
12-
*-13.036575 16052670 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
12+
*-7.171101 13500140 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
1313
[treeopen] main.
1414
[treeopen] main.top.
15-
[sst_width] 446
16-
[signals_width] 526
15+
[sst_width] 596
16+
[signals_width] 645
1717
[sst_expanded] 1
1818
[sst_vpaned_height] 404
1919
@200
@@ -22,6 +22,7 @@
2222
@28
2323
main.top.bench_controller_lpmode_
2424
main.top.bench_controller_resetl_
25+
main.top.bench_modprsl_r
2526
@200
2627
-
2728
@28
@@ -47,22 +48,29 @@ main.top.bench_controller_rdata_fifo_deq
4748
main.top.bench_controller_wdata_fifo_deq
4849
@200
4950
-
50-
-
5151
-I2CCore
52+
@28
53+
main.top.bench_controller_i2c_core_bit_ctrl_sda_in
54+
main.top.bench_controller_i2c_core_bit_ctrl_scl_in
5255
@22
5356
main.top.bench_controller_i2c_core_state_r[3:0]
5457
@28
5558
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretch_seen_r
56-
@29
5759
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretch_timeout_r
58-
@28
5960
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretching
61+
@24
62+
main.top.bench_periph_scl_stretch_countdown_count_r[15:0]
63+
@28
64+
main.top.bench_periph_scl_stretch_countdown_q
65+
@24
66+
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretch_timeout_cntr_count[15:0]
67+
@28
68+
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretch_timeout_cntr_q
69+
@25
70+
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretch_sample_strobe_count[5:0]
71+
@28
72+
main.top.bench_controller_i2c_core_bit_ctrl_scl_stretch_sample_strobe_q
6073
@200
61-
-FIFO: Next Command
62-
-FIFO: BitControl Incoming Events
63-
-FIFO: BitControl Outgoing Events
64-
-FIFO: RX Data
65-
-FIFO: TX Data
6674
-
6775
-WDATA FIFO
6876
@28
@@ -104,7 +112,6 @@ main.top.bench_controller_rdata_fifo_memory.ADDRB[7:0]
104112
main.top.bench_fifo_idx[7:0]
105113
@200
106114
-
107-
-
108115
-Model - I2CPeripheralModel
109116
@28
110117
main.top.bench_periph_scl_in

0 commit comments

Comments
 (0)