Xilinx AXI stream tutorial – Part 2

Hi again,

In the previous chapter of this tutorial, we presented the AXI Streaming interface, its main signals, and some of its applications.

Now let’s go for the funnier stuff, that is, to actually make and test some VHDL code to implement our AXI master. We will proceed gradually, adding features as we go. At the end of this tutorial, you will have code that:

  • Implements an AXI master with variable packet length
  • Flow control support (ready and valid)
  • Option for generation of several kinds of data patterns
  • Testbench to check that all features work OK
  • Include an instantiation of Xilinx’s AXI Stream protocol checker IP to verify the correctness of our AXI master core.

So let’s see the first version of an AXI master. In this version, the packets will have a fixed data length, and the data will be a progression of ascending numbers (the same counter that controls that the packet length is reached, is used to generate the packet data):

entity AXIStream_Master_v1 is
  port (      
    start          : in std_logic;                 -- Starts an axi_stream transaction

    -- axi stream ports
    m_axis_clk    : in  std_logic;
    m_axis_tvalid : out std_logic;
    m_axis_tdata : out std_logic_vector(31 downto 0);
    m_axis_tstrb : out std_logic_vector(3 downto 0);
    m_axis_tlast : out std_logic  
  );
end AXIStream_Master_v1;

architecture rtl of AXIStream_Master_v1 is
                              
  constant PACKET_LEN : natural :=  8;
  constant PACKET_W   : natural :=  4;                                                                                   

  -- Define the states of state machine                                             
  type    state is (IDLE, SEND_STREAM);          

  signal  sm_state : state := IDLE;                                                   

  -- AXI Stream internal signals
  signal data           : unsigned(31 downto 0) := (others => '0');
  signal packet_len_cnt : unsigned(PACKET_W-1 downto 0)  := (others => '0');

begin
  -- I/O Connections assignments
  m_axis_tstrb   <= (others => '1');     -- byte enables always high
  data(PACKET_W-1 downto 0)  <= packet_len_cnt; 
  m_axis_tdata   <= std_logic_vector(data);
   
  -- Control state machine implementation                                               
  sm_pr : process(m_axis_clk)                                                                        
  begin                                                                                       
    if (rising_edge (m_axis_clk)) then                                                       
      case (sm_state) is                                                              
        when IDLE =>
          m_axis_tvalid     <= '0';
          m_axis_tlast      <= '0';
          packet_len_cnt    <= (others => '0');      
          
          -- start sending 
          if (start = '1') then
            sm_state <= SEND_STREAM;  
          end if;                                                                                                      
        when SEND_STREAM  =>   
          m_axis_tvalid     <= '1';               
          packet_len_cnt    <= packet_len_cnt + 1;
          if (packet_len_cnt = PACKET_LEN-1 ) then
            m_axis_tlast    <= '1';
            sm_state        <= IDLE;
          end if;                                                                                                        
      end case;                                                                             
    end if;                                                                                 
  end process sm_pr;                                                                                
end rtl;

In line 31 we see that the data is assigned to the same counter that is used to check the packet length.

The AXI Master is implemented using a very simple state machine. Once a packet is sent, its length is checked and if it is equal to the desired value, the S.M. returns to the IDLE state.

In the waveform from the simulation, we can see two packets, each one eight clocks in length. We can see that once start is asserted, packets are sent non-stop. If start was a signal coming from a FIFO (FIFO not empty), this behavior could be wanted. However, in the next chapter, we will also introduce a mechanism that asks for a control signal like start to go from low to high to generate a new packet.

We will also introduce several more features and enhancements.

As usual, the VHDL source and test-bench (including waveform files for Vivado) are available on GitHub

Leave a Reply

Your email address will not be published. Required fields are marked *