diff --git a/hdl/ip/vhd/i2c/i2c_core.vhd b/hdl/ip/vhd/i2c/i2c_core.vhd index 50667dd3..339afd13 100644 --- a/hdl/ip/vhd/i2c/i2c_core.vhd +++ b/hdl/ip/vhd/i2c/i2c_core.vhd @@ -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 @@ -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 @@ -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'; diff --git a/hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd b/hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd index 8daeeee0..411be9bb 100644 --- a/hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd +++ b/hdl/ip/vhd/i2c/link_layer/i2c_link_layer.vhd @@ -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, @@ -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); @@ -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 @@ -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; @@ -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 @@ -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; diff --git a/hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd b/hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd index 58c456df..bf5bf065 100644 --- a/hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd +++ b/hdl/ip/vhd/i2c/sims/i2c_peripheral.vhd @@ -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 @@ -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 @@ -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 @@ -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; @@ -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'); diff --git a/hdl/ip/vhd/i2c/sims/i2c_tb.vhd b/hdl/ip/vhd/i2c/sims/i2c_tb.vhd index c99187f9..e6dc99ad 100644 --- a/hdl/ip/vhd/i2c/sims/i2c_tb.vhd +++ b/hdl/ip/vhd/i2c/sims/i2c_tb.vhd @@ -44,13 +44,13 @@ begin bench: process alias reset is << signal th.reset : std_logic >>; - variable msg : msg_t; variable command : cmd_t; variable ack : boolean := false; variable data : std_logic_vector(7 downto 0); variable expected_addr : unsigned(7 downto 0); variable expected_data : std_logic_vector(7 downto 0); + variable byte_len : natural; begin -- Always the first thing in the process, set up things for the VUnit test runner test_runner_setup(runner, runner_cfg); @@ -66,12 +66,13 @@ begin start_byte_ack(net, I2C_PERIPHERAL_VC, ack); check_false(ack, "Peripheral did not NACK incorrect address"); -- transaction is over, receive STOP event - wait for 10 us; + expect_stop(net, I2C_PERIPHERAL_VC); elsif run("write_and_read_one_byte") then - -- write some data in - expected_addr := X"9E"; -- arbitrary for the test + -- arbitrary for the test + expected_addr := X"9E"; expected_data := X"A5"; + -- write some data in command := ( op => WRITE, addr => address(I2C_PERIPHERAL_VC), @@ -92,7 +93,7 @@ begin command := ( op => READ, addr => address(I2C_PERIPHERAL_VC), - reg => std_logic_vector(expected_addr), + reg => X"--", -- READ uses internal address set by a WRITE to the peripheral len => to_unsigned(1, command.len'length) ); push_i2c_cmd(net, I2C_CMD_VC, command); @@ -105,8 +106,61 @@ begin expect_stop(net, I2C_PERIPHERAL_VC); elsif run("write_and_read_many_bytes") then + -- arbitrary for the test + expected_addr := X"00"; + byte_len := 8; - end if; + -- write some data in + command := ( + op => WRITE, + addr => address(I2C_PERIPHERAL_VC), + reg => std_logic_vector(expected_addr), + len => to_unsigned(byte_len, command.len'length) + ); + push_i2c_cmd(net, I2C_CMD_VC, command); + + start_byte_ack(net, I2C_PERIPHERAL_VC, ack); + check_true(ack, "Peripheral did not ACK correct address"); + + for byte_idx in 0 to byte_len - 1 loop + data := std_logic_vector(to_unsigned(byte_idx, data'length)); + expected_addr := to_unsigned(byte_idx, expected_addr'length); + push_basic_stream(net, TX_DATA_SOURCE_VC, data); + end loop; + + for byte_idx in 0 to byte_len - 1 loop + data := std_logic_vector(to_unsigned(byte_idx, data'length)); + expected_addr := to_unsigned(byte_idx, expected_addr'length); + check_written_byte(net, I2C_PERIPHERAL_VC, data, expected_addr); + end loop; + + expect_stop(net, I2C_PERIPHERAL_VC); + + -- read it back out + expected_addr := X"00"; + command := ( + op => RANDOM_READ, + addr => address(I2C_PERIPHERAL_VC), + reg => std_logic_vector(expected_addr), + len => to_unsigned(byte_len, command.len'length) + ); + push_i2c_cmd(net, I2C_CMD_VC, command); + + -- ACK WRITE portion of the random read + start_byte_ack(net, I2C_PERIPHERAL_VC, ack); + check_true(ack, "Peripheral did not ACK correct address"); + + -- ACK READ portion of the random read + start_byte_ack(net, I2C_PERIPHERAL_VC, ack); + check_true(ack, "Peripheral did not ACK correct address"); + + for byte_idx in 0 to byte_len - 1 loop + expected_data := std_logic_vector(to_unsigned(byte_idx, expected_data'length)); + pop_basic_stream(net, RX_DATA_SINK_VC, data); + check_equal(data, expected_data, "Expected read data to match"); + end loop; + + expect_stop(net, I2C_PERIPHERAL_VC); end if; end loop; diff --git a/hdl/ip/vhd/i2c/sims/tb_i2c.gtkw b/hdl/ip/vhd/i2c/sims/tb_i2c.gtkw index 96c763df..7554dee6 100644 --- a/hdl/ip/vhd/i2c/sims/tb_i2c.gtkw +++ b/hdl/ip/vhd/i2c/sims/tb_i2c.gtkw @@ -1,17 +1,18 @@ [*] [*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI -[*] Fri Nov 22 17:02:19 2024 +[*] Tue Nov 26 23:00:33 2024 [*] -[dumpfile] "/home/aaron/Oxide/git/quartz/vunit_out/test_output/lib.i2c_tb.read_one_byte_f5003d4ca7a772188d7e5908ca58541f5b186a63/nvc/i2c_tb.fst" -[dumpfile_mtime] "Fri Nov 22 16:55:54 2024" -[dumpfile_size] 4917 +[dumpfile] "/home/aaron/Oxide/git/quartz/vunit_out/test_output/lib.i2c_tb.write_and_read_many_bytes_735b3cb8aa5b45ddaa977875ceec621188fd3a14/nvc/i2c_tb.fst" +[dumpfile_mtime] "Tue Nov 26 23:00:26 2024" +[dumpfile_size] 6733 [savefile] "/home/aaron/Oxide/git/quartz/hdl/ip/vhd/i2c/sims/tb_i2c.gtkw" -[timestart] 122700000000 -[size] 2688 1283 -[pos] 1183 23 -*-32.015984 138388000000 -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 +[timestart] 0 +[size] 2592 1283 +[pos] -1 -1 +*-36.192200 430732000000 -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 [treeopen] i2c_tb. [treeopen] i2c_tb.th. +[treeopen] i2c_tb.th.dut. [treeopen] i2c_tb.th.dut.i2c_link_layer_inst. [treeopen] i2c_tb.th.dut.sm_reg. [treeopen] i2c_tb.th.dut.sm_reg_next. @@ -71,7 +72,7 @@ i2c_tb.th.dut.i2c_link_layer_inst.transition_sda -Transmit Stream @28 i2c_tb.th.dut.i2c_link_layer_inst.tx_data_valid -@22 +@23 i2c_tb.th.dut.i2c_link_layer_inst.tx_data[7:0] @200 -Receive Stream @@ -133,15 +134,17 @@ i2c_tb.th.peripheral.stop_condition @22 i2c_tb.th.peripheral.reg_addr[7:0] @28 +i2c_tb.th.peripheral.addr_set +i2c_tb.th.peripheral.addr_incr i2c_tb.th.peripheral.rx_done @22 i2c_tb.th.peripheral.rx_data[7:0] i2c_tb.th.peripheral.rx_bit_count[3:0] @28 i2c_tb.th.peripheral.tx_done -@800023 +@c00022 i2c_tb.th.peripheral.tx_data[7:0] -@29 +@28 (0)i2c_tb.th.peripheral.tx_data[7:0] (1)i2c_tb.th.peripheral.tx_data[7:0] (2)i2c_tb.th.peripheral.tx_data[7:0] @@ -150,7 +153,7 @@ i2c_tb.th.peripheral.tx_data[7:0] (5)i2c_tb.th.peripheral.tx_data[7:0] (6)i2c_tb.th.peripheral.tx_data[7:0] (7)i2c_tb.th.peripheral.tx_data[7:0] -@1001201 +@1401200 -group_end @22 i2c_tb.th.peripheral.tx_bit_count[3:0]