Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron-Hartwig committed Nov 20, 2024
1 parent ae671ec commit 5ec472d
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 81 deletions.
2 changes: 1 addition & 1 deletion hdl/ip/vhd/i2c/i2c_core.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ begin

-- read as many bytes as requested
when READ =>
if sm_reg.cmd.len = sm_reg.bytes_done then
if sm_reg.cmd.len = sm_reg.bytes_done and ll_ready = '1' then
v.state := STOP;
elsif ll_rx_data_valid then
v.bytes_done := sm_reg.bytes_done + 1;
Expand Down
22 changes: 13 additions & 9 deletions hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ architecture rtl of i2c_link_layer is
ack_sending : std_logic;

-- interfaces
data : std_logic_vector(7 downto 0);
rx_data : std_logic_vector(7 downto 0);
rx_data_valid : std_logic;
tx_data : std_logic_vector(7 downto 0);
sda_oe : std_logic;
rx_ack : std_logic;
rx_ack_valid : std_logic;
Expand All @@ -121,9 +122,10 @@ architecture rtl of i2c_link_layer is
'0', -- count_decr
'0', -- count_clr
'0', -- sda_change
'0', -- ack_sending;
(others => '0'),-- data
'0', -- ack_sending
(others => '0'),-- rx_data
'0', -- rx_data_valid
(others => '0'),-- tx_data
'0', -- sda_oe
'0', -- rx_ack
'0' -- rx_ack_valid
Expand Down Expand Up @@ -304,7 +306,7 @@ begin
elsif tx_data_valid then
-- data to transmit
v.state := BYTE_TX;
v.data := tx_data;
v.tx_data := tx_data;
v.sda_change := '1';
else
-- if nothing else, read
Expand All @@ -318,8 +320,8 @@ begin
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.data(0);
v.data := '1' & sm_reg.data(7 downto 1);
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;
end if;
Expand All @@ -336,14 +338,15 @@ begin

-- Clock in a byte and then send an ACK
when BYTE_RX =>
v.sda_oe := '0';
v.sda_oe := '0';
-- v.rx_data_valid := '0';

if sm_reg.bits_shifted = 8 then
v.state := ACK_TX;
v.rx_data_valid := '1';
v.bits_shifted := 0;
elsif scl_redge then
v.data := sda_in_syncd & sm_reg.data(7 downto 1);
v.rx_data := sda_in_syncd & sm_reg.rx_data(7 downto 1);
v.bits_shifted := sm_reg.bits_shifted + 1;
end if;

Expand All @@ -353,6 +356,7 @@ begin
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
Expand Down Expand Up @@ -406,7 +410,7 @@ begin
ready <= sm_reg.ready;
tx_ackd <= sm_reg.rx_ack;
tx_ackd_valid <= sm_reg.rx_ack_valid;
rx_data <= sm_reg.data;
rx_data <= sm_reg.rx_data;
rx_data_valid <= sm_reg.rx_data_valid;

-- I2C is open-drain, so we only ever drive low
Expand Down
3 changes: 1 addition & 2 deletions hdl/ip/vhd/i2c/sims/i2c_cmd_vc_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ package body i2c_cmd_vc_pkg is
variable is_read : boolean;
variable is_random : boolean;
begin
-- breaking down our type since we can't push custom types in VUnit
-- breaking down our type since we can't push enums in VUnit
is_read := false when cmd.op = WRITE else true;
is_random := true when cmd.op = RANDOM_READ else false;
push(msg, is_read);
Expand All @@ -76,7 +76,6 @@ package body i2c_cmd_vc_pkg is
push(msg, cmd.reg);
push(msg, cmd.len);
send(net, i2c_cmd_vc.p_actor, msg);
-- wait_until_idle(net, i2c_cmd_vc.p_actor);
end;


Expand Down
60 changes: 36 additions & 24 deletions hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@ library vunit_lib;
context vunit_lib.com_context;
use vunit_lib.sync_pkg.all;

use work.tristate_if_pkg.all;

use work.i2c_peripheral_pkg.all;

entity i2c_peripheral is
generic (
i2c_peripheral_vc : i2c_peripheral_t
);
port (
-- initilize to 'H' to simulate pull-ups
scl : inout std_logic := 'H';
sda : inout std_logic := 'H';
-- Tri-state signals to I2C interface
scl_if : view tristate_if;
sda_if : view tristate_if;
);
end entity;

Expand All @@ -42,7 +44,7 @@ architecture model of i2c_peripheral is

signal start_condition : std_logic := '0';
signal stop_condition : std_logic := '0';
signal sda_last : std_logic := 'H';
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';
Expand All @@ -53,10 +55,20 @@ architecture model of i2c_peripheral is

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

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

begin
-- I2C interface is open-drain
scl_if.o <= '0';
sda_if.o <= '0';

scl_if.oe <= scl_oe;
sda_if.oe <= sda_oe;

start_condition <= '1' when sda_last = 'H' and sda = '0' and scl = 'H' else '0';
stop_condition <= '1' when sda_last = '0' and sda = 'H' and scl = 'H' else '0';
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';

-- message_handler: process
-- variable msg_type : msg_type_t;
Expand All @@ -70,7 +82,7 @@ begin
sda_monitor: process
begin
wait for 20 ns;
sda_last <= sda;
sda_last <= sda_if.i;
end process;

transaction_sm: process
Expand All @@ -85,8 +97,8 @@ begin

-- GET_BYTE: check address and acknowledge appropriately
wait on rx_done;
wait until falling_edge(scl);
if rx_data(7 downto 1) = i2c_peripheral_vc.address then
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';
event_msg := new_msg(address_matched);
Expand All @@ -98,7 +110,7 @@ begin
end if;

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

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 rising_edge(scl);
wait until rising_edge(scl_if.i);
state <= GET_ACK;

-- GET_ACK: see if the controller wants to continue reading or is finished
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;
end if;

-- GET_STOP: wait for a STOP
wait on stop_condition;
event_msg := new_msg(got_stop);
send(net, i2c_peripheral_vc.p_actor, event_msg);
state <= IDLE;
state <= IDLE;
end process;

in_receiving_state <= state = GET_BYTE or state = GET_ACK;
Expand All @@ -139,19 +153,17 @@ begin

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

if rx_done then
rx_bit_count <= (others => '0');
elsif state = GET_ACK then
-- '0' = ACK, 'H' = NACK
rx_ackd <= TRUE when sda = '0' else FALSE;
-- '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
sda_v := '1' when sda = 'H' else '0';
data_next := sda_v & rx_data(7 downto 1);
data_next := sda_if.i & rx_data(7 downto 1);
rx_bit_count <= rx_bit_count + 1;
end if;

Expand All @@ -171,22 +183,22 @@ begin
tx_bit_count <= (others => '0');
end if;

wait until falling_edge(scl);
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 <= 'H';
sda_oe <= '0';
elsif state = SEND_ACK or state = SEND_NACK then
sda <= '0' when state = SEND_ACK else 'H';
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 <= 'H' when data_next(to_integer(tx_bit_count)) = '1' else '0';
sda_oe <= not data_next(to_integer(tx_bit_count));
tx_bit_count <= tx_bit_count + 1;
end if;

wait until rising_edge(scl);
wait until rising_edge(scl_if.i);

end process;

Expand Down
30 changes: 19 additions & 11 deletions hdl/ip/vhd/i2c/sims/i2c_peripheral_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,28 @@ package i2c_peripheral_pkg is
constant address_different : msg_type_t := new_msg_type("address_different");
constant send_ack : msg_type_t := new_msg_type("send_ack");
constant got_ack : msg_type_t := new_msg_type("got_ack");
constant send_byte : msg_type_t := new_msg_type("send_byte");
constant got_byte : msg_type_t := new_msg_type("got_byte");
constant got_stop : msg_type_t := new_msg_type("got_stop");

type i2c_peripheral_t is record
-- I2C peripheral address
address : std_logic_vector(6 downto 0);
-- private
p_actor : actor_t;
p_memory : memory_t;
p_buffer : buffer_t;
p_logger : logger_t;
-- I2C peripheral address
P_address : std_logic_vector(6 downto 0);
end record;

constant i2c_peripheral_vc_logger : logger_t := get_logger("work:i2c_peripheral_vc");

impure function new_i2c_peripheral_vc (
name : string;
address : std_logic_vector(6 downto 0);
memory : memory_t;
address : std_logic_vector(6 downto 0) := b"1010101";
logger : logger_t := i2c_peripheral_vc_logger
) return i2c_peripheral_t;

impure function address(i2c_periph: i2c_peripheral_t) return std_logic_vector;

procedure expect_message (
signal net : inout network_t;
constant vc : i2c_peripheral_t;
Expand All @@ -66,19 +66,27 @@ package body i2c_peripheral_pkg is

impure function new_i2c_peripheral_vc (
name : string;
address : std_logic_vector(6 downto 0);
memory : memory_t;
address : std_logic_vector(6 downto 0) := b"1010101";
logger : logger_t := i2c_peripheral_vc_logger
) return i2c_peripheral_t is
variable buf : buffer_t;
begin
-- I2C can address 256 bytes, so construct an internal buffer to reflect that
buf := allocate(new_memory, 256, name & "_MEM", 8, read_and_write);

return (
address => address,
p_actor => new_actor(name),
p_memory => to_vc_interface(memory, logger),
p_logger => logger
p_buffer => buf,
p_logger => logger,
p_address => address
);
end;

impure function address (i2c_periph: i2c_peripheral_t) return std_logic_vector is
begin
return i2c_periph.p_address;
end function;

procedure expect_message (
signal net : inout network_t;
constant vc : i2c_peripheral_t;
Expand Down
Loading

0 comments on commit 5ec472d

Please sign in to comment.