~ruther/vhdl-i2c

ac3b7a68aae49ab0f456158a2226fb5a63211193 — Rutherther 1 year, 3 months ago e29d030
feat: add i2c rx, tx entities
2 files changed, 352 insertions(+), 0 deletions(-)

A src/i2c/rx.vhd
A src/i2c/tx.vhd
A src/i2c/rx.vhd => src/i2c/rx.vhd +165 -0
@@ 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;

A src/i2c/tx.vhd => src/i2c/tx.vhd +187 -0
@@ 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;

Do not follow this link