Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
11 years ago

Use Avalon MM Slave interface to connect to AXI Master?

Hello all!

I recently acquired the SoCKit from Arrow and I am trying to make a custom project.

I would like to create my custom VHDL PWM module in the FPGA, transform it into a Slave for the HPS and connect it to either the H2F AXI bridge or the LightWeight H2F AXI bridge.

The first question is whether my Slave custom module could implement an Avalon MM Slave interface that would automatically communicate with the AXI Master or I DO need to create an AXI3 Slave interface. A continuation question is if I can use an AXI4 slave (is it back compatible?) because I can find example implementation somewhere.

The second question is where I could find example implementations of Avalon MM Slave or AXI3 Slave. If I find that then I could just connect the registers inside the example implementation and connect them to my custom module.

Would appreciate the help!

6 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    I would like to create my custom VHDL PWM module in the FPGA

    --- Quote End ---

    1. Update your code so that it has an Avalon-MM interface. In the case of a PWM controller, this is likely as simple as adding a registers interface, where the registers have an Avalon-MM slave interface.

    2. Create a Qsys _hw.tcl file for your component.

    3. Use Qsys and the Avalon-MM master BFM to create a testbench and check that your interface works correctly.

    4. Add it to a Qsys system along with the HPS bridge to the ARM core. Qsys will add the AXI-to-Avalon-MM conversion logic.

    You cannot simulate AXI components without a Quartus subscription edition license and the IPS-AXIBFM models. I'd recommend sticking with Avalon-MM for now.

    Cheers,

    Dave
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Ok good! Thanks for the answer!

    But to be more precise, I want the actual implementation of the Avalon MM Slave (there should be some typical skeleton VHDL functionality except for the Avalon MM ports right?, In case of the AXI there is..).
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    But to be more precise, I want the actual implementation of the Avalon MM Slave (there should be some typical skeleton VHDL functionality except for the Avalon MM ports right?

    --- Quote End ---

    Its pretty straight-forward to code the Avalon-MM interface handshake, eg., here's an Avalon-MM to RAM wrapper/adapter ...

    
    -- ----------------------------------------------------------------
    -- avs_ram_interface.vhd
    -- ----------------------------------------------------------------
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    -- ----------------------------------------------------------------
    entity avs_ram_interface is
    	generic (
    		ADDR_WIDTH   : integer := 5;  -- 2 x M512
    		BYTEEN_WIDTH : integer := 4;
    		DATA_WIDTH   : integer := 32
    	);
    	port (
    		-- Reset and clock
    		rstN        : in  std_logic;
    		clk         : in  std_logic;
    		-- Avalon Slave Interface
    		avs_read    : in  std_logic;
    		avs_write   : in  std_logic;
    		avs_addr    : in  std_logic_vector(  ADDR_WIDTH-1 downto 0);
    		avs_byteen  : in  std_logic_vector(BYTEEN_WIDTH-1 downto 0);
    		avs_wrdata  : in  std_logic_vector(  DATA_WIDTH-1 downto 0);
    		avs_rddata  : out std_logic_vector(  DATA_WIDTH-1 downto 0);
    	    avs_rdvalid : out std_logic;
    	    avs_wait    : out std_logic;
    	    -- RAM interface
    		ram_wren    : out std_logic;
    		ram_addr    : out std_logic_vector(  ADDR_WIDTH-1 downto 0);
    		ram_byteen  : out std_logic_vector(BYTEEN_WIDTH-1 downto 0);
    		ram_wrdata  : out std_logic_vector(  DATA_WIDTH-1 downto 0);
    		ram_rddata  : in  std_logic_vector(  DATA_WIDTH-1 downto 0)
    	);
    end entity;
    -- ----------------------------------------------------------------
    architecture slave of avs_ram_interface is
    	-- Read valid pipeline
    	signal rdvalid : std_logic_vector(2 downto 1);
    begin
    	-- ------------------------------------------------------------
    	-- Wait-request handshake
    	-- ------------------------------------------------------------
    	--
    	process(clk, rstN)
    	begin
    		if (rstN = '0') then
    			-- High during reset per Avalon verification suite
    			avs_wait <= '1';
    		elsif rising_edge(clk) then
    			avs_wait <= '0';
    		end if;
    	end process;
    	-- ------------------------------------------------------------
    	-- Read data valid pipeline
    	-- ------------------------------------------------------------
    	--
    	process(clk, rstN)
    	begin
    		if (rstN = '0') then
    			rdvalid <= (others => '0');
    		elsif rising_edge(clk) then
    			rdvalid(1) <= avs_read;
    			rdvalid(2) <= rdvalid(1);
    		end if;
    	end process;
    	avs_rdvalid <= rdvalid(2);
        -- ------------------------------------------------------------
        -- RAM interface (pass-through connections)
        -- ------------------------------------------------------------
    	--
    	ram_wren    <= avs_write;
    	ram_addr    <= avs_addr;
    	ram_byteen  <= avs_byteen;
    	ram_wrdata  <= avs_wrdata;
    	avs_rddata  <= ram_rddata;
    end architecture;
    

    Similarly, here's some control registers code ...

    
    -- ----------------------------------------------------------------
    -- avs_spi_master_registers.vhd
    -- ----------------------------------------------------------------
    library ieee;
    use ieee.std_logic_1164.all;
    -- Control library
    library control;
    use control.utilities_pkg.all;
    use control.components.all;
    -- ----------------------------------------------------------------
    entity avs_spi_master_registers is
    	generic (
    		-- SPI select width (number of devices)
    		SWIDTH : integer := 1
    	);
    	port (
    		rstN        : in  std_logic;
    		clk         : in  std_logic;
    		-- --------------------------------------------------------
    		-- Avalon-MM slave interface
    		-- --------------------------------------------------------
    		--
    		avs_write   : in  std_logic;
    		avs_read    : in  std_logic;
    		avs_addr    : in  std_logic_vector( 2 downto 0);
    		avs_byteen  : in  std_logic_vector( 3 downto 0);
    		avs_wrdata  : in  std_logic_vector(31 downto 0);
    		avs_rddata  : out std_logic_vector(31 downto 0);
    		avs_rdvalid : out std_logic;
    		avs_wait    : out std_logic;
    		-- --------------------------------------------------------
    		-- Control/status
    		-- --------------------------------------------------------
    		--
    		spi_enable  : out std_logic;
    		spi_manual  : out std_logic;
    		spi_done    : in  std_logic;
    		spi_miso    : in  std_logic;
    		spi_width   : out std_logic_vector( 4 downto 0);
    		spi_baud    : out std_logic_vector(31 downto 0);
    		spi_wrdata  : out std_logic_vector(31 downto 0);
    		spi_rddata  : in  std_logic_vector(31 downto 0);
    		spi_sel     : out std_logic_vector(SWIDTH-1 downto 0)
    	);
    end entity;
    -- ----------------------------------------------------------------
    architecture mixed of avs_spi_master_registers is
    	-- ------------------------------------------------------------
    	-- Register offsets
    	-- ------------------------------------------------------------
    	--
    	constant REG_SPI_CONTROL  : integer := 16#00#;
    	constant REG_SPI_STATUS   : integer := 16#01#;
    	constant REG_SPI_WIDTH    : integer := 16#02#;
    	constant REG_SPI_BAUD     : integer := 16#03#;
    	--
    	constant REG_SPI_WRDATA   : integer := 16#04#;
    	constant REG_SPI_RDDATA   : integer := 16#05#;
    	constant REG_SPI_SEL      : integer := 16#06#;
    	--
    	-- First unused address
    	constant REG_LAST         : integer := 16#07#;
    	-- ------------------------------------------------------------
    	-- Address parameters
    	-- ------------------------------------------------------------
    	--
    	constant ADDR_WIDTH : integer := avs_addr'length;
    	constant ADDR_MAX   : integer := 2**ADDR_WIDTH;
    	-- ------------------------------------------------------------
    	-- Signals
    	-- ------------------------------------------------------------
    	--
    	-- Input registers
    	signal avs_write_in  : std_logic;
    	signal avs_read_in   : std_logic;
    	signal avs_addr_in   : std_logic_vector(ADDR_WIDTH-1 downto 0);
    	signal avs_byteen_in : std_logic_vector( 3 downto 0);
    	signal avs_wrdata_in : std_logic_vector(31 downto 0);
    	-- Read data multiplexer
    	type rddata_mux_t is array (0 to ADDR_MAX-1) of
    		std_logic_vector(31 downto 0);
    	signal rddata_mux : rddata_mux_t;
    	-- Address decoder write-enable outputs
    	signal en : std_logic_vector(ADDR_MAX-1 downto 0);
    begin
    	-- ------------------------------------------------------------
    	-- Input registers
    	-- ------------------------------------------------------------
    	--
    	process(clk, rstN)
    	begin
    		if (rstN = '0') then
    			avs_write_in  <= '0';
    			avs_read_in   <= '0';
    			avs_addr_in   <= (others => '0');
    			avs_byteen_in <= (others => '0');
    			avs_wrdata_in <= (others => '0');
    		elsif rising_edge(clk) then
    			avs_write_in  <= avs_write;
    			avs_read_in   <= avs_read;
    			avs_addr_in   <= avs_addr;
    			avs_byteen_in <= avs_byteen;
    			avs_wrdata_in <= avs_wrdata;
    		end if;
    	end process;
    	-- ------------------------------------------------------------
    	-- Wait acknowledge
    	-- ------------------------------------------------------------
    	--
    	-- The registers can accept write/read transactions on every
    	-- clock so the wait signal never asserts, other than at reset.
    	--
    	process(clk, rstN)
    	begin
    		if (rstN = '0') then
    			-- Assert wait-request during reset
    			avs_wait    <= '1';
    		elsif rising_edge(clk) then
    			avs_wait <= '0';
    		end if;
    	end process;
    	-- ------------------------------------------------------------
    	-- Read-data and valid handshake
    	-- ------------------------------------------------------------
    	--
    	-- The output data is registered, as are the input controls,
    	-- so there is a two clock pipeline delay from the acceptance
    	-- of a read command to the delivery of read data.
    	--
    	process(clk,rstN)
    	begin
    		if (rstN = '0') then
    			avs_rdvalid <= '0';
    			avs_rddata  <= (others => '0');
    		elsif rising_edge(clk) then
    			-- Read-data valid
    			avs_rdvalid <= avs_read_in;
    			-- Read-data
    			avs_rddata <= rddata_mux(to_int(avs_addr_in));
    		end if;
    	end process;
    	-- ------------------------------------------------------------
    	-- Write enable address decoder
    	-- ------------------------------------------------------------
    	--
    	u1: address_decoder
    		generic map (
    			ADDR_WIDTH   => ADDR_WIDTH,
    			ADDR_MAX     => ADDR_MAX
    		)
    		port map (
    			addr   => avs_addr_in,
    			wr_in  => avs_write_in,
    			wr_out => en
    		);
    	-- ============================================================
    	-- Registers
    	-- ============================================================
    	--
    	-- ------------------------------------------------------------
    	-- Control
    	-- ------------------------------------------------------------
    	--
    	u2: control_register
    		generic map (
    			BYTEEN_WIDTH  => 4,
    			DATA_WIDTH    => 32,
    			CONTROL_WIDTH => 2,
    			DEFAULT_VALUE => 0
    		)
    		port map (
    			clk        => clk,
    			rstN       => rstN,
    			wren       => en(REG_SPI_CONTROL),
    			byteen     => avs_byteen_in,
    			wrdata     => avs_wrdata_in,
    			rddata     => rddata_mux(REG_SPI_CONTROL),
    			control(0) => spi_enable,
    			control(1) => spi_manual
    		);
    

    (I had to snip the end of that code to be within the text limit).

    From this code, you can see how you can pipeline the Avalon-MM signals to process write and read transactions.

    Cheers,

    Dave
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thank you!

    In case I want to use AXI, do you know if the H2F interface is compatible with AXI4? Or is it only AXI3 compatible. Again just for Control Register passing (setting the PWM module reference), not real throughput applications.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    In case I want to use AXI, do you know if the H2F interface is compatible with AXI4? Or is it only AXI3 compatible. Again just for Control Register passing (setting the PWM module reference), not real throughput applications.

    --- Quote End ---

    I do not know. I haven't looked at the HPS AXI interface yet. The Cyclone V handbook and the online resources should tell you. I watched some of the SoC hardware tutorials and they have a pretty good overview of the HPS buses (though I do not recall the exact details of the AXI support).

    Cheers,

    Dave
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    The bridges are AXI3.

    By the way, for questions like these post them in the SoC device discussion sub-forum, it'll be easier for other users to see the post.