Skip to content

Commit

Permalink
basics are all working
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron-Hartwig committed Nov 26, 2024
1 parent 75a5471 commit 91bfc5c
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 125 deletions.
20 changes: 10 additions & 10 deletions hdl/ip/vhd/i2c/i2c_core.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ begin
);

reg_sm_next: process(all)
variable v : sm_reg_t;
variable is_read : std_logic;
variable txd_v : std_logic_vector(7 downto 0);
variable txd_valid_v: std_logic;
variable v : sm_reg_t;
variable is_read : std_logic;
variable txd_v : std_logic_vector(7 downto 0);
variable txd_valid_v : std_logic;
begin
v := sm_reg;
is_read := '0' when sm_reg.cmd.op = WRITE else '1';
is_read := '1' when sm_reg.cmd.op = READ or sm_reg.in_random_read else '0';
txd_valid_v := '0';

case sm_reg.state is
Expand All @@ -131,14 +131,14 @@ begin

-- single cycle state to initiate a START
when START =>
v.state := WAIT_START;
v.state := WAIT_START;

-- wait for link layer to finish START sequence and load up the address byte
when WAIT_START =>
txd_v := sm_reg.cmd.addr & is_read;
txd_valid_v := '1';
if ll_ready then
v.state := WAIT_ADDR_ACK;
v.state := WAIT_ADDR_ACK;
end if;

-- wait for address byte to have been sent and for the peripheral to ACK
Expand All @@ -147,11 +147,11 @@ begin
if ll_ackd then
v.bytes_done := (others => '0');
if sm_reg.cmd.op = Read or sm_reg.in_random_read then
v.state := READ;
v.state := READ;
-- nack after the first byte when only reading one byte
v.do_ack := '0' when sm_reg.cmd.len = 1 else '1';
v.do_ack := '0' when sm_reg.cmd.len = 1 else '1';
else
v.state := WAIT_WRITE_ACK;
v.state := WAIT_WRITE_ACK;
-- load up the register address
txd_v := sm_reg.cmd.reg;
txd_valid_v := '1';
Expand Down
21 changes: 16 additions & 5 deletions hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ architecture rtl of i2c_link_layer is
WAIT_TBUF,
START_SETUP,
START_HOLD,
WAIT_REPEAT_START,
HANDLE_NEXT_PRE,
HANDLE_NEXT,
BYTE_TX,
Expand Down Expand Up @@ -100,6 +101,7 @@ architecture rtl of i2c_link_layer is
count_clr : std_logic;
sda_changed : std_logic;
ack_sending : std_logic;
scl_fedge_seen : std_logic;

-- interfaces
rx_data : std_logic_vector(7 downto 0);
Expand All @@ -123,6 +125,7 @@ architecture rtl of i2c_link_layer is
'0', -- count_clr
'0', -- sda_changed
'0', -- ack_sending
'0', -- scl_fedge_seen
(others => '0'),-- rx_data
'0', -- rx_data_valid
(others => '0'),-- tx_data
Expand Down Expand Up @@ -270,9 +273,18 @@ begin
v.count_decr := '1';
end if;

when WAIT_REPEAT_START =>
if not sm_reg.scl_fedge_seen then
v.scl_fedge_seen := scl_fedge;
elsif scl_redge then
v.state := START_SETUP;
v.scl_active := '0';
end if;

-- In the event of a repeated START account for setup requirements
when START_SETUP =>
v.sda_oe := '0';

if sm_count_done then
v.state := START_HOLD;
v.counter := START_SETUP_HOLD_TICKS;
Expand All @@ -297,9 +309,9 @@ begin
when HANDLE_NEXT =>
if tx_start then
-- A repeated start was issued mid-transaction
v.state := START_SETUP;
v.counter := START_SETUP_HOLD_TICKS;
v.count_load := '1';
v.state := WAIT_REPEAT_START;
v.counter := START_SETUP_HOLD_TICKS;
v.count_load := '1';
elsif tx_stop then
v.state := STOP_SDA;
elsif tx_data_valid then
Expand Down Expand Up @@ -379,8 +391,7 @@ begin

when STOP_SETUP =>
if sm_count_done then
v.state := IDLE;
v.sda_oe := '0';
v := SM_REG_RESET;
else
v.count_decr := '1';
end if;
Expand Down
175 changes: 83 additions & 92 deletions hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ architecture model of i2c_peripheral is

type state_t is (
IDLE,
START,
SEND_ACK,
SEND_NACK,
SEND_BYTE,
GET_START_BYTE,
GET_BYTE,
GET_ACK,
GET_STOP
Expand Down Expand Up @@ -71,14 +73,6 @@ begin
start_condition <= sda_last = '1' and sda_if.i = '0' and scl_if.i = '1';
stop_condition <= sda_last = '0' and sda_if.i = '1' and scl_if.i = '1';

-- message_handler: process
-- variable msg_type : msg_type_t;
-- variable request_msg, reply_msg : msg_t;
-- begin
-- receive(net, i2c_peripheral_vc.p_actor, request_msg);
-- msg_type := message_type(request_msg);
-- end process;

-- sample SDA regularly to catch transitions
sda_monitor: process
begin
Expand All @@ -87,100 +81,96 @@ begin
end process;

transaction_sm: process
variable event_msg : msg_t;
variable is_read : boolean := FALSE;
variable stop_during_write : boolean := FALSE;
variable event_msg : msg_t;
variable is_read : boolean := FALSE;
variable reg_addr_v : unsigned(7 downto 0) := (others => '0');
variable stop_during_write : boolean := FALSE;
begin
-- IDLE: wait for a START
wait on start_condition;
event_msg := new_msg(got_start);
send(net, i2c_peripheral_vc.p_actor, event_msg);
state <= GET_BYTE;

-- GET_BYTE: check address and acknowledge appropriately
wait on rx_done;
if rx_data(7 downto 1) = address(i2c_peripheral_vc) then
state <= SEND_ACK;
is_read := rx_data(0) = '1';
event_msg := new_msg(address_matched);
send(net, i2c_peripheral_vc.p_actor, event_msg);
else
state <= SEND_NACK;
event_msg := new_msg(address_different);
send(net, i2c_peripheral_vc.p_actor, event_msg);
end if;

-- SEND_ACK/NACK: acknowledge the START byte
wait until falling_edge(scl_if.i);
if state = SEND_ACK then
if is_read then
state <= SEND_BYTE;
wait for 1 ns;
else
state <= GET_BYTE;
end if;
else
-- NACK'd
state <= GET_STOP;
end if;

if is_read then
-- loop to respond to a controller read request
while state /= GET_STOP loop
-- SEND_BYTE: send the byte and then wait for an acknowledge
wait on tx_done;
wait until falling_edge(scl_if.i);
state <= GET_ACK;

-- GET_ACK: see if the controller wants to continue reading or is finished
case state is

when IDLE =>
wait on start_condition;
state <= START;

when START =>
event_msg := new_msg(got_start);
send(net, i2c_peripheral_vc.p_actor, event_msg);
state <= GET_START_BYTE;

when GET_START_BYTE =>
wait on rx_done;
state <= SEND_BYTE when rx_ackd else GET_STOP;
-- the loop condition needs this to realize when state gets set to GET_STOP
wait for 1 ns;
end loop;
else
-- loop to respond to a controller write request
while state /= GET_STOP loop
if stop_condition then
state <= GET_STOP;
-- the loop condition needs this to realize when state gets set to GET_STOP
wait for 1 ns;
if rx_data(7 downto 1) = address(i2c_peripheral_vc) then
state <= SEND_ACK;
is_read := rx_data(0) = '1';
event_msg := new_msg(address_matched);
send(net, i2c_peripheral_vc.p_actor, event_msg);
else
state <= SEND_NACK;
event_msg := new_msg(address_different);
send(net, i2c_peripheral_vc.p_actor, event_msg);
end if;

-- GET_BYTE: get the byte and then send an acknowledge
wait until rx_done or stop_condition;
state <= GET_STOP when stop_condition else SEND_ACK;
-- the loop condition needs this to realize when state gets set to GET_STOP
wait for 1 ns;
when GET_BYTE =>
wait until rx_done or start_condition or stop_condition;

if state /= GET_STOP then
wait on tx_done;
wait until falling_edge(scl_if.i);
state <= GET_BYTE;
if start_condition then
state <= START;
elsif stop_condition then
state <= GET_STOP;
stop_during_write := TRUE;
else
state <= SEND_ACK;

if addr_incr then
reg_addr_v := reg_addr + 1;
end if;

if addr_set then
write_word(memory(i2c_peripheral_vc), to_integer(reg_addr), rx_data);
write_word(memory(i2c_peripheral_vc), to_integer(reg_addr_v), rx_data);
event_msg := new_msg(got_byte);
send(net, i2c_peripheral_vc.p_actor, event_msg);
if addr_incr then
reg_addr <= reg_addr + 1;
end if;
addr_incr <= TRUE;
else
addr_set <= TRUE;
reg_addr <= unsigned(rx_data);
reg_addr_v := unsigned(rx_data);
end if;
end if;

when SEND_ACK =>
wait until falling_edge(scl_if.i) and tx_done;
if is_read then
state <= SEND_BYTE;
else
stop_during_write := TRUE;
state <= GET_BYTE;
end if;
end loop;
end if;

-- GET_STOP: wait for a STOP
wait until (stop_condition or stop_during_write);
event_msg := new_msg(got_stop);
send(net, i2c_peripheral_vc.p_actor, event_msg);
state <= IDLE;
stop_during_write := FALSE;
when SEND_NACK =>
wait until falling_edge(scl_if.i);
state <= GET_STOP;

when SEND_BYTE =>
wait until falling_edge(scl_if.i) and tx_done;
reg_addr_v := reg_addr + 1;
state <= GET_ACK;

when GET_ACK =>
wait on rx_done;
state <= SEND_BYTE when rx_ackd else GET_STOP;

when GET_STOP =>
wait until (stop_condition or stop_during_write);
event_msg := new_msg(got_stop);
send(net, i2c_peripheral_vc.p_actor, event_msg);
state <= IDLE;
addr_set <= FALSE;
addr_incr <= FALSE;
stop_during_write := FALSE;

end case;

reg_addr <= reg_addr_v;

wait for 1 fs;
end process;

receive_sm: process
Expand All @@ -191,18 +181,19 @@ begin
-- '0' = ACK, '1' = NACK
rx_ackd <= TRUE when sda_if.i = '0' else FALSE;
rx_bit_count <= to_unsigned(1, rx_bit_count'length);
elsif state = GET_BYTE then
elsif state = GET_START_BYTE or state = GET_BYTE then
rx_data <= sda_if.i & rx_data(7 downto 1);
rx_bit_count <= rx_bit_count + 1;
end if;

wait until falling_edge(scl_if.i) or stop_condition;
if stop_condition then
wait until falling_edge(scl_if.i) or start_condition or stop_condition;
if not falling_edge(scl_if.i) then
rx_bit_count <= (others => '0');
wait until falling_edge(scl_if.i);
end if;

if (state = GET_BYTE and rx_bit_count = 8) or (state = GET_ACK and rx_bit_count = 1) then
if ((state = GET_START_BYTE or state = GET_BYTE) and rx_bit_count = 8) or
(state = GET_ACK and rx_bit_count = 1) then
rx_done <= TRUE;
rx_bit_count <= (others => '0');
end if;
Expand Down Expand Up @@ -235,7 +226,7 @@ begin

wait until rising_edge(scl_if.i);

if ((state = SEND_ACK or state = SEND_ACK) and tx_bit_count = 1) or
if ((state = SEND_ACK or state = SEND_NACK) and tx_bit_count = 1) or
(state = SEND_BYTE and tx_bit_count = 8) then
tx_done <= TRUE;
tx_bit_count <= (others => '0');
Expand Down
Loading

0 comments on commit 91bfc5c

Please sign in to comment.