0
\$\begingroup\$

I'm working on a design right now but I'm struggling with the axistream bus. I just want to be sure that I'm understanding well how it works. To do so I'm using the uvvm library to do a generator that send some data through a axis bus to my IP and then my IP sends it back to a uvvm axistream slave. The IP is very simple: I just read what's in the databus coming from the master then I transmit it to the slave.

    library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity AXI4_Stream_IP is
    port (
        -- Clock and Reset Signals
        axi_clk     : in  std_logic;
        axi_rstn    : in  std_logic;

        -- AXI4-Stream Input Signals
        tvalid_in   : in  std_logic;            -- Input Data Valid
        tready_in   : out std_logic;            -- Ready for Input Data
        tdata_in    : in  std_logic_vector(7 downto 0); -- Input Data
        tlast_in    : in  std_logic; 
        tkeep_in    : in  std_logic_vector(0 downto 0); -- Assuming tkeep is not used in the component
        tuser_in    : in  std_logic_vector(0 downto 0); -- Assuming tuser is not used in the component
        tid_in      : in  std_logic_vector(0 downto 0); -- Assuming tid is not used in the component
        tstrb_in    : in  std_logic_vector(0 downto 0); -- Assuming tstrb is not used in the component
        t_dest_in   : in  std_logic_vector(0 downto 0);

        -- AXI4-Stream Output Signals
        tvalid_out  : out std_logic;            -- Output Data Valid
        tready_out  : in  std_logic;            -- Ready for Output Data
        tdata_out   : out std_logic_vector(7 downto 0); -- Output Data
        tlast_out   : out std_logic;            -- Output Last Data Indicator
        tkeep_out   : out std_logic_vector(0 downto 0); -- Assuming tkeep is not used in the component
        tuser_out   : out std_logic_vector(0 downto 0); -- Assuming tuser is not used in the component
        tid_out     : out std_logic_vector(0 downto 0); -- Assuming tid is not used in the component
        tstrb_out   : out std_logic_vector(0 downto 0); -- Assuming tstrb is not used in the component
        t_dest_out  : out std_logic_vector(0 downto 0) -- Assuming t_dest is not used in the component
    );
end entity AXI4_Stream_IP;

architecture rtl of AXI4_Stream_IP is
    -- Internal Signals for Input Data Storage and Read Control
    signal reg_data_in : std_logic_vector(7 downto 0) := (others => '0');
    signal state : std_logic_vector(1 downto 0) := "00";

begin
    -- Process for IP Logic
    process(axi_clk, axi_rstn)
    begin
        if axi_rstn = '0' then
            -- Reset conditions
            tvalid_out <= '0';
            tdata_out <= (others => '0');
            tlast_out <= '0';
            reg_data_in <= (others => '0'); -- Reset internal register
           -- state <= "00";
            tready_in <= '0';
        elsif rising_edge(axi_clk) then
            if tvalid_in = '1' then
                tready_in <= '1';
            else
                tready_in <= '0';
            end if;

            case state is 
                when "00" => 
                    if tvalid_in = '1' and tready_in = '1' then
                        reg_data_in <= tdata_in;
                        tvalid_out <= '1';
                        state <= "01";
                    end if;
                    
                when "01" =>
                    if tready_out = '1' and tvalid_out = '1' then
                        tdata_out <= reg_data_in;
                        tvalid_out <= '0';
                        state <= "00";
                    end if;
                when others =>
                    state <= "00";
            end case;
        end if;
    end process;
end architecture rtl;

This is the code of the IP, and I use the following testbench:

    library ieee;
use ieee.std_logic_1164.all;

library std;
use std.env.all;

library uvvm_util;
context uvvm_util.uvvm_util_context;
library bitvis_vip_axistream;
use bitvis_vip_axistream.axistream_bfm_pkg.all;

use std.textio.all;
use std.env.finish;

entity alorsla is
end alorsla;

architecture behavioral of alorsla is

  -- Clock signal
  signal clk : std_logic;
  signal rst_n:std_logic;

  -- Record definition for the AXI4-Stream bus
  subtype t_axistream is t_axistream_if (
    tdata (7 downto 0),
    tkeep (0 downto 0),
    tuser (0 downto 0),
    tstrb (0 downto 0),
    tid   (0 downto 0),
    tdest (0 downto 0)
  );
  signal m_axistream : t_axistream;
  signal s_axistream : t_axistream;
    -- AXI4-Stream data
    signal tdata_array : t_slv_array(0 to 15) (7 downto 0) := ( x"00", x"11", x"22", x"33",
    x"44", x"55", x"66", x"77",
    x"88", x"59", x"AA", x"BB",
    x"CC", x"DD", x"EE", x"FF");


    signal tdata_expected_array : t_slv_array(0 to 15) (7 downto 0) := ( x"00", x"11", x"22", x"33",
    x"44", x"55", x"66", x"77",
    x"88", x"59", x"AA", x"BB",
    x"CC", x"DD", x"EE", x"FF");

    signal msg_id_panel : t_msg_id_panel := shared_msg_id_panel;

    -- Configure the AXI4-Stream BFM
    constant C_AXISTREAM_BFM_CONFIG_DEFAULT : t_axistream_bfm_config := (
        max_wait_cycles                => 100,
        max_wait_cycles_severity       => ERROR,
        clock_period                   => -1 ns,
        clock_period_margin            => 0 ns,
        clock_margin_severity          => TB_ERROR,
        setup_time                     => -1 ns,
        hold_time                      => -1 ns,
        bfm_sync                       => SYNC_ON_CLOCK_ONLY,
        match_strictness               => MATCH_EXACT,
        byte_endianness                => LOWER_BYTE_LEFT,
        valid_low_at_word_num          => 0,
        valid_low_multiple_random_prob => 0.5,
        valid_low_duration             => 0,
        valid_low_max_random_duration  => 5,
        check_packet_length            => false,
        protocol_error_severity        => ERROR,
        ready_low_at_word_num          => 0,
        ready_low_multiple_random_prob => 0.5,
        ready_low_duration             => 0,
        ready_low_max_random_duration  => 5,
        ready_default_value            => '0',
        id_for_bfm                     => ID_BFM
      );

      constant C_AXISTREAM_SLAVE_BFM_CONFIG_DEFAULT : t_axistream_bfm_config := (
        max_wait_cycles                => 100,
        max_wait_cycles_severity       => ERROR,
        clock_period                   => -1 ns,
        clock_period_margin            => 0 ns,
        clock_margin_severity          => TB_ERROR,
        setup_time                     => -1 ns,
        hold_time                      => -1 ns,
        bfm_sync                       => SYNC_ON_CLOCK_ONLY,
        match_strictness               => MATCH_EXACT,
        byte_endianness                => LOWER_BYTE_LEFT,
        valid_low_at_word_num          => 0,
        valid_low_multiple_random_prob => 0.5,
        valid_low_duration             => 0,
        valid_low_max_random_duration  => 5,
        check_packet_length            => false,
        protocol_error_severity        => ERROR,
        ready_low_at_word_num          => 0,
        ready_low_multiple_random_prob => 0.5,
        ready_low_duration             => 0,
        ready_low_max_random_duration  => 5,
        ready_default_value            => '0',
        id_for_bfm                     => ID_BFM
      );


      component AXI4_Stream_IP is
        port (
            -- Clock and Reset Signals
            axi_clk     : in  std_logic;
            axi_rstn    : in  std_logic;
    
            -- AXI4-Stream Input Signals
            tvalid_in   : in  std_logic;            -- Input Data Valid
            tready_in   : out std_logic;            -- Ready for Input Data
            tdata_in    : in  std_logic_vector(7 downto 0); -- Input Data
            tlast_in    : in  std_logic; 
            tkeep_in    : in  std_logic_vector(0 downto 0); -- Assuming tkeep is not used in the component
            tuser_in    : in  std_logic_vector(0 downto 0); -- Assuming tuser is not used in the component
            tid_in      : in  std_logic_vector(0 downto 0); -- Assuming tid is not used in the component
            tstrb_in    : in  std_logic_vector(0 downto 0); -- Assuming tstrb is not used in the component
            t_dest_in   : in  std_logic_vector(0 downto 0);
    
            -- AXI4-Stream Output Signals
            tvalid_out  : out std_logic;            -- Output Data Valid
            tready_out  : in  std_logic;            -- Ready for Output Data
            tdata_out   : out std_logic_vector(7 downto 0); -- Output Data
            tlast_out   : out std_logic;            -- Output Last Data Indicator
            tkeep_out   : out std_logic_vector(0 downto 0); -- Assuming tkeep is not used in the component
            tuser_out   : out std_logic_vector(0 downto 0); -- Assuming tuser is not used in the component
            tid_out     : out std_logic_vector(0 downto 0); -- Assuming tid is not used in the component
            tstrb_out   : out std_logic_vector(0 downto 0); -- Assuming tstrb is not used in the component
            t_dest_out  : out std_logic_vector(0 downto 0) -- Assuming t_dest is not used in the component
        );
    end component AXI4_Stream_IP;

      begin

        -- Generate the clock
        clock_generator(clk, 10 ns);
    uut: AXI4_Stream_IP
     port map (

        axi_clk =>clk,
        axi_rstn =>rst_n,

        tdata_in    => m_axistream.tdata,
        tkeep_in     => m_axistream.tkeep,
        tuser_in    => m_axistream.tuser,
        tvalid_in   => m_axistream.tvalid,
        tlast_in    => m_axistream.tlast,
        tready_out    => s_axistream.tready,
        tstrb_in    => m_axistream.tstrb,
        tid_in        => m_axistream.tid,
        t_dest_in    => m_axistream.tdest,

        tdata_out    => s_axistream.tdata,
        tkeep_out    => s_axistream.tkeep,
        tuser_out    => s_axistream.tuser,
        tvalid_out   => s_axistream.tvalid,
        tlast_out    => s_axistream.tlast,
        tready_in    => m_axistream.tready,
        tstrb_out     => s_axistream.tstrb,
        tid_out        => s_axistream.tid,
        t_dest_out     => s_axistream.tdest
    );

--     uut: AXI4_Stream_IP 
--   port map (
--      axi_clk     => clk,
--     axi_rstn    => rst_n,
--     tvalid_in   => m_axistream.tvalid,
--     tready_out  => s_axistream.tready,
--     tdata_in    => m_axistream.tdata,
--     tlast_in    => m_axistream.tlast,
    --tkeep_in    => (others => '0'), -- Assuming tkeep is not used in the component
    --tuser_in    => (others => '0'), -- Assuming tuser is not used in the component
    --tid_in      => (others => '0'), -- Assuming tid is not used in the component
    --tstrb_in    => (others => '0'), -- Assuming tstrb is not used in the component
    --t_dest_in   => (others => '0'), -- Assuming t_dest is not used in the component
    -- tvalid_out  => s_axistream.tvalid,
    -- tready_in   => m_axistream.tready,
    -- tdata_out   => s_axistream.tdata,
    -- tlast_out   => s_axistream.tlast
    --tkeep_out   => s_axistream.tkeep-- Assuming tkeep is not used in the component
  --);



        -- Drive tready
        --m_axistream.tready <= '1';

   
      
        stimuli : process
          constant C_SCOPE : string := "STIMULI Process";
        begin
            -- reset
            rst_n <='0';
            wait for 10 ns;
            rst_n <= '1';
          -- Log setup
          enable_log_msg(ALL_MESSAGES, "Logging all Messages", NON_QUIET, C_SCOPE);
          log(ID_LOG_HDR, "SIMULATION START", C_SCOPE);
      
          -- Send data over AXI4-Stream
          axistream_transmit(tdata_array, "Send AXI4-Stream data", clk, m_axistream, C_SCOPE, msg_id_panel, C_AXISTREAM_BFM_CONFIG_DEFAULT);
          wait for 50 ns;
      
          -- End of simulation
          report_alert_counters(FINAL);
          log(ID_LOG_HDR, "SIMULATION END", C_SCOPE);
          std.env.stop;
          wait;
        end process stimuli;

    check_data : process
        constant C_SCOPE : string := "CHECK PROCESS";
        begin
        -- Check data over AXI4-Stream
        axistream_expect( tdata_expected_array, "Check AXI4-Stream data", clk, s_axistream,
                            WARNING, C_SCOPE, msg_id_panel, C_AXISTREAM_SLAVE_BFM_CONFIG_DEFAULT);
        log(ID_LOG_HDR, "AXI4-Stream Data checked", C_SCOPE);  
        wait;
    end process check_data;


      
      end architecture behavioral;

When I do the simulation I end up with this signal:

simulation

You can see I am missing some data in the reception part and I don't know why. I read that is it recommended to use a buffer sometimes, but it seems a little bit complex and I want to be sure that it's not something else before doing that.

Can someone help me? I have been working on this for a very long time now and I did not find anything else on the internet than "use the vivavado axistream so you have nothing to do".

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

The key concept in AXI streaming is that data is transferred from source to destination in any clock cycle in which both TVALID and TREADY are high. Among other things, this means that when you assert TVALID as a source, your TDATA value must already be on the output port of your module. You can't wait for TREADY to be asserted and then update TDATA, which is what you're currently doing.

Here's an example of a simple (but nontrivial) module that illustrates how flow control gets done on the both the input side and the output side of a module:

--! @file axi_two_stage_fifo.vhdl

--! This module is the next-simplest possible AXI FIFO. Its advantage over the
--! one-stage FIFO is that it allows the flow control (TREADY) to be registered,
--! breaking long combinatorial paths.
--!
--! This is a fall-though FIFO -- the data appears on the output pins regardless
--! of the state of vout_tready.
--!
--! There are two registers, which hold zero, one or two words of data. The
--! state is encoded as the states of vin_tready and vout_tvalid:
--! 0 words: vin_tready = 1, vout_tvalid = 0
--! 1 words: vin_tready = 1, vout_tvalid = 1
--! 2 words: vin_tready = 0, vout_tvalid = 1

-- History:
--  2022-01-05 DT   Debug translation.
--  2022-01-04 DT   Translate from axi_two_stage_fifo.v

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity axi_two_stage_fifo is
  generic (
    TDATA_WIDTH         : integer := 1;
    TUSER_WIDTH         : integer := 1
  );
  port (
    -- AXI in
    vin_tready          : out   std_logic;
    vin_tvalid          : in    std_logic;
    vin_tdata           : in    std_logic_vector (TDATA_WIDTH-1 downto 0);
    vin_tuser           : in    std_logic_vector (TUSER_WIDTH-1 downto 0);
    vin_tlast           : in    std_logic;
    -- AXI out
    vout_tready         : in    std_logic;
    vout_tvalid         : out   std_logic;
    vout_tdata          : out   std_logic_vector (TDATA_WIDTH-1 downto 0);
    vout_tuser          : out   std_logic_vector (TUSER_WIDTH-1 downto 0);
    vout_tlast          : out   std_logic;
    -- clock, reset
    clock               : in    std_logic;
    reset               : in    std_logic 
  );
end axi_two_stage_fifo;

architecture rtl of axi_two_stage_fifo is
  signal fifo_tdata     : std_logic_vector (TDATA_WIDTH-1 downto 0);
  signal fifo_tuser     : std_logic_vector (TUSER_WIDTH-1 downto 0);
  signal fifo_tlast     : std_logic;
  signal input_full     : std_logic;
  signal output_full    : std_logic;
  signal status         : std_logic_vector (1 downto 0);
begin

  vin_tready <= not input_full;
  vout_tvalid <= output_full;
  status <= input_full & output_full;

  process (clock) is
  begin
    if rising_edge(clock) then
      if reset = '1' then
        input_full <= '0';
        output_full <= '0';
      else
        case status is
        when "00" =>
          -- 0 words, vin_tready is already 1
          if vin_tvalid = '1' then
            output_full <= '1';
            vout_tdata <= vin_tdata;
            vout_tuser <= vin_tuser;
            vout_tlast <= vin_tlast;
          end if;

        when "01" =>
          -- 1 word, vin_tready is already 1, vout_tvalid is already 1
          if (vin_tvalid = '1') and (vout_tready = '0') then
            input_full <= '1';
            fifo_tdata <= vin_tdata;
            fifo_tuser <= vin_tuser;
            fifo_tlast <= vin_tlast;
          elsif (vin_tvalid = '0') and (vout_tready = '1') then
            output_full <= '0';
          elsif (vin_tvalid = '1') and (vout_tready = '1') then
            vout_tdata <= vin_tdata;
            vout_tuser <= vin_tuser;
            vout_tlast <= vin_tlast;
          end if;

        when "11" =>
          -- 2 words, vout_tvalid is already 1
          if vout_tready = '1' then
            input_full <= '0';
            vout_tdata <= fifo_tdata;
            vout_tuser <= fifo_tuser;
            vout_tlast <= fifo_tlast;
          end if;

        when others =>
          input_full <= '0';
          output_full <= '0';
        end case;
      end if;
    end if;
  end process;
end rtl;
\$\endgroup\$
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.