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;
-- --------------------------------------------------------------------------
-- 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 (edits only, see Original)
-- Purpose: General AXI stream record defintion
-- Original:
-- https://git.astron.nl/desp/gemini/-/blob/master/libraries/base/axi4/src/vhdl/axi4_stream_pkg.vhd
-- Remarks:
-- * Choose smallest maximum SOSI slv lengths that fit all use cases, because unconstrained record
-- fields slv is not allowed.
-- * The large SOSI data field width of 256b has some disadvantages:
-- . about 10% extra simulation time and PC memory usage compared to 72b
-- (measured using tb_unb_tse_board)
-- . a 256b number has 64 hex digits in the Wave window which is awkward because of the leading
-- zeros when typically
-- only 32b are used, fortunately integer representation still works OK (except 0 which is shown
-- as blank).
-- However the alternatives are not attractive, because they affect the implementation of the
-- streaming
-- components that use the SOSI record. Alternatives are e.g.:
-- . define an extra long SOSI data field ldata[255:0] in addition to the existing data[71:0]
-- field
-- . use the array of SOSI records to contain wider data, all with the same SOSI control field
-- values
-- . define another similar SOSI record with data[255:0].
-- Therefore define data width as 256b, because the disadvantages are acceptable and the benefit
-- is great, because all
-- streaming components can remain as they are.
-- * Added sync and bsn to SOSI to have timestamp information with the data
-- * Added re and im to SOSI to support complex data for DSP
--------------------------------------------------------------------------------
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;
PACKAGE axi4_stream_pkg Is
CONSTANT c_axi4_stream_data_w : NATURAL := 512; -- Data width, upto 512bit for Xilinx IP
CONSTANT c_axi4_stream_user_w : NATURAL := 70; -- User data, upto 70bit for Xilinx IP
CONSTANT c_axi4_stream_tid_w : NATURAL := 4; -- Thread ID, upto 4bit for Xilinx IP
CONSTANT c_axi4_stream_dest_w : NATURAL := 32; -- Routing data, upto 32bit for Xilinx IP
CONSTANT c_axi4_stream_keep_w : NATURAL := c_axi4_stream_data_w/8; -- 1 bit for each byte in data.
CONSTANT c_axi4_stream_strb_w : NATURAL := c_axi4_stream_data_w/8; -- 1 bit for each byte in daya.
TYPE t_axi4_siso IS RECORD -- Source In and Sink Out
tready : STD_LOGIC; -- Ready to accept data from destination
END RECORD;
TYPE t_axi4_sosi IS RECORD -- Source Out and Sink In
tvalid : STD_LOGIC; -- Data valid
tdata : STD_LOGIC_VECTOR(c_axi4_stream_data_w-1 DOWNTO 0); -- Data bus
tstrb : STD_LOGIC_VECTOR(c_axi4_stream_strb_w-1 DOWNTO 0); -- Byte valids, indicates if data is position (0) or data (1). Generally not used
tkeep : STD_LOGIC_VECTOR(c_axi4_stream_keep_w-1 DOWNTO 0); -- Indicate valid data bytes (1) or null bytes (0).
tlast : STD_LOGIC; -- Last transaction in a burst
tid : STD_LOGIC_VECTOR(c_axi4_stream_tid_w-1 DOWNTO 0); -- Transaction ID
tdest : STD_LOGIC_VECTOR(c_axi4_stream_dest_w-1 DOWNTO 0); -- Destination rounting information
tuser : STD_LOGIC_VECTOR(c_axi4_stream_user_w-1 DOWNTO 0); -- Tranaction user fields
END RECORD;
-- Initialise signal declarations with c_axi4_stream_rst/rdy to ease the interpretation of slv fields with unused bits
CONSTANT c_axi4_siso_rst : t_axi4_siso := (tready => '0');
CONSTANT c_axi4_siso_x : t_axi4_siso := (tready => 'X');
CONSTANT c_axi4_siso_hold : t_axi4_siso := (tready => '0');
CONSTANT c_axi4_siso_rdy : t_axi4_siso := (tready => '1');
CONSTANT c_axi4_siso_flush : t_axi4_siso := (tready => '1');
CONSTANT c_axi4_sosi_rst : t_axi4_sosi := (tvalid => '0', tdata => (OTHERS=>'0'), tstrb => (OTHERS=>'0'), tkeep => (OTHERS=>'0'), tlast => '0', tid => (OTHERS=>'0'), tdest => (OTHERS=>'0'), tuser => (OTHERS=>'0'));
CONSTANT c_axi4_sosi_x : t_axi4_sosi := ('X', (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X'), 'X', (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X'));
-- Multi port or multi register array for DP stream records
TYPE t_axi4_siso_arr IS ARRAY (INTEGER RANGE <>) OF t_axi4_siso;
TYPE t_axi4_sosi_arr IS ARRAY (INTEGER RANGE <>) OF t_axi4_sosi;
-- Multi-dimemsion array types with fixed LS-dimension
-- . 2 dimensional array with 2 fixed LS sosi/siso interfaces (axi4_split, axi4_concat)
TYPE t_axi4_siso_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_axi4_siso_arr(1 DOWNTO 0);
TYPE t_axi4_sosi_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_axi4_sosi_arr(1 DOWNTO 0);
-- 2-dimensional streaming array type:
-- Note:
-- This t_*_mat is less useful then a t_*_2arr array of arrays, because assignments can only be done per element (i.e. not per row). However for t_*_2arr
-- the arrays dimension must be fixed, so these t_*_2arr types are application dependent and need to be defined where used.
TYPE t_axi4_siso_mat IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF t_axi4_siso;
TYPE t_axi4_sosi_mat IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF t_axi4_sosi;
-- Check sosi.valid against siso.ready
PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN NATURAL;
SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi : IN t_axi4_sosi;
SIGNAL siso : IN t_axi4_siso;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR);
-- Default RL=1
PROCEDURE proc_axi4_siso_alert(SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi : IN t_axi4_sosi;
SIGNAL siso : IN t_axi4_siso;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR);
-- SOSI/SISO array version
PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN NATURAL;
SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi_arr : IN t_axi4_sosi_arr;
SIGNAL siso_arr : IN t_axi4_siso_arr;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR);
-- SOSI/SISO array version with RL=1
PROCEDURE proc_axi4_siso_alert(SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi_arr : IN t_axi4_sosi_arr;
SIGNAL siso_arr : IN t_axi4_siso_arr;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR);
-- Keep part of head data and combine part of tail data, use the other sosi from head_sosi
FUNCTION func_axi4_data_shift_first(head_sosi, tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail : NATURAL) RETURN t_axi4_sosi;
-- Shift and combine part of previous data and this data, use the other sosi from prev_sosi
FUNCTION func_axi4_data_shift( prev_sosi, this_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_this : NATURAL) RETURN t_axi4_sosi;
-- Shift part of tail data and account for input empty
FUNCTION func_axi4_data_shift_last( tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail, input_empty : NATURAL) RETURN t_axi4_sosi;
-- Determine resulting empty if two streams are concatenated or split
FUNCTION func_axi4_empty_concat(head_empty, tail_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR;
FUNCTION func_axi4_empty_split(input_empty, head_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR;
-- Multiplex the t_axi4_sosi_arr based on the valid, assuming that at most one input is active valid.
FUNCTION func_axi4_sosi_arr_mux(axi4 : t_axi4_sosi_arr) RETURN t_axi4_sosi;
-- Determine the combined logical value of corresponding STD_LOGIC fields in t_axi4_*_arr (for all elements or only for the mask[]='1' elements)
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC;
FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC;
-- Functions to set or get a STD_LOGIC field as a STD_LOGIC_VECTOR to or from an siso or an sosi array
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_siso_arr;
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; sl : STD_LOGIC; str : STRING) RETURN t_axi4_siso_arr;
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; sl : STD_LOGIC; str : STRING) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC_VECTOR;
FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC_VECTOR;
-- Functions to select elements from two siso or two sosi arrays (sel[] = '1' selects a, sel[] = '0' selects b)
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_siso) RETURN t_axi4_siso_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_sosi) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso_arr; b : t_axi4_siso) RETURN t_axi4_siso_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi_arr; b : t_axi4_sosi) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso; b : t_axi4_siso_arr) RETURN t_axi4_siso_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi; b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_siso_arr) RETURN t_axi4_siso_arr;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr;
-- Fix reversed buses due to connecting TO to DOWNTO range arrays.
FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_siso_arr) RETURN t_axi4_siso_arr;
-- Functions to combinatorially hold the data fields and to set or reset the control fields in an sosi array
FUNCTION func_axi4_stream_arr_set_control( axi4 : t_axi4_sosi_arr; ctrl : t_axi4_sosi) RETURN t_axi4_sosi_arr;
FUNCTION func_axi4_stream_arr_reset_control(axi4 : t_axi4_sosi_arr ) RETURN t_axi4_sosi_arr;
-- Functions to convert dp streaming to axi4 streaming
FUNCTION func_axi4_stream_from_dp_sosi(dp_sosi : t_dp_sosi) RETURN t_axi4_sosi;
FUNCTION func_axi4_stream_from_dp_siso(dp_siso : t_dp_siso) RETURN t_axi4_siso;
-- Functions to convert dp streaming to axi4 streaming
FUNCTION func_axi4_stream_to_dp_sosi(axi4_sosi : t_axi4_sosi) RETURN t_dp_sosi;
FUNCTION func_axi4_stream_to_dp_siso(axi4_siso : t_axi4_siso) RETURN t_dp_siso;
-- Function to derive DP empty from AXI4 tkeep by counting the 0s in TKEEP.
FUNCTION func_axi4_stream_tkeep_to_dp_empty(tkeep : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;
END axi4_stream_pkg;
PACKAGE BODY axi4_stream_pkg IS
-- Check sosi.valid against siso.ready
PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN NATURAL;
SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi : IN t_axi4_sosi;
SIGNAL siso : IN t_axi4_siso;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS
BEGIN
ready_reg(0) <= siso.tready;
-- Register siso.ready in c_ready_latency registers
IF rising_edge(clk) THEN
-- Check DP sink
IF sosi.tvalid = '1' AND ready_reg(c_ready_latency) = '0' THEN
REPORT "RL ERROR" SEVERITY FAILURE;
END IF;
ready_reg( 1 TO c_ready_latency) <= ready_reg( 0 TO c_ready_latency-1);
END IF;
END proc_axi4_siso_alert;
-- Default RL=1
PROCEDURE proc_axi4_siso_alert(SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi : IN t_axi4_sosi;
SIGNAL siso : IN t_axi4_siso;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS
BEGIN
proc_axi4_siso_alert(1, clk, sosi, siso, ready_reg);
END proc_axi4_siso_alert;
-- SOSI/SISO array version
PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN NATURAL;
SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi_arr : IN t_axi4_sosi_arr;
SIGNAL siso_arr : IN t_axi4_siso_arr;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS
BEGIN
FOR i IN 0 TO sosi_arr'LENGTH-1 LOOP
ready_reg(i*(c_ready_latency+1)) <= siso_arr(i).tready; -- SLV is used as an array: nof_streams*(0..c_ready_latency)
END LOOP;
-- Register siso.ready in c_ready_latency registers
IF rising_edge(clk) THEN
FOR i IN 0 TO sosi_arr'LENGTH-1 LOOP
-- Check DP sink
IF sosi_arr(i).tvalid = '1' AND ready_reg(i*(c_ready_latency+1)+1) = '0' THEN
REPORT "RL ERROR" SEVERITY FAILURE;
END IF;
ready_reg(i*(c_ready_latency+1)+1 TO i*(c_ready_latency+1)+c_ready_latency) <= ready_reg(i*(c_ready_latency+1) TO i*(c_ready_latency+1)+c_ready_latency-1);
END LOOP;
END IF;
END proc_axi4_siso_alert;
-- SOSI/SISO array version with RL=1
PROCEDURE proc_axi4_siso_alert(SIGNAL clk : IN STD_LOGIC;
SIGNAL sosi_arr : IN t_axi4_sosi_arr;
SIGNAL siso_arr : IN t_axi4_siso_arr;
SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS
BEGIN
proc_axi4_siso_alert(1, clk, sosi_arr, siso_arr, ready_reg);
END proc_axi4_siso_alert;
-- Keep part of head data and combine part of tail data
FUNCTION func_axi4_data_shift_first(head_sosi, tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail : NATURAL) RETURN t_axi4_sosi IS
VARIABLE vN : NATURAL := nof_symbols_per_data;
VARIABLE v_sosi : t_axi4_sosi;
BEGIN
ASSERT nof_symbols_from_tail<vN REPORT "func_axi4_data_shift_first : no symbols from head" SEVERITY FAILURE;
-- use the other sosi from head_sosi
v_sosi := head_sosi; -- I = nof_symbols_from_tail = 0
FOR I IN 1 TO vN-1 LOOP -- I > 0
IF nof_symbols_from_tail = I THEN
v_sosi.tdata(I*symbol_w-1 DOWNTO 0) := tail_sosi.tdata(vN*symbol_w-1 DOWNTO (vN-I)*symbol_w);
END IF;
END LOOP;
RETURN v_sosi;
END func_axi4_data_shift_first;
-- Shift and combine part of previous data and this data,
FUNCTION func_axi4_data_shift(prev_sosi, this_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_this : NATURAL) RETURN t_axi4_sosi IS
VARIABLE vK : NATURAL := nof_symbols_from_this;
VARIABLE vN : NATURAL := nof_symbols_per_data;
VARIABLE v_sosi : t_axi4_sosi;
BEGIN
-- use the other sosi from this_sosi if nof_symbols_from_this > 0 else use other sosi from prev_sosi
IF vK>0 THEN
v_sosi := this_sosi;
ELSE
v_sosi := prev_sosi;
END IF;
-- use sosi data from both if 0 < nof_symbols_from_this < nof_symbols_per_data (i.e. 0 < I < vN)
IF vK<nof_symbols_per_data THEN -- I = vK = nof_symbols_from_this < vN
-- Implementation using variable vK directly instead of via I in a LOOP
-- IF vK > 0 THEN
-- v_sosi.data(vN*symbol_w-1 DOWNTO vK*symbol_w) := prev_sosi.data((vN-vK)*symbol_w-1 DOWNTO 0);
-- v_sosi.data( vK*symbol_w-1 DOWNTO 0) := this_sosi.data( vN *symbol_w-1 DOWNTO (vN-vK)*symbol_w);
-- END IF;
-- Implementaion using LOOP vK rather than VARIABLE vK directly as index to help synthesis and avoid potential multiplier
v_sosi.tdata := prev_sosi.tdata; -- I = vK = nof_symbols_from_this = 0
FOR I IN 1 TO vN-1 LOOP -- I = vK = nof_symbols_from_this > 0
IF vK = I THEN
v_sosi.tdata(vN*symbol_w-1 DOWNTO I*symbol_w) := prev_sosi.tdata((vN-I)*symbol_w-1 DOWNTO 0);
v_sosi.tdata( I*symbol_w-1 DOWNTO 0) := this_sosi.tdata( vN *symbol_w-1 DOWNTO (vN-I)*symbol_w);
END IF;
END LOOP;
END IF;
RETURN v_sosi;
END func_axi4_data_shift;
-- Shift part of tail data and account for input empty
FUNCTION func_axi4_data_shift_last(tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail, input_empty : NATURAL) RETURN t_axi4_sosi IS
VARIABLE vK : NATURAL := nof_symbols_from_tail;
VARIABLE vL : NATURAL := input_empty;
VARIABLE vN : NATURAL := nof_symbols_per_data;
VARIABLE v_sosi : t_axi4_sosi;
BEGIN
ASSERT vK > 0 REPORT "func_axi4_data_shift_last : no symbols from tail" SEVERITY FAILURE;
ASSERT vK+vL<=vN REPORT "func_axi4_data_shift_last : impossible shift" SEVERITY FAILURE;
v_sosi := tail_sosi;
-- Implementation using variable vK directly instead of via I in a LOOP
-- IF vK > 0 THEN
-- v_sosi.data(vN*symbol_w-1 DOWNTO (vN-vK)*symbol_w) <= tail_sosi.data((vK+vL)*symbol_w-1 DOWNTO vL*symbol_w);
-- END IF;
-- Implementation using LOOP vK rather than VARIABLE vK directly as index to help synthesis and avoid potential multiplier
-- Implementation using LOOP vL rather than VARIABLE vL directly as index to help synthesis and avoid potential multiplier
FOR I IN 1 TO vN-1 LOOP
IF vK = I THEN
FOR J IN 0 TO vN-1 LOOP
IF vL = J THEN
v_sosi.tdata(vN*symbol_w-1 DOWNTO (vN-I)*symbol_w) := tail_sosi.tdata((I+J)*symbol_w-1 DOWNTO J*symbol_w);
END IF;
END LOOP;
END IF;
END LOOP;
RETURN v_sosi;
END func_axi4_data_shift_last;
-- Determine resulting empty if two streams are concatenated
-- . both empty must use the same nof symbols per data
FUNCTION func_axi4_empty_concat(head_empty, tail_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR IS
VARIABLE v_a, v_b, v_empty : NATURAL;
BEGIN
v_a := TO_UINT(head_empty);
v_b := TO_UINT(tail_empty);
v_empty := v_a + v_b;
IF v_empty >= nof_symbols_per_data THEN
v_empty := v_empty - nof_symbols_per_data;
END IF;
RETURN TO_UVEC(v_empty, head_empty'LENGTH);
END func_axi4_empty_concat;
FUNCTION func_axi4_empty_split(input_empty, head_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR IS
VARIABLE v_a, v_b, v_empty : NATURAL;
BEGIN
v_a := TO_UINT(input_empty);
v_b := TO_UINT(head_empty);
IF v_a >= v_b THEN
v_empty := v_a - v_b;
ELSE
v_empty := (nof_symbols_per_data + v_a) - v_b;
END IF;
RETURN TO_UVEC(v_empty, head_empty'LENGTH);
END func_axi4_empty_split;
-- Multiplex the t_axi4_sosi_arr based on the valid, assuming that at most one input is active valid.
FUNCTION func_axi4_sosi_arr_mux(axi4 : t_axi4_sosi_arr) RETURN t_axi4_sosi IS
VARIABLE v_sosi : t_axi4_sosi := c_axi4_sosi_rst;
BEGIN
FOR I IN axi4'RANGE LOOP
IF axi4(I).tvalid='1' THEN
v_sosi := axi4(I);
EXIT;
END IF;
END LOOP;
RETURN v_sosi;
END func_axi4_sosi_arr_mux;
-- Determine the combined logical value of corresponding STD_LOGIC fields in t_axi4_*_arr (for all elements or only for the mask[]='1' elements)
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1'); -- set default v_vec such that unmasked input have no influence on operation result
VARIABLE v_any : STD_LOGIC := '0';
BEGIN
-- map siso field to v_vec
FOR I IN axi4'RANGE LOOP
IF mask(I)='1' THEN
v_any := '1';
IF str="READY" THEN v_vec(I) := axi4(I).tready;
ELSE REPORT "Error in func_axi4_stream_arr_and for t_axi4_siso_arr";
END IF;
END IF;
END LOOP;
-- do operation on the selected record field
IF v_any='1' THEN
RETURN vector_and(v_vec); -- return AND of the masked input fields
ELSE
RETURN '0'; -- return '0' if no input was masked
END IF;
END func_axi4_stream_arr_and;
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1'); -- set default v_vec such that unmasked input have no influence on operation result
VARIABLE v_any : STD_LOGIC := '0';
BEGIN
-- map siso field to v_vec
FOR I IN axi4'RANGE LOOP
IF mask(I)='1' THEN
v_any := '1';
IF str="VALID" THEN v_vec(I) := axi4(I).tvalid;
ELSE REPORT "Error in func_axi4_stream_arr_and for t_axi4_sosi_arr";
END IF;
END IF;
END LOOP;
-- do operation on the selected record field
IF v_any='1' THEN
RETURN vector_and(v_vec); -- return AND of the masked input fields
ELSE
RETURN '0'; -- return '0' if no input was masked
END IF;
END func_axi4_stream_arr_and;
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC IS
CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
BEGIN
RETURN func_axi4_stream_arr_and(axi4, c_mask, str);
END func_axi4_stream_arr_and;
FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC IS
CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
BEGIN
RETURN func_axi4_stream_arr_and(axi4, c_mask, str);
END func_axi4_stream_arr_and;
FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'0'); -- set default v_vec such that unmasked input have no influence on operation result
VARIABLE v_any : STD_LOGIC := '0';
BEGIN
-- map siso field to v_vec
FOR I IN axi4'RANGE LOOP
IF mask(I)='1' THEN
v_any := '1';
IF str="READY" THEN v_vec(I) := axi4(I).tready;
ELSE REPORT "Error in func_axi4_stream_arr_or for t_axi4_siso_arr";
END IF;
END IF;
END LOOP;
-- do operation on the selected record field
IF v_any='1' THEN
RETURN vector_or(v_vec); -- return OR of the masked input fields
ELSE
RETURN '0'; -- return '0' if no input was masked
END IF;
END func_axi4_stream_arr_or;
FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'0'); -- set default v_vec such that unmasked input have no influence on operation result
VARIABLE v_any : STD_LOGIC := '0';
BEGIN
-- map siso field to v_vec
FOR I IN axi4'RANGE LOOP
IF mask(I)='1' THEN
v_any := '1';
IF str="VALID" THEN v_vec(I) := axi4(I).tvalid;
ELSE REPORT "Error in func_axi4_stream_arr_or for t_axi4_sosi_arr";
END IF;
END IF;
END LOOP;
-- do operation on the selected record field
IF v_any='1' THEN
RETURN vector_or(v_vec); -- return OR of the masked input fields
ELSE
RETURN '0'; -- return '0' if no input was masked
END IF;
END func_axi4_stream_arr_or;
FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC IS
CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
BEGIN
RETURN func_axi4_stream_arr_or(axi4, c_mask, str);
END func_axi4_stream_arr_or;
FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC IS
CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
BEGIN
RETURN func_axi4_stream_arr_or(axi4, c_mask, str);
END func_axi4_stream_arr_or;
-- Functions to set or get a STD_LOGIC field as a STD_LOGIC_VECTOR to or from an siso or an sosi array
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_siso_arr IS
VARIABLE v_axi4 : t_axi4_siso_arr(axi4'RANGE) := axi4; -- default
VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := slv; -- map to ensure same range as for axi4
BEGIN
FOR I IN axi4'RANGE LOOP
IF str="READY" THEN v_axi4(I).tready := v_slv(I);
ELSE REPORT "Error in func_axi4_stream_arr_set for t_axi4_siso_arr";
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_set;
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(axi4'RANGE) := axi4; -- default
VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := slv; -- map to ensure same range as for axi4
BEGIN
FOR I IN axi4'RANGE LOOP
IF str="VALID" THEN v_axi4(I).tvalid := v_slv(I);
ELSE REPORT "Error in func_axi4_stream_arr_set for t_axi4_sosi_arr";
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_set;
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; sl : STD_LOGIC; str : STRING) RETURN t_axi4_siso_arr IS
VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>sl);
BEGIN
RETURN func_axi4_stream_arr_set(axi4, v_slv, str);
END func_axi4_stream_arr_set;
FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; sl : STD_LOGIC; str : STRING) RETURN t_axi4_sosi_arr IS
VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>sl);
BEGIN
RETURN func_axi4_stream_arr_set(axi4, v_slv, str);
END func_axi4_stream_arr_set;
FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC_VECTOR IS
VARIABLE v_ctrl : STD_LOGIC_VECTOR(axi4'RANGE);
BEGIN
FOR I IN axi4'RANGE LOOP
IF str="READY" THEN v_ctrl(I) := axi4(I).tready;
ELSE REPORT "Error in func_axi4_stream_arr_get for t_axi4_siso_arr";
END IF;
END LOOP;
RETURN v_ctrl;
END func_axi4_stream_arr_get;
FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC_VECTOR IS
VARIABLE v_ctrl : STD_LOGIC_VECTOR(axi4'RANGE);
BEGIN
FOR I IN axi4'RANGE LOOP
IF str="VALID" THEN v_ctrl(I) := axi4(I).tvalid;
ELSE REPORT "Error in func_axi4_stream_arr_get for t_axi4_sosi_arr";
END IF;
END LOOP;
RETURN v_ctrl;
END func_axi4_stream_arr_get;
-- Functions to select elements from two siso or two sosi arrays (sel[] = '1' selects a, sel[] = '0' selects b)
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_siso) RETURN t_axi4_siso_arr IS
VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a;
ELSE
v_axi4(I) := b;
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso_arr; b : t_axi4_siso) RETURN t_axi4_siso_arr IS
VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a(I);
ELSE
v_axi4(I) := b;
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso; b : t_axi4_siso_arr) RETURN t_axi4_siso_arr IS
VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a;
ELSE
v_axi4(I) := b(I);
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_siso_arr) RETURN t_axi4_siso_arr IS
VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a(I);
ELSE
v_axi4(I) := b(I);
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_sosi) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a;
ELSE
v_axi4(I) := b;
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi_arr; b : t_axi4_sosi) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a(I);
ELSE
v_axi4(I) := b;
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi; b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a;
ELSE
v_axi4(I) := b(I);
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
BEGIN
FOR I IN sel'RANGE LOOP
IF sel(I)='1' THEN
v_axi4(I) := a(I);
ELSE
v_axi4(I) := b(I);
END IF;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_select;
FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_siso_arr) RETURN t_axi4_siso_arr IS
VARIABLE v_to_range : t_axi4_siso_arr(0 TO in_arr'HIGH);
VARIABLE v_downto_range : t_axi4_siso_arr(in_arr'HIGH DOWNTO 0);
BEGIN
FOR i IN in_arr'RANGE LOOP
v_to_range(i) := in_arr(in_arr'HIGH-i);
v_downto_range(i) := in_arr(in_arr'HIGH-i);
END LOOP;
IF in_arr'LEFT>in_arr'RIGHT THEN
RETURN v_downto_range;
ELSIF in_arr'LEFT<in_arr'RIGHT THEN
RETURN v_to_range;
ELSE
RETURN in_arr;
END IF;
END func_axi4_stream_arr_reverse_range;
FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
VARIABLE v_to_range : t_axi4_sosi_arr(0 TO in_arr'HIGH);
VARIABLE v_downto_range : t_axi4_sosi_arr(in_arr'HIGH DOWNTO 0);
BEGIN
FOR i IN in_arr'RANGE LOOP
v_to_range(i) := in_arr(in_arr'HIGH-i);
v_downto_range(i) := in_arr(in_arr'HIGH-i);
END LOOP;
IF in_arr'LEFT>in_arr'RIGHT THEN
RETURN v_downto_range;
ELSIF in_arr'LEFT<in_arr'RIGHT THEN
RETURN v_to_range;
ELSE
RETURN in_arr;
END IF;
END func_axi4_stream_arr_reverse_range;
-- Functions to combinatorially hold the data fields and to set or reset the control fields in an sosi array
FUNCTION func_axi4_stream_arr_set_control(axi4 : t_axi4_sosi_arr; ctrl : t_axi4_sosi) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(axi4'RANGE) := axi4; -- hold sosi data
BEGIN
FOR I IN axi4'RANGE LOOP -- set sosi control
v_axi4(I).tvalid := ctrl.tvalid;
v_axi4(I).tuser := ctrl.tuser;
v_axi4(I).tdest := ctrl.tdest;
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_set_control;
FUNCTION func_axi4_stream_arr_reset_control(axi4 : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
VARIABLE v_axi4 : t_axi4_sosi_arr(axi4'RANGE) := axi4; -- hold sosi data
BEGIN
FOR I IN axi4'RANGE LOOP -- reset sosi control
v_axi4(I).tvalid := '0';
END LOOP;
RETURN v_axi4;
END func_axi4_stream_arr_reset_control;
-- Functions to convert dp streaming to axi4 streaming
FUNCTION func_axi4_stream_from_dp_sosi(dp_sosi : t_dp_sosi) RETURN t_axi4_sosi IS
CONSTANT c_max_empty_w : NATURAL := ceil_log2(c_axi4_stream_keep_w);
VARIABLE v_axi4_sosi : t_axi4_sosi := c_axi4_sosi_rst;
VARIABLE v_empty_int : NATURAL := 0;
VARIABLE v_keep : STD_LOGIC_VECTOR(c_axi4_stream_keep_w-1 DOWNTO 0) := (OTHERS => '1');
BEGIN
v_empty_int := TO_UINT(dp_sosi.empty(c_max_empty_w-1 DOWNTO 0));
IF dp_sosi.eop = '1' THEN -- empty is only valid on eop
v_keep(v_empty_int-1 DOWNTO 0) := (OTHERS => '0'); -- Keep is one hot encoded.
ELSE
v_keep := (OTHERS => '1');
END IF;
v_axi4_sosi.tvalid := dp_sosi.valid;
v_axi4_sosi.tdata := dp_sosi.data(c_axi4_stream_data_w-1 DOWNTO 0);
v_axi4_sosi.tstrb := v_keep;
v_axi4_sosi.tkeep := v_keep;
v_axi4_sosi.tlast := dp_sosi.eop;
v_axi4_sosi.tid := dp_sosi.channel(c_axi4_stream_tid_w-1 DOWNTO 0);
v_axi4_sosi.tdest := (OTHERS => '0');
v_axi4_sosi.tuser := (OTHERS => '0');
RETURN v_axi4_sosi;
END func_axi4_stream_from_dp_sosi;
FUNCTION func_axi4_stream_from_dp_siso(dp_siso : t_dp_siso) RETURN t_axi4_siso IS
VARIABLE v_axi4_siso : t_axi4_siso := c_axi4_siso_rst;
-- Note that dp_siso.xon is not used.
BEGIN
v_axi4_siso.tready := dp_siso.ready;
RETURN v_axi4_siso;
END func_axi4_stream_from_dp_siso;
-- Functions to convert dp streaming to axi4 streaming
FUNCTION func_axi4_stream_to_dp_sosi(axi4_sosi : t_axi4_sosi) RETURN t_dp_sosi IS
VARIABLE v_dp_sosi : t_dp_sosi := c_dp_sosi_rst;
BEGIN
v_dp_sosi.data(c_axi4_stream_data_w-1 DOWNTO 0) := axi4_sosi.tdata;
v_dp_sosi.valid := axi4_sosi.tvalid;
v_dp_sosi.sop := '0'; -- Should be generated (first valid after eop).
v_dp_sosi.eop := axi4_sosi.tlast;
-- Cannot always derive from tkeep as it can have any bytes (not just the last bytes)
-- selected to be valid, the empty field can only select the last bytes to be invalid
-- for which you can use func_axi4_stream_tkeep_to_dp_empty.
v_dp_sosi.empty := (OTHERS => '0');
v_dp_sosi.channel(c_axi4_stream_tid_w-1 DOWNTO 0) := axi4_sosi.tid;
RETURN v_dp_sosi;
END func_axi4_stream_to_dp_sosi;
FUNCTION func_axi4_stream_to_dp_siso(axi4_siso : t_axi4_siso) RETURN t_dp_siso IS
VARIABLE v_dp_siso : t_dp_siso := c_dp_siso_rdy;
BEGIN
v_dp_siso.ready := axi4_siso.tready;
v_dp_siso.xon := '1';
RETURN v_dp_siso;
END func_axi4_stream_to_dp_siso;
-- Function to derive DP empty from AXI4 tkeep by counting the 0s in TKEEP.
-- This function assumes that only the the last bytes in an AXI4 data element are set to be null by TKEEP.
-- e.g. TKEEP = 11111000 will be valid but TKEEP = 11011111 is not.
FUNCTION func_axi4_stream_tkeep_to_dp_empty(tkeep : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
VARIABLE v_count : NATURAL := 0;
BEGIN
FOR I IN tkeep'RANGE LOOP
IF tkeep(I) = '0' THEN
v_count := v_count + 1;
END IF;
END LOOP;
RETURN(TO_UVEC(v_count, c_dp_stream_empty_w));
END func_axi4_stream_tkeep_to_dp_empty;
END axi4_stream_pkg;
-- --------------------------------------------------------------------------
-- 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.
Please register or to comment