-
Reinier van der Walle authoredReinier van der Walle authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
axi4_stream_dp_bridge.vhd 6.99 KiB
-- --------------------------------------------------------------------------
-- Copyright 2023
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- --------------------------------------------------------------------------
-- Author:
-- . Reinier van der Walle
-- Purpose:
-- . Bridge between AXI4 stream and DP stream interfaces.
-- Description:
-- . This core consists of:
-- . Combinatorial translation of one interface to the other.
-- . SOP insertion for AXI4 -> DP as AXI4 does not have a SOP equivalent.
-- . Implementation for deriving dp.empty from axi4.tkeep (see details).
-- . Ready latency adapters.
-- . Details:
-- . g_nof_bytes is used to indicate the number of bytes in the data field. Only
-- used for deriving the DP empty field.
-- . g_use_empty: when true, it will derive the DP empty field from the AXI4 tkeep signal.
-- tkeep is the AXI4 signal that indicates with a bit for each byte of the data whether
-- it is considered a valid or null byte. '1' for a valid byte or '0' for a null byte.--
-- The DP empty field is derived by counting the number of '0' bits in
-- tkeep(g_nof_bytes DOWNTO 0). This means that it is only possible to derive the empty
-- field correctly when tkeep only has bits set to '0' at the end of the vector. For example:
-- . tkeep = "11111000" is valid and will translate to empty = 0011 (3 Decimal).
-- . tkeep = "11011101" is not valid and will result in a wrong empty signal of 0010 (2 Decimal).
-- . Note that AXI4 always uses a symbol width of 8 (= byte) and thus the only DP symbol width
-- used by the empty signal that is supported is also 8.
-- . g_axi4_rl is the ready latency of the axi4_in/out interfaces.
-- . g_dp_rl is the ready latency of the dp_in/out interfaces.
-- . g_active_low_rst should be set to TRUE when in_rst is active low. This is useful as an
-- AXI4 interface often comes with an active-low reset while DP comes with an active-high
-- reset.
-- Remarks:
-- . AXI4 does not have a DP Xon or sync equivalent.
LIBRARY IEEE, common_lib, dp_lib, technology_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE work.axi4_stream_pkg.ALL;
ENTITY axi4_stream_dp_bridge IS
GENERIC (
g_nof_bytes : NATURAL := 64; -- Max = 64 bytes
g_use_empty : BOOLEAN := FALSE; -- When True the dp empty field is derived from axi4 tkeep.
g_axi4_rl : NATURAL := 0;
g_dp_rl : NATURAL := 1;
g_active_low_rst : BOOLEAN := FALSE -- When True, in_rst is interpreted as active-low.
);
PORT (
in_clk : IN STD_LOGIC := '0';
in_rst : IN STD_LOGIC := is_true(g_active_low_rst); -- Default state is "not in reset".
aresetn : OUT STD_LOGIC := '1'; -- AXI4 active-low reset
dp_rst : OUT STD_LOGIC := '0'; -- DP active-high reset
axi4_in_sosi : IN t_axi4_sosi := c_axi4_sosi_rst;
axi4_in_siso : OUT t_axi4_siso := c_axi4_siso_rst;
axi4_out_sosi : OUT t_axi4_sosi := c_axi4_sosi_rst;
axi4_out_siso : IN t_axi4_siso := c_axi4_siso_rst;
dp_in_sosi : IN t_dp_sosi := c_dp_sosi_rst;
dp_in_siso : OUT t_dp_siso := c_dp_siso_rst;
dp_out_sosi : OUT t_dp_sosi := c_dp_sosi_rst;
dp_out_siso : IN t_dp_siso := c_dp_siso_rst
);
END axi4_stream_dp_bridge;
ARCHITECTURE str OF axi4_stream_dp_bridge IS
SIGNAL i_rst : STD_LOGIC := '0'; -- Internal active high reset.
SIGNAL axi4_from_dp_sosi : t_dp_sosi;
SIGNAL axi4_from_dp_siso : t_dp_siso;
SIGNAL dp_from_axi4_sosi : t_dp_sosi;
SIGNAL dp_from_axi4_siso : t_dp_siso;
TYPE t_reg IS RECORD
r_eop : STD_LOGIC;
dp_from_axi4_sosi : t_dp_sosi;
END RECORD;
CONSTANT c_reg_init : t_reg := ('1', c_dp_sosi_rst) ;
-- Registers
SIGNAL d_reg : t_reg := c_reg_init;
SIGNAL q_reg : t_reg := c_reg_init;
BEGIN
i_rst <= NOT in_rst WHEN g_active_low_rst ELSE in_rst;
aresetn <= NOT i_rst;
dp_rst <= i_rst;
----------------------------
-- Translate DP to AXI4
----------------------------
axi4_out_sosi <= func_axi4_stream_from_dp_sosi(axi4_from_dp_sosi);
axi4_from_dp_siso <= func_axi4_stream_to_dp_siso(axi4_out_siso);
-- Adapt Ready Latency
u_dp_latency_adapter_dp_to_axi : ENTITY dp_lib.dp_latency_adapter
GENERIC MAP (
g_in_latency => g_dp_rl,
g_out_latency => g_axi4_rl
)
PORT MAP (
clk => in_clk,
rst => i_rst,
snk_in => dp_in_sosi,
snk_out => dp_in_siso,
src_out => axi4_from_dp_sosi,
src_in => axi4_from_dp_siso
);
----------------------------
-- Translate AXI4 to DP
----------------------------
q_reg <= d_reg WHEN rising_edge(in_clk);
p_axi4_to_dp : PROCESS(i_rst, q_reg, axi4_in_sosi, dp_from_axi4_siso.ready)
VARIABLE v : t_reg := c_reg_init;
BEGIN
v := q_reg;
v.dp_from_axi4_sosi := func_axi4_stream_to_dp_sosi(axi4_in_sosi); -- Does not have sop or empty yet.
-- Generate sop on first valid after eop.
IF axi4_in_sosi.tvalid = '1' AND g_axi4_rl = 1 THEN -- axi4 ready latency = 1
IF axi4_in_sosi.tlast = '1' THEN
v.r_eop := '1';
ELSIF q_reg.r_eop = '1' THEN
v.dp_from_axi4_sosi.sop := '1';
v.r_eop := '0';
ELSE
v.dp_from_axi4_sosi.sop := '0';
END IF;
ELSIF axi4_in_sosi.tvalid = '1' AND g_axi4_rl = 0 THEN -- axi4 ready latency = 0
IF axi4_in_sosi.tlast = '1' AND dp_from_axi4_siso.ready = '1' THEN
v.r_eop := '1';
ELSIF q_reg.r_eop = '1' AND dp_from_axi4_siso.ready = '1' THEN
v.r_eop := '0';
END IF;
v.dp_from_axi4_sosi.sop := q_reg.r_eop;
END IF;
-- Derive Empty from tkeep
IF g_use_empty THEN
v.dp_from_axi4_sosi.empty := func_axi4_stream_tkeep_to_dp_empty(axi4_in_sosi.tkeep(g_nof_bytes-1 DOWNTO 0));
END IF;
-- Reset
IF i_rst = '1' THEN
v := c_reg_init;
END IF;
d_reg <= v;
END PROCESS;
dp_from_axi4_sosi <= d_reg.dp_from_axi4_sosi;
axi4_in_siso <= func_axi4_stream_from_dp_siso(dp_from_axi4_siso);
-- Adapt Ready Latency
u_dp_latency_adapter_axi_to_dp : ENTITY dp_lib.dp_latency_adapter
GENERIC MAP (
g_in_latency => g_axi4_rl,
g_out_latency => g_dp_rl
)
PORT MAP (
clk => in_clk,
rst => i_rst,
snk_in => dp_from_axi4_sosi,
snk_out => dp_from_axi4_siso,
src_out => dp_out_sosi,
src_in => dp_out_siso
);
END str;