Skip to content

Commit 288d08c

Browse files
committed
I2C controller: add ability to abort transactions
1 parent f87b7d2 commit 288d08c

9 files changed

+236
-54
lines changed

hdl/ip/vhd/i2c/common/sims/i2c_cmd_vc.vhd

+10-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ entity i2c_cmd_vc is
2323
port (
2424
cmd : out cmd_t := CMD_RESET;
2525
valid : out std_logic := '0';
26+
abort : out std_logic := '0';
2627
ready : in std_logic;
2728
);
2829
end entity;
@@ -57,8 +58,16 @@ begin
5758
command.len := pop(msg);
5859
cmd <= command;
5960
valid <= '1';
60-
wait until ready;
61+
62+
-- once the command is accepted, release valid
63+
wait until not ready;
6164
valid <= '0';
65+
elsif msg_type = abort_msg then
66+
abort <= '1';
67+
68+
-- once the core is ready, release abort
69+
wait until ready;
70+
abort <= '0';
6271
else
6372
unexpected_msg_type(msg_type);
6473
end if;

hdl/ip/vhd/i2c/common/sims/i2c_cmd_vc_pkg.vhd

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-- License, v. 2.0. If a copy of the MPL was not distributed with this
33
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
44
--
5-
-- Copyright 2024 Oxide Computer Company
5+
-- Copyright 2025 Oxide Computer Company
66

77
library ieee;
88
use ieee.std_logic_1164.all;
@@ -32,14 +32,20 @@ package i2c_cmd_vc_pkg is
3232
logger : logger_t := i2c_cmd_vc_logger;
3333
) return i2c_cmd_vc_t;
3434

35-
constant push_i2c_cmd_msg : msg_type_t := new_msg_type("push_i2c_cmd");
35+
constant push_i2c_cmd_msg : msg_type_t := new_msg_type("push_i2c_cmd");
36+
constant abort_msg : msg_type_t := new_msg_type("abort");
3637

3738
procedure push_i2c_cmd(
3839
signal net : inout network_t;
3940
i2c_cmd_vc : i2c_cmd_vc_t;
4041
cmd : cmd_t;
4142
);
4243

44+
procedure push_abort(
45+
signal net : inout network_t;
46+
i2c_cmd_vc : i2c_cmd_vc_t;
47+
);
48+
4349
end package;
4450

4551
package body i2c_cmd_vc_pkg is
@@ -82,5 +88,13 @@ package body i2c_cmd_vc_pkg is
8288
send(net, i2c_cmd_vc.p_actor, msg);
8389
end;
8490

91+
procedure push_abort(
92+
signal net : inout network_t;
93+
i2c_cmd_vc : i2c_cmd_vc_t;
94+
) is
95+
variable msg : msg_t := new_msg(abort_msg);
96+
begin
97+
send(net, i2c_cmd_vc.p_actor, msg);
98+
end;
8599

86100
end package body;

hdl/ip/vhd/i2c/common/sims/i2c_target_vc.vhd

+38-20
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,21 @@ begin
9898
state <= GET_START_BYTE;
9999

100100
when GET_START_BYTE =>
101-
wait on rx_done;
102-
if rx_data(7 downto 1) = address(i2c_target_vc) then
103-
state <= SEND_ACK;
104-
is_read := rx_data(0) = '1';
105-
event_msg := new_msg(address_matched);
106-
send(net, i2c_target_vc.p_actor, event_msg);
101+
wait until rx_done or stop_condition;
102+
103+
if stop_condition then
104+
state <= GET_STOP;
107105
else
108-
state <= SEND_NACK;
109-
event_msg := new_msg(address_different);
110-
send(net, i2c_target_vc.p_actor, event_msg);
106+
if rx_data(7 downto 1) = address(i2c_target_vc) then
107+
state <= SEND_ACK;
108+
is_read := rx_data(0) = '1';
109+
event_msg := new_msg(address_matched);
110+
send(net, i2c_target_vc.p_actor, event_msg);
111+
else
112+
state <= SEND_NACK;
113+
event_msg := new_msg(address_different);
114+
send(net, i2c_target_vc.p_actor, event_msg);
115+
end if;
111116
end if;
112117

113118
when GET_BYTE =>
@@ -135,30 +140,43 @@ begin
135140
reg_addr_v := unsigned(rx_data);
136141
end if;
137142
end if;
138-
143+
139144
when SEND_ACK =>
140-
wait until falling_edge(scl_if.i) and tx_done;
141-
if is_read then
142-
state <= SEND_BYTE;
145+
wait until (falling_edge(scl_if.i) and tx_done) or stop_condition;
146+
147+
if stop_condition then
148+
state <= GET_STOP;
143149
else
144-
state <= GET_BYTE;
150+
if is_read then
151+
state <= SEND_BYTE;
152+
else
153+
state <= GET_BYTE;
154+
end if;
145155
end if;
146156

147157
when SEND_NACK =>
148-
wait until falling_edge(scl_if.i);
158+
wait until falling_edge(scl_if.i) or stop_condition;
149159
state <= GET_STOP;
150160

151161
when SEND_BYTE =>
152-
wait until falling_edge(scl_if.i) and tx_done;
153-
reg_addr_v := reg_addr + 1;
154-
state <= GET_ACK;
162+
wait until (falling_edge(scl_if.i) and tx_done) or stop_condition;
163+
164+
if stop_condition then
165+
state <= GET_STOP;
166+
else
167+
reg_addr_v := reg_addr + 1;
168+
state <= GET_ACK;
169+
end if;
155170

156171
when GET_ACK =>
157-
wait on rx_done;
172+
wait until rx_done;
158173
state <= SEND_BYTE when rx_ackd else GET_STOP;
159174

160175
when GET_STOP =>
161-
wait until (stop_condition or stop_during_write);
176+
if not stop_condition then
177+
wait until stop_condition or stop_during_write;
178+
end if;
179+
162180
event_msg := new_msg(got_stop);
163181
send(net, i2c_target_vc.p_actor, event_msg);
164182
state <= IDLE;

hdl/ip/vhd/i2c/common/sims/i2c_target_vc_pkg.vhd

+27-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-- License, v. 2.0. If a copy of the MPL was not distributed with this
33
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
44
--
5-
-- Copyright 2024 Oxide Computer Company
5+
-- Copyright 2025 Oxide Computer Company
66

77
library ieee;
88
use ieee.std_logic_1164.all;
@@ -69,6 +69,12 @@ package i2c_target_vc_pkg is
6969
variable addr : std_logic_vector;
7070
);
7171

72+
procedure expect_abort(
73+
signal net : inout network_t;
74+
constant vc : i2c_target_vc_t;
75+
variable aborted : boolean;
76+
);
77+
7278
end package;
7379

7480
package body i2c_target_vc_pkg is
@@ -115,8 +121,11 @@ package body i2c_target_vc_pkg is
115121
variable matched : boolean;
116122
begin
117123
receive(net, vc.p_actor, msg);
124+
-- a bit of a hack since check_equal is not implemented for msg_t
118125
matched := message_type(msg) = expected_msg;
119-
check_true(matched, "Received message did not match expected message.");
126+
if not matched then
127+
unexpected_msg_type(message_type(msg));
128+
end if;
120129
end procedure;
121130

122131
procedure expect_stop (
@@ -162,4 +171,20 @@ package body i2c_target_vc_pkg is
162171
end if;
163172
end procedure;
164173

174+
procedure expect_abort (
175+
signal net : inout network_t;
176+
constant vc : i2c_target_vc_t;
177+
variable aborted : boolean;
178+
) is
179+
variable msg : msg_t;
180+
begin
181+
if aborted then
182+
-- if we are aborted, expect the next event to to STOP
183+
expect_stop(net, vc);
184+
else
185+
-- otherwise, pop an event to get it out of the queue
186+
receive(net, vc.p_actor, msg);
187+
end if;
188+
end procedure;
189+
165190
end package body;

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

+32-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
--
55
-- Copyright 2025 Oxide Computer Company
66

7+
-- I2C Control Link Layer
8+
--
9+
-- 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.
11+
--
12+
-- Notes:
13+
-- - This block currently does not support block stretching.
14+
-- - This block currently does not do ackknowledge-polling after a write.
15+
716
library ieee;
817
use ieee.std_logic_1164.all;
918
use ieee.numeric_std_unsigned.all;
@@ -101,6 +110,7 @@ architecture rtl of i2c_ctrl_link_layer is
101110
transition_sda_cntr_en : std_logic;
102111
ack_sending : std_logic;
103112
sr_scl_fedge_seen : std_logic;
113+
stop_requested : std_logic;
104114

105115
-- interfaces
106116
rx_data : std_logic_vector(7 downto 0);
@@ -125,6 +135,7 @@ architecture rtl of i2c_ctrl_link_layer is
125135
'0', -- transition_sda_cntr_en
126136
'0', -- ack_sending
127137
'0', -- sr_scl_fedge_seen
138+
'0', -- stop_requested
128139
(others => '0'),-- rx_data
129140
'0', -- rx_data_valid
130141
(others => '0'),-- tx_data
@@ -255,6 +266,12 @@ begin
255266
v.transition_sda_cntr_en := '0';
256267
end if;
257268

269+
-- Latch if a stop is requested to handle the case when it may need to happen before the end
270+
-- of the transaction.
271+
if sm_reg.stop_requested = '0' then
272+
v.stop_requested := '1' when tx_stop = '1' and txn_next_valid = '1' else '0';
273+
end if;
274+
258275
case sm_reg.state is
259276

260277
-- Ready and awaiting the next transaction
@@ -311,12 +328,14 @@ begin
311328
end if;
312329

313330
when HANDLE_NEXT =>
314-
if txn_next_valid then
331+
if v.stop_requested then
332+
v.state := STOP_SDA;
333+
elsif txn_next_valid then
315334
if tx_start then
316335
-- A repeated start was issued mid-transaction
317-
v.state := WAIT_REPEAT_START;
318-
v.counter := START_SETUP_HOLD_TICKS;
319-
v.count_load := '1';
336+
v.state := WAIT_REPEAT_START;
337+
v.counter := START_SETUP_HOLD_TICKS;
338+
v.count_load := '1';
320339
elsif tx_stop then
321340
v.state := STOP_SDA;
322341
elsif tx_data_valid then
@@ -335,6 +354,8 @@ begin
335354
if sm_reg.bits_shifted = 8 then
336355
v.state := ACK_RX;
337356
v.bits_shifted := 0;
357+
elsif v.stop_requested then
358+
v.state := STOP_SDA;
338359
else
339360
v.sda_oe := not sm_reg.tx_data(7);
340361
v.tx_data := sm_reg.tx_data(sm_reg.tx_data'high-1 downto sm_reg.tx_data'low) & '1';
@@ -347,7 +368,7 @@ begin
347368
v.sda_oe := '0';
348369

349370
if scl_redge then
350-
v.state := HANDLE_NEXT;
371+
v.state := STOP_SDA when v.stop_requested else HANDLE_NEXT;
351372
v.rx_ack := not sda_in_syncd;
352373
v.rx_ack_valid := '1';
353374
end if;
@@ -370,13 +391,14 @@ begin
370391
-- at the first transition_sda pulse start sending the (N)ACK
371392
if transition_sda = '1' then
372393
if sm_reg.ack_sending = '0' then
373-
v.sda_oe := tx_ack;
394+
v.sda_oe := '1' when (tx_ack = '1' and v.stop_requested = '0')
395+
else '0';
374396
v.ack_sending := '1';
375397
else
376398
-- at the next transition point release the bus
377399
v.sda_oe := '0';
378400
v.ack_sending := '0';
379-
v.state := HANDLE_NEXT;
401+
v.state := STOP_SDA when sm_reg.stop_requested else HANDLE_NEXT;
380402
end if;
381403
end if;
382404

@@ -405,7 +427,9 @@ begin
405427
end case;
406428

407429
-- next state logic
408-
v.ready := '1' when v.state = IDLE or v.state = HANDLE_NEXT else '0';
430+
v.ready := '1' when v.state = IDLE or
431+
(v.state = HANDLE_NEXT and sm_reg.stop_requested = '0')
432+
else '0';
409433

410434
sm_reg_next <= v;
411435
end process;

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

+18-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
--
55
-- Copyright 2025 Oxide Computer Company
66

7+
-- I2C Controller Transaction Layer
8+
--
9+
-- This block orchestrates the actions to complete a supplied I2C command (`cmd`).
10+
--
11+
-- Notes:
12+
-- - For write transactions data is expected to be streamed in via `tx_st_if`
13+
-- - For read transactions data is expected to be streamed in via `rx_st_if`
14+
-- - A pulse of the `abort` signal will result in an I2C transaction being ended as fast as possible
15+
716
library ieee;
817
use ieee.std_logic_1164.all;
918
use ieee.numeric_std_unsigned.all;
@@ -13,7 +22,7 @@ use work.tristate_if_pkg.all;
1322

1423
use work.i2c_common_pkg.all;
1524

16-
entity i2c_txn_layer is
25+
entity i2c_ctrl_txn_layer is
1726
generic (
1827
CLK_PER_NS : positive;
1928
MODE : mode_t;
@@ -29,6 +38,7 @@ entity i2c_txn_layer is
2938
-- I2C command interface
3039
cmd : in cmd_t;
3140
cmd_valid : in std_logic;
41+
abort : in std_logic;
3242
core_ready : out std_logic;
3343

3444
-- Transmit data stream
@@ -39,7 +49,7 @@ entity i2c_txn_layer is
3949
);
4050
end entity;
4151

42-
architecture rtl of i2c_txn_layer is
52+
architecture rtl of i2c_ctrl_txn_layer is
4353

4454
type state_t is (
4555
IDLE,
@@ -223,6 +233,12 @@ begin
223233
end if;
224234
end case;
225235

236+
-- if a transaction is in progress and abort is asserted we should immediate STOP
237+
if abort = '1' and v.state /= IDLE and v.state /= STOP and v.state /= WAIT_STOP then
238+
v.state := STOP;
239+
v.next_valid := '1';
240+
end if;
241+
226242
-- next state logic
227243
v.do_start := '1' when v.state = START else '0';
228244
v.do_stop := '1' when v.state = STOP else '0';
@@ -235,7 +251,6 @@ begin
235251
sm_reg_next <= v;
236252
end process;
237253

238-
239254
reg_sm: process(clk, reset)
240255
begin
241256
if reset then

0 commit comments

Comments
 (0)