From 3da7b65b7ff70a6da911618e580841bc4de23da9 Mon Sep 17 00:00:00 2001
From: Eric Kooistra <kooistra@astron.nl>
Date: Tue, 3 Oct 2023 17:13:23 +0200
Subject: [PATCH] Verify beamlet header for multiple destinations.

---
 .../sdp/tb/vhdl/tb_sdp_beamformer_output.vhd  | 304 ++++++++++++------
 .../tb/vhdl/tb_tb_sdp_beamformer_output.vhd   |   4 +
 2 files changed, 214 insertions(+), 94 deletions(-)

diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_beamformer_output.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_beamformer_output.vhd
index 8f27b9b8b2..8516ea9ef8 100644
--- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_beamformer_output.vhd
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_beamformer_output.vhd
@@ -38,6 +38,7 @@ use common_lib.tb_common_mem_pkg.all;
 use common_lib.common_network_layers_pkg.all;
 use dp_lib.dp_stream_pkg.all;
 use work.sdp_pkg.all;
+use work.sdp_bdo_pkg.all;
 use work.tb_sdp_pkg.all;
 
 entity tb_sdp_beamformer_output is
@@ -68,14 +69,23 @@ architecture tb of tb_sdp_beamformer_output is
   constant c_gn_id_slv        : std_logic_vector(c_sdp_W_gn_id - 1 downto 0) :=
                                   to_uvec(c_gn_id, c_sdp_W_gn_id);
   constant c_id               : std_logic_vector(7 downto 0) := to_uvec(c_gn_id, 8);
-  constant c_cep_eth_src_mac  : std_logic_vector(47 downto 0) := c_sdp_cep_eth_src_mac_47_16 & func_sdp_gn_index_to_mac_15_0(c_gn_id);
-  constant c_cep_ip_src_addr  : std_logic_vector(31 downto 0) := c_sdp_cep_ip_src_addr_31_16 & func_sdp_gn_index_to_ip_15_0(c_gn_id);
+  constant c_cep_eth_src_mac  : std_logic_vector(47 downto 0) := c_sdp_cep_eth_src_mac_47_16 &
+                                                                 func_sdp_gn_index_to_mac_15_0(c_gn_id);
+  constant c_cep_ip_src_addr  : std_logic_vector(31 downto 0) := c_sdp_cep_ip_src_addr_31_16 &
+                                                                 func_sdp_gn_index_to_ip_15_0(c_gn_id);
   constant c_cep_udp_src_port : std_logic_vector(15 downto 0) := c_sdp_cep_udp_src_port_15_8 & c_id;
 
+  constant c_reorder_nof_blocks_arr : t_natural_arr(1 to g_nof_destinations_max) :=
+             func_sdp_bdo_reorder_nof_blocks_look_up_table(g_nof_destinations_max);
+
+  constant c_nof_beamlets_per_block_first_destinations_arr : t_natural_arr(1 to g_nof_destinations_max) :=
+             func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table(g_nof_destinations_max);
+  constant c_nof_beamlets_per_block : natural := c_nof_beamlets_per_block_first_destinations_arr(g_nof_destinations);
+
   -- Checksum value obtained from rx_sdp_cep_header.ip.header_checksum in wave window
-  constant c_exp_ip_header_checksum : natural := 16#5BDB#;
-  constant c_exp_payload_error      : std_logic := '0';
-  constant c_exp_beamlet_index      : natural := g_beamset_id * c_sdp_S_sub_bf;
+  constant c_exp_ip_header_checksum  : natural := 16#5BDB#;
+  constant c_exp_payload_error       : std_logic := '0';
+  constant c_exp_beamlet_index       : natural := g_beamset_id * c_sdp_S_sub_bf;
 
   constant c_exp_sdp_info : t_sdp_info := (to_uvec(7, 6),  -- antenna_field_index
                                            to_uvec(601, 10),  -- station_id
@@ -88,6 +98,7 @@ architecture tb of tb_sdp_beamformer_output is
                                            x"1400"  -- block_period = 5120
                                           );
 
+  signal mm_init      : std_logic := '1';
   signal tb_end       : std_logic := '0';
   signal dp_clk       : std_logic := '1';
   signal dp_rst       : std_logic;
@@ -116,14 +127,24 @@ architecture tb of tb_sdp_beamformer_output is
   signal rx_hdr_fields_out   : std_logic_vector(1023 downto 0);
   signal rx_hdr_fields_raw   : std_logic_vector(1023 downto 0) := (others => '0');
   signal rx_beamlet_header   : t_sdp_cep_header;
-  signal exp_beamlet_header  : t_sdp_cep_header;
-  signal exp_dp_bsn          : natural;
+
+  signal exp_beamlet_header         : t_sdp_cep_header;
+  signal exp_beamlet_index          : natural;
+  signal exp_nof_blocks_per_packet  : natural;
+  signal exp_nof_beamlets_per_block : natural;
+  signal exp_dp_bsn                 : natural;
+
+  signal rx_offload_sosi     : t_dp_sosi := c_dp_sosi_rst;
+  signal rx_offload_sop_cnt  : natural := 0;
+  signal rx_DI               : natural := 0;
+
+  -- rx merge
+  signal rx_merge_sosi                  : t_dp_sosi := c_dp_sosi_rst;
+  signal rx_merge_sop_cnt    : natural := 0;
 
   -- Beamlets packets data
   signal rx_beamlet_data     : std_logic_vector(c_longword_w - 1 downto 0);  -- 64 bit
   signal rx_beamlet_sosi     : t_dp_sosi := c_dp_sosi_rst;
-  signal rx_beamlet_sop_cnt  : natural := 0;
-  signal rx_beamlet_eop_cnt  : natural := 0;
 
   -- [0 : 3] =  X, Y, X, Y
   signal rx_beamlet_arr_re   : t_sdp_beamlet_part_arr;
@@ -149,20 +170,36 @@ begin
   mm_clk <= (not mm_clk) or tb_end after c_mm_clk_period / 2;
 
   p_mm : process
+    variable v_offset : natural;
   begin
     proc_common_wait_until_low(dp_clk, mm_rst);
     proc_common_wait_some_cycles(mm_clk, 10);
 
     ----------------------------------------------------------------------------
-    -- BDO nof destinations
+    -- BDO multiple destinations info in sdp_bdo_destinations_reg
     ----------------------------------------------------------------------------
+    -- . Set nof_destinations = g_nof_destinations
     proc_mem_mm_bus_wr(128, g_nof_destinations, mm_clk, reg_destinations_cipo, reg_destinations_copi);
 
+    -- . Use same destination MAC/IP/UPD for all destinations, to ease rx_beamlet_header verification
+    --   and to have same c_exp_ip_header_checksum value for all g_nof_destinations.
+    for DI in 0 to g_nof_destinations - 1 loop
+      proc_mem_mm_bus_wr(DI * 2 + 1, to_uint(c_sdp_cep_eth_dst_mac(47 downto 32)), mm_clk, reg_destinations_cipo, reg_destinations_copi);
+      proc_mem_mm_bus_wr(DI * 2, to_sint(c_sdp_cep_eth_dst_mac(31 downto 0)), mm_clk, reg_destinations_cipo, reg_destinations_copi);
+    end loop;
+    v_offset := c_sdp_bdo_mm_nof_destinations_max * 2;
+    for DI in 0 to g_nof_destinations - 1 loop
+      proc_mem_mm_bus_wr(v_offset + DI, to_sint(c_sdp_cep_ip_dst_addr), mm_clk, reg_destinations_cipo, reg_destinations_copi);
+    end loop;
+    v_offset := c_sdp_bdo_mm_nof_destinations_max * 3;
+    for DI in 0 to g_nof_destinations - 1 loop
+      proc_mem_mm_bus_wr(v_offset + DI, to_uint(c_sdp_cep_udp_dst_port), mm_clk, reg_destinations_cipo, reg_destinations_copi);
+    end loop;
+
     ----------------------------------------------------------------------------
-    -- BDO header fields
+    -- BDO header src and single destination fields in dp_offload_tx_v3
     ----------------------------------------------------------------------------
-    -- . Use sim default dst and src MAC, IP, UDP port from sdp_pkg.vhd and
-    --   based on c_gn_id
+    -- . Use sim default dst and src MAC, IP, UDP port from sdp_pkg.vhd and based on c_gn_id
     -- . use signed to fit 32 b in integer
     proc_mem_mm_bus_wr(39, to_uint(c_cep_eth_src_mac(47 downto 32)), mm_clk, hdr_dat_cipo, hdr_dat_copi);
     proc_mem_mm_bus_wr(38, to_sint(c_cep_eth_src_mac(31 downto 0)), mm_clk, hdr_dat_cipo, hdr_dat_copi);
@@ -177,6 +214,9 @@ begin
     -- Enable beamlet output (dp_xonoff)
     ----------------------------------------------------------------------------
     proc_mem_mm_bus_wr(0, 1, mm_clk, reg_dp_xonoff_cipo, reg_dp_xonoff_copi);
+
+    proc_common_wait_cross_clock_domain_latency(c_mm_clk_period, c_dp_clk_period, c_common_cross_clock_domain_latency * 2);
+    mm_init <= '0';
     wait;
   end process;
 
@@ -201,7 +241,7 @@ begin
     g_wait_last_evt  => 100
   )
   port map (
-    rst               => dp_rst,
+    rst               => mm_init,
     clk               => dp_clk,
 
     -- Generate stimuli
@@ -275,7 +315,7 @@ begin
     snk_in_arr(0)         => bdo_sosi,
     snk_out_arr(0)        => bdo_siso,
 
-    src_out_arr(0)        => rx_beamlet_sosi,
+    src_out_arr(0)        => rx_offload_sosi,
 
     hdr_fields_out_arr(0) => rx_hdr_fields_out,
     hdr_fields_raw_arr(0) => rx_hdr_fields_raw
@@ -285,101 +325,177 @@ begin
   -- Beamlet offload packet header
   -----------------------------------------------------------------------------
 
-  -- Counters to time expected cep_header fields per offload packet
-  p_test_counters : process(dp_clk)
+  -- Counters to time expected cep_header fields per rx_offload_sosi packet
+  p_rx_counters : process(dp_clk)
   begin
     if rising_edge(dp_clk) then
-      -- Count rx_beamlet_sosi packets
-      if rx_beamlet_sosi.sop = '1' then
-        rx_beamlet_sop_cnt <= rx_beamlet_sop_cnt + 1;  -- early count
+      -- Count rx_offload_sosi packets, for fields per destination
+      if rx_offload_sosi.sop = '1' then
+        rx_offload_sop_cnt <= rx_offload_sop_cnt + 1;
       end if;
-      if rx_beamlet_sosi.eop = '1' then
-        rx_beamlet_eop_cnt <= rx_beamlet_eop_cnt + 1;  -- after count
+      -- Count rx_merge_sosi packets, for BSN of all destinations
+      if rx_merge_sosi.sop = '1' then
+        rx_merge_sop_cnt <= rx_merge_sop_cnt + 1;
       end if;
     end if;
   end process;
 
-  -- Prepare exp_beamlet_header before rx_beamlet_sosi.eop, so that
-  -- p_verify_beamlet_header can verify it at rx_beamlet_sosi.eop.
+  -- Destination index (DI)
+  rx_DI <= rx_offload_sop_cnt mod g_nof_destinations;
+
+  -- Prepare exp_beamlet_header before rx_offload_sosi.eop, so that
+  -- p_verify_beamlet_header can verify it at rx_offload_sosi.eop.
   exp_beamlet_header <= func_sdp_compose_cep_header(c_exp_ip_header_checksum,
                                                     c_exp_sdp_info,
                                                     c_gn_id,
                                                     c_exp_payload_error,
                                                     c_exp_beamlet_scale,
-                                                    c_exp_beamlet_index,
+                                                    exp_beamlet_index,
+                                                    exp_nof_blocks_per_packet,
+                                                    exp_nof_beamlets_per_block,
                                                     exp_dp_bsn);
 
   rx_beamlet_header <= func_sdp_map_cep_header(rx_hdr_fields_raw);
 
-  p_verify_beamlet_header : process
-    variable v_bool    : boolean;
-  begin
-    wait until rising_edge(dp_clk);
-    -- Prepare exp_sdp_cep_header at sop, so that it can be verified at eop
-    if rx_beamlet_sosi.sop = '1' then
-      -- Expected BSN increments by c_sdp_cep_nof_blocks_per_packet = 4 blocks
-      -- per packet
-      exp_dp_bsn <= c_init_bsn + rx_beamlet_sop_cnt * c_sdp_cep_nof_blocks_per_packet;
-    end if;
+  gen_verify_one_destination : if g_nof_destinations_max = 1 generate
+    -- Wires
+    rx_merge_sosi <= rx_offload_sosi;
+    rx_beamlet_sosi <= rx_offload_sosi;
+
+    ---------------------------------------------------------------------------
+    -- Verify beamlet header
+    ---------------------------------------------------------------------------
+    exp_beamlet_index <= c_exp_beamlet_index;
+    exp_nof_blocks_per_packet <= c_sdp_cep_nof_blocks_per_packet;
+    exp_nof_beamlets_per_block <= c_sdp_cep_nof_beamlets_per_block;
+
+    p_verify_beamlet_header : process
+      variable v_bool    : boolean;
+    begin
+      wait until rising_edge(dp_clk);
+      -- Prepare exp_sdp_cep_header at sop, so that it can be verified at eop
+      if rx_offload_sosi.sop = '1' then
+        -- Expected BSN increments by c_sdp_cep_nof_blocks_per_packet = 4 blocks per packet
+        exp_dp_bsn <= c_init_bsn + rx_offload_sop_cnt * c_sdp_cep_nof_blocks_per_packet;
+      end if;
 
-    -- Verify header at eop
-    if rx_beamlet_sosi.eop = '1' then
-      v_bool := func_sdp_verify_cep_header(rx_beamlet_header, exp_beamlet_header);
-    end if;
-  end process;
+      -- Verify header at eop
+      if rx_offload_sosi.eop = '1' then
+        v_bool := func_sdp_verify_cep_header(rx_beamlet_header, exp_beamlet_header);
+      end if;
+    end process;
+
+    -----------------------------------------------------------------------------
+    -- Verify beamlet data
+    -----------------------------------------------------------------------------
+    -- To view the 64 bit 10GbE offload data more easily in the Wave window
+    rx_beamlet_data <= rx_beamlet_sosi.data(c_longword_w - 1 downto 0);
+
+    proc_sdp_rx_beamlet_octets(dp_clk,
+                               rx_beamlet_sosi,
+                               rx_beamlet_cnt,
+                               rx_beamlet_valid,
+                               rx_beamlet_arr_re,
+                               rx_beamlet_arr_im,
+                               rx_packet_list_re,
+                               rx_packet_list_im);
+
+    p_verify_rx_beamlet_list : process
+      -- Nof complex (= nof re = nof im = c_N) values in t_sdp_beamlet_packet_list
+      constant c_N : natural := c_sdp_cep_nof_beamlets_per_packet * c_sdp_N_pol_bf;
+      variable v_re : natural;
+      variable v_im : natural;
+    begin
+      -- Wait until end of a beamlet packet
+      -- . use at least one wait statement in process to avoid Modelsim warning: (vcom-1090)
+      wait until rising_edge(dp_clk);
+      proc_common_wait_until_hi_lo(dp_clk, rx_beamlet_sosi.eop);
+      if g_use_transpose then
+        -- Undo the beamlet output transpose, to have original beamlet order
+        rx_beamlet_list_re <= func_sdp_undo_transpose_beamlet_packet(rx_packet_list_re);
+        rx_beamlet_list_im <= func_sdp_undo_transpose_beamlet_packet(rx_packet_list_im);
+      else
+        -- Copy identity beamlet output order
+        rx_beamlet_list_re <= rx_packet_list_re;
+        rx_beamlet_list_im <= rx_packet_list_im;
+      end if;
+      rx_beamlet_list_val <= '1';
+
+      -- Wait until rx_beamlet_list is valid
+      wait until rising_edge(dp_clk);
+      rx_beamlet_list_val <= '0';
+      -- Verify rx_beamlet_list
+      -- . get last values from previous block
+      v_re := prev_re;
+      v_im := prev_im;
+      for vI in 0 to c_N - 1 loop
+        -- Verify incrementing beamlets
+        v_re := (v_re + 1) mod c_beamlet_mod;
+        v_im := (v_im + 1) mod c_beamlet_mod;
+        assert to_uint(rx_beamlet_list_re(vI)) = v_re report "Wrong re_beamlet." severity error;
+        assert to_uint(rx_beamlet_list_im(vI)) = v_im report "Wrong im_beamlet." severity error;
+      end loop;
+      -- . hold last values for next block
+      prev_re <= v_re;
+      prev_im <= v_im;
+    end process;
+  end generate;
+
+  gen_verify_multi_destinations : if g_nof_destinations_max > 1 generate
+    -----------------------------------------------------------------------------
+    -- Merge rx offload packet data
+    -----------------------------------------------------------------------------
+    -- Merge g_nof_destinations rx_offload_sosi packets into one rx_merge_sosi
+    -- packet to:
+    -- - determine same expected BSN for all g_nof_destinations,
+    -- - have all beamlet data for all beamlet indices in one packet.
+    u_dp_packet_merge : entity dp_lib.dp_packet_merge
+      generic map(
+        g_use_ready     => false,  -- no flow control
+        g_nof_pkt       => g_nof_destinations,
+        g_bsn_increment => 1
+      )
+      port map(
+        rst     => dp_rst,
+        clk     => dp_clk,
+
+        snk_in  => rx_offload_sosi,
+        src_out => rx_merge_sosi
+      );
+
+    ---------------------------------------------------------------------------
+    -- Verify beamlet header
+    ---------------------------------------------------------------------------
+    exp_nof_blocks_per_packet <= c_reorder_nof_blocks_arr(g_nof_destinations_max);
+
+    p_verify_beamlet_header : process
+      variable v_nof_beamlets   : natural;
+      variable v_bool           : boolean;
+    begin
+      wait until rising_edge(dp_clk);
+      -- Prepare exp_sdp_cep_header, so that it can be verified at rx_offload_sosi.eop
+      if rx_offload_sosi.sop = '1' then
+        -- Default expect nof_beamlets_per_block for first destinations
+        exp_nof_beamlets_per_block <= c_nof_beamlets_per_block;
+        if rx_DI = g_nof_destinations - 1 then
+          -- Remaining nof_beamlets_per_block for last destination
+          exp_nof_beamlets_per_block <= c_sdp_S_sub_bf - rx_DI * exp_nof_beamlets_per_block;
+        end if;
+
+        -- Expected beamlet index increments by c_nof_beamlets_per_block per destination index
+        exp_beamlet_index <= rx_DI * c_nof_beamlets_per_block;
+      end if;
 
-  -----------------------------------------------------------------------------
-  -- Beamlet offload packet data
-  -----------------------------------------------------------------------------
-  -- To view the 64 bit 10GbE offload data more easily in the Wave window
-  rx_beamlet_data <= rx_beamlet_sosi.data(c_longword_w - 1 downto 0);
-
-  proc_sdp_rx_beamlet_octets(dp_clk,
-                             rx_beamlet_sosi,
-                             rx_beamlet_cnt,
-                             rx_beamlet_valid,
-                             rx_beamlet_arr_re,
-                             rx_beamlet_arr_im,
-                             rx_packet_list_re,
-                             rx_packet_list_im);
-
-  p_verify_rx_beamlet_list : process
-    -- Nof complex (= nof re = nof im = c_N) values in t_sdp_beamlet_packet_list
-    constant c_N : natural := c_sdp_cep_nof_beamlets_per_packet * c_sdp_N_pol_bf;
-    variable v_re : natural;
-    variable v_im : natural;
-  begin
-    -- Wait until end of a beamlet packet
-    -- . use at least one wait statement in process to avoid Modelsim warning: (vcom-1090)
-    wait until rising_edge(dp_clk);
-    proc_common_wait_until_hi_lo(dp_clk, rx_beamlet_sosi.eop);
-    if g_use_transpose then
-      -- Undo the beamlet output transpose, to have original beamlet order
-      rx_beamlet_list_re <= func_sdp_undo_transpose_beamlet_packet(rx_packet_list_re);
-      rx_beamlet_list_im <= func_sdp_undo_transpose_beamlet_packet(rx_packet_list_im);
-    else
-      -- Copy identity beamlet output order
-      rx_beamlet_list_re <= rx_packet_list_re;
-      rx_beamlet_list_im <= rx_packet_list_im;
-    end if;
-    rx_beamlet_list_val <= '1';
-
-    -- Wait until rx_beamlet_list is valid
-    wait until rising_edge(dp_clk);
-    rx_beamlet_list_val <= '0';
-    -- Verify rx_beamlet_list
-    -- . get last values from previous block
-    v_re := prev_re;
-    v_im := prev_im;
-    for vI in 0 to c_N - 1 loop
-      -- Verify incrementing beamlets
-      v_re := (v_re + 1) mod c_beamlet_mod;
-      v_im := (v_im + 1) mod c_beamlet_mod;
-      assert to_uint(rx_beamlet_list_re(vI)) = v_re report "Wrong re_beamlet." severity error;
-      assert to_uint(rx_beamlet_list_im(vI)) = v_im report "Wrong im_beamlet." severity error;
-    end loop;
-    -- . hold last values for next block
-    prev_re <= v_re;
-    prev_im <= v_im;
-  end process;
+      if rx_merge_sosi.sop = '1' then
+        -- Expected BSN increments by exp_nof_blocks_per_packet, after every merged packet
+        exp_dp_bsn <= c_init_bsn + rx_merge_sop_cnt * exp_nof_blocks_per_packet;
+      end if;
+
+      -- Verify header at eop
+      if rx_offload_sosi.eop = '1' then
+        v_bool := func_sdp_verify_cep_header(rx_beamlet_header, exp_beamlet_header);
+      end if;
+    end process;
+
+  end generate;
 end tb;
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_beamformer_output.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_beamformer_output.vhd
index 60deb2ee5f..41feb65aad 100644
--- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_beamformer_output.vhd
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_beamformer_output.vhd
@@ -42,6 +42,10 @@ begin
   -- g_nof_destinations_max  : natural := 1;
   -- g_nof_destinations      : natural := 1
 
+  -- One BDO destination
   u_one_identity   : entity work.tb_sdp_beamformer_output generic map( 50, 0, false, 1, 1);
   u_one_transpose  : entity work.tb_sdp_beamformer_output generic map( 50, 0,  true, 1, 1);
+
+  -- Multiple BDO destinations
+  u_multi_16_16    : entity work.tb_sdp_beamformer_output generic map( 50, 0,  true, 16, 16);
 end tb;
-- 
GitLab