Skip to content

Commit b9192e5

Browse files
committed
make I2C controller push-pull
1 parent 5b3ed40 commit b9192e5

File tree

5 files changed

+110
-92
lines changed

5 files changed

+110
-92
lines changed

hdl/ip/vhd/i2c/controller/link_layer/i2c_ctrl_link_layer.vhd

+73-64
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
-- I2C Control Link Layer
88
--
99
-- This block handles the bit-level details of an I2C transaction. It requires higher order logic
10-
-- to actually orchestrate the transaction and is designed for use with i2c_ctrl_txn_layer.
10+
-- to actually orchestrate the transaction and is designed for use with i2c_ctrl_txn_layer. This
11+
-- block is written such that the tristate interfaces are push-pull, but that is trivial to change
12+
-- to open-drain at a higher level if desired.
1113
--
1214
-- Notes:
1315
-- - This block currently does not support block stretching.
@@ -118,41 +120,43 @@ architecture rtl of i2c_ctrl_link_layer is
118120
rx_data : std_logic_vector(7 downto 0);
119121
rx_data_valid : std_logic;
120122
tx_data : std_logic_vector(7 downto 0);
121-
sda_oe : std_logic;
122123
rx_ack : std_logic;
123124
rx_ack_valid : std_logic;
125+
sda_o : std_logic;
126+
sda_oe : std_logic;
124127
end record;
125128

126129
constant SM_REG_RESET : sm_reg_t := (
127-
IDLE, -- state
128-
0, -- bits_shifted
129-
'0', -- ready
130-
'0', -- scl_start
131-
'0', -- scl_active
132-
'0', -- sda_hold
133-
(others => '0'),-- counter
134-
'0', -- count_load
135-
'0', -- count_decr
136-
'0', -- count_clr
137-
'0', -- transition_sda_cntr_en
138-
'0', -- ack_sending
139-
'0', -- sr_scl_fedge_seen
140-
'0', -- stop_requested
141-
(others => '0'),-- rx_data
142-
'0', -- rx_data_valid
143-
(others => '0'),-- tx_data
144-
'0', -- sda_oe
145-
'0', -- rx_ack
146-
'0' -- rx_ack_valid
130+
state => IDLE,
131+
bits_shifted => 0,
132+
ready => '0',
133+
scl_start => '0',
134+
scl_active => '0',
135+
sda_hold => '0',
136+
counter => (others => '0'),
137+
count_load => '0',
138+
count_decr => '0',
139+
count_clr => '0',
140+
transition_sda_cntr_en => '0',
141+
ack_sending => '0',
142+
sr_scl_fedge_seen => '0',
143+
stop_requested => '0',
144+
rx_data => (others => '0'),
145+
rx_data_valid => '0',
146+
tx_data => (others => '0'),
147+
rx_ack => '0',
148+
rx_ack_valid =>'0',
149+
sda_o => '1',
150+
sda_oe => '1'
147151
);
148152

149153
signal sm_reg, sm_reg_next : sm_reg_t;
150154

151155
signal sm_count_done : std_logic;
152156

153157
signal scl_toggle : std_logic;
154-
signal scl_oe : std_logic;
155-
signal scl_oe_last : std_logic;
158+
signal scl_o : std_logic;
159+
signal scl_o_last : std_logic;
156160
signal scl_redge : std_logic;
157161
signal scl_fedge : std_logic;
158162

@@ -176,25 +180,23 @@ begin
176180
);
177181

178182
scl_reg: process(clk, reset)
179-
variable scl_oe_next : std_logic := '0';
180183
begin
181184
if reset then
182-
scl_oe <= '0';
183-
scl_oe_last <= '0';
185+
scl_o <= '1';
186+
scl_o_last <= '1';
184187
elsif rising_edge(clk) then
185-
scl_oe_last <= scl_oe;
186-
scl_oe_next := not scl_oe;
188+
scl_o_last <= scl_o;
187189

188190
if not sm_reg.scl_active then
189-
scl_oe <= '0';
191+
scl_o <= '1';
190192
elsif scl_toggle = '1' or sm_reg.scl_start = '1' then
191-
scl_oe <= scl_oe_next;
193+
scl_o <= not scl_o;
192194
end if;
193195
end if;
194196
end process;
195197

196-
scl_redge <= '1' when scl_oe = '0' and scl_oe_last = '1' else '0';
197-
scl_fedge <= '1' when scl_oe = '1' and scl_oe_last = '0' else '0';
198+
scl_redge <= '1' when scl_o = '1' and scl_o_last = '0' else '0';
199+
scl_fedge <= '1' when scl_o = '0' and scl_o_last = '1' else '0';
198200

199201
--
200202
-- SDA Control
@@ -252,7 +254,8 @@ begin
252254
--
253255

254256
sm_next_state: process(all)
255-
variable v : sm_reg_t;
257+
variable v : sm_reg_t;
258+
variable counter_done : boolean;
256259
begin
257260
v := sm_reg;
258261

@@ -261,8 +264,6 @@ begin
261264
v.count_decr := '0';
262265
v.count_clr := '0';
263266
v.scl_start := '0';
264-
v.rx_ack := '0';
265-
v.rx_ack_valid := '0';
266267
v.rx_data_valid := '0';
267268

268269
-- Every time we see a falling edge on SCL we count the number of cycles until we should
@@ -286,11 +287,15 @@ begin
286287
v.stop_requested := '1' when tx_stop = '1' and txn_next_valid = '1' else '0';
287288
end if;
288289

290+
-- It takes a cycle for load to propagate and the counter to reflect the new value.
291+
counter_done := sm_count_done = '1' and sm_reg.count_load = '0';
292+
289293
case sm_reg.state is
290294

291295
-- Ready and awaiting the next transaction
292296
when IDLE =>
293-
v.sda_oe := '0';
297+
v.sda_o := '1';
298+
v.sda_oe := '1';
294299

295300
if tx_start then
296301
-- Coming back to IDLE after a transaction means we've waited out tbuf, and tbuf
@@ -311,27 +316,28 @@ begin
311316

312317
-- In the event of a repeated START account for setup requirements
313318
when START_SETUP =>
314-
v.sda_oe := '0';
319+
v.sda_o := '1';
320+
v.sda_oe := '1';
321+
v.count_decr := '1';
315322

316-
if sm_count_done then
323+
if counter_done then
317324
v.state := START_HOLD;
318325
v.counter := START_SETUP_HOLD_TICKS;
319326
v.count_load := '1';
320-
else
321-
v.count_decr := '1';
322327
end if;
323328

324329
when START_HOLD =>
325-
v.sda_oe := '1';
326-
if sm_count_done then
330+
v.sda_o := '0';
331+
v.sda_oe := '1';
332+
v.count_decr := '1';
333+
if counter_done then
327334
v.state := HANDLE_NEXT;
328335
v.scl_start := '1'; -- drop SCL to finish START condition
329336
v.scl_active := '1'; -- begin free running counter for SCL transitions
330-
else
331-
v.count_decr := '1';
332337
end if;
333338

334339
when HANDLE_NEXT =>
340+
v.rx_ack_valid := '0';
335341
if v.stop_requested then
336342
v.state := STOP_SDA;
337343
elsif txn_next_valid then
@@ -353,17 +359,19 @@ begin
353359
end if;
354360

355361
-- Clock out a byte and then wait for an ACK
356-
when BYTE_TX =>
357-
if transition_sda = '1' then
362+
when BYTE_TX =>
363+
v.sda_oe := '1';
364+
365+
if transition_sda then
358366
if sm_reg.bits_shifted = 8 then
359367
v.state := ACK_RX;
360368
v.bits_shifted := 0;
361369
elsif v.stop_requested then
362370
-- this is a valid SDA transition cycle so drive SDA low and skip STOP_SDA
363371
v.state := STOP_SCL;
364-
v.sda_oe := '1';
372+
v.sda_o := '0';
365373
else
366-
v.sda_oe := not sm_reg.tx_data(7);
374+
v.sda_o := sm_reg.tx_data(7);
367375
v.tx_data := sm_reg.tx_data(sm_reg.tx_data'high-1 downto sm_reg.tx_data'low) & '1';
368376
v.bits_shifted := sm_reg.bits_shifted + 1;
369377
end if;
@@ -374,8 +382,9 @@ begin
374382
v.sda_oe := '0';
375383

376384
if scl_redge then
377-
v.state := STOP_SDA when v.stop_requested else HANDLE_NEXT;
378385
v.rx_ack := not sda_in_syncd;
386+
elsif scl_fedge then
387+
v.state := STOP_SDA when v.stop_requested else HANDLE_NEXT;
379388
v.rx_ack_valid := '1';
380389
end if;
381390

@@ -394,11 +403,12 @@ begin
394403

395404
-- ACK the target
396405
when ACK_TX =>
406+
v.sda_oe := '1';
397407
-- at the first transition_sda pulse start sending the (N)ACK
398-
if transition_sda = '1' then
408+
if transition_sda then
399409
if sm_reg.ack_sending = '0' then
400-
v.sda_oe := '1' when (tx_ack = '1' and v.stop_requested = '0')
401-
else '0';
410+
v.sda_o := '0' when (tx_ack = '1' and v.stop_requested = '0')
411+
else '1';
402412
v.ack_sending := '1';
403413
else
404414
-- at the next transition point release the bus
@@ -412,6 +422,7 @@ begin
412422
when STOP_SDA =>
413423
if transition_sda then
414424
v.state := STOP_SCL;
425+
v.sda_o := '0';
415426
v.sda_oe := '1';
416427
end if;
417428

@@ -424,23 +435,21 @@ begin
424435
end if;
425436

426437
when STOP_SETUP =>
427-
if sm_count_done then
438+
v.count_decr := '1';
439+
if counter_done then
428440
v.state := WAIT_TBUF;
429441
v.counter := STO_TO_STA_BUF_TICKS;
430442
v.count_load := '1';
431-
v.sda_oe := '0';
432-
else
433-
v.count_decr := '1';
443+
v.sda_o := '1';
444+
v.sda_oe := '1';
434445
end if;
435446

436447
-- Wait out tbuf to ensure STOP/START spacing
437448
when WAIT_TBUF =>
438-
if sm_count_done then
449+
v.count_decr := '1';
450+
if counter_done then
439451
v := SM_REG_RESET;
440-
else
441-
v.count_decr := '1';
442452
end if;
443-
444453
end case;
445454

446455
v.ready := '1' when v.state = IDLE or
@@ -466,9 +475,9 @@ begin
466475
rx_data_valid <= sm_reg.rx_data_valid;
467476

468477
-- I2C is open-drain, so we only ever drive low
469-
scl_if.o <= '0';
470-
scl_if.oe <= scl_oe;
471-
sda_if.o <= '0';
478+
scl_if.o <= scl_o;
479+
scl_if.oe <= '1'; -- SCL stretching is not currently supported
480+
sda_if.o <= sm_reg.sda_o;
472481
sda_if.oe <= sm_reg.sda_oe;
473482

474483
end rtl;

hdl/ip/vhd/i2c/controller/txn_layer/i2c_ctrl_txn_layer.vhd

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ begin
139139

140140
-- watch for a new command to arrive then kick off a START
141141
when IDLE =>
142-
if cmd_valid = '1' and ll_ready = '1' then
142+
if cmd_valid = '1' and ll_ready = '1' and abort = '0' then
143143
v.state := START;
144144
v.cmd := cmd;
145145
end if;

hdl/ip/vhd/i2c/controller/txn_layer/sims/i2c_ctrl_txn_layer_tb.gtkw

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
[*]
22
[*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3-
[*] Fri Feb 7 21:04:37 2025
3+
[*] Wed Mar 12 14:09:08 2025
44
[*]
5-
[dumpfile] "/home/aaron/Oxide/git/quartz/vunit_out/test_output/lib.i2c_ctrl_txn_layer_tb.abort_transaction_5030992a55fd175f5a816e54580c7a725cf50715/nvc/i2c_ctrl_txn_layer_tb.fst"
6-
[dumpfile_mtime] "Fri Feb 7 16:35:44 2025"
7-
[dumpfile_size] 5828
5+
[dumpfile] "/home/aaron/Oxide/git/quartz/vunit_out/test_output/lib.i2c_ctrl_txn_layer_tb.write_and_read_one_byte_344489395475d58724aafb4fc63c500bd501b62f/nvc/i2c_ctrl_txn_layer_tb.fst"
6+
[dumpfile_mtime] "Wed Mar 12 14:04:36 2025"
7+
[dumpfile_size] 4352
88
[savefile] "/home/aaron/Oxide/git/quartz/hdl/ip/vhd/i2c/controller/txn_layer/sims/i2c_ctrl_txn_layer_tb.gtkw"
99
[timestart] 0
10-
[size] 1344 1283
11-
[pos] 2559 23
12-
*-34.710972 86100000000 -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
10+
[size] 1376 1283
11+
[pos] 2514 -22
12+
*-32.845215 8124000000 -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] i2c_ctrl_txn_layer_tb.
1414
[treeopen] i2c_ctrl_txn_layer_tb.th.
1515
[treeopen] i2c_ctrl_txn_layer_tb.th.dut.
@@ -47,11 +47,13 @@ i2c_ctrl_txn_layer_tb.th.dut.abort
4747
-SCL
4848
@28
4949
i2c_ctrl_txn_layer_tb.th.dut.scl_if.i
50+
i2c_ctrl_txn_layer_tb.th.dut.scl_if.o
5051
i2c_ctrl_txn_layer_tb.th.dut.scl_if.oe
5152
@200
5253
-SDA
5354
@28
5455
i2c_ctrl_txn_layer_tb.th.dut.sda_if.i
56+
i2c_ctrl_txn_layer_tb.th.dut.sda_if.o
5557
i2c_ctrl_txn_layer_tb.th.dut.sda_if.oe
5658
@200
5759
-State
@@ -106,6 +108,7 @@ i2c_ctrl_txn_layer_tb.th.dut.i2c_ctrl_link_layer_inst.sm_countdown.counter[7:0]
106108
@28
107109
i2c_ctrl_txn_layer_tb.th.dut.i2c_ctrl_link_layer_inst.sm_countdown.decr
108110
i2c_ctrl_txn_layer_tb.th.dut.i2c_ctrl_link_layer_inst.sm_countdown.load
111+
@29
109112
i2c_ctrl_txn_layer_tb.th.dut.i2c_ctrl_link_layer_inst.sm_countdown.done
110113
@200
111114
-
@@ -127,15 +130,12 @@ i2c_ctrl_txn_layer_tb.th.tx_source_vc.data[7:0]
127130
-target Model
128131
-SCL
129132
@28
130-
i2c_ctrl_txn_layer_tb.th.target.scl_if.i
131-
i2c_ctrl_txn_layer_tb.th.target.scl_if.o
132-
i2c_ctrl_txn_layer_tb.th.target.scl_if.oe
133+
i2c_ctrl_txn_layer_tb.th.scl
133134
@200
134135
-SDA
135136
@28
136-
i2c_ctrl_txn_layer_tb.th.target.sda_if.i
137-
i2c_ctrl_txn_layer_tb.th.target.sda_if.o
138-
i2c_ctrl_txn_layer_tb.th.target.sda_if.oe
137+
i2c_ctrl_txn_layer_tb.th.sda
138+
i2c_ctrl_txn_layer_tb.th.target.sda_oe
139139
i2c_ctrl_txn_layer_tb.th.target.state
140140
i2c_ctrl_txn_layer_tb.th.target.start_condition
141141
i2c_ctrl_txn_layer_tb.th.target.stop_condition

hdl/ip/vhd/i2c/controller/txn_layer/sims/i2c_ctrl_txn_layer_th.vhd

+4-3
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@ begin
122122
data => rx_data_stream.data
123123
);
124124

125-
-- wire the bus to the controller's tristate ports
126-
scl <= ctrl_scl_tristate.o when ctrl_scl_tristate.oe else 'H';
127-
sda <= ctrl_sda_tristate.o when ctrl_sda_tristate.oe else 'H';
125+
-- wire the bus to the controller's tristate ports, the controller is push-pull so we make it
126+
-- open-drain here
127+
scl <= '0' when (ctrl_scl_tristate.oe = '1' and ctrl_scl_tristate.o = '0') else 'H';
128+
sda <= '0' when (ctrl_sda_tristate.oe = '1' and ctrl_sda_tristate.o = '0') else 'H';
128129
ctrl_scl_tristate.i <= scl;
129130
ctrl_sda_tristate.i <= sda;
130131

0 commit comments

Comments
 (0)