Skip to content

Instantly share code, notes, and snippets.

@Yatekii
Created May 14, 2020 12:51
Show Gist options
  • Save Yatekii/6a05dbcf031822fd57c829b36611a798 to your computer and use it in GitHub Desktop.
Save Yatekii/6a05dbcf031822fd57c829b36611a798 to your computer and use it in GitHub Desktop.
--------------------------------------------------------------------------------------------------------------
--
-- Generic AXI 4 Light Slave Interface
--
-- (c) 2014
-- A. Traber
-- L. Schrittwieser
--
--------------------------------------------------------------------------------------------------------------
--
-- AxiLightSlave.vhd
--
-- Generic AXI 4 Light slave interface for peripherals. It implements an AXI 4 Light state machine which
-- creates
-- read and write strobes for a configurable number of registers. The registers have to be implemented by the
-- core itself.
--
--------------------------------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity AxiLiteSlave is
generic (
NRegs : integer := 4; -- number of registers selects
AddrWidth : integer := 4; -- number of address bits, has to be >= 2+log2(NRegs)
DataWidth : integer := 32); -- so far, only 32 bit busses are supported
port (
-- AXI Slave Interface
AxiAClkxCI : in std_logic; -- axi clock
AxiAResetxRBI : in std_logic; -- axi reset, active low
AxiAWAddrxDI : in std_logic_vector(AddrWidth-1 downto 0);
AxiAWProtxDI : in std_logic_vector(2 downto 0); -- unused, for complience
AxiAWValidxSI : in std_logic;
AxiAWReadyxSO : out std_logic;
AxiWDataxDI : in std_logic_vector(DataWidth-1 downto 0);
AxiWStrbxSI : in std_logic_vector((DataWidth/8)-1 downto 0);
AxiWValidxSI : in std_logic;
AxiWReadyxSO : out std_logic;
AxiBRespxDO : out std_logic_vector(1 downto 0);
AxiBValidxSO : out std_logic;
AxiBReadyxSI : in std_logic;
AxiARAddrxDI : in std_logic_vector(AddrWidth-1 downto 0);
AxiARProtxDI : in std_logic_vector(2 downto 0); -- unused, for complience
AxiARValidxSI : in std_logic;
AxiARReadyxSO : out std_logic;
AxiRDataxDO : out std_logic_vector(DataWidth-1 downto 0);
AxiRRespxDO : out std_logic_vector(1 downto 0);
AxiRValidxSO : out std_logic;
AxiRReadyxSI : in std_logic;
-- Interface to peripheral
RExSO : out std_logic_vector(NRegs-1 downto 0); -- read enable for register
RAddrxDO : out std_logic_vector(AddrWidth-1-2 downto 0); -- index of the read register
WExSO : out std_logic_vector(NRegs-1 downto 0); -- write enable for register
RDataxDI : in std_logic_vector(DataWidth-1 downto 0);
WDataxDO : out std_logic_vector(DataWidth-1 downto 0);
WStrbxSO : out std_logic_vector((DataWidth/8)-1 downto 0); -- write strobe (byte enable)
WAckxSI : in std_logic; -- asserted by peripheral once data is accepted
-- can be tied high if peripheral is single cycle capable for all accesses
WErrxSI : in std_logic; -- indicate error on write
RAckxSI : in std_logic;
RErrxSI : in std_logic);
end entity AxiLiteSlave;
architecture Behavioral of AxiLiteSlave is
-- AXI signalling constants
constant RESP_OKAY : std_logic_vector(1 downto 0) := "00";
--constant RESP_EXOKAY : std_logic_vector(1 downto 0) := "01"; -- not supported in AXI-LIGHT
constant RESP_SLVERR : std_logic_vector(1 downto 0) := "10";
constant RESP_DECERR : std_logic_vector(1 downto 0) := "11";
constant AddrLsb : integer := (DataWidth/32)+1; -- lower 2 bits are unused for 32 bit interface
--, lowest 3 for 64 bit
--signal WAddrxDN, WAddrxDP : std_logic_vector(AddrWidth-1 downto 0) := (others => '0');
--signal RAddrxDN, RAddrxDP : std_logic_vector(AddrWidth-1 downto 0) := (others => '0');
signal RAccessxSN, RAccessxSP : std_logic := '0';
-- output FFs AXI
signal AWReadyxSN, AWReadyxSP : std_logic := '0';
signal WReadyxSN, WReadyxSP : std_logic := '0';
signal BRespxDN, BRespxDP : std_logic_vector(1 downto 0) := (others => '0');
signal BValidxSN, BValidxSP : std_logic := '0';
signal ARReadyxSN, ARReadyxSP : std_logic := '0';
signal RReadyxSN, RReadyxSP : std_logic := '0';
signal RDataxDN, RDataxDP : std_logic_vector(DataWidth-1 downto 0) := (others => '0');
signal RRespxDN, RRespxDP : std_logic_vector(1 downto 0) := (others => '0');
signal RValidxSN, RValidxSP : std_logic := '0';
-- output FFs peripheral
signal RExSN, RExSP : std_logic_vector(NRegs-1 downto 0) := (others => '0');
signal RAddrxDN, RAddrxDP : std_logic_vector(AddrWidth-1-AddrLsb downto 0) := (others => '0');
signal WExSN, WExSP : std_logic_vector(NRegs-1 downto 0) := (others => '0');
signal WStrbxSN, WStrbxSP : std_logic_vector((DataWidth/8)-1 downto 0) := (others => '0');
signal WDataxDN, WDataxDP : std_logic_vector(DataWidth-1 downto 0) := (others => '0');
begin
-- write access logic
process (AWReadyxSP, AxiAWAddrxDI, AxiAWValidxSI, AxiBReadyxSI, AxiWDataxDI, AxiWStrbxSI, AxiWValidxSI,
BRespxDP, BValidxSP, WAckxSI, WErrxSI, WReadyxSP) is
begin -- process
WStrbxSN <= AxiWStrbxSI;
WExSN <= (others => '0');
AWReadyxSN <= '0';
WReadyxSN <= '0';
BValidxSN <= BValidxSP; -- write response must be kept valid until master acks transfer
BRespxDN <= BRespxDP;
WDataxDN <= AxiWDataxDI;
if AxiAWValidxSI = '1' and AxiWValidxSI = '1' and WReadyxSP = '0' and AWReadyxSP = '0' then
if to_integer(unsigned(AxiAWAddrxDI(AddrWidth-1 downto AddrLsb))) < NRegs then
-- write data and address are available, execute write access
WExSN(to_integer(unsigned(AxiAWAddrxDI(AddrWidth-1 downto AddrLsb)))) <= '1';
-- wait until the peripheral acks or aborts the transfer
if WErrxSI = '1' then
BRespxDN <= RESP_SLVERR;
BValidxSN <= '1'; -- indicate valid write response
AWReadyxSN <= '1'; -- acknowledge write address
WReadyxSN <= '1'; -- acknowledge write data
else
if WAckxSI = '1' then
-- access finished, return response to master
BRespxDN <= RESP_OKAY;
BValidxSN <= '1';
AWReadyxSN <= '1'; -- acknowledge write address
WReadyxSN <= '1'; -- acknowledge write data
end if;
end if;
else
-- access to non-existent register, ack and store data on /dev/null
BRespxDN <= RESP_OKAY;
BValidxSN <= '1';
AWReadyxSN <= '1'; -- acknowledge write address
WReadyxSN <= '1'; -- acknowledge write data
end if;
end if;
-- deassert write response ready once master has acknowledged it
-- need a check on BValidxSP to ensure it is high for at least one cycle
if AxiBReadyxSI = '1' and BValidxSP = '1' then
BValidxSN <= '0'; -- no need to change data lines
end if;
end process;
-- read access logic
process (ARReadyxSP, AxiARAddrxDI, AxiARValidxSI, AxiRReadyxSI, RAccessxSP, RAckxSI, RDataxDI, RDataxDP,
RErrxSI, RRespxDP, RValidxSP) is
begin
RAccessxSN <= '0';
RExSN <= (others => '0'); -- default assumption: no accesses
RAddrxDN <= (others => '0');
ARReadyxSN <= '0';
RRespxDN <= RRespxDP;
RValidxSN <= RValidxSP;
RDataxDN <= RDataxDP;
-- wait for a valid access by the master
if AxiARValidxSI = '1' and ARReadyxSP = '0' then
if to_integer(unsigned(AxiARAddrxDI(AddrWidth-1 downto AddrLsb))) < NRegs then
RAccessxSN <= '1';
-- read address from master is valid, decode it
RExSN(to_integer(unsigned(AxiARAddrxDI(AddrWidth-1 downto AddrLsb)))) <= '1';
RAddrxDN <= AxiARAddrxDI(AddrWidth-1 downto AddrLsb);
if RErrxSI = '1' and RAccessxSP = '1' then
RRespxDN <= RESP_SLVERR;
RValidxSN <= '1'; -- indicate read data is valid
ARReadyxSN <= '1'; -- indicate address transfer is done
else
if RAckxSI = '1' and RAccessxSP = '1' then
RRespxDN <= RESP_OKAY;
RValidxSN <= '1';
RDataxDN <= RDataxDI; -- sample data
ARReadyxSN <= '1';
end if;
end if;
else
-- master tries to access an unavailable register, return 0
RRespxDN <= RESP_OKAY;
RValidxSN <= '1';
RDataxDN <= (others => '0');
ARReadyxSN <= '1';
end if;
end if;
-- deassert valid signal once master has accepted the data
if AxiRReadyxSI = '1' and RValidxSP = '1' then
RValidxSN <= '0';
end if;
end process;
process (AxiAClkxCI) is
begin -- process
if AxiAClkxCI'event and AxiAClkxCI = '1' then -- rising clock edge
if AxiAResetxRBI = '0' then -- sync reset (active low)
RAccessxSP <= '0';
AWReadyxSP <= '0';
WReadyxSP <= '0';
WDataxDP <= (others => '0');
BRespxDP <= (others => '0');
BValidxSP <= '0';
ARReadyxSP <= '0';
RReadyxSP <= '0';
RDataxDP <= (others => '0');
RRespxDP <= (others => '0');
RValidxSP <= '0';
--
RExSP <= (others => '0');
WExSP <= (others => '0');
WStrbxSP <= (others => '0');
else
RAccessxSP <= RAccessxSN;
AWReadyxSP <= AWReadyxSN;
WReadyxSP <= WReadyxSN;
WDataxDP <= WDataxDN;
BRespxDP <= BRespxDN;
BValidxSP <= BValidxSN;
ARReadyxSP <= ARReadyxSN;
RReadyxSP <= RReadyxSN;
RDataxDP <= RDataxDN;
RRespxDP <= RRespxDN;
RValidxSP <= RValidxSN;
--
RExSP <= RExSN;
RAddrxDP <= RAddrxDN;
WExSP <= WExSN;
WStrbxSP <= WStrbxSN;
end if;
end if;
end process;
AxiAWReadyxSO <= AWReadyxSP;
AxiWReadyxSO <= WReadyxSP;
AxiBRespxDO <= BRespxDP;
AxiBValidxSO <= BValidxSP;
AxiARReadyxSO <= ARReadyxSP;
AxiRDataxDO <= RDataxDP;
AxiRRespxDO <= RRespxDP;
AxiRValidxSO <= RValidxSP;
RExSO <= RExSP;
RAddrxDO <= RAddrxDP;
WExSO <= WExSP;
WStrbxSO <= WStrbxSP;
WDataxDO <= WDataxDP;
end architecture Behavioral;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment