Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 185 additions & 36 deletions axi/axi-lite/rtl/AxiLiteAsync.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ end AxiLiteAsync;

architecture STRUCTURE of AxiLiteAsync is

signal s2mRst : sl; -- Slave rst sync'd to master clk
signal sRst : sl; -- Slave rst sync'd to slave clk
signal m2sRst : sl; -- Master rst sync'd to slave clk

signal readSlaveToMastDin : slv(NUM_ADDR_BITS_G+2 downto 0);
Expand Down Expand Up @@ -89,6 +89,40 @@ architecture STRUCTURE of AxiLiteAsync is
signal writeMastToSlaveRead : sl;
signal writeMastToSlaveWrite : sl;

-- Depth of every channel FIFO instantiated below
constant FIFO_ADDR_WIDTH_C : positive := 4;

-- Reset terms normalized to active HIGH, independent of RST_POLARITY_G
signal m2sRstActive : sl;
signal mAxiRstActive : sl;

-- Registered active-HIGH reset request for every FIFO in the bridge
signal fifoRstReq : sl;
signal fifoRst : sl;

-- Slave side handshakes, kept local so the error responder can observe them
signal sArReady : sl;
signal sRValid : sl;
signal sAwReady : sl;
signal sWReady : sl;
signal sBValid : sl;

type RegType is record
errMode : sl; -- Answering locally with AXI_ERROR_RESP_G
rPend : sl; -- Read accepted, not yet answered
awPend : sl; -- Write address accepted, not yet answered
wPend : sl; -- Write data accepted, not yet answered
end record RegType;

constant REG_INIT_C : RegType := (
errMode => '0',
rPend => '0',
awPend => '0',
wPend => '0');

signal r : RegType := REG_INIT_C;
signal rin : RegType;

begin

GEN_SYNC : if (COMMON_CLK_G = true) generate
Expand All @@ -102,18 +136,18 @@ begin

GEN_ASYNC : if (COMMON_CLK_G = false) generate

-- Synchronize each reset across to the other clock domain
LOC_S2M_RstSync : entity surf.RstSync
-- Synchronize the local reset release before it controls fifoRst
LOC_S_RstSync : entity surf.RstSync
generic map (
TPD_G => TPD_G,
IN_POLARITY_G => RST_POLARITY_G,
OUT_POLARITY_G => RST_POLARITY_G,
OUT_REG_RST_G => false)
OUT_POLARITY_G => RST_POLARITY_G)
port map (
clk => mAxiClk,
clk => sAxiClk,
asyncRst => sAxiClkRst,
syncRst => s2mRst);
syncRst => sRst);

-- Synchronize the remote reset into the slave/control clock domain
LOC_M2S_RstSync : entity surf.RstSync
generic map (
TPD_G => TPD_G,
Expand All @@ -125,6 +159,112 @@ begin
asyncRst => mAxiClkRst,
syncRst => m2sRst);

-- Normalize reset indications to active HIGH
m2sRstActive <= '1' when (m2sRst = RST_POLARITY_G) else '0';
mAxiRstActive <= '1' when (mAxiClkRst = RST_POLARITY_G) else '0';

-- Build one glitch-free FIFO reset request in the slave/control domain.
-- The local reset asserts it asynchronously, so the FIFOs are reset even
-- if sAxiClk is stopped. The remote reset is synchronized above before it
-- sets this register. Deassertion is synchronous and delayed until error
-- mode has drained every abandoned transaction. FifoAsync then
-- resynchronizes this single registered request into both FIFO domains.
fifoRstReq <= m2sRstActive or r.errMode;

U_FifoRstReg : entity surf.RegisterVector
generic map (
TPD_G => TPD_G,
RST_POLARITY_G => RST_POLARITY_G,
RST_ASYNC_G => true,
WIDTH_G => 1,
INIT_G => "1")
port map (
clk => sAxiClk, -- [in]
rst => sRst, -- [in]
sig_i(0) => fifoRstReq, -- [in]
reg_o(0) => fifoRst); -- [out]

-- Transaction tracking and local error responder.
--
-- One transaction per channel is in flight at a time, matching
-- AxiLiteCrossbar, whose per-slot state machine does not release a slot
-- until the response completes. The ready outputs below enforce that bound
-- rather than assuming the master honours it.
--
-- The same state decides what the bridge owes the slave side while the
-- remote domain is in reset, when each access is answered locally instead
-- of being forwarded. That keeps AXI-Lite ordering intact, because a read
-- response only follows an accepted AR and a write response only follows
-- both an accepted AW and W, and it covers the transaction discarded by
-- fifoRst above, which still owes the slave side a response.
comb : process (m2sRstActive, r, sArReady, sAwReady, sAxiClkRst,
sAxiReadMaster, sAxiWriteMaster, sBValid, sRValid, sWReady) is
variable v : RegType;
variable arTxn : sl;
variable rTxn : sl;
variable awTxn : sl;
variable wTxn : sl;
variable bTxn : sl;
begin
-- Latch the current value
v := r;

-- Slave side handshakes
arTxn := sAxiReadMaster.arvalid and sArReady;
rTxn := sRValid and sAxiReadMaster.rready;
awTxn := sAxiWriteMaster.awvalid and sAwReady;
wTxn := sAxiWriteMaster.wvalid and sWReady;
bTxn := sBValid and sAxiWriteMaster.bready;

-- Read accepted but not yet answered. Set and clear are mutually
-- exclusive because ARREADY is held low while the read is pending.
if (arTxn = '1') then
v.rPend := '1';
elsif (rTxn = '1') then
v.rPend := '0';
end if;

-- Write address accepted but not yet answered
if (awTxn = '1') then
v.awPend := '1';
elsif (bTxn = '1') then
v.awPend := '0';
end if;

-- Write data accepted but not yet answered
if (wTxn = '1') then
v.wPend := '1';
elsif (bTxn = '1') then
v.wPend := '0';
end if;

-- Enter error mode when the remote domain resets and stay there until
-- the abandoned transaction has been answered
if (m2sRstActive = '1') then
v.errMode := '1';
elsif (v.rPend = '0') and (v.awPend = '0') and (v.wPend = '0') then
v.errMode := '0';
end if;

-- Synchronous Reset
if (RST_ASYNC_G = false) and (sAxiClkRst = RST_POLARITY_G) then
v := REG_INIT_C;
end if;

-- Register the variable for the next clock cycle
rin <= v;

end process comb;

seq : process (sAxiClk, sAxiClkRst) is
begin
if (RST_ASYNC_G) and (sAxiClkRst = RST_POLARITY_G) then
r <= REG_INIT_C after TPD_G;
elsif rising_edge(sAxiClk) then
r <= rin after TPD_G;
end if;
end process seq;

------------------------------------
-- Read: Slave to Master
------------------------------------
Expand All @@ -133,19 +273,19 @@ begin
U_ReadSlaveToMastFifo : entity surf.FifoASync
generic map (
TPD_G => TPD_G,
RST_POLARITY_G => RST_POLARITY_G,
RST_POLARITY_G => '1',
RST_ASYNC_G => RST_ASYNC_G,
MEMORY_TYPE_G => "distributed", -- Use Dist Ram
FWFT_EN_G => true,
SYNC_STAGES_G => 3,
PIPE_STAGES_G => PIPE_STAGES_G,
DATA_WIDTH_G => NUM_ADDR_BITS_G+3,
ADDR_WIDTH_G => 4,
ADDR_WIDTH_G => FIFO_ADDR_WIDTH_C,
INIT_G => "0",
FULL_THRES_G => 15,
EMPTY_THRES_G => 1)
port map (
rst => s2mRst,
rst => fifoRst,
wr_clk => sAxiClk,
wr_en => readSlaveToMastWrite,
din => readSlaveToMastDin,
Expand All @@ -171,9 +311,12 @@ begin
readSlaveToMastDin(2 downto 0) <= sAxiReadMaster.arprot;
readSlaveToMastDin(NUM_ADDR_BITS_G+2 downto 3) <= sAxiReadMaster.araddr(NUM_ADDR_BITS_G-1 downto 0);

-- Write control and ready generation
sAxiReadSlave.arready <= ite(m2sRst = '0', not readSlaveToMastFull, '1');
readSlaveToMastWrite <= sAxiReadMaster.arvalid and (not readSlaveToMastFull);
-- Write control and ready generation. The request is never queued while the
-- bridge is answering locally, otherwise an access already reported as
-- failed would still reach the master side.
sArReady <= (not r.rPend) when (r.errMode = '1') else ((not readSlaveToMastFull) and (not r.rPend));
sAxiReadSlave.arready <= sArReady;
readSlaveToMastWrite <= sAxiReadMaster.arvalid and sArReady and (not r.errMode);

-- Data Out
mAxiReadMaster.arprot <= readSlaveToMastDout(2 downto 0);
Expand All @@ -196,19 +339,19 @@ begin
U_ReadMastToSlaveFifo : entity surf.FifoASync
generic map (
TPD_G => TPD_G,
RST_POLARITY_G => RST_POLARITY_G,
RST_POLARITY_G => '1',
RST_ASYNC_G => RST_ASYNC_G,
MEMORY_TYPE_G => "distributed", -- Use Dist Ram
FWFT_EN_G => true,
SYNC_STAGES_G => 3,
PIPE_STAGES_G => PIPE_STAGES_G,
DATA_WIDTH_G => 34,
ADDR_WIDTH_G => 4,
ADDR_WIDTH_G => FIFO_ADDR_WIDTH_C,
INIT_G => "0",
FULL_THRES_G => 15,
EMPTY_THRES_G => 1)
port map (
rst => m2sRst,
rst => fifoRst,
wr_clk => mAxiClk,
wr_en => readMastToSlaveWrite,
din => readMastToSlaveDin,
Expand All @@ -235,15 +378,17 @@ begin
readMastToSlaveDin(33 downto 2) <= mAxiReadSlave.rdata;

-- Write control and ready generation
mAxiReadMaster.rready <= ite(mAxiClkRst = '0', not readMastToSlaveFull, '1');
mAxiReadMaster.rready <= '1' when (mAxiRstActive = '1') else (not readMastToSlaveFull);
readMastToSlaveWrite <= mAxiReadSlave.rvalid and (not readMastToSlaveFull);

-- Data Out
sAxiReadSlave.rresp <= ite(m2sRst = '0', readMastToSlaveDout(1 downto 0), AXI_ERROR_RESP_G);
sAxiReadSlave.rresp <= AXI_ERROR_RESP_G when (r.errMode = '1') else readMastToSlaveDout(1 downto 0);
sAxiReadSlave.rdata <= readMastToSlaveDout(33 downto 2);

-- Read control and valid
sAxiReadSlave.rvalid <= ite(m2sRst = '0', readMastToSlaveValid, '1');
-- Read control and valid. Answering locally requires an accepted AR, so the
-- response can never arrive ahead of its request.
sRValid <= r.rPend when (r.errMode = '1') else readMastToSlaveValid;
sAxiReadSlave.rvalid <= sRValid;
readMastToSlaveRead <= sAxiReadMaster.rready;

------------------------------------
Expand All @@ -254,19 +399,19 @@ begin
U_WriteAddrSlaveToMastFifo : entity surf.FifoASync
generic map (
TPD_G => TPD_G,
RST_POLARITY_G => RST_POLARITY_G,
RST_POLARITY_G => '1',
RST_ASYNC_G => RST_ASYNC_G,
MEMORY_TYPE_G => "distributed", -- Use Dist Ram
FWFT_EN_G => true,
SYNC_STAGES_G => 3,
PIPE_STAGES_G => PIPE_STAGES_G,
DATA_WIDTH_G => NUM_ADDR_BITS_G+3,
ADDR_WIDTH_G => 4,
ADDR_WIDTH_G => FIFO_ADDR_WIDTH_C,
INIT_G => "0",
FULL_THRES_G => 15,
EMPTY_THRES_G => 1)
port map (
rst => s2mRst,
rst => fifoRst,
wr_clk => sAxiClk,
wr_en => writeAddrSlaveToMastWrite,
din => writeAddrSlaveToMastDin,
Expand All @@ -293,8 +438,9 @@ begin
writeAddrSlaveToMastDin(NUM_ADDR_BITS_G+2 downto 3) <= sAxiWriteMaster.awaddr(NUM_ADDR_BITS_G-1 downto 0);

-- Write control and ready generation
sAxiWriteSlave.awready <= ite(m2sRst = '0', not writeAddrSlaveToMastFull, '1');
writeAddrSlaveToMastWrite <= sAxiWriteMaster.awvalid and (not writeAddrSlaveToMastFull);
sAwReady <= (not r.awPend) when (r.errMode = '1') else ((not writeAddrSlaveToMastFull) and (not r.awPend));
sAxiWriteSlave.awready <= sAwReady;
writeAddrSlaveToMastWrite <= sAxiWriteMaster.awvalid and sAwReady and (not r.errMode);

-- Data Out
mAxiWriteMaster.awprot <= writeAddrSlaveToMastDout(2 downto 0);
Expand All @@ -317,19 +463,19 @@ begin
U_WriteDataSlaveToMastFifo : entity surf.FifoASync
generic map (
TPD_G => TPD_G,
RST_POLARITY_G => RST_POLARITY_G,
RST_POLARITY_G => '1',
RST_ASYNC_G => RST_ASYNC_G,
MEMORY_TYPE_G => "distributed", -- Use Dist Ram
FWFT_EN_G => true,
SYNC_STAGES_G => 3,
PIPE_STAGES_G => PIPE_STAGES_G,
DATA_WIDTH_G => 36,
ADDR_WIDTH_G => 4,
ADDR_WIDTH_G => FIFO_ADDR_WIDTH_C,
INIT_G => "0",
FULL_THRES_G => 15,
EMPTY_THRES_G => 1)
port map (
rst => s2mRst,
rst => fifoRst,
wr_clk => sAxiClk,
wr_en => writeDataSlaveToMastWrite,
din => writeDataSlaveToMastDin,
Expand All @@ -356,8 +502,9 @@ begin
writeDataSlaveToMastDin(35 downto 4) <= sAxiWriteMaster.wdata;

-- Write control and ready generation
sAxiWriteSlave.wready <= ite(m2sRst = '0', not writeDataSlaveToMastFull, '1');
writeDataSlaveToMastWrite <= sAxiWriteMaster.wvalid and (not writeDataSlaveToMastFull);
sWReady <= (not r.wPend) when (r.errMode = '1') else ((not writeDataSlaveToMastFull) and (not r.wPend));
sAxiWriteSlave.wready <= sWReady;
writeDataSlaveToMastWrite <= sAxiWriteMaster.wvalid and sWReady and (not r.errMode);

-- Data Out
mAxiWriteMaster.wstrb <= writeDataSlaveToMastDout(3 downto 0);
Expand All @@ -375,19 +522,19 @@ begin
U_WriteMastToSlaveFifo : entity surf.FifoASync
generic map (
TPD_G => TPD_G,
RST_POLARITY_G => RST_POLARITY_G,
RST_POLARITY_G => '1',
RST_ASYNC_G => RST_ASYNC_G,
MEMORY_TYPE_G => "distributed", -- Use Dist Ram
FWFT_EN_G => true,
SYNC_STAGES_G => 3,
PIPE_STAGES_G => PIPE_STAGES_G,
DATA_WIDTH_G => 2,
ADDR_WIDTH_G => 4,
ADDR_WIDTH_G => FIFO_ADDR_WIDTH_C,
INIT_G => "0",
FULL_THRES_G => 15,
EMPTY_THRES_G => 1)
port map (
rst => m2sRst,
rst => fifoRst,
wr_clk => mAxiClk,
wr_en => writeMastToSlaveWrite,
din => writeMastToSlaveDin,
Expand Down Expand Up @@ -417,10 +564,12 @@ begin
writeMastToSlaveWrite <= mAxiWriteSlave.bvalid and (not writeMastToSlaveFull);

-- Data Out
sAxiWriteSlave.bresp <= ite(m2sRst = '0', writeMastToSlaveDout, AXI_ERROR_RESP_G);
sAxiWriteSlave.bresp <= AXI_ERROR_RESP_G when (r.errMode = '1') else writeMastToSlaveDout;

-- Read control and valid
sAxiWriteSlave.bvalid <= ite(m2sRst = '0', writeMastToSlaveValid, '1');
-- Read control and valid. Answering locally requires both an accepted AW and
-- an accepted W, so the two channels can still arrive in either order.
sBValid <= (r.awPend and r.wPend) when (r.errMode = '1') else writeMastToSlaveValid;
sAxiWriteSlave.bvalid <= sBValid;
writeMastToSlaveRead <= sAxiWriteMaster.bready;

end generate;
Expand Down
Loading