Skip to content
Snippets Groups Projects
Commit 166007f1 authored by Reinier van der Walle's avatar Reinier van der Walle
Browse files

added axi4 stream library

parent 48b1d49f
Branches
No related tags found
1 merge request!312Resolve HPR-86
Pipeline #45241 failed
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 wheter
-- 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.
-- . 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 oftem 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".
aclk : OUT STD_LOGIC := '0'; -- AXI4 clk
aresetn: OUT STD_LOGIC := '1'; -- AXI4 active-low reset
dp_clk : OUT STD_LOGIC := '0'; -- DP clk
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;
aclk <= in_clk;
aresetn <= NOT i_rst;
dp_clk <= in_clk;
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 (C) 2017
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------
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
------------------------------------------------------------------------------
-- General DP stream record defintion
------------------------------------------------------------------------------
-- 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
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