-- 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);
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(curr_state, start_read_i, scl_pulse_i, confirm_read_i) 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_rx_buffer(7) = '1' 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 else '0';
curr_saving <= '1' when curr_state = SAVING or curr_state = SAVING_STRETCHING else '0';
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
curr_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_saving = '1' else
'0';
next_rx_buffer <= curr_rx_buffer(6 downto 0) & sda_i when curr_receiving = '1' and scl_pulse_i = '1' else
curr_rx_buffer when curr_receiving = '1' else
"00000001";
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_read_data_filled <= '0';
curr_state <= IDLE;
else
curr_read_data <= next_read_data;
curr_rx_buffer <= next_rx_buffer;
curr_read_data_filled <= next_read_data_filled;
curr_state <= next_state;
end if;
end if;
end process set_regs;
end architecture a1;