Skip to content
Snippets Groups Projects
Commit bd37f0f3 authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Merge branch 'HPR-86' into 'master'

Resolve HPR-86

Closes HPR-86

See merge request desp/hdl!312
parents e0b960a2 34374b5a
No related branches found
No related tags found
1 merge request!312Resolve HPR-86
Pipeline #45260 passed
hdl_lib_name = axi4
hdl_library_clause_name = axi4_lib
hdl_lib_uses_synth = common dp
hdl_lib_uses_sim =
hdl_lib_technology =
synth_files =
src/vhdl/axi4_stream_pkg.vhd
src/vhdl/axi4_stream_dp_bridge.vhd
test_bench_files =
tb/vhdl/tb_axi4_stream_dp_bridge.vhd
tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
regression_test_vhdl =
tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
[modelsim_project_file]
[quartus_project_file]
[vivado_project_file]
-- --------------------------------------------------------------------------
-- 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;
This diff is collapsed.
-- --------------------------------------------------------------------------
-- 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:
-- . TB for axi4_stream_dp_bridge.
-- Description:
-- The testbench generates a stimuli DP stream which streams into the DUT. The
-- resulting AXI4 stream is looped back into the DUT that results in the final
-- DP stream. The resulting DP stream is verified.
LIBRARY IEEE, common_lib, dp_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE dp_lib.tb_dp_pkg.ALL;
USE work.axi4_stream_pkg.ALL;
ENTITY tb_axi4_stream_dp_bridge IS
GENERIC (
g_dp_rl : NATURAL := 1;
g_axi4_rl : NATURAL := 0
);
END tb_axi4_stream_dp_bridge;
ARCHITECTURE tb OF tb_axi4_stream_dp_bridge IS
-- TX ready latency to DUT chain
CONSTANT c_tx_void : NATURAL := sel_a_b(g_dp_rl, 1, 0); -- used to avoid empty range VHDL warnings when g_dp_rl=0
CONSTANT c_tx_offset_sop : NATURAL := 0;
CONSTANT c_tx_period_sop : NATURAL := 7; -- sop in data valid cycle 0, 7, 14, ...
CONSTANT c_tx_offset_eop : NATURAL := 6; -- eop in data valid cycle 6, 13, 20, ...
CONSTANT c_tx_period_eop : NATURAL := c_tx_period_sop;
CONSTANT c_tx_offset_sync : NATURAL := 3; -- sync in data valid cycle 3, 20, 37, ...
CONSTANT c_tx_period_sync : NATURAL := 17;
CONSTANT c_verify_en_wait : NATURAL := 10; -- wait some cycles before asserting verify enable
CONSTANT c_empty_offset : NATURAL := 1;
CONSTANT c_channel_offset : NATURAL := 2;
CONSTANT c_random_w : NATURAL := 19;
CONSTANT c_max_empty_w : NATURAL := ceil_log2(c_axi4_stream_keep_w);
CONSTANT c_max_channel_w : NATURAL := c_axi4_stream_tid_w;
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL clk : STD_LOGIC := '0';
SIGNAL rst : STD_LOGIC;
SIGNAL sync : STD_LOGIC;
SIGNAL lfsr1 : STD_LOGIC_VECTOR(c_random_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL lfsr2 : STD_LOGIC_VECTOR(c_random_w DOWNTO 0) := (OTHERS=>'0');
SIGNAL cnt_dat : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
SIGNAL cnt_val : STD_LOGIC;
SIGNAL cnt_en : STD_LOGIC;
SIGNAL tx_data : t_dp_data_arr(0 TO g_dp_rl + c_tx_void);
SIGNAL tx_val : STD_LOGIC_VECTOR(0 TO g_dp_rl + c_tx_void);
SIGNAL in_ready : STD_LOGIC;
SIGNAL in_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
SIGNAL in_empty : STD_LOGIC_VECTOR(c_dp_stream_empty_w-1 DOWNTO 0);
SIGNAL in_channel : STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0);
SIGNAL in_sync : STD_LOGIC;
SIGNAL in_val : STD_LOGIC;
SIGNAL in_sop : STD_LOGIC;
SIGNAL in_eop : STD_LOGIC;
SIGNAL valid_data : STD_LOGIC := '1';
-- DUT index c_nof_dut-1 = out_data
SIGNAL dut_siso : t_dp_siso;
SIGNAL dut_sosi : t_dp_sosi := c_dp_sosi_rst;
SIGNAL dut_axi4_sosi : t_axi4_sosi := c_axi4_sosi_rst;
SIGNAL dut_axi4_siso : t_axi4_siso := c_axi4_siso_rst;
SIGNAL dut_out_siso : t_dp_siso;
SIGNAL dut_out_sosi : t_dp_sosi := c_dp_sosi_rst;
SIGNAL out_ready : STD_LOGIC;
SIGNAL prev_out_ready : STD_LOGIC_VECTOR(0 TO g_dp_rl);
SIGNAL out_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
SIGNAL out_empty : STD_LOGIC_VECTOR(c_dp_empty_w-1 DOWNTO 0);
SIGNAL out_channel : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
SIGNAL out_sync : STD_LOGIC;
SIGNAL out_val : STD_LOGIC;
SIGNAL out_sop : STD_LOGIC;
SIGNAL out_eop : STD_LOGIC;
SIGNAL hold_out_sop : STD_LOGIC;
SIGNAL prev_out_data : STD_LOGIC_VECTOR(out_data'RANGE);
SIGNAL state : t_dp_state_enum;
SIGNAL verify_en : STD_LOGIC;
SIGNAL verify_done : STD_LOGIC;
SIGNAL exp_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0) := sel_a_b(g_dp_rl = g_axi4_rl, TO_UVEC(18953, c_dp_data_w), TO_UVEC(19279, c_dp_data_w));
SIGNAL exp_empty : STD_LOGIC_VECTOR(c_dp_stream_empty_w-1 DOWNTO 0);
SIGNAL exp_channel : STD_LOGIC_VECTOR(c_dp_channel_w-1 DOWNTO 0);
BEGIN
clk <= NOT clk OR tb_end AFTER clk_period/2;
rst <= '1', '0' AFTER clk_period*7;
-- Sync interval
proc_dp_sync_interval(clk, sync);
-- Input data
cnt_val <= in_ready AND cnt_en;
proc_dp_cnt_dat(rst, clk, cnt_val, cnt_dat);
proc_dp_tx_data(g_dp_rl, rst, clk, cnt_val, cnt_dat, tx_data, tx_val, in_data, in_val);
proc_dp_tx_ctrl(c_tx_offset_sync, c_tx_period_sync, in_data, in_val, in_sync);
proc_dp_tx_ctrl(c_tx_offset_sop, c_tx_period_sop, in_data, in_val, in_sop);
proc_dp_tx_ctrl(c_tx_offset_eop, c_tx_period_eop, in_data, in_val, in_eop);
in_empty <= RESIZE_UVEC(INCR_UVEC(in_data, c_empty_offset)(c_max_empty_w-1 DOWNTO 0), c_dp_stream_empty_w) WHEN in_eop = '1' ELSE (OTHERS => '0');
in_channel <= RESIZE_UVEC(INCR_UVEC(in_data, c_channel_offset)(c_max_channel_w-1 DOWNTO 0), c_dp_stream_channel_w);
-- Stimuli control
proc_dp_count_en(rst, clk, sync, lfsr1, state, verify_done, tb_end, cnt_en);
proc_dp_out_ready(rst, clk, sync, lfsr2, out_ready);
-- Output verify
proc_dp_verify_en(c_verify_en_wait, rst, clk, sync, verify_en);
proc_dp_verify_data("out_data", g_dp_rl, clk, verify_en, out_ready, out_val, out_data, prev_out_data);
proc_dp_verify_valid(g_dp_rl, clk, verify_en, out_ready, prev_out_ready, out_val);
proc_dp_verify_ctrl(c_tx_offset_sop, c_tx_period_sop, "sop", clk, verify_en, out_data, out_val, out_sop);
proc_dp_verify_ctrl(c_tx_offset_eop, c_tx_period_eop, "eop", clk, verify_en, out_data, out_val, out_eop);
proc_dp_verify_sop_and_eop(g_dp_rl, FALSE, clk, out_ready, out_val, out_sop, out_eop, hold_out_sop); -- Verify that sop and eop come in pairs, no check on valid between eop and sop
exp_empty <= RESIZE_UVEC(INCR_UVEC(out_data, c_empty_offset)(c_max_empty_w-1 DOWNTO 0), c_dp_empty_w) WHEN out_eop = '1' ELSE (OTHERS => '0');
exp_channel <= RESIZE_UVEC(INCR_UVEC(out_data, c_channel_offset)(c_max_channel_w-1 DOWNTO 0), c_dp_channel_w);
proc_dp_verify_other_sosi("empty", exp_empty, clk, out_val, out_empty);
proc_dp_verify_other_sosi("channel", exp_channel, clk, verify_en, out_channel);
-- Check that the test has ran at all
proc_dp_verify_value(e_equal, clk, verify_done, exp_data, out_data);
------------------------------------------------------------------------------
-- DUT
------------------------------------------------------------------------------
-- map sl, slv to record
in_ready <= dut_siso.ready; -- SISO
dut_sosi.data(c_dp_data_w-1 DOWNTO 0) <= in_data; -- SOSI
dut_sosi.empty <= in_empty;
dut_sosi.channel <= in_channel;
dut_sosi.sync <= in_sync;
dut_sosi.valid <= in_val;
dut_sosi.sop <= in_sop;
dut_sosi.eop <= in_eop;
dut : ENTITY work.axi4_stream_dp_bridge
GENERIC MAP (
g_use_empty => TRUE,
g_axi4_rl => g_axi4_rl,
g_dp_rl => g_dp_rl
)
PORT MAP (
in_rst => rst,
in_clk => clk,
-- ST sink
dp_in_siso => dut_siso,
dp_in_sosi => dut_sosi,
-- ST source
dp_out_siso => dut_out_siso,
dp_out_sosi => dut_out_sosi,
-- AXI4 Loopback
axi4_in_sosi => dut_axi4_sosi,
axi4_in_siso => dut_axi4_siso,
axi4_out_sosi => dut_axi4_sosi,
axi4_out_siso => dut_axi4_siso
);
-- map record to sl, slv
dut_out_siso.ready <= out_ready; -- SISO
out_data <= dut_out_sosi.data(c_dp_data_w-1 DOWNTO 0); -- SOSI
out_empty(c_dp_empty_w-1 DOWNTO 0) <= dut_out_sosi.empty;
out_channel(c_dp_channel_w-1 DOWNTO 0) <= dut_out_sosi.channel;
out_sync <= dut_out_sosi.sync;
out_val <= dut_out_sosi.valid;
out_sop <= dut_out_sosi.sop;
out_eop <= dut_out_sosi.eop;
END tb;
-- --------------------------------------------------------------------------
-- 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:
-- . Multi-TB for axi4_stream_dp_bridge.
-- Description:
-- . Tests multiple instances of tb_axi4_stream_dp_bridge with different
-- ready-latency configurations.
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
ENTITY tb_tb_axi4_stream_dp_bridge IS
END tb_tb_axi4_stream_dp_bridge;
ARCHITECTURE tb OF tb_tb_axi4_stream_dp_bridge IS
SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
BEGIN
-- > as 2
-- > run -all --> OK
-- g_dp_rl : NATURAL := 1;
-- g_axi4_rl : NATURAL := 0;
u_dp_1_axi4_0 : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (1, 0);
u_dp_1_axi4_1 : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (1, 1);
u_dp_0_axi4_0 : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (0, 0);
u_dp_0_axi4_1 : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (0, 1);
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment