Created
May 14, 2020 12:51
-
-
Save Yatekii/6a05dbcf031822fd57c829b36611a798 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-------------------------------------------------------------------------------------------------------------- | |
-- | |
-- 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