From ac3b7a68aae49ab0f456158a2226fb5a63211193 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 28 Dec 2023 15:38:50 +0100 Subject: [PATCH] feat: add i2c rx, tx entities --- src/i2c/rx.vhd | 165 +++++++++++++++++++++++++++++++++++++++++++ src/i2c/tx.vhd | 187 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 src/i2c/rx.vhd create mode 100644 src/i2c/tx.vhd diff --git a/src/i2c/rx.vhd b/src/i2c/rx.vhd new file mode 100644 index 0000000..438915c --- /dev/null +++ b/src/i2c/rx.vhd @@ -0,0 +1,165 @@ +-- i2c interface + -- scl_pulse_i + -- scl_stretch_o + -- sda_o + +-- control interface + -- clk_i + -- rst_in + -- start_read_i + +-- read interface + -- read_valid_o + -- read_ready_o + -- read_data_o + -- confirm_read_i +-- +library ieee; +use ieee.std_logic_1164.all; + +entity rx is + + port ( + -- control part + clk_i : in std_logic; -- Clock + rst_in : in std_logic; -- Reset (asynchronous) + start_read_i : in std_logic; -- Start reading with next scl_pulse + + scl_pulse_i : in std_logic; -- SCL rising edge pulse + scl_stretch_o : out std_logic; -- Stretch SCL (keep SCL 0) + sda_i : in std_logic; -- SDA data line state + + read_valid_o : out std_logic; -- Are there any data on read_data_o? + read_ready_o : out std_logic; -- Is it possible to read anymore, or + -- does data have to be read to flush buffer? + read_data_o : out std_logic_vector(7 downto 0); -- The received data + confirm_read_i : in std_logic); -- Confirm that data have been read + +end entity rx; + +architecture a1 of rx is + -- IDLE - not doing anythign + -- RECEIVING - currently receiving data to the buffer + -- SAVING - trying to save to the read data output, waiting for data being + -- read if cannot save + -- SAVING_STRETCHING - waiting for data being read, should + -- be sending already, but cannot, since the data are not read, + -- so stretching SCL + type rx_state_t is (IDLE, RECEIVING, SAVING, SAVING_STRETCHING); + signal curr_state : rx_state_t; + signal next_state : rx_state_t; + + -- Whether state = RECEIVING + signal curr_receiving : std_logic; + -- Whether state = SAVING or SAVING_STRETCHING + signal curr_saving : std_logic; + + -- Whether the read data output is filled + -- already (it's a register) + signal curr_read_data_filled : std_logic; + signal next_read_data_filled : std_logic; + + -- The received data + signal curr_rx_buffer : std_logic_vector(7 downto 0); + signal next_rx_buffer : std_logic_vector(7 downto 0); + + -- Bit index that is being received + signal curr_bit_index : natural range 0 to 7; + signal next_bit_index : natural range 0 to 7; + + signal curr_read_data : std_logic_vector(7 downto 0); + signal next_read_data : std_logic_vector(7 downto 0); +begin -- architecture a1 + read_ready_o <= '1' when curr_state /= SAVING_STRETCHING else '0'; + scl_stretch_o <= '1' when curr_state = SAVING_STRETCHING else '0'; + read_data_o <= curr_read_data; + + -- IDLE -> RECEIVING on start_read, + -- RECEIVING -> RECEIVING when not received full data + -- RECEIVING -> SAVING when received all data + -- SAVING -> SAVING_STRETCHING when cannot overrode data AND start_read + -- SAVING -> RECEIVING when start_read AND overriding data now + -- SAVING -> SAVING when cannot override data (data not read yet) + -- SAVING -> IDLE when can override data + -- SAVING_STRETCHING -> SAVING_STRETCHING when data not read yet + -- SAVING_STRETCHING -> RECEIVING when data read + + set_next_state: process is + begin -- process set_next_state + next_state <= curr_state; + + if curr_state = IDLE then + if start_read_i = '1' then + next_state <= RECEIVING; + end if; + elsif curr_state = RECEIVING then + if curr_bit_index = 0 and scl_pulse_i = '1' then + next_state <= SAVING; + end if; + elsif curr_state = SAVING then + if confirm_read_i = '1' then + if start_read_i = '1' then + next_state <= RECEIVING; -- skip SAVING_STRETCHING + else + next_state <= IDLE; + end if; + elsif start_read_i = '1' then + next_state <= SAVING_STRETCHING; + end if; + elsif curr_state = SAVING_STRETCHING then + if confirm_read_i = '1' then + next_state <= RECEIVING; + end if; + end if; + end process set_next_state; + + curr_receiving <= '1' when curr_state = RECEIVING; + curr_saving <= '1' when curr_state = SAVING or curr_state = SAVING_STRETCHING; + + read_valid_o <= curr_read_data_filled; + + -- TODO: (speedup by one cycle when saving?) + next_read_data <= curr_read_data when curr_read_data_filled = '1' else + next_rx_buffer when next_read_data_filled = '1' and curr_read_data_filled = '0' else + (others => '0'); + + next_read_data_filled <= '1' when curr_read_data_filled = '1' and confirm_read_i = '0' else + '0' when curr_read_data_filled = '1' and confirm_read_i = '1' else + '1' when curr_receiving = '1' and curr_bit_index = 0 else + '0'; + + next_bit_index <= curr_bit_index when scl_pulse_i = '0' else + (curr_bit_index - 1) when curr_receiving = '1' and scl_pulse_i = '1' else + 7; -- when curr_state /= RECEIVING and next_state = RECEIVING; + + set_next_read_data: process (sda_i, curr_rx_buffer, curr_receiving, curr_saving, curr_bit_index, scl_pulse_i) is + begin -- process set_next_read_data + next_rx_buffer <= curr_rx_buffer; + + if curr_receiving = '1' and curr_saving = '0' and scl_pulse_i = '1' then + next_rx_buffer(curr_bit_index) <= sda_i; + end if; + end process set_next_read_data; + + set_regs: process (clk_i) is + begin -- process set_regs + if rising_edge(clk_i) then -- rising clock edge + if rst_in = '0' then -- synchronous reset (active low) + curr_read_data <= (others => '0'); + curr_rx_buffer <= (others => '0'); + curr_bit_index <= 0; + curr_read_data_filled <= '0'; + curr_saving <= '0'; + curr_receiving <= '0'; + curr_state <= IDLE; + else + curr_read_data <= next_read_data; + curr_rx_buffer <= next_rx_buffer; + curr_bit_index <= next_bit_index; + curr_read_data_filled <= next_read_data_filled; + curr_state <= next_state; + end if; + end if; + end process set_regs; + +end architecture a1; diff --git a/src/i2c/tx.vhd b/src/i2c/tx.vhd new file mode 100644 index 0000000..58dd808 --- /dev/null +++ b/src/i2c/tx.vhd @@ -0,0 +1,187 @@ + +-- i2c interface + -- scl_pulse_i + -- scl_stretch_o + -- sda_o + +-- control interface + -- clk_i + -- rst_in + -- start_write_i + +-- write interface +-- ready_o +-- valid_i +-- write_data + +library ieee; +use ieee.std_logic_1164.all; + +library utils; + +entity tx is + generic ( + DELAY_SDA_FOR : natural := 5); -- How many cycles to delay setting SDA + -- after falling edge of scl + port ( + clk_i : in std_logic; + rst_in : in std_logic; + start_write_i : in std_logic; + + scl_rising_pulse_i : in std_logic; + scl_falling_pulse_i : in std_logic; + + scl_i : in std_logic; + scl_stretch_o : out std_logic; + sda_o : out std_logic; + + ready_o : out std_logic; + valid_i : in std_logic; + write_data_i : in std_logic_vector(7 downto 0)); + +end entity tx; + +architecture a1 of tx is + -- IDLE - not doing anything + -- SENDING - data are in the buffer, being sent + -- WAITING_FOR_SEND there were data written to the buffer, but cannot send now + -- WAITING_FOR_DATA should be sending, but there are no data - stretching + type tx_state_t is (IDLE, WAITING_FOR_FALLING_EDGE, SENDING, WAITING_FOR_DATA); + signal curr_state : tx_state_t; + signal next_state : tx_state_t; + + type tx_buffers is array (0 to 1) of std_logic_vector(7 downto 0); + signal curr_tx_buffers : tx_buffers; + signal next_tx_buffers : tx_buffers; + + -- Index to save next new data to. + signal curr_saving_buffer_index : natural range 0 to 1; + signal next_saving_buffer_index : natural range 0 to 1; + + signal curr_tx_buffer_index : natural range 0 to 1; + signal next_tx_buffer_index : natural range 0 to 1; + + signal curr_tx_buffers_filled : std_logic_vector(1 downto 0); + signal next_tx_buffers_filled : std_logic_vector(1 downto 0); + + signal tx_buffer : std_logic_vector(7 downto 0); + signal tx_buffer_filled : std_logic; + + signal curr_bit_index : natural range 0 to 7; + signal next_bit_index : natural range 0 to 7; + + signal scl_delayed_pulse : std_logic; + signal curr_scl : std_logic; + signal next_scl : std_logic; + + signal ready : std_logic; +begin -- architecture a1 + entity utils.delay + generic map ( + DELAY => DELAY_SDA_FOR) + port map ( + clk_i => clk_i, + rst_in => rst_in, + signal_i => scl_falling_pulse_i, + signal_o => scl_delayed_pulse); + + scl_stretch_o <= '1' when curr_state = WAITING_FOR_DATA else '0'; + ready_o <= ready; + sda_o <= tx_buffer(curr_bit_index) when curr_state = SENDING else '1'; + + ready <= '0' when curr_tx_buffers_filled(curr_saving_buffer_index) = '1' else '1'; + tx_buffer <= curr_tx_buffers(curr_tx_buffer_index); + tx_buffer_filled <= curr_tx_buffers_filled(curr_tx_buffer_index); + + next_scl <= '1' when scl_rising_pulse_i = '1' else + '0' when scl_falling_pulse_i = '1' else + curr_scl; + + next_bit_index <= 0 when start_write_i = '1' else + (curr_bit_index + 1) mod 7 when curr_state = SENDING and scl_delayed_pulse = '1' else + 0; + + next_tx_buffer_index <= (curr_tx_buffer_index + 1) mod 1 when curr_bit_index = 7 and scl_delayed_pulse = '1' else + curr_tx_buffer_index; + + next_saving_buffer_index <= (curr_saving_buffer_index + 1) mod 1 when ready = '1' and valid_i = '1' else + curr_saving_buffer_index; + + set_next_tx_buffers: process is + begin -- process set_next_tx_buffer + next_tx_buffers <= curr_tx_buffers; + if ready = '1' and valid_i = '1' then + next_tx_buffers(curr_saving_buffer_index) <= write_data_i; + end if; + end process set_next_tx_buffer; + + set_next_buffer_filled: process is + begin -- process set_next_buffer_filled + next_tx_buffers_filled <= curr_tx_buffers_filled; + if curr_bit_index = 7 and scl_delayed_pulse = '1' then + next_tx_buffers_filled(curr_tx_buffer_index) <= '0'; + end if; + + if ready = '1' and valid_i = '1' then + next_tx_buffers_filled(curr_saving_buffer_index) <= '1'; + end if; + end process set_next_buffer_filled; + + set_next_state: process(curr_state, scl_delayed_pulse, scl_scl, tx_buffer_filled, valid_i) is + variable start_sending : std_logic := '0'; + begin -- process set_next_state + next_state <= curr_state; + + if curr_state = WAITING_FOR_FALLING_EDGE then + if scl_delayed_pulse = '1' then + next_state <= SENDING; + end if; + elsif curr_state = SENDING then + if curr_index = 7 and scl_delayed_pulse = '1' then + next_state <= IDLE; + end if; + elsif curr_state = WAITING_FOR_DATA then + if valid_i = '1' then + start_sending := '1'; + end if; + end if; + + if start_write_i = '1' then + if tx_buffer_filled = '1' then + start_sending := '1'; + else + next_state <= WAITING_FOR_DATA; + end if; + end if; + + if start_sending = '1' then + if curr_scl = '0' then + next_state <= SENDING; + else + next_state <= WAITING_FOR_FALLING_EDGE; + end if; + end if; + end process set_next_state; + + set_regs: process (clk_i) is + begin -- process set_next + if rising_edge(clk_i) then -- rising clock edge + if rst_in = '0' then -- synchronous reset (active low) + curr_state <= IDLE; + curr_tx_buffers <= (others => (others => '0')); + curr_tx_buffer_index <= 0; + curr_tx_buffers_filled <= "00"; + curr_bit_index <= 0; + curr_scl <= '1'; -- assume 1 (the default, no one transmitting) + else + curr_state <= next_state; + curr_tx_buffers <= next_tx_buffers; + curr_tx_buffer_index <= next_tx_buffer_index; + curr_tx_buffers_filled <= next_tx_buffers_filled; + curr_bit_index <= next_bit_index; + curr_scl <= next_scl; + end if; + end if; + end process set_regs; + +end architecture a1; -- 2.48.1