Forum Discussion
Altera_Forum
Honored Contributor
11 years agoUse 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
Honored 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
Honored 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
Honored 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 ...
Similarly, here's some control registers code ...-- ---------------------------------------------------------------- -- 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;
(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-- ---------------------------------------------------------------- -- 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 ); - Altera_Forum
Honored 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
Honored 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
Honored 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.