Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron-Hartwig committed Nov 21, 2024
1 parent 5ec472d commit 7242305
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 102 deletions.
19 changes: 10 additions & 9 deletions hdl/ip/vhd/i2c/i2c_core.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ 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;
begin
v := sm_reg;
is_read := '0' when sm_reg.cmd.op = WRITE else '1';

-- defaults
v.tx_byte_valid := '0';
txd_valid_v := '0';

case sm_reg.state is

Expand All @@ -135,8 +135,8 @@ begin

-- wait for link layer to finish START sequence and load up the address byte
when WAIT_START =>
v.tx_byte := sm_reg.cmd.addr & is_read;
v.tx_byte_valid := '1';
txd_v := sm_reg.cmd.addr & is_read;
txd_valid_v := '1';
if ll_ready then
v.state := WAIT_ADDR_ACK;
end if;
Expand All @@ -153,8 +153,8 @@ begin
else
v.state := WAIT_WRITE_ACK;
-- load up the register address
v.tx_byte := sm_reg.cmd.reg;
v.tx_byte_valid := '1';
txd_v := sm_reg.cmd.reg;
txd_valid_v := '1';
end if;
else
-- TODO: address nack error
Expand All @@ -176,8 +176,6 @@ begin
when WRITE =>
if tx_st_if.valid then
v.state := WAIT_WRITE_ACK;
v.tx_byte := tx_st_if.data;
v.tx_byte_valid := '1';
end if;

-- take action based off of the operation type and the ACK
Expand Down Expand Up @@ -218,6 +216,9 @@ begin
v.do_start := '1' when v.state = START else '0';
v.do_stop := '1' when v.state = STOP else '0';

v.tx_byte := txd_v when sm_reg.state = WAIT_START or sm_reg.state = WAIT_ADDR_ACK else tx_st_if.data;
v.tx_byte_valid := txd_valid_v when sm_reg.state = WAIT_START or sm_reg.state = WAIT_ADDR_ACK else tx_st_if.valid;

sm_reg_next <= v;
end process;

Expand Down
61 changes: 28 additions & 33 deletions hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ architecture rtl of i2c_link_layer is
count_load : std_logic;
count_decr : std_logic;
count_clr : std_logic;
sda_change : std_logic;
sda_changed : std_logic;
ack_sending : std_logic;

-- interfaces
Expand All @@ -121,7 +121,7 @@ architecture rtl of i2c_link_layer is
'0', -- count_load
'0', -- count_decr
'0', -- count_clr
'0', -- sda_change
'0', -- sda_changed
'0', -- ack_sending
(others => '0'),-- rx_data
'0', -- rx_data_valid
Expand Down Expand Up @@ -202,7 +202,7 @@ begin
port map (
clk => clk,
reset => reset,
enable => sm_reg.sda_change,
enable => not sm_reg.sda_changed,
strobe => transition_sda
);

Expand Down Expand Up @@ -240,11 +240,10 @@ begin
v.rx_ack_valid := '0';
v.rx_data_valid := '0';

-- after a scl fedge sda should be updated
if scl_fedge = '1' and (sm_reg.state = BYTE_TX or
sm_reg.state = ACK_TX or
sm_reg.state = STOP_SDA) then
v.sda_change := '1';
if scl_fedge then
v.sda_changed := '0';
elsif transition_sda then
v.sda_changed := '1';
end if;

case sm_reg.state is
Expand Down Expand Up @@ -307,23 +306,22 @@ begin
-- data to transmit
v.state := BYTE_TX;
v.tx_data := tx_data;
v.sda_change := '1';
else
-- if nothing else, read
v.state := BYTE_RX;
v.state := BYTE_RX;
end if;

-- Clock out a byte and then wait for an ACK
when BYTE_TX =>
if transition_sda = '1' and sm_reg.bits_shifted = 8 then
v.state := ACK_RX;
v.sda_change := '0';
v.bits_shifted := 0;
elsif transition_sda = '1' and sm_reg.sda_change = '1' then
v.sda_oe := not sm_reg.tx_data(0);
v.tx_data := '1' & sm_reg.tx_data(7 downto 1);
v.sda_change := '0';
v.bits_shifted := sm_reg.bits_shifted + 1;
when BYTE_TX =>
if transition_sda = '1' then
if sm_reg.bits_shifted = 8 then
v.state := ACK_RX;
v.bits_shifted := 0;
else
v.sda_oe := not sm_reg.tx_data(0);
v.tx_data := '1' & sm_reg.tx_data(7 downto 1);
v.bits_shifted := sm_reg.bits_shifted + 1;
end if;
end if;

-- See if the target ACKs
Expand All @@ -339,7 +337,6 @@ begin
-- Clock in a byte and then send an ACK
when BYTE_RX =>
v.sda_oe := '0';
-- v.rx_data_valid := '0';

if sm_reg.bits_shifted = 8 then
v.state := ACK_TX;
Expand All @@ -353,18 +350,16 @@ begin
-- ACK the target
when ACK_TX =>
-- at the first transition_sda pulse start sending the (N)ACK
if transition_sda = '1' and sm_reg.sda_change = '1' then
v.sda_oe := tx_ack;
v.ack_sending := '1';
v.sda_change := '0';
end if;

-- at the next transition point release the bus
if transition_sda = '1' and sm_reg.ack_sending = '1' then
v.sda_oe := '0';
v.ack_sending := '0';
v.sda_change := '0';
v.state := HANDLE_NEXT_PRE;
if transition_sda = '1' then
if sm_reg.ack_sending = '0' then
v.sda_oe := tx_ack;
v.ack_sending := '1';
else
-- at the next transition point release the bus
v.sda_oe := '0';
v.ack_sending := '0';
v.state := HANDLE_NEXT_PRE;
end if;
end if;

-- drive SDA through final SCL cycle
Expand Down
110 changes: 77 additions & 33 deletions hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use ieee.numeric_std.all;
library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.com_context;
context vunit_lib.vc_context;
use vunit_lib.sync_pkg.all;

use work.tristate_if_pkg.all;
Expand Down Expand Up @@ -42,23 +43,21 @@ architecture model of i2c_peripheral is

signal state : state_t := IDLE;

signal start_condition : std_logic := '0';
signal stop_condition : std_logic := '0';
signal start_condition : boolean := FALSE;
signal stop_condition : boolean := FALSE;
signal sda_last : std_logic := '1';
signal rx_data : std_logic_vector(7 downto 0) := (others => '0');
signal rx_bit_count : unsigned(3 downto 0) := (others => '0');
signal rx_done : std_logic := '0';
signal rx_ackd : boolean := FALSE;
signal tx_data : std_logic_vector(7 downto 0) := (others => '0');
signal tx_bit_count : unsigned(3 downto 0) := (others => '0');
signal tx_done : std_logic := '0';

signal in_receiving_state : boolean := FALSE;
signal in_transmitting_state : boolean := FALSE;

signal scl_oe : std_logic := '0';
signal sda_oe : std_logic := '0';

signal reg_addr : unsigned(7 downto 0) := (others => '0');
signal is_addr_set : boolean := FALSE;
begin
-- I2C interface is open-drain
scl_if.o <= '0';
Expand All @@ -67,8 +66,8 @@ begin
scl_if.oe <= scl_oe;
sda_if.oe <= sda_oe;

start_condition <= '1' when sda_last = '1' and sda_if.i = '0' and scl_if.i = '1' else '0';
stop_condition <= '1' when sda_last = '0' and sda_if.i = '1' and scl_if.i = '1' else '0';
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;
Expand All @@ -88,6 +87,7 @@ begin
transaction_sm: process
variable event_msg : msg_t;
variable is_read : boolean := FALSE;
variable stop_during_write : boolean := FALSE;
begin
-- IDLE: wait for a START
wait on start_condition;
Expand All @@ -97,7 +97,6 @@ begin

-- GET_BYTE: check address and acknowledge appropriately
wait on rx_done;
wait until falling_edge(scl_if.i);
if rx_data(7 downto 1) = address(i2c_peripheral_vc) then
state <= SEND_ACK;
is_read := rx_data(0) = '1';
Expand All @@ -110,6 +109,7 @@ begin
end if;

-- SEND_ACK/NACK: acknowledge the START byte
wait on tx_done;
wait until falling_edge(scl_if.i);
if state = SEND_ACK then
if is_read then
Expand All @@ -121,7 +121,7 @@ begin
-- NACK'd
state <= GET_STOP;
end if;
wait until rising_edge(scl_if.i);
-- wait until rising_edge(scl_if.i);

if is_read then
-- loop to respond to a controller read request
Expand All @@ -137,69 +137,113 @@ begin
-- 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;
end if;

-- GET_BYTE: get the byte and then send an acknowledge
wait on rx_done;
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;

if state /= GET_STOP then
wait on tx_done;
wait until falling_edge(scl_if.i);
state <= GET_BYTE;

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

-- GET_STOP: wait for a STOP
wait on stop_condition;
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;
end process;

in_receiving_state <= state = GET_BYTE or state = GET_ACK;
rx_done <= '1' when (state = GET_BYTE and rx_bit_count = 8) or
(state = GET_ACK and rx_bit_count = 1)
(state = GET_ACK and rx_bit_count = 1) or
stop_condition
else '0';

receive_sm: process
variable data_next : std_logic_vector(7 downto 0) := (others => '0');
begin
wait until rising_edge(scl_if.i);

if rx_done then
rx_bit_count <= (others => '0');
elsif state = GET_ACK then
if state = GET_ACK then
-- '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
data_next := sda_if.i & rx_data(7 downto 1);
rx_bit_count <= rx_bit_count + 1;
end if;

rx_data <= data_next;

wait until falling_edge(scl_if.i);

if state = GET_ACK then
rx_bit_count <= to_unsigned(1, rx_bit_count'length);
elsif state = GET_BYTE then
rx_bit_count <= rx_bit_count + 1;
else
rx_bit_count <= (others => '0');
end if;
end process;


in_transmitting_state <= state = SEND_ACK or state = SEND_NACK or state = SEND_BYTE;
tx_done <= '1' when ((state = SEND_ACK or state = SEND_ACK) and tx_bit_count = 1) or
(state = SEND_BYTE and tx_bit_count = 8)
else '0';

transmit_sm: process
variable data_next : std_logic_vector(7 downto 0) := X"CC";
variable data_v : std_logic_vector(7 downto 0) := X"CC";
begin
if tx_done then
tx_bit_count <= (others => '0');
end if;

wait until falling_edge(scl_if.i);
-- delay the SDA transition to a bit after SCL falls to allow the controller to release SDA
wait for 25 ns;

if tx_done then
-- release bus
sda_oe <= '0';
elsif state = SEND_ACK or state = SEND_NACK then
wait for 100 ns;
if state = SEND_ACK or state = SEND_NACK then
sda_oe <= '1' when state = SEND_ACK else '0';
tx_bit_count <= to_unsigned(1, tx_bit_count'length);
elsif state = SEND_BYTE then
sda_oe <= not data_next(to_integer(tx_bit_count));
tx_bit_count <= tx_bit_count + 1;
if tx_bit_count = 0 then
data_v := read_word(i2c_peripheral_vc.p_buffer.p_memory_ref, natural(to_integer(reg_addr)), 1);
end if;
sda_oe <= not data_v(to_integer(tx_bit_count));
else
-- release the bus
sda_oe <= '0';
end if;

wait until rising_edge(scl_if.i);

-- update counter once bit has been sampled
if state = SEND_ACK or state = SEND_NACK then
tx_bit_count <= to_unsigned(1, tx_bit_count'length);
elsif state = SEND_BYTE then
tx_bit_count <= tx_bit_count + 1;
else
tx_bit_count <= (others => '0');
end if;

end process;

end architecture;
Loading

0 comments on commit 7242305

Please sign in to comment.