~ruther/vhdl-i2c

0f8e8a5290dbcd5644f04e88b4ab973021e69f88 — Rutherther 1 year, 3 months ago 23c5f31
feat: handle errors in slave state

Moves logic of error handling to slave_state
from tx entity.
M src/i2c/rx.vhd => src/i2c/rx.vhd +2 -2
@@ 24,7 24,7 @@ entity rx is
    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
    ss_condition_i : in  std_logic;     -- Reset rx circuitry
    rst_i2c_i      : in  std_logic;     -- Reset rx circuitry

    scl_pulse_i    : in  std_logic;     -- SCL rising edge pulse
    scl_falling_delayed_i    : in  std_logic;     -- SCL rising edge pulse


@@ 125,7 125,7 @@ begin  -- architecture a1
      end if;
    end if;

    if ss_condition_i = '1' then
    if rst_i2c_i = '1' then
      next_state <= IDLE;
    end if;
  end process set_next_state;

M src/i2c/slave.vhd => src/i2c/slave.vhd +14 -10
@@ 32,6 32,7 @@ entity slave is

    -- errors
    err_noack_o  : out std_logic;
    err_sda_o  : out std_logic;

    -- state
    rw_o         : out std_logic;       -- 1 - read, 0 - write


@@ 55,6 56,7 @@ architecture a1 of slave is

  signal tx_sda_enable : std_logic;
  signal tx_scl_stretch : std_logic;
  signal tx_noack, tx_unexpected_sda : std_logic;

  signal rx_sda_enable : std_logic;
  signal rx_scl_stretch : std_logic;


@@ 69,14 71,13 @@ architecture a1 of slave is
  signal address_detection : std_logic;

  signal rw : std_logic;
  signal ss_condition : std_logic;
  signal rst_i2c : std_logic;

  signal scl_falling_delayed : std_logic;
begin  -- architecture a1
  rw_o <= rw;
  dev_busy_o <= transmitting or receiving;
  bus_busy_o <= bus_busy;
  ss_condition <= start_condition or stop_condition;

  scl_enable_o <= tx_scl_stretch when transmitting = '1' and tx_stretch_i = '1' and scl_i = '0' else
                  rx_scl_stretch when receiving = '1' and rx_stretch_i = '1' and scl_i = '0' else


@@ 124,13 125,13 @@ begin  -- architecture a1
      stop_o  => stop_condition);

  -- rx
  rx: entity work.rx
  rx : entity work.rx
    port map (
      clk_i                 => clk_i,
      rst_in                => rst_in,
      start_read_i          => receiving,
      generate_ack_i        => generate_ack_i,
      ss_condition_i        => ss_condition,
      rst_i2c_i             => rst_i2c,
      scl_pulse_i           => scl_rising_pulse,
      scl_falling_delayed_i => scl_falling_delayed,
      scl_stretch_o         => rx_scl_stretch,


@@ 148,17 149,14 @@ begin  -- architecture a1
      rst_in                => rst_in,
      clear_buffer_i        => tx_clear_buffer_i,
      start_write_i         => transmitting,
      expect_ack_i          => expect_ack_i,
      err_noack_o           => err_noack_o,
      ss_condition_i        => ss_condition,
      rst_i2c_i             => rst_i2c,
      scl_rising_pulse_i    => scl_rising_pulse,
      scl_falling_delayed_i => scl_falling_delayed,
      scl_stretch_o         => tx_scl_stretch,
      sda_i                 => sda_i,
      sda_enable_o          => tx_sda_enable,
      unexpected_sda_o      => open,    -- ignore in the slave, nothing to do?
                                        -- TODO output an error if unexpected
                                        -- level when transmitting
      unexpected_sda_o      => tx_unexpected_sda,
      noack_o               => tx_noack,
      ready_o               => tx_ready_o,
      valid_i               => tx_valid_i,
      write_data_i          => tx_data_i);


@@ 181,8 179,14 @@ begin  -- architecture a1
    port map (
      clk_i                    => clk_i,
      rst_in                   => rst_in,
      rst_i2c_o                => rst_i2c,
      start_condition_i        => start_condition,
      stop_condition_i         => stop_condition,
      err_noack_o              => err_noack_o,
      err_sda_o                => err_sda_o,
      unexpected_sda_i         => tx_unexpected_sda,
      expect_ack_i             => expect_ack_i,
      noack_i                  => tx_noack,
      rw_i                     => rw,
      address_detect_success_i => address_detect_success,
      address_detect_fail_i    => address_detect_fail,

M src/i2c/slave_state.vhd => src/i2c/slave_state.vhd +34 -5
@@ 5,11 5,18 @@ entity i2c_slave_state is
  port (
    clk_i                    : in  std_logic;
    rst_in                   : in  std_logic;
    rst_i2c_o                : out std_logic;

    noack_i                  : in  std_logic;
    expect_ack_i             : in  std_logic;
    unexpected_sda_i         : in  std_logic;
    err_noack_o              : out std_logic;
    err_sda_o                : out std_logic;

    start_condition_i        : in  std_logic;
    stop_condition_i         : in  std_logic;

    rw_i                     : in  std_logic;

    address_detect_success_i : in  std_logic;
    address_detect_fail_i    : in  std_logic;
    address_detect_start_o   : out std_logic;


@@ 33,22 40,40 @@ architecture a1 of i2c_slave_state is
  signal curr_state : slave_state := BUS_FREE;
  signal next_state : slave_state;

  signal curr_err_noack : std_logic;
  signal next_err_noack : std_logic;

  signal curr_err_sda : std_logic;
  signal next_err_sda : std_logic;

  signal communicating_with_master : std_logic;
begin  -- architecture a1

  communicating_with_master <= '1' when curr_state = BUS_ADDRESS or curr_state = RECEIVING or curr_state = TRANSMITTING else '0';

  address_detect_start_o <= '1' when start_condition_i = '1' else '0';
  address_detect_o <= '1' when curr_state = BUS_ADDRESS else '0';
  address_detect_start_o    <= '1' when start_condition_i = '1'                                                         else '0';
  address_detect_o          <= '1' when curr_state = BUS_ADDRESS                                                        else '0';
  err_noack_o               <= curr_err_noack;
  err_sda_o                 <= curr_err_sda;
  rst_i2c_o                 <= curr_err_noack or curr_err_sda or start_condition_i or stop_condition_i;

  receive_o <= '1' when curr_state = RECEIVING else '0';
  transmit_o <= '1' when curr_state = TRANSMITTING else '0';
  bus_busy_o <= '1' when curr_state = BUS_BUSY else '0';

  next_err_sda <= '0' when start_condition_i = '1' or stop_condition_i = '1' else
                  '1' when curr_err_sda = '1' else
                  '1' when unexpected_sda_i = '1' and curr_state = TRANSMITTING else
                  '0';

  next_err_noack <= '0' when start_condition_i = '1' or stop_condition_i = '1' else
                  '1' when curr_err_noack = '1' else
                  '1' when noack_i = '1' and curr_state = TRANSMITTING and expect_ack_i = '1' else
                  '0';

  next_state <= BUS_ADDRESS when start_condition_i = '1' else
                -- BUS_BUSY when curr_state = BUS_FREE and (sda_i = '0' or scl_i = '0') else --
                  -- assume busy when there is something on the bus?
                BUS_FREE when stop_condition_i = '1' else
                BUS_BUSY when next_err_sda = '1' or next_err_noack = '1' else
                RECEIVING when curr_state = BUS_ADDRESS and address_detect_success_i = '1' and rw_i = '0' else
                TRANSMITTING when curr_state = BUS_ADDRESS and address_detect_success_i = '1' and rw_i = '1' else
                BUS_BUSY when curr_state = BUS_ADDRESS and address_detect_fail_i = '1' else


@@ 59,8 84,12 @@ begin  -- architecture a1
    if rising_edge(clk_i) then          -- rising clock edge
      if rst_in = '0' then              -- synchronous reset (active low)
        curr_state <= BUS_FREE;
        curr_err_sda <= '0';
        curr_err_noack <= '0';
      else
        curr_state <= next_state;
        curr_err_noack <= next_err_noack;
        curr_err_sda <= next_err_sda;
      end if;
    end if;
  end process set_regs;

M src/i2c/tx.vhd => src/i2c/tx.vhd +6 -19
@@ 24,12 24,11 @@ entity tx is
    clk_i               : in  std_logic;
    rst_in              : in  std_logic;
    start_write_i       : in  std_logic;
    ss_condition_i      : in  std_logic;    -- Reset rx circuitry
    rst_i2c_i           : in  std_logic;    -- Reset rx circuitry
    clear_buffer_i      : in  std_logic;

    expect_ack_i        : in  std_logic;
    unexpected_sda_o    : out std_logic;
    err_noack_o         : out std_logic;
    noack_o             : out std_logic;

    scl_rising_pulse_i  : in  std_logic;
    scl_falling_delayed_i : in  std_logic;


@@ 79,10 78,10 @@ architecture a1 of tx is
  signal ready : std_logic;
begin  -- architecture a1
  scl_stretch_o <= '1' when curr_state = WAITING_FOR_DATA else '0';
  ready_o <= ready and not curr_err_noack;
  ready_o <= ready;
  sda_enable_o <= not tx_buffer(8) when curr_state = SENDING else '0';
  unexpected_sda_o <= '1' when curr_state = SENDING and sda_i /= tx_buffer(8) else '0';
  err_noack_o <= curr_err_noack;
  unexpected_sda_o <= '1' when curr_state = SENDING and sda_i /= tx_buffer(8) and scl_rising_pulse_i = '1' else '0';
  noack_o <= '1' when curr_state = ACK and sda_i = '1' and scl_rising_pulse_i = '1' else '0';

  ready <= '0' when curr_tx_buffers_filled(curr_saving_buffer_index) = '1' or curr_err_noack = '1' else '1';
  tx_buffer <= curr_tx_buffers(curr_tx_buffer_index);


@@ 98,11 97,6 @@ begin  -- architecture a1
  next_saving_buffer_index <= (curr_saving_buffer_index + 1) mod 2 when ready = '1' and valid_i = '1' else
                              curr_saving_buffer_index;

  next_err_noack <= '0' when expect_ack_i = '0' else
                    '1' when curr_err_noack = '1' else
                    '1' when curr_state = ACK and scl_rising_pulse_i = '1' and sda_i = '1' else
                    '0';

  set_next_tx_buffers: process(all) is
  begin  -- process set_next_tx_buffer
    next_tx_buffers <= curr_tx_buffers;


@@ 172,10 166,6 @@ begin  -- architecture a1
      end if;
    end if;

    if curr_err_noack = '1' then
      next_state <= IDLE;               -- not doing anything after an error.
                                        -- Waiting for ss condition or reset
    end if;
  end process set_next_state;

  set_regs: process (clk_i) is


@@ 188,11 178,9 @@ begin  -- architecture a1
        curr_tx_buffers_filled <= "00";
        curr_saving_buffer_index <= 0;
        curr_scl <= '1';                -- assume 1 (the default, no one transmitting)
        curr_err_noack <= '0';
      elsif ss_condition_i = '1' then
      elsif rst_i2c_i = '1' then
        curr_state <= IDLE;
        curr_scl <= '1';                -- assume 1 (the default, no one transmitting)
        curr_err_noack <= '0';
      else
        curr_state <= next_state;
        curr_tx_buffers <= next_tx_buffers;


@@ 200,7 188,6 @@ begin  -- architecture a1
        curr_tx_buffers_filled <= next_tx_buffers_filled;
        curr_saving_buffer_index <= next_saving_buffer_index;
        curr_scl <= next_scl;
        curr_err_noack <= next_err_noack;
      end if;
    end if;
  end process set_regs;

M tb/i2c/rx_tb.vhd => tb/i2c/rx_tb.vhd +1 -1
@@ 87,7 87,7 @@ begin  -- architecture a1
    port map (
      clk_i                 => clk,
      rst_in                => rst_n,
      ss_condition_i        => '0',
      rst_i2c_i             => '0',
      generate_ack_i        => '1',
      start_read_i          => start_read,
      scl_pulse_i           => scl_rising_pulse,

M tb/i2c/slave_tb.vhd => tb/i2c/slave_tb.vhd +38 -10
@@ 31,7 31,7 @@ architecture tb of slave_tb is
  signal slave_scl : std_logic;

  signal dev_busy, bus_busy : std_logic;
  signal err_noack : std_logic;
  signal err_noack, err_sda : std_logic;
  signal rw : std_logic;

  signal rx_confirm : std_logic := '0';


@@ 78,14 78,15 @@ begin  -- architecture tb
      tx_stretch_i   => '0',
      tx_clear_buffer_i => '0',

      err_noack_o    => err_noack,
      rw_o           => rw,
      dev_busy_o     => dev_busy,
      bus_busy_o     => bus_busy,
      sda_i          => slave_sda,
      scl_i          => slave_scl,
      sda_enable_o   => slave_sda_enable,
      scl_enable_o   => slave_scl_enable);
      err_noack_o  => err_noack,
      err_sda_o    => err_sda,
      rw_o         => rw,
      dev_busy_o   => dev_busy,
      bus_busy_o   => bus_busy,
      sda_i        => slave_sda,
      scl_i        => slave_scl,
      sda_enable_o => slave_sda_enable,
      scl_enable_o => slave_scl_enable);

  -- stable sda_enable when scl high
  sda_stability_check: check_stable(clk, one, scl, not_scl, slave_sda_enable);


@@ 156,7 157,6 @@ begin  -- architecture tb

        i2c_master_receive("11010100", scl, sda, ack => '0');
        check_equal(rw, '1');
        check_equal(dev_busy, '1');

        check_equal(err_noack, '1');



@@ 164,6 164,34 @@ begin  -- architecture tb

        wait until falling_edge(clk);
        wait until falling_edge(clk);
        wait until falling_edge(clk);
        check_equal(err_noack, '0');

        wait until falling_edge(clk);
        wait until falling_edge(clk);
        check_equal(dev_busy, '0');
        check_equal(bus_busy, '0');
      elsif run("read_unexpected_sda") then
        address <= "1100001";

        i2c_master_start("1100001", '1', scl, sda);

        tx_write_data("11010100", tx_data, tx_valid);

        scl_fall(scl);
        sda <= '0';

        scl_rise(scl);

        check_equal(err_sda, '1');

        i2c_master_stop(scl, sda);

        wait until falling_edge(clk);
        wait until falling_edge(clk);
        wait until falling_edge(clk);

        check_equal(err_sda, '0');
        check_equal(dev_busy, '0');
        check_equal(bus_busy, '0');
      elsif run("write_read") then

M tb/i2c/tb_i2c_master_pkg.vhd => tb/i2c/tb_i2c_master_pkg.vhd +5 -5
@@ 92,8 92,8 @@ package body tb_i2c_master_pkg is

  procedure i2c_master_receive (
    constant exp_data       : in    std_logic_vector(7 downto 0);
    signal scl     : inout std_logic;
    signal sda     : inout std_logic;
    signal scl              : inout std_logic;
    signal sda              : inout std_logic;
    constant ack            : in    std_logic := '1';
    constant stop_condition : in    std_logic := '0') is



@@ 134,9 134,9 @@ package body tb_i2c_master_pkg is
  procedure i2c_master_start (
    constant address : in    std_logic_vector(6 downto 0);
    constant rw      : in    std_logic;
    signal scl : inout std_logic;
    signal sda : inout std_logic;
    constant exp_ack : in std_logic := '1') is
    signal scl       : inout std_logic;
    signal sda       : inout std_logic;
    constant exp_ack : in    std_logic := '1') is
  begin
    if scl = 'H' and sda = '0' then
      scl_fall(scl);

M tb/i2c/tx_tb.vhd => tb/i2c/tx_tb.vhd +25 -8
@@ 31,12 31,15 @@ architecture a1 of tx_tb is

  signal unexpected_sda : std_logic;

  signal err_noack : std_logic;
  signal noack : std_logic;

  signal validate_sda_stable_when_scl_high : std_logic := '0';
  signal check_noerr : std_logic := '0';
  signal zero : std_logic := '0';

  signal curr_noack : std_logic;
  signal next_noack : std_logic;

  procedure trigger_scl_pulse(
    signal scl : inout std_logic;
    signal scl_rising_pulse : inout std_logic;


@@ 99,10 102,10 @@ architecture a1 of tx_tb is
    end if;

    for i in 7 downto 0 loop
      check_equal(sda_enable, not data(i));
      check_equal(sda_enable, not data(i), "Unexpected sda enable");

      if check_ready /= 'Z' then
        check_equal(ready, check_ready);
        check_equal(ready, check_ready, "Unexpected ready");
      end if;

      check(scl_stretch = '0', "Cannot send when stretch is active", failure);


@@ 128,10 131,9 @@ begin  -- architecture a1
      clk_i                 => clk,
      rst_in                => rst_n,
      start_write_i         => start_write,
      ss_condition_i        => '0',
      expect_ack_i          => '1',
      rst_i2c_i             => '0',
      clear_buffer_i        => '0',
      err_noack_o           => err_noack,
      noack_o               => noack,
      scl_stretch_o         => scl_stretch,
      scl_rising_pulse_i    => scl_rising_pulse,
      scl_falling_delayed_i => scl_falling_pulse,


@@ 145,9 147,23 @@ begin  -- architecture a1
  clk <= not clk after CLK_PERIOD / 2;
  rst_n <= '1' after 2 * CLK_PERIOD;


  sda <= '0' when sda_override = '1' else
         not sda_enable;

  next_noack <= '1' when noack;

  set_noack: process (clk) is
  begin  -- process set_noack
    if rising_edge(clk) then          -- rising clock edge
      if rst_n = '0' then              -- synchronous reset (active low)
        curr_noack <= '0';
      else
        curr_noack <= next_noack;
      end if;
    end if;
  end process set_noack;

  main: process is
  begin  -- process
    scl <= '1';


@@ 229,7 245,7 @@ begin  -- architecture a1
        valid <= '0';
        check_noerr <= '0'; -- disable no err check
        check_received_data("11010100", '1', '0', sda_override, scl, scl_rising_pulse, scl_falling_pulse);
        check_equal(err_noack, '1');
        check_equal(curr_noack, '1');
        check_equal(sda_enable, '0');
      end if;
    end loop;


@@ 237,6 253,7 @@ begin  -- architecture a1
    test_runner_cleanup(runner);
  end process;

  no_err: check_stable(clk, check_noerr, check_noerr, zero, err_noack);
  no_noack_err: check_stable(clk, check_noerr, check_noerr, zero, noack);
  no_sda_unexpected_err: check_stable(clk, check_noerr, check_noerr, zero, unexpected_sda);
  stability_check: check_stable(clk, validate_sda_stable_when_scl_high, scl_rising_pulse, scl_falling_pulse, sda_enable);
end architecture a1;

Do not follow this link