Adam Taylor
Published © LGPL

Debugging AMD FPGA AXI designs with a RPi Pico

FPGA design use AXI networks, this can be a complex protocol to work with. Lets look at how you can access AXI using a UART and RPi PICO

IntermediateFull instructions provided3 hours4,179
Debugging AMD FPGA AXI designs with a RPi Pico

Things used in this project

Story

Read more

Code

Uart

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

use work.adiuvo_uart.all;

entity uart is generic (
  reset_level : std_logic := '0'; -- reset level which causes a reset
  clk_freq    : natural   := 100_000_000; -- oscillator frequency
  baud_rate   : natural   := 115200 -- baud rate 
);
port (
  --!System Inputs 
  clk   : in std_logic;
  reset : in std_logic;

  --!External Interfaces
  rx : in std_logic;
  tx  : out std_logic;

  --! Master AXIS Interface  
  m_axis_tready : in  std_logic;
  m_axis_tdata  : out std_logic_vector(7 downto 0);
  m_axis_tvalid : out std_logic;

  --! Slave AXIS Interface
  s_axis_tready : out  std_logic;
  s_axis_tdata  : in std_logic_vector(7 downto 0);
  s_axis_tvalid : in std_logic
  
  );
  
end entity;
architecture rtl of uart is

  constant bit_period : integer := (clk_freq/baud_rate) - 1;
  

  type cntrl_fsm is (idle, set_tx,wait_tx);
  type rx_fsm is (idle, start, sample, check, wait_axis);

  signal current_state : cntrl_fsm; --:= idle;
  signal rx_state      : rx_fsm;-- := idle;
  signal baud_counter  : unsigned(vector_size(real(clk_freq), real(baud_rate)) downto 0) := (others => '0'); --timer for outgoing signals 
  signal baud_en       : std_logic                                                       := '0';
  signal meta_reg      : std_logic_vector(3 downto 0)                                    := (others => '0'); -- fe detection too
  signal capture       : std_logic_vector(7 downto 0)                                    := (others => '0'); -- data and parity
  signal bit_count     : integer range 0 to 1023                                         := 0;
  signal pos_count     : integer range 0 to 15                                           := 0;
  signal running       : std_logic                                                       := '0';
  signal load_tx  : std_logic := '0';
  signal complete : std_logic := '0';

  signal tx_reg  : std_logic_vector(11 downto 0) := (others => '0');
  signal tmr_reg : std_logic_vector(11 downto 0) := (others => '0');
  signal payload : std_logic_vector(7 downto 0)  := (others => '0');
  constant zero  : std_logic_vector(tmr_reg'range) := (others => '0');
begin

  process (reset, clk)
  begin
    if reset = reset_level then
      current_state <= idle;
      payload       <= (others => '0');
      load_tx <= '0';
    elsif rising_edge(clk) then
      load_tx <= '0';
      case current_state is
        when idle =>
          if s_axis_tvalid = '1' then
            current_state <= set_tx;
            load_tx       <= '1';
            payload       <= s_axis_tdata;
          end if;
        when set_tx =>
          current_state <= wait_tx;
        when wait_tx =>
          if complete = '1' then
            current_state <= idle;
          end if;
        when others => 
         current_state <= idle;
      end case;
    end if;
  end process;

  s_axis_tready <= '1' when (current_state = idle) else '0';

  process (reset, clk)
  --! baud counter for output TX 
  begin
    if reset = reset_level then
      baud_counter <= (others => '0');
      baud_en      <= '0';
    elsif rising_edge(clk) then
      baud_en <= '0';
      if (load_tx = '1') then
        baud_counter <= (others => '0');
      elsif (baud_counter = bit_period) then
        baud_en      <= '1';
        baud_counter <= (others => '0');
      else
        baud_counter <= baud_counter + 1;
      end if;
    end if;
  end process;

  process (reset, clk)
  --!metastability protection rx signal
  begin
    if reset = reset_level then
      meta_reg <= (others => '1');
    elsif rising_edge(clk) then
      meta_reg <= meta_reg(meta_reg'high - 1 downto meta_reg'low) & rx;
    end if;
  end process;

  process (reset, clk)
  begin
    if reset = reset_level then
      pos_count <= 0;
      bit_count <= 0;
      capture     <= (others => '0');
      rx_state    <= idle;
      m_axis_tvalid <= '0';
      m_axis_tdata     <= (others => '0');
      
    elsif rising_edge(clk) then
      case rx_state is
 
        when idle =>
          m_axis_tvalid  <= '0';
          if meta_reg(meta_reg'high downto meta_reg'high - 1) = fe_det then 
            pos_count <= 0;
            bit_count <= 0;
            capture  <= (others => '0');
            rx_state <= start;
          end if;
        when start =>
          if bit_count = bit_period then
            bit_count <= 0;
            rx_state  <= sample;
          else
            bit_count <= bit_count + 1;
          end if;
        when sample =>
          bit_count <= bit_count + 1;
          rx_state  <= sample;
          if bit_count = (bit_period/2) and (pos_count < 8) then 
            capture <= meta_reg(meta_reg'high) & capture(capture'high downto capture'low + 1);
          elsif bit_count = bit_period then
            if pos_count = 8 then 
              rx_state <= check;
            else
              pos_count <= pos_count + 1;
              bit_count <= 0;
            end if;
          end if;
        when check =>
          if parity(capture) = '1' then
            m_axis_tvalid <= '1';
            m_axis_tdata  <= capture(7 downto 0);
            rx_state      <= wait_axis;
          else
            m_axis_tvalid <= '1';
            m_axis_tdata  <= capture(7 downto 0);
            rx_state      <= wait_axis;
          end if;
        when wait_axis =>
          if m_axis_tready = '1' then 
            m_axis_tvalid <= '0';
            rx_state      <= idle;    
          end if;
      end case;
    end if;
  end process;

  op_uart : process (reset, clk)
  begin
    if reset = reset_level then
      tx_reg  <= (others => '1');
      tmr_reg <= (others => '0');
    elsif rising_edge(clk) then
      if load_tx = '1' then
        tx_reg  <= stop_bit & not(parity(payload)) & payload & start_bit ;
        tmr_reg <= (others => '1');
      elsif baud_en = '1' then
        tx_reg  <= '1' & tx_reg(tx_reg'high downto tx_reg'low + 1);
        tmr_reg <= tmr_reg(tmr_reg'high - 1 downto tmr_reg'low) & '0';
      end if;
    end if;
  end process;

  tx       <= tx_reg(tx_reg'low);
  complete <= '1' when (tmr_reg = zero and current_state = wait_tx) else '0';
end architecture;

UART PKG

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


package adiuvo_uart is 

    function vector_size(clk_freq, baud_rate : real) return integer;
    function parity (a : std_logic_vector) return std_logic;
    constant fe_det     : std_logic_vector(1 downto 0) := "10";
    constant start_bit  : std_logic                    := '0';
    constant stop_bit   : std_logic_vector             := "11";
end package;

package body adiuvo_uart is 

    function vector_size(clk_freq, baud_rate : real) return integer is
        variable div                             : real;
        variable res                             : real;
      begin
        div := (clk_freq/baud_rate);
        res := CEIL(LOG(div)/LOG(2.0));
        return integer(res - 1.0);
      end;
    
      function parity (a : std_logic_vector) return std_logic is
        variable y         : std_logic := '0';
      begin
        for i in a'range loop
          y := y xor a(i);
        end loop;
        return y;
      end parity;
    

      
end package body adiuvo_uart;

protocol

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

--Declare entity
entity axi_protocol is
	generic(
            G_AXIL_DATA_WIDTH    :integer   := 32;                                          --Width of AXI Lite data bus
            G_AXI_ADDR_WIDTH     :integer   := 32;                                          --Width of AXI Lite Address Bu
            G_AXI_ID_WIDTH       :integer   := 8;                                           --Width of AXI ID Bus
            G_AXI_AWUSER_WIDTH   :integer   := 1                                            --Width of AXI AW User bus
	);
	port(	
			--Master clock & reset
			clk 			 :in std_ulogic;                                            --System clock
			reset			 :in std_ulogic;                                            --System reset, async active low

            --! Master AXIS Interface  
            m_axis_tready : in  std_logic;
            m_axis_tdata  : out std_logic_vector(7 downto 0);
            m_axis_tvalid : out std_logic;

            --! Slave AXIS Interface
            s_axis_tready : out  std_logic;
            s_axis_tdata  : in std_logic_vector(7 downto 0);
            s_axis_tvalid : in std_logic;
            
            --! AXIL Interface
            --!Write address
            axi_awaddr    : out std_logic_vector(G_AXI_ADDR_WIDTH-1 downto 0);                  
            axi_awprot    : out std_logic_vector(2 downto 0);                   
            axi_awvalid   : out std_logic;
            --!write data
            axi_wdata     : out std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);  
            axi_wstrb     : out std_logic_vector(G_AXIL_DATA_WIDTH/8-1 downto 0);
            axi_wvalid    : out std_logic; 
            --!write response
            axi_bready    : out std_logic;
            --!read address
            axi_araddr    : out std_logic_vector(G_AXI_ADDR_WIDTH-1 downto 0);               
            axi_arprot    : out std_logic_vector(2 downto 0);                   
            axi_arvalid   : out std_logic; 
            --!read data
            axi_rready    : out std_logic;    
            --write address
            axi_awready   : in std_logic;
            --write data
            axi_wready    : in std_logic;
            --write response
            axi_bresp     : in std_logic_vector(1 downto 0);                   
            axi_bvalid    : in std_logic;
            --read address
            axi_arready   : in std_logic;
            --read data       
            axi_rdata     : in std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);  
            axi_rresp     : in std_logic_vector(1 downto 0);                   
            axi_rvalid    : in std_logic 

		);
		
end entity axi_protocol;

architecture rtl of axi_protocol is 

    constant C_SINGLE_READ            : std_logic_vector(7 downto 0) := x"05";
    constant C_SINGLE_WRITE           : std_logic_vector(7 downto 0) := x"09";

    constant C_NUMB_ADDR_BYTES        : integer := 4;
    constant C_NUMB_LENGTH_BYTES      : integer := 1;
    constant C_NUMB_DATA_BYTES        : integer := 4;
    constant C_NUMB_AXIL_DATA_BYTES   : integer := 4;
    constant C_NUMB_CRC_BYTES         : integer := 4;   
    constant C_MAX_NUMB_BYTES         : integer := 4; -- max number of the above constant for number of bytes 
    constant C_ZERO_PAD               : std_logic_vector(7 downto 0) := (others => '0');
    
    type t_fsm is (idle, address, length, dummy, write_payload, read_payload, crc, write_axil, write_axi, read_axi, read_axil);
    type t_op_fsm is (idle, output, check);
    type t_array is array (0 to 7) of std_logic_vector(31 downto 0);
    type axil_read_fsm is (IDLE, START, CHECK_ADDR_RESP, READ_DATA, DONE);
    type axil_write_fsm is (IDLE, START, CHECK_ADDR_RESP, WRITE_DATA, RESP_READY, CHECK_RESP, DONE);
    signal write_state : axil_write_fsm;
    signal read_state  : axil_read_fsm;

    signal s_current_state : t_fsm;

    signal s_command            : std_logic_vector(7 downto 0);
    signal s_address            : std_logic_vector((C_NUMB_ADDR_BYTES * 8)-1 downto 0);
    signal s_length             : std_logic_vector(7 downto 0);
    signal s_length_axi         : std_logic_vector(7 downto 0);
    signal s_buf_cnt            : unsigned(7 downto 0);
    signal s_byte_pos           : integer range 0 to C_MAX_NUMB_BYTES; 
    signal s_num_bytes          : integer range 0 to C_MAX_NUMB_BYTES; 
    signal s_s_tready           : std_logic;
    signal s_write_buffer       : t_array :=(others=>(others=>'0'));
    signal s_read_buffer        : t_array :=(others=>(others=>'0'));
    signal s_write_buffer_temp  : std_logic_vector(31 downto 0);
    signal s_read_buffer_temp   : std_logic_vector(31 downto 0);

    --axil lite data interface 
    signal s_axil_data          : std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);
    signal s_axil_valid         : std_logic;
    signal s_axil_idata         : std_logic_vector(G_AXIL_DATA_WIDTH-1 downto 0);


    --axi mstream 
    signal s_opptr              : unsigned(7 downto 0);
    signal s_start              : std_logic;
    signal s_op_state           : t_op_fsm;
    signal s_op_byte            : integer range 0 to C_MAX_NUMB_BYTES; 
    signal start_read           : std_logic;
    signal start_write          : std_logic;
    signal s_m_axis_tvalid      : std_logic;

begin

    s_axis_tready <= s_s_tready;

FSM : process(clk, reset )
begin 
    if (reset = '0') then 
        start_read  <= '0';
        start_write <= '0';

        s_s_tready  <= '0';
    elsif rising_edge(clk) then
        s_s_tready  <= '1';
        s_start     <= '0';
        start_read  <= '0';
        start_write <= '0';
        case s_current_state is

            when idle => -- to do needs to check the command is valid
                s_buf_cnt           <= (others =>'0');
                if (s_axis_tvalid = '1' and s_s_tready = '1') and 
                    (s_axis_tdata = C_SINGLE_READ  or s_axis_tdata = C_SINGLE_WRITE) then
                        s_s_tready <= '0';
                        s_command <= s_axis_tdata;
                        s_current_state <= address;
                        s_byte_pos <= C_NUMB_ADDR_BYTES;
                end if;

            when address =>
                if s_byte_pos = 0 then
                    s_s_tready <= '0';
                    s_byte_pos <= C_NUMB_LENGTH_BYTES;
                    s_current_state <= length;    
                elsif s_axis_tvalid = '1' and s_s_tready = '1'  then
                    s_address <= s_address(s_address'length-8-1 downto 0) & s_axis_tdata;
                    s_byte_pos <= s_byte_pos - 1;
                    if s_byte_pos = 1 then 
                        s_s_tready <= '0';
                    end if; 
                end if;

            when length => 
                if s_byte_pos = 0 then
                    s_s_tready <= '0';
                    if s_command = C_SINGLE_READ and unsigned(s_length) = 1 then
                        s_current_state <= read_axil; 
                        start_read      <= '1';
                        s_num_bytes     <= C_NUMB_AXIL_DATA_BYTES;
                    elsif s_command = C_SINGLE_WRITE then
                        s_buf_cnt       <= (others =>'0');
                        s_byte_pos      <= C_NUMB_AXIL_DATA_BYTES;
                        s_num_bytes     <= C_NUMB_AXIL_DATA_BYTES;
                        s_current_state <= write_payload;
                    end if;    
                elsif s_axis_tvalid = '1' and s_s_tready = '1'  then
                    s_length            <= s_axis_tdata;
                    s_length_axi        <= std_logic_vector(unsigned(s_axis_tdata)-1);
                    s_byte_pos          <= s_byte_pos - 1;
                    s_s_tready <= '0';
                end if;

            when read_axil =>  
                if s_axil_valid = '1' then 
                    s_start             <= '1';
                    s_read_buffer(0)(G_AXIL_DATA_WIDTH-1 downto 0) <= s_axil_data;
                end if;
                if (read_state = DONE) then
                    s_current_state <= read_payload;
                end if;
            

            when write_payload =>
                if s_buf_cnt = unsigned(s_length) then 
                    s_s_tready <= '0';
                    s_current_state <= write_axil;
                    start_write <= '1';
                else
                    if s_byte_pos = 0 then 
                        s_s_tready <= '0';
                        s_byte_pos <= s_num_bytes;
                        s_write_buffer(to_integer(s_buf_cnt)) <= s_write_buffer_temp;
                        s_buf_cnt <= s_buf_cnt + 1;  
                    elsif (s_axis_tvalid = '1' and s_s_tready = '1')  then
                        s_write_buffer_temp <= s_write_buffer_temp(s_write_buffer_temp'length-8-1 downto 0) & s_axis_tdata;
                        s_byte_pos <= s_byte_pos - 1;  
                        if s_byte_pos = 1 then 
                            s_s_tready <= '0';
                        end if;   
                    end if;
                end if;

            when write_axil =>  
                s_s_tready <= '0';
                s_axil_idata <= s_write_buffer(0);
                if (write_state = DONE) then
                    s_current_state <= idle;
                end if;

            when read_payload =>
                s_current_state <= idle;
            when others => null;
        end case;
    end if;

end process;


m_axis_tvalid <= s_m_axis_tvalid;

process(clk, reset)
begin
    if (reset = '0') then 
        s_m_axis_tvalid      <= '0';
        m_axis_tdata        <= (others =>'0');
        s_opptr             <= (others => '0');
        s_op_byte           <= C_NUMB_AXIL_DATA_BYTES;
    elsif rising_edge(clk) then 
        case s_op_state is  
            when idle => 
                s_m_axis_tvalid <= '0';
                if s_start = '1' then 
                    s_opptr     <= (others => '0');
                    s_read_buffer_temp <= s_read_buffer(0);
                    s_op_byte   <= s_num_bytes;
                    s_op_state  <= output;
                end if;
            when output =>
                if s_opptr = unsigned(s_length) then 
                    s_op_state <= idle;
                    s_m_axis_tvalid <= '0';
                else 
                    s_m_axis_tvalid <= '1';
                    m_axis_tdata <= s_read_buffer_temp(7 downto 0);
                    if s_op_byte = 0 then 
                        s_op_byte   <= s_num_bytes;
                        s_opptr     <= s_opptr + 1;
                        s_m_axis_tvalid <= '0';   
                    elsif m_axis_tready = '1'  then 
                        s_m_axis_tvalid <= '1';                  
                        s_read_buffer_temp <= C_ZERO_PAD & s_read_buffer_temp(s_read_buffer_temp'length-1 downto 8);
                        s_op_byte <= s_op_byte - 1; 
                        s_op_state  <= check;  
                    end if;      
                end if;
            when check =>
                s_m_axis_tvalid <= '0';   
                s_op_state  <= output;
        end case;
    end if;

end process;


process(clk, reset)
begin  

    if (reset = '0') then 
        write_state <= IDLE;

        axi_awaddr  <= (others =>'0');
        axi_awprot  <= (others =>'0');
        axi_awvalid <= '0';
        axi_wdata   <= (others =>'0');
        axi_wstrb   <= (others =>'0');
        axi_wvalid  <= '0';
        axi_bready  <= '0';
    elsif rising_edge(clk) then 
        axi_wstrb   <= (others =>'0');
        case write_state is
            --Send write address
            when IDLE =>
                if start_write = '1' then
                    write_state <= START;
                end if;
            when START =>

                axi_awaddr  <= s_address;
                axi_awprot  <= "010";
                axi_awvalid <= '1';
                axi_wdata   <= s_axil_idata;
                axi_wvalid  <= '1';
                axi_wstrb   <= (others =>'1');
                write_state <= WRITE_DATA;--CHECK_ADDR_RESP;

            --Wait for slave to acknowledge receipt
            when CHECK_ADDR_RESP =>

                if (axi_awready = '1' ) then

                    axi_awaddr  <= (others => '0');
                    axi_awprot  <= (others => '0');
                    axi_awvalid <= '0';

                    write_state <= WRITE_DATA;
                else
                    write_state <= CHECK_ADDR_RESP;
                end if;

            --Send write data
            when WRITE_DATA => 

                if (axi_awready = '1' ) then
                    axi_awaddr  <= (others => '0');
                    axi_awprot  <= (others => '0');
                    axi_awvalid <= '0';
                    axi_wstrb   <= (others =>'0');
                end if;
            
                axi_wdata  <= s_axil_idata;
                axi_wvalid <= '1';
                axi_wstrb   <= (others =>'1');
                if (axi_wready = '1') then     
                    write_state <= RESP_READY;
                else
                    write_state <= WRITE_DATA;
                end if;

            --Set response ready
            when RESP_READY => 
                axi_wstrb   <= (others =>'0'); 
                axi_wvalid <= '0';
                axi_bready <= '1';
                write_state <= CHECK_RESP;

            --Check the response
            when CHECK_RESP =>
                if (axi_bvalid = '1') then
                    axi_bready <= '0';
                    write_state <= DONE;
                end if; 

            --Indicate the transaction has completed
            when DONE =>
                write_state <= IDLE;

            when others =>
                write_state <= START;

        end case;
    end if;
end process;

process(clk, reset)
begin  

    if (reset = '0') then 
        read_state <= IDLE;
        
        axi_araddr  <= (others =>'0');
        axi_arprot  <= (others =>'0');
        axi_arvalid <= '0';
        axi_rready  <= '0';

    elsif rising_edge(clk) then 
case read_state is
    when IDLE =>
        if start_read = '1' then
         read_state <= START;
        end if;
    --Send read address
    when START =>

        axi_araddr  <= s_address;
        axi_arprot  <= "010";
        axi_arvalid <= '1';

        s_axil_valid <= '0';

        read_state <= CHECK_ADDR_RESP;

    --Wait for the slave to acknowledge receipt of the address
    when CHECK_ADDR_RESP =>

        if (axi_arready = '1' ) then

            axi_araddr  <= (others => '0');
            axi_arprot  <= (others => '0');
            axi_arvalid <= '0';

            read_state <= READ_DATA;
        else
            read_state <= CHECK_ADDR_RESP;
        end if;

        s_axil_valid <= '0';

    --Read data from the slave
    when READ_DATA =>

        s_axil_data  <= axi_rdata; 

        if (axi_rvalid = '1') then                   
            s_axil_valid <= '1';
            read_state <= DONE;
        else
            s_axil_valid <= '0';
            read_state <= READ_DATA;
        end if;

        axi_rready <= '1';
                    
    --Indicate the transaction has completed
    when DONE =>

        axi_rready <= '0';
        s_axil_data  <= (others => '0');
        s_axil_valid <= '0';
        read_state <= IDLE;
    
    when others =>
         read_state <= START;

end case;
end if;
end process;

end architecture;

Credits

Adam Taylor
139 projects • 2385 followers
Adam Taylor is an expert in design and development of embedded systems and FPGA’s for several end applications (Space, Defense, Automotive)
Contact

Comments

Please log in or sign up to comment.