From 190dcc546deeae971f565c02adcf3744117ef904 Mon Sep 17 00:00:00 2001
From: Reinier van der Walle <walle@astron.nl>
Date: Fri, 28 Feb 2020 10:50:08 +0100
Subject: [PATCH] Added dp_offload_tx_v3

---
 libraries/base/dp/hdllib.cfg                  |   2 +
 .../base/dp/src/vhdl/dp_offload_tx_v3.vhd     | 232 ++++++++++
 .../base/dp/tb/vhdl/tb_dp_offload_tx_v3.vhd   | 436 ++++++++++++++++++
 3 files changed, 670 insertions(+)
 create mode 100644 libraries/base/dp/src/vhdl/dp_offload_tx_v3.vhd
 create mode 100644 libraries/base/dp/tb/vhdl/tb_dp_offload_tx_v3.vhd

diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg
index 864bb29d1a..4981181020 100644
--- a/libraries/base/dp/hdllib.cfg
+++ b/libraries/base/dp/hdllib.cfg
@@ -128,6 +128,7 @@ synth_files =
     src/vhdl/dp_field_blk.vhd
     src/vhdl/dp_concat_field_blk.vhd
     src/vhdl/dp_offload_tx.vhd
+    src/vhdl/dp_offload_tx_v3.vhd
     src/vhdl/dp_offload_rx_filter.vhd
     src/vhdl/dp_offload_rx_filter_mm.vhd
     src/vhdl/dp_offload_rx.vhd
@@ -289,6 +290,7 @@ test_bench_files =
     tb/vhdl/tb_tb_dp_xonoff.vhd
     
     tb/vhdl/tb_tb_tb_dp_backpressure.vhd
+    tb/vhdl/tb_dp_offload_tx_v3.vhd
     tb/vhdl/tb_dp_offload_rx_filter.vhd
 
 regression_test_vhdl = 
diff --git a/libraries/base/dp/src/vhdl/dp_offload_tx_v3.vhd b/libraries/base/dp/src/vhdl/dp_offload_tx_v3.vhd
new file mode 100644
index 0000000000..e841015d54
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/dp_offload_tx_v3.vhd
@@ -0,0 +1,232 @@
+-------------------------------------------------------------------------------
+--
+-- 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/>.
+--
+-------------------------------------------------------------------------------
+
+-- Purpose:
+-- . Concatenate a user-defined header to a DP frame e.g. to create an Ethernet frame
+-- Description:
+-- . The header contents can be controlled dynamically by data path or MM control (selected by g_hdr_field_sel)
+-- . The header and data can be concatened at symbol level. The g_symbol_w defines the
+--   resolution of the empty field. The g_data_w must be an integer multiple of the 
+--   g_symbol_w. If the empty field is not used or if the empty field is always 0 then
+--   set g_symbol_w = g_data_w.
+-- . For example to concat header and data for an Ethernet frame use:
+--   - g_data_w = 32 (1GbE) or 64 (10GbE)
+--   - g_symbol_w = c_byte_w = 8 if either the header or the data can have an
+--                  non-zero empty field, so when they are not a multiple of 4 bytes
+--                  (= 32b) or 8 bytes (= 64b).
+--     g_symbol_w = g_data_w if the empty field is always 0, so the number of bits in
+--                  the header and data are an integer number of g_data_w.
+
+
+LIBRARY IEEE, common_lib, technology_lib, mm_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+USE common_lib.common_field_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+
+ENTITY dp_offload_tx_v3 IS
+  GENERIC (
+    g_nof_streams    : NATURAL;
+    g_data_w         : NATURAL;
+    g_symbol_w       : NATURAL;
+    g_hdr_field_arr  : t_common_field_arr; -- User defined header fields
+    g_hdr_field_sel  : STD_LOGIC_VECTOR;    -- For each header field, select the source: 0=data path, 1=MM controlled
+    g_pipeline_ready : BOOLEAN := FALSE
+  ); 
+  PORT (
+    mm_rst               : IN  STD_LOGIC;
+    mm_clk               : IN  STD_LOGIC; 
+    
+    dp_rst               : IN  STD_LOGIC;
+    dp_clk               : IN  STD_LOGIC;  
+
+    reg_hdr_dat_mosi     : IN  t_mem_mosi := c_mem_mosi_rst;
+    reg_hdr_dat_miso     : OUT t_mem_miso;
+
+    snk_in_arr           : IN  t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); 
+    snk_out_arr          : OUT t_dp_siso_arr(g_nof_streams-1 DOWNTO 0);              
+
+    src_out_arr          : OUT t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); 
+    src_in_arr           : IN  t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_dp_siso_rdy);
+
+    hdr_fields_in_arr    : IN  t_slv_1024_arr(g_nof_streams-1 DOWNTO 0); -- hdr_fields_in_arr(i) is considered valid @ snk_in_arr(i).sop
+    hdr_fields_out_arr   : OUT t_slv_1024_arr(g_nof_streams-1 DOWNTO 0)
+  );
+END dp_offload_tx_v3;
+
+
+ARCHITECTURE str OF dp_offload_tx_v3 IS 
+
+  CONSTANT c_dp_field_blk_snk_data_w : NATURAL := field_slv_out_len(field_arr_set_mode(g_hdr_field_arr , "RW"));
+  CONSTANT c_dp_field_blk_src_data_w : NATURAL := g_data_w;
+
+  SIGNAL dbg_c_dp_field_blk_snk_data_w : NATURAL := c_dp_field_blk_snk_data_w;
+  SIGNAL dbg_c_dp_field_blk_src_data_w : NATURAL := c_dp_field_blk_src_data_w;
+  
+  SIGNAL dp_field_blk_snk_in_arr     : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL dp_field_blk_snk_out_arr    : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0);
+
+  SIGNAL dp_field_blk_src_out_arr    : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL dp_field_blk_src_in_arr     : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0);
+
+  SIGNAL dp_concat_snk_in_2arr       : t_dp_sosi_2arr_2(g_nof_streams-1 DOWNTO 0);
+  SIGNAL dp_concat_snk_out_2arr      : t_dp_siso_2arr_2(g_nof_streams-1 DOWNTO 0);
+
+  SIGNAL reg_hdr_dat_mosi_arr        : t_mem_mosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL reg_hdr_dat_miso_arr        : t_mem_miso_arr(g_nof_streams-1 DOWNTO 0); 
+
+  SIGNAL mm_fields_slv_out_arr       : t_slv_1024_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL field_override_arr          : STD_LOGIC_VECTOR(g_hdr_field_arr'RANGE) := g_hdr_field_sel; --1 override bit per field
+
+         
+BEGIN
+
+  ASSERT c_dp_field_blk_snk_data_w <= c_dp_stream_data_w REPORT "Number of header bits must fit in t_dp_sosi data field." SEVERITY FAILURE;
+  ---------------------------------------------------------------------------------------
+  -- Create header block & concatenate header to offload stream.
+  ---------------------------------------------------------------------------------------
+  p_wire_valid : PROCESS(snk_in_arr, hdr_fields_in_arr)
+  BEGIN
+    FOR i IN 0 TO g_nof_streams-1 LOOP
+      -- default pass on the other snk_in_arr fields as well, especially the sync, bsn and channel can
+      -- be useful to preserve for the packetized output, even though only the sosi.data of the
+      -- packetized output will get transmitted.
+      dp_field_blk_snk_in_arr(i)       <= snk_in_arr(i);
+      -- Prepare packet header as a data block with only one data word, so valid = sop = eop. If
+      -- c_dp_field_blk_snk_data_w > c_dp_field_blk_src_data_w then dp_repack_data in dp_field_blk will
+      -- repack this data word into a multi word header block, else dp_field_blk will pass on the
+      -- dp_field_blk_snk_in_arr as a single word header block.
+      dp_field_blk_snk_in_arr(i).data  <= RESIZE_DP_DATA(hdr_fields_in_arr(i)(field_slv_len(g_hdr_field_arr)-1 DOWNTO 0));
+      dp_field_blk_snk_in_arr(i).valid <= snk_in_arr(i).sop;
+      dp_field_blk_snk_in_arr(i).sop   <= snk_in_arr(i).sop;  -- necessary for single word header block
+      dp_field_blk_snk_in_arr(i).eop   <= snk_in_arr(i).sop;  -- necessary for single word header block
+    END LOOP;
+  END PROCESS;
+
+  gen_dp_field_blk : FOR i IN 0 TO g_nof_streams-1 GENERATE
+
+    -- Both dp_concat inputs must be ready. One of the inputs toggles ready via dp_field_blk.  
+    snk_out_arr(i).ready <= dp_field_blk_snk_out_arr(i).ready AND dp_concat_snk_out_2arr(i)(0).ready;
+    snk_out_arr(i).xon <= src_in_arr(i).xon;
+
+    -- Wire hdr_fields_out_arr 
+    -- MM override bits determine source for each field
+    gen_field_wires: FOR j IN g_hdr_field_arr'RANGE GENERATE
+      hdr_fields_out_arr(i)(field_hi(g_hdr_field_arr, j) DOWNTO field_lo(g_hdr_field_arr, j)) <= mm_fields_slv_out_arr(i)(field_hi(g_hdr_field_arr, j) DOWNTO field_lo(g_hdr_field_arr, j)) 
+                                                                                              WHEN field_override_arr(j) = '1' ELSE 
+                                                                                              hdr_fields_in_arr(i)(field_hi(g_hdr_field_arr, j) DOWNTO field_lo(g_hdr_field_arr, j));
+    END GENERATE;
+
+    ---------------------------------------------------------------------------------------
+    -- mm_fields for MM access to each field
+    ---------------------------------------------------------------------------------------
+    u_mm_fields_slv: ENTITY mm_lib.mm_fields
+    GENERIC MAP(
+      g_field_arr => field_arr_set_mode(g_hdr_field_arr , "RW")
+    )
+    PORT MAP (
+      mm_clk     => mm_clk,
+      mm_rst     => mm_rst,
+  
+      mm_mosi    => reg_hdr_dat_mosi_arr(i),
+      mm_miso    => OPEN, -- Not used
+      
+      slv_clk    => dp_clk,
+      slv_rst    => dp_rst, 
+
+      slv_out    => mm_fields_slv_out_arr(i)(field_slv_len(g_hdr_field_arr)-1 DOWNTO 0)
+    );
+
+
+    -- Create multi-cycle header block from single-cycle wide header SLV
+    u_dp_field_blk : ENTITY work.dp_field_blk
+    GENERIC MAP (
+      g_field_arr      => field_arr_set_mode(g_hdr_field_arr , "RW"),
+      g_field_sel      => g_hdr_field_sel,
+      g_snk_data_w     => c_dp_field_blk_snk_data_w,
+      g_src_data_w     => c_dp_field_blk_src_data_w,
+      g_in_symbol_w    => g_symbol_w,
+      g_out_symbol_w   => g_symbol_w,
+      g_pipeline_ready => g_pipeline_ready
+    )
+    PORT MAP (
+      dp_clk       => dp_clk,
+      dp_rst       => dp_rst,
+
+      mm_clk       => mm_clk,
+      mm_rst       => mm_rst,
+  
+      snk_in       => dp_field_blk_snk_in_arr(i),
+      snk_out      => dp_field_blk_snk_out_arr(i),
+
+      src_out      => dp_field_blk_src_out_arr(i),
+      src_in       => dp_field_blk_src_in_arr(i),
+
+      reg_slv_mosi => reg_hdr_dat_mosi_arr(i),
+      reg_slv_miso => reg_hdr_dat_miso_arr(i)
+    );
+
+    dp_field_blk_src_in_arr(i) <= dp_concat_snk_out_2arr(i)(1);
+
+  END GENERATE;
+
+  -- Prepend the header block to the input block
+  gen_dp_concat : FOR i IN 0 TO g_nof_streams-1 GENERATE
+
+    dp_concat_snk_in_2arr(i)(0) <= snk_in_arr(i);
+    dp_concat_snk_in_2arr(i)(1) <= dp_field_blk_src_out_arr(i);
+
+    u_dp_concat : ENTITY work.dp_concat
+    GENERIC MAP (
+      g_data_w    => g_data_w,
+      g_symbol_w  => g_symbol_w
+    )
+    PORT MAP (
+      rst         => dp_rst,
+      clk         => dp_clk,
+
+      snk_out_arr => dp_concat_snk_out_2arr(i),
+      snk_in_arr  => dp_concat_snk_in_2arr(i),
+
+      src_in      => src_in_arr(i),
+      src_out     => src_out_arr(i)
+    ); 
+  END GENERATE;
+
+  ---------------------------------------------------------------------------------------
+  -- MM control & monitoring
+  ---------------------------------------------------------------------------------------
+  u_common_mem_mux_hdr_dat : ENTITY common_lib.common_mem_mux
+  GENERIC MAP (    
+    g_nof_mosi    => g_nof_streams,
+    g_mult_addr_w => ceil_log2(field_nof_words(g_hdr_field_arr, c_word_w))
+  )
+  PORT MAP (
+    mosi     => reg_hdr_dat_mosi,
+    miso     => reg_hdr_dat_miso,
+    mosi_arr => reg_hdr_dat_mosi_arr,
+    miso_arr => reg_hdr_dat_miso_arr
+  );
+
+END str;
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_offload_tx_v3.vhd b/libraries/base/dp/tb/vhdl/tb_dp_offload_tx_v3.vhd
new file mode 100644
index 0000000000..0856662122
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_dp_offload_tx_v3.vhd
@@ -0,0 +1,436 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2015
+-- 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/>.
+--
+-------------------------------------------------------------------------------
+
+-- Purpose:
+-- . Test bench for dp_offload_tx_v3 and dp_offload_rx
+-- Description:
+--                 u_tx                     u_rx
+--                 ___________________      ___________________ 
+--                |dp_offload_tx_v3|    |dp_offload_rx      |
+-- stimuli_src -->|                   |--->|                   |--> verify_snk
+--                | in            out | |  | in            out |
+--                |___________________| |  |___________________|
+--                                      |
+--                               link_offload_sosi
+-- Usage:                                  
+-- > as 10
+-- > run -all
+--
+
+LIBRARY IEEE, common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_lfsr_sequences_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.common_field_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+USE work.tb_dp_pkg.ALL;
+
+
+ENTITY tb_dp_offload_tx_v3 IS
+  GENERIC (
+    -- general
+    g_flow_control_stimuli   : t_dp_flow_control_enum := e_pulse;  -- always e_active, e_random or e_pulse flow control
+    g_flow_control_verify    : t_dp_flow_control_enum := e_active;  -- always e_active, e_random or e_pulse flow control
+    -- specific
+    g_data_w                 : NATURAL := 64;
+    g_nof_repeat             : NATURAL := 100;
+    g_pkt_len                : NATURAL := 240;
+    g_pkt_gap                : NATURAL := 16
+  );
+END tb_dp_offload_tx_v3;
+
+
+ARCHITECTURE tb OF tb_dp_offload_tx_v3 IS
+
+  CONSTANT c_mm_clk_period : TIME := 1 ns;
+  CONSTANT c_dp_clk_period : TIME := 5 ns;
+
+  -- dp_stream_stimuli
+  CONSTANT c_stimuli_pulse_active     : NATURAL := 3;
+  CONSTANT c_stimuli_pulse_period     : NATURAL := 4;
+
+  -- dp_stream_verify
+  CONSTANT c_verify_pulse_active      : NATURAL := 1;
+  CONSTANT c_verify_pulse_period      : NATURAL := 5;
+
+  CONSTANT c_data_max                 : UNSIGNED(g_data_w-1 DOWNTO 0) := (OTHERS=>'1');
+  CONSTANT c_dsp_max                  : UNSIGNED(g_data_w-1 DOWNTO 0) := (OTHERS=>'1');
+
+  --CONSTANT c_verify_snk_in_cnt_max    : t_dp_sosi_unsigned := c_dp_sosi_unsigned_rst;  -- default 0 is no wrap
+  CONSTANT c_verify_snk_in_cnt_max    : t_dp_sosi_unsigned := TO_DP_SOSI_UNSIGNED('0', '0', '0', '0', c_data_max, c_dsp_max, c_dsp_max, c_unsigned_0, c_unsigned_0, c_unsigned_0, c_unsigned_0);
+  CONSTANT c_verify_snk_in_cnt_gap    : t_dp_sosi_unsigned := c_dp_sosi_unsigned_ones; -- default only accept increment +1
+
+  CONSTANT c_expected_pkt_len         : NATURAL := g_pkt_len;
+  CONSTANT c_sync_period              : NATURAL := 5;
+  CONSTANT c_sync_offset              : NATURAL := 2;
+  
+  CONSTANT c_hdr_len                  : NATURAL := 7;
+  CONSTANT c_wait_last_evt            : NATURAL := 100 + g_nof_repeat * c_hdr_len;
+
+  -----------------------------------------------------------------------------
+  -- Tx offload
+  -----------------------------------------------------------------------------
+  -- From apertif_udp_offload_pkg.vhd:
+  CONSTANT c_udp_offload_nof_hdr_fields : NATURAL := 3+12+4+3; -- 448b; 7 64b words
+  -- Notes: 
+  -- . pre-calculated ip_header_checksum is valid only for UNB0, FN0 targeting IP 10.10.10.10
+  -- . udp_total_length = 176 beamlets * 64b / 8b = 1408B + 14 DP bytes + 8 UDP bytes = 1430B 
+  CONSTANT c_udp_offload_hdr_field_arr : t_common_field_arr(c_udp_offload_nof_hdr_fields-1 DOWNTO 0) := (
+         ( field_name_pad("eth_dst_mac"            ), "RW", 48, field_default(x"001B214368AC") ),
+         ( field_name_pad("eth_src_mac"            ), "RW", 48, field_default(0) ),
+         ( field_name_pad("eth_type"               ), "RW", 16, field_default(x"0800") ),
+         ( field_name_pad("ip_version"             ), "RW",  4, field_default(4) ),
+         ( field_name_pad("ip_header_length"       ), "RW",  4, field_default(5) ),
+         ( field_name_pad("ip_services"            ), "RW",  8, field_default(0) ),
+         ( field_name_pad("ip_total_length"        ), "RW", 16, field_default(1450) ), 
+         ( field_name_pad("ip_identification"      ), "RW", 16, field_default(0) ),
+         ( field_name_pad("ip_flags"               ), "RW",  3, field_default(2) ),
+         ( field_name_pad("ip_fragment_offset"     ), "RW", 13, field_default(0) ),
+         ( field_name_pad("ip_time_to_live"        ), "RW",  8, field_default(127) ),
+         ( field_name_pad("ip_protocol"            ), "RW",  8, field_default(17) ),
+         ( field_name_pad("ip_header_checksum"     ), "RW", 16, field_default(29928) ),
+         ( field_name_pad("ip_src_addr"            ), "RW", 32, field_default(x"C0A80009") ),
+         ( field_name_pad("ip_dst_addr"            ), "RW", 32, field_default(x"C0A80001") ),
+         ( field_name_pad("udp_src_port"           ), "RW", 16, field_default(0) ), 
+         ( field_name_pad("udp_dst_port"           ), "RW", 16, field_default(0) ), 
+         ( field_name_pad("udp_total_length"       ), "RW", 16, field_default(1430) ),
+         ( field_name_pad("udp_checksum"           ), "RW", 16, field_default(0) ),
+         ( field_name_pad("dp_reserved"            ), "RW", 47, field_default(0) ),
+         ( field_name_pad("dp_sync"                ), "RW",  1, field_default(0) ),
+         ( field_name_pad("dp_bsn"                 ), "RW", 64, field_default(0) ) );
+
+  -- From apertif_unb1_fn_beamformer_udp_offload.vhd:
+  -- Override ('1') only the Ethernet fields so we can use MM defaults there.
+  CONSTANT c_hdr_field_ovr_init : STD_LOGIC_VECTOR(c_udp_offload_nof_hdr_fields-1 DOWNTO 0) := "101"&"111111111111"&"1111"&"100";
+
+  CONSTANT c_NODE_ID                    : STD_LOGIC_VECTOR(7 DOWNTO 0) := TO_UVEC(0, 8);
+
+  SIGNAL id_backplane                   : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0);
+  SIGNAL id_chip                        : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0);
+
+  SIGNAL dp_fifo_sc_src_in              : t_dp_siso := c_dp_siso_rdy;
+  SIGNAL dp_fifo_sc_src_out             : t_dp_sosi;
+
+  SIGNAL dp_offload_tx_snk_in_arr       : t_dp_sosi_arr(0 DOWNTO 0);
+  SIGNAL dp_offload_tx_snk_out_arr      : t_dp_siso_arr(0 DOWNTO 0);
+  
+  SIGNAL tx_hdr_fields_in_arr           : t_slv_1024_arr(0 DOWNTO 0);
+  SIGNAL tx_hdr_fields_out_arr          : t_slv_1024_arr(0 DOWNTO 0);
+  
+  SIGNAL reg_dp_offload_tx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL reg_dp_offload_tx_hdr_dat_miso : t_mem_miso;
+
+  -----------------------------------------------------------------------------
+  -- Link
+  -----------------------------------------------------------------------------
+  
+  SIGNAL tx_offload_sosi_arr       : t_dp_sosi_arr(0 DOWNTO 0);
+  SIGNAL tx_offload_siso_arr       : t_dp_siso_arr(0 DOWNTO 0);
+
+  SIGNAL link_offload_sosi_arr     : t_dp_sosi_arr(0 DOWNTO 0);
+  SIGNAL link_offload_siso_arr     : t_dp_siso_arr(0 DOWNTO 0);
+
+  -----------------------------------------------------------------------------
+  -- Rx offload
+  -----------------------------------------------------------------------------
+  SIGNAL dp_offload_rx_src_out_arr      : t_dp_sosi_arr(0 DOWNTO 0);
+  SIGNAL dp_offload_rx_src_in_arr       : t_dp_siso_arr(0 DOWNTO 0);
+  
+  SIGNAL rx_hdr_fields_out_arr          : t_slv_1024_arr(0 DOWNTO 0);
+  SIGNAL rx_hdr_fields_raw_arr          : t_slv_1024_arr(0 DOWNTO 0);
+  
+  SIGNAL reg_dp_offload_rx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL reg_dp_offload_rx_hdr_dat_miso : t_mem_miso;
+
+  -----------------------------------------------------------------------------
+  -- Test
+  -----------------------------------------------------------------------------
+  SIGNAL mm_clk                     : STD_LOGIC := '1';
+  SIGNAL mm_rst                     : STD_LOGIC := '1';
+  SIGNAL dp_clk                     : STD_LOGIC := '1';
+  SIGNAL dp_rst                     : STD_LOGIC := '1';
+  SIGNAL tb_end                     : STD_LOGIC := '0';
+  
+  SIGNAL stimuli_src_in             : t_dp_siso := c_dp_siso_rdy;
+  SIGNAL stimuli_src_out            : t_dp_sosi;
+  SIGNAL stimuli_src_out_data       : STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+
+  SIGNAL verify_snk_in_enable       : t_dp_sosi_sl := c_dp_sosi_sl_rst;
+  SIGNAL last_snk_in                : t_dp_sosi;
+  SIGNAL last_snk_in_evt            : STD_LOGIC;
+  SIGNAL verify_last_snk_in_evt     : t_dp_sosi_sl := c_dp_sosi_sl_rst;
+
+  SIGNAL verify_snk_out             : t_dp_siso := c_dp_siso_rdy;
+  SIGNAL verify_snk_in              : t_dp_sosi;
+  SIGNAL verify_snk_in_data         : STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+
+BEGIN
+
+  ------------------------------------------------------------------------------
+  -- Clock & reset
+  ------------------------------------------------------------------------------
+  mm_clk <= (NOT mm_clk) OR tb_end AFTER c_mm_clk_period/2;
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
+  dp_clk <= (NOT dp_clk) OR tb_end AFTER c_dp_clk_period/2;
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
+
+  ------------------------------------------------------------------------------
+  -- DATA GENERATION
+  ------------------------------------------------------------------------------
+
+  u_dp_stream_stimuli : ENTITY work.dp_stream_stimuli
+  GENERIC MAP (
+    g_instance_nr    => 0,                        -- only one stream so choose index 0
+    -- flow control
+    g_random_w       => 15,                       -- use different random width for stimuli and for verify to have different random sequences
+    g_pulse_active   => c_stimuli_pulse_active,
+    g_pulse_period   => c_stimuli_pulse_period,
+    g_flow_control   => g_flow_control_stimuli,   -- always active, random or pulse flow control
+    -- initializations
+    g_sync_period    => c_sync_period,
+    g_sync_offset    => c_sync_offset,
+    -- specific
+    g_in_dat_w       => g_data_w,
+    g_nof_repeat     => g_nof_repeat,
+    g_pkt_len        => g_pkt_len,
+    g_pkt_gap        => g_pkt_gap,
+    g_wait_last_evt  => c_wait_last_evt
+  )
+  PORT MAP (
+    rst                 => dp_rst,
+    clk                 => dp_clk,
+
+    -- Generate stimuli
+    src_in              => stimuli_src_in,
+    src_out             => stimuli_src_out,
+
+    -- End of stimuli
+    last_snk_in         => last_snk_in,      -- expected verify_snk_in after end of stimuli
+    last_snk_in_evt     => last_snk_in_evt,  -- trigger verify to verify the last_snk_in
+    tb_end              => tb_end            -- signal end of tb as far as this dp_stream_stimuli is concerned
+  );
+
+
+  ------------------------------------------------------------------------------
+  -- DATA VERIFICATION
+  ------------------------------------------------------------------------------
+
+  -- Select fields that need to be verified
+  -- . during the test
+  verify_snk_in_enable.sync    <= '1';
+  verify_snk_in_enable.bsn     <= '1';
+  verify_snk_in_enable.data    <= '1';
+  verify_snk_in_enable.re      <= '0';
+  verify_snk_in_enable.im      <= '0';
+  verify_snk_in_enable.valid   <= '1';
+  verify_snk_in_enable.sop     <= '1';
+  verify_snk_in_enable.eop     <= '1';
+  verify_snk_in_enable.empty   <= '0';
+  verify_snk_in_enable.channel <= '0';
+  verify_snk_in_enable.err     <= '0';
+
+  -- . after the test
+  verify_last_snk_in_evt.sync    <= last_snk_in_evt;
+  verify_last_snk_in_evt.bsn     <= last_snk_in_evt;   -- thanks to using rx_hdr_fields_raw_arr for bsn field
+  verify_last_snk_in_evt.data    <= last_snk_in_evt;
+  verify_last_snk_in_evt.re      <= '0';
+  verify_last_snk_in_evt.im      <= '0';
+  verify_last_snk_in_evt.valid   <= last_snk_in_evt;
+  verify_last_snk_in_evt.sop     <= last_snk_in_evt;
+  verify_last_snk_in_evt.eop     <= last_snk_in_evt;
+  verify_last_snk_in_evt.empty   <= '0';
+  verify_last_snk_in_evt.channel <= '0';
+  verify_last_snk_in_evt.err     <= '0';
+
+  u_dp_stream_verify : ENTITY work.dp_stream_verify
+  GENERIC MAP (
+    g_instance_nr    => 0,                        -- only one stream so choose index 0
+    -- flow control
+    g_random_w       => 14,                       -- use different random width for stimuli and for verify to have different random sequences
+    g_pulse_active   => c_verify_pulse_active,
+    g_pulse_period   => c_verify_pulse_period,
+    g_flow_control   => g_flow_control_verify,    -- always active, random or pulse flow control
+    -- initializations
+    g_sync_period    => c_sync_period,
+    g_sync_offset    => c_sync_offset,
+    g_snk_in_cnt_max => c_verify_snk_in_cnt_max,
+    g_snk_in_cnt_gap => c_verify_snk_in_cnt_gap,
+    -- specific
+    g_in_dat_w       => g_data_w,
+    g_pkt_len        => c_expected_pkt_len
+  )
+  PORT MAP (
+    rst                        => dp_rst,
+    clk                        => dp_clk,
+
+    -- Verify data
+    snk_out                    => verify_snk_out,
+    snk_in                     => verify_snk_in,
+
+    -- During stimuli
+    verify_snk_in_enable       => verify_snk_in_enable,  -- enable verify to verify that the verify_snk_in fields are incrementing
+
+    -- End of stimuli
+    expected_snk_in            => last_snk_in,            -- expected verify_snk_in after end of stimuli
+    verify_expected_snk_in_evt => verify_last_snk_in_evt  -- trigger verify to verify the last_snk_in
+  );
+
+  ------------------------------------------------------------------------------
+  -- DUT offload Tx
+  ------------------------------------------------------------------------------
+  stimuli_src_in <= c_dp_siso_rdy;
+  
+  -- Use FIFO to mimic apertif_unb1_fn_beamformer_udp_offload.vhd, without FIFO dp_stream_stimuli
+  -- would handle the back pressure
+  u_dp_fifo_sc : ENTITY work.dp_fifo_sc
+  GENERIC MAP (
+    g_data_w         => g_data_w,
+    g_bsn_w          => 64,
+    g_use_sync       => TRUE,
+    g_use_bsn        => TRUE,
+    g_fifo_size      => 1024
+  )
+  PORT MAP (
+    rst         => dp_rst,
+    clk         => dp_clk,
+
+    snk_out     => OPEN,     -- stimuli_src_in
+    snk_in      => stimuli_src_out,
+
+    src_in      => dp_fifo_sc_src_in,
+    src_out     => dp_fifo_sc_src_out
+  );
+  
+  dp_offload_tx_snk_in_arr(0) <= dp_fifo_sc_src_out;
+  dp_fifo_sc_src_in           <= dp_offload_tx_snk_out_arr(0);
+
+  -- Extract the chip and backplane numbers from c_NODE_ID
+  id_backplane <= RESIZE_UVEC(c_NODE_ID(7 DOWNTO 3), c_byte_w);
+  id_chip      <= RESIZE_UVEC(c_NODE_ID(2 DOWNTO 0), c_byte_w);
+
+  -- Wire the hardwired header fields to DP signals and c_NODE_ID
+  tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "eth_src_mac" ) DOWNTO field_lo(c_udp_offload_hdr_field_arr, "eth_src_mac"     )) <= x"00228608" & id_backplane & id_chip;
+  tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "udp_src_port") DOWNTO field_lo(c_udp_offload_hdr_field_arr, "udp_src_port"    )) <= x"D0" & c_NODE_ID;
+  tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "udp_dst_port") DOWNTO field_lo(c_udp_offload_hdr_field_arr, "udp_dst_port"    )) <= x"D0" & c_NODE_ID;
+  tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "ip_src_addr" ) DOWNTO field_lo(c_udp_offload_hdr_field_arr, "ip_src_addr"     )) <= x"0A63" & id_backplane & INCR_UVEC(id_chip, 1);
+                                                                                                               
+  tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_sync"     ) DOWNTO field_lo(c_udp_offload_hdr_field_arr, "dp_sync"         )) <= slv(dp_offload_tx_snk_in_arr(0).sync);
+  tx_hdr_fields_in_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_bsn"      ) DOWNTO field_lo(c_udp_offload_hdr_field_arr, "dp_bsn"          )) <=     dp_offload_tx_snk_in_arr(0).bsn(63 DOWNTO 0); 
+
+  u_tx : ENTITY work.dp_offload_tx_v3
+  GENERIC MAP (
+    g_nof_streams    => 1,
+    g_data_w         => g_data_w,
+    g_symbol_w       => g_data_w,
+    g_hdr_field_arr  => c_udp_offload_hdr_field_arr,
+    g_hdr_field_sel  => c_hdr_field_ovr_init
+  ) 
+  PORT MAP (
+    mm_rst                => mm_rst,
+    mm_clk                => mm_clk,
+    
+    dp_rst                => dp_rst,
+    dp_clk                => dp_clk,
+
+    reg_hdr_dat_mosi      => reg_dp_offload_tx_hdr_dat_mosi,
+    reg_hdr_dat_miso      => reg_dp_offload_tx_hdr_dat_miso,
+
+    snk_in_arr            => dp_offload_tx_snk_in_arr,
+    snk_out_arr           => dp_offload_tx_snk_out_arr,
+
+    src_out_arr           => tx_offload_sosi_arr,
+    src_in_arr            => tx_offload_siso_arr,
+
+    hdr_fields_in_arr     => tx_hdr_fields_in_arr,
+    hdr_fields_out_arr    => tx_hdr_fields_out_arr
+  );
+
+  ------------------------------------------------------------------------------
+  -- Link
+  ------------------------------------------------------------------------------
+
+  p_link_offload : PROCESS(tx_offload_sosi_arr, link_offload_siso_arr)
+  BEGIN
+    -- Model active packet fields of tr_10GbE Rx sosi output interface
+    link_offload_sosi_arr(0)       <= c_dp_sosi_rst;
+    link_offload_sosi_arr(0).data  <= tx_offload_sosi_arr(0).data;
+    link_offload_sosi_arr(0).empty <= tx_offload_sosi_arr(0).empty;
+    link_offload_sosi_arr(0).valid <= tx_offload_sosi_arr(0).valid;
+    link_offload_sosi_arr(0).sop   <= tx_offload_sosi_arr(0).sop;
+    link_offload_sosi_arr(0).eop   <= tx_offload_sosi_arr(0).eop;
+    
+    tx_offload_siso_arr <= (OTHERS=>c_dp_siso_rdy);
+  END PROCESS;
+  
+  ------------------------------------------------------------------------------
+  -- DUT offload Rx
+  ------------------------------------------------------------------------------
+
+  u_rx : ENTITY work.dp_offload_rx
+  GENERIC MAP (
+    g_nof_streams         => 1,
+    g_data_w              => g_data_w,
+    g_hdr_field_arr       => c_udp_offload_hdr_field_arr,
+    g_remove_crc          => FALSE,
+    g_crc_nof_words       => 0 
+  )
+  PORT MAP (
+    mm_rst                => mm_rst,
+    mm_clk                => mm_clk,
+    
+    dp_rst                => dp_rst,
+    dp_clk                => dp_clk,
+  
+    reg_hdr_dat_mosi      => reg_dp_offload_rx_hdr_dat_mosi,
+    reg_hdr_dat_miso      => reg_dp_offload_rx_hdr_dat_miso,
+  
+    snk_in_arr            => link_offload_sosi_arr,
+    snk_out_arr           => link_offload_siso_arr,
+               
+    src_out_arr           => dp_offload_rx_src_out_arr,
+    src_in_arr            => dp_offload_rx_src_in_arr,
+  
+    hdr_fields_out_arr    => rx_hdr_fields_out_arr,
+    hdr_fields_raw_arr    => rx_hdr_fields_raw_arr
+  );
+  
+  p_restore_sync_bsn : PROCESS(dp_offload_rx_src_out_arr, rx_hdr_fields_out_arr)
+  BEGIN
+    verify_snk_in      <= dp_offload_rx_src_out_arr(0);
+    verify_snk_in.sync <=          sl(rx_hdr_fields_out_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_sync") DOWNTO field_lo(c_udp_offload_hdr_field_arr, "dp_sync" )));
+    verify_snk_in.bsn  <= RESIZE_UVEC(rx_hdr_fields_raw_arr(0)(field_hi(c_udp_offload_hdr_field_arr, "dp_bsn" ) DOWNTO field_lo(c_udp_offload_hdr_field_arr, "dp_bsn"  )), c_dp_stream_bsn_w);
+  END PROCESS;
+
+  dp_offload_rx_src_in_arr    <= (OTHERS=>c_dp_siso_rdy);
+  dp_offload_rx_src_in_arr(0) <= verify_snk_out;
+  
+  ------------------------------------------------------------------------------
+  -- Auxiliary
+  ------------------------------------------------------------------------------
+
+  -- Map to slv to ease monitoring in wave window
+  stimuli_src_out_data <= stimuli_src_out.data(g_data_w-1 DOWNTO 0);
+  verify_snk_in_data   <= verify_snk_in.data(g_data_w-1 DOWNTO 0);
+
+END tb;
-- 
GitLab