From b434adf97722035a30d867abfb4f216bb35f8400 Mon Sep 17 00:00:00 2001
From: Eric Kooistra <kooistra@astron.nl>
Date: Tue, 4 Oct 2022 15:18:26 +0200
Subject: [PATCH] Clarify tb working by using g_first_gn, g_nof_rn and
 g_global_sp.

---
 .../tb_lofar2_unb2c_sdp_station_bf_ring.vhd   | 394 ++++++++++--------
 1 file changed, 214 insertions(+), 180 deletions(-)

diff --git a/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf_ring/tb_lofar2_unb2c_sdp_station_bf_ring.vhd b/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf_ring/tb_lofar2_unb2c_sdp_station_bf_ring.vhd
index 84baf1ad8b..35a57bab05 100644
--- a/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf_ring/tb_lofar2_unb2c_sdp_station_bf_ring.vhd
+++ b/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf_ring/tb_lofar2_unb2c_sdp_station_bf_ring.vhd
@@ -26,41 +26,41 @@
 --
 -- Description:
 --   This tb is a balance between verification coverage and keeping it simple:
---   - Use only one signal input (g_sp). Put same remnant WG signal on the
---     other signal inputs.
+--   - Use only one signal input (g_global_sp) with strong WG. Put same remnant
+--     weak WG signal on the other g_nof_rn * S_pn - 1 signal inputs.
 --   - Use different BF weight for the two beamlet polarizations (g_bf_x_gain,
---     g_bf_x_phase and g_bf_y_phase, g_bf_y_phase) of signal input g_sp.
+--     g_bf_x_phase and g_bf_y_phase, g_bf_y_phase) of signal input g_global_sp.
 --     Using different BF weights for the N_pol_bf = 2 BF polarizations allows
 --     verification of the dual polarization beamlet.
---   - Use same remnant BF weight for the other S_pn - 1 = 11 signal inputs.
+--   - Use same remnant BF weight for the other g_nof_rn * S_pn - 1 signal inputs.
 --     The remnant signal inputs and BF weights allow verifying the BF sum if
 --     they are not 0. Using the same settings for all remnant SP simplyfies
 --     the tb, while still testing the BF sum.
---   - Select one beamlet for the subband (g_beamlet). Selecting one beamlet
---     other than the default beamlet for the subband is sufficient to verify
---     the beamlet subband select.
+--   - Select one beamlet (g_beamlet) for the subband (g_subband). Selecting
+--     one beamlet other than the default beamlet for the subband is sufficient
+--     to verify the beamlet subband select.
 --   - Use same stimuli for both beamsets.
 --
 --   MM control actions:
 --
---   1) Enable calc mode for WG on signal input (si) g_sp via reg_diag_wg with:
+--   1) Enable calc mode for WG on signal input (si) g_global_sp via reg_diag_wg with:
 --        g_subband = 102 --> 102 * f_sub = 19.921875 MHz
 --        g_sp_ampl = 1.0 --> 1.0 yield WG ampl = 2**13
 --        g_sp_phase --> subband phase
---      Use g_sp_remnant_ampl = 0.1 and g_sp_remnant_phase = 0.0 for the other
---      S_pn-1 = 11 signal inputs, than g_sp, that are not used in the BF.
+--      Use g_sp_remnant_ampl = 0.1 and g_sp_remnant_phase = 0.0 for all other
+--      g_nof_rn * S_pn - 1 signal inputs, than g_global_sp, that are not used in the BF.
 --   
 --   2) Read current BSN from reg_bsn_scheduler_wg and write
 --      reg_bsn_scheduler_wg to trigger start of WG at BSN.
 --     
---   3) Read and verify subband statistics (SST) for g_sp. This also reads the
---      SST of the other signal input of the WPFB that processes g_sp.
+--   3) Read and verify subband statistics (SST) for g_global_sp. This also reads the
+--      SST of the other signal input of the WPFB that processes g_global_sp.
 --
 --   4) Select subband g_subband for beamlets in g_beamlet
 --
---   5) Apply BF weight to g_beamlet X beam and Y beam, so for example if g_sp
---      = 3, then w_x3 = g_bf_x_gain/phase and w_y3 = = g_bf_x_gain/phase. The
---      other BF weights are 0.
+--   5) Apply BF weight to g_beamlet X beam and Y beam, so for example if g_global_sp
+--      = 3, then w_x3 = g_bf_x_gain,phase and w_y3 = g_bf_y_gain,phase. The
+--      BF weights for all other beamlets (/= g_beamlet) are 0.
 --
 --      WG          BF               BF
 --      si          weight           weight
@@ -93,7 +93,6 @@
 --                          \----------------|---> beamlet_x
 --                                            \--> beamlet_y
 --
---
 --   6) Read and verify beamlet statistics (BST)
 --        View sp_sst in Wave window
 --        View bst_x_arr, bst_y_arr in Wave window
@@ -109,7 +108,7 @@
 -- Usage:
 --   > as 7    # default
 --   > as 12   # for detailed debugging
---   # Manually add missing signal
+--   # Manually add missing signals and constants using objects in GUI
 --   > add wave -position insertpoint  \
 --     sim:/tb_lofar2_unb2c_sdp_station_bf_ring/sp_ssts_arr2 \
 --     sim:/tb_lofar2_unb2c_sdp_station_bf_ring/bsts_arr2
@@ -139,7 +138,9 @@ USE tech_pll_lib.tech_pll_component_pkg.ALL;
 
 ENTITY tb_lofar2_unb2c_sdp_station_bf_ring IS
   GENERIC (
-    g_sp                 : NATURAL := 3;      -- WG signal path (SP) index in range(S_pn = 12)
+    g_first_gn           : NATURAL := 0;  -- first global node (GN) in ring
+    g_nof_rn             : NATURAL := 2;  -- nof ring nodes (RN) in ring
+    g_global_sp          : NATURAL := 3;      -- WG gloabl signal path (SP) index in range(g_first_gn * S_pn, (g_first_gn + g_nof_rn) * S_pn - 1), S_pn = 12
     g_sp_ampl            : REAL := 0.5;       -- WG normalized amplitude
     g_sp_phase           : REAL := -110.0;      -- WG phase in degrees = subband phase
     g_sp_remnant_ampl    : REAL := 0.1;       -- WG normalized amplitude for remnant sp
@@ -147,14 +148,14 @@ ENTITY tb_lofar2_unb2c_sdp_station_bf_ring IS
     g_subband            : NATURAL := 102;    -- select g_subband at index 102 = 102/1024 * 200MHz = 19.921875 MHz
     g_beamlet            : NATURAL := c_sdp_S_sub_bf-1;     -- map g_subband to g_beamlet index in beamset in range(c_sdp_S_sub_bf = 488)
     g_beamlet_scale      : REAL := 1.0 / 2.0**9;  -- g_beamlet output scale factor
-    g_bf_x_gain          : REAL := 0.7;       -- g_beamlet X BF weight normalized gain for g_sp
-    g_bf_y_gain          : REAL := 0.6;       -- g_beamlet Y BF weight normalized gain for g_sp
-    g_bf_x_phase         : REAL := 30.0;      -- g_beamlet X BF weight phase rotation in degrees for g_sp
-    g_bf_y_phase         : REAL := 40.0;      -- g_beamlet Y BF weight phase rotation in degrees for g_sp
+    g_bf_x_gain          : REAL := 0.7;       -- g_beamlet X BF weight normalized gain for g_global_sp
+    g_bf_y_gain          : REAL := 0.6;       -- g_beamlet Y BF weight normalized gain for g_global_sp
+    g_bf_x_phase         : REAL := 30.0;      -- g_beamlet X BF weight phase rotation in degrees for g_global_sp
+    g_bf_y_phase         : REAL := 40.0;      -- g_beamlet Y BF weight phase rotation in degrees for g_global_sp
     g_bf_remnant_x_gain  : REAL := 0.05;       -- g_beamlet X BF weight normalized gain for remnant sp
     g_bf_remnant_y_gain  : REAL := 0.04;       -- g_beamlet Y BF weight normalized gain for remnant sp
-    g_bf_remnant_x_phase : REAL := 170.0;       -- g_beamlet X BF weight phase rotation in degrees for g_sp
-    g_bf_remnant_y_phase : REAL := -135.0;       -- g_beamlet Y BF weight phase rotation in degrees for g_sp
+    g_bf_remnant_x_phase : REAL := 170.0;       -- g_beamlet X BF weight phase rotation in degrees for remnant sp
+    g_bf_remnant_y_phase : REAL := -135.0;       -- g_beamlet Y BF weight phase rotation in degrees for remnant sp
     g_read_all_SST       : BOOLEAN := FALSE;  -- when FALSE only read SST for g_subband, to save sim time
     g_read_all_BST       : BOOLEAN := FALSE   -- when FALSE only read BST for g_beamlet, to save sim time
   );
@@ -163,16 +164,21 @@ END tb_lofar2_unb2c_sdp_station_bf_ring;
 ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
 
   CONSTANT c_sim                 : BOOLEAN := TRUE;
-  CONSTANT c_unb_nr              : NATURAL := 0; -- UniBoard 0
-  CONSTANT c_node_nr             : NATURAL := 0;
-  CONSTANT c_nof_rn              : NATURAL := 2;
-  CONSTANT c_last_unb_nr         : NATURAL := (c_nof_rn-1) / c_quad;
-  CONSTANT c_last_rn_nr          : NATURAL := c_nof_rn-1;
-  CONSTANT c_gn_index            : NATURAL := c_unb_nr * c_quad + c_nof_rn-1; -- end node GN
+  CONSTANT c_first_unb_nr        : NATURAL := g_first_gn / c_quad;  -- c_quad = 4 FPGAs per UniBoard2
+  CONSTANT c_first_node_nr       : NATURAL := g_first_gn MOD c_quad;  -- first node_nr in range(c_quad) = [0:3] on c_first_unb_nr
+  CONSTANT c_last_rn             : NATURAL := g_nof_rn - 1;  -- first RN has index 0 by definition.
+  CONSTANT c_last_gn             : NATURAL := g_first_gn + c_last_rn; -- last global node (GN) in ring
+  CONSTANT c_last_unb_nr         : NATURAL := c_last_gn / c_quad;
+  CONSTANT c_last_node_nr        : NATURAL := c_last_gn MOD c_quad;
+  CONSTANT c_global_sp_gn        : NATURAL := g_global_sp / c_sdp_S_pn;  -- global node (GN) of where g_global_sp is located
+  CONSTANT c_local_sp            : NATURAL := g_global_sp MOD c_sdp_S_pn;  -- local SP index of g_global_sp on c_global_sp_gn
+  CONSTANT c_global_sp_unb_nr    : NATURAL := c_global_sp_gn / c_quad;  -- unb_nr of where g_global_sp is located
+  CONSTANT c_global_sp_node_nr   : NATURAL := c_global_sp_gn MOD c_quad;  -- unb_nr of where g_global_sp is located
+
   CONSTANT c_init_bsn            : NATURAL := 17;  -- some recognizable value >= 0
   CONSTANT c_nof_lanes           : NATURAL := c_sdp_N_beamsets;
 
-  CONSTANT c_id                  : STD_LOGIC_VECTOR(7 DOWNTO 0) := TO_UVEC(c_gn_index, 8);
+  CONSTANT c_last_id             : STD_LOGIC_VECTOR(7 DOWNTO 0) := TO_UVEC(c_last_gn, 8);
   CONSTANT c_version             : STD_LOGIC_VECTOR(1 DOWNTO 0) := "00";
   CONSTANT c_fw_version          : t_unb2c_board_fw_version := (1, 0);
 
@@ -199,11 +205,11 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   CONSTANT c_beamlet_output_delta : INTEGER := 2;  -- +-delta margin
 
   -- header fields
-  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_index);
-  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_index);
-  CONSTANT c_cep_udp_src_port    : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_sdp_cep_udp_src_port_15_8 & c_id;  -- D0 & c_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_last_gn);
+  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_last_gn);
+  CONSTANT c_cep_udp_src_port    : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_sdp_cep_udp_src_port_15_8 & c_last_id;  -- D0 & c_last_id
 
-  CONSTANT c_exp_ip_header_checksum : NATURAL := 16#5BDD#;  -- value obtained from rx_sdp_cep_header.ip.header_checksum in wave window for c_nof_rn = 2.
+  CONSTANT c_exp_ip_header_checksum : NATURAL := 16#5BDD#;  -- value obtained from rx_sdp_cep_header.ip.header_checksum in wave window for g_nof_rn = 2.
 
   CONSTANT c_exp_beamlet_scale   : NATURAL := NATURAL(g_beamlet_scale * REAL(c_sdp_unit_beamlet_scale));  -- c_sdp_unit_beamlet_scale = 2**15;
   CONSTANT c_exp_beamlet_index   : NATURAL := 0;  -- depends on beamset bset * c_sdp_S_sub_bf
@@ -234,8 +240,8 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   CONSTANT c_wg_remnant_phase     : REAL := g_sp_remnant_phase + c_wg_phase_offset;  -- WG phase in degrees
 
   -- WPFB
-  CONSTANT c_pol_index                    : NATURAL := g_sp MOD c_sdp_Q_fft;
-  CONSTANT c_pfb_index                    : NATURAL := g_sp / c_sdp_Q_fft;  -- only read used WPFB unit out of range(c_sdp_P_pfb = 6)
+  CONSTANT c_pol_index                    : NATURAL := c_local_sp MOD c_sdp_Q_fft;
+  CONSTANT c_pfb_index                    : NATURAL := c_local_sp / c_sdp_Q_fft;  -- only read used WPFB unit out of range(c_sdp_P_pfb = 6)
   CONSTANT c_subband_phase_offset         : REAL := -90.0;  -- WG with zero phase sinus yields subband with -90 degrees phase (negative Im, zero Re)
   CONSTANT c_subband_weight_gain          : REAL := 1.0;  -- use default unit subband weights
   CONSTANT c_subband_weight_phase         : REAL := 0.0;  -- use default unit subband weights
@@ -255,7 +261,7 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   -- . select
   CONSTANT c_exp_beamlet_x_index          : NATURAL := g_beamlet * c_sdp_N_pol_bf;      -- X index in beamset 0
   CONSTANT c_exp_beamlet_y_index          : NATURAL := g_beamlet * c_sdp_N_pol_bf + 1;  -- Y index in beamset 0
-  -- . Beamlet weights for selected g_sp
+  -- . Beamlet weights for selected g_global_sp
   CONSTANT c_bf_x_weight_re               : INTEGER := INTEGER(COMPLEX_RE(g_bf_x_gain * REAL(c_sdp_unit_bf_weight), g_bf_x_phase));
   CONSTANT c_bf_x_weight_im               : INTEGER := INTEGER(COMPLEX_IM(g_bf_x_gain * REAL(c_sdp_unit_bf_weight), g_bf_x_phase));
   CONSTANT c_bf_y_weight_re               : INTEGER := INTEGER(COMPLEX_RE(g_bf_y_gain * REAL(c_sdp_unit_bf_weight), g_bf_y_phase));
@@ -265,10 +271,10 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   CONSTANT c_bf_remnant_y_weight_re       : INTEGER := INTEGER(COMPLEX_RE(g_bf_remnant_y_gain * REAL(c_sdp_unit_bf_weight), g_bf_remnant_y_phase));
   CONSTANT c_bf_remnant_y_weight_im       : INTEGER := INTEGER(COMPLEX_IM(g_bf_remnant_y_gain * REAL(c_sdp_unit_bf_weight), g_bf_remnant_y_phase));
 
-  -- Model the SDP beamformer for one g_sp and S_pn-1 = 11 remnant signal inputs
+  -- Model the SDP beamformer for one g_global_sp and g_nof_rn * S_pn - 1 remnant signal inputs
   FUNCTION bf_calculate_expected_beamlet(sp_subband_ampl, sp_subband_phase, sp_bf_gain, sp_bf_phase,
                                          rem_subband_ampl, rem_subband_phase, rem_bf_gain, rem_bf_phase : REAL) RETURN t_real_arr IS  -- 0:3 = ampl, phase, re, im
-    CONSTANT c_nof_rem : REAL := REAL(c_nof_rn * c_sdp_S_pn - 1);  -- BF for one g_sp and N_rn * S_PN - 1 remnant signal inputs
+    CONSTANT c_nof_rem : REAL := REAL(g_nof_rn * c_sdp_S_pn - 1);  -- BF for one g_global_sp and N_rn * S_PN - 1 remnant signal inputs
     VARIABLE v_sp_ampl, v_sp_phase, v_sp_re, v_sp_im     : REAL;
     VARIABLE v_rem_ampl, v_rem_phase, v_rem_re, v_rem_im : REAL;
     VARIABLE v_sum_ampl, v_sum_phase, v_sum_re, v_sum_im : REAL;
@@ -345,21 +351,19 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   CONSTANT c_mm_span_reg_dp_xonoff        : NATURAL := 2**c_addr_w_reg_dp_xonoff;
   CONSTANT c_mm_span_ram_st_bst           : NATURAL := 2**c_addr_w_ram_st_bst;
 
-  CONSTANT c_mm_file_reg_ppsh             : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "PIO_PPS";
-  CONSTANT c_mm_file_reg_bsn_source_v2    : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BSN_SOURCE_V2";
-  CONSTANT c_mm_file_reg_bsn_scheduler_wg : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BSN_SCHEDULER";
-  CONSTANT c_mm_file_reg_diag_wg          : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_WG";
-  CONSTANT c_mm_file_ram_equalizer_gains  : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_EQUALIZER_GAINS";
-  CONSTANT c_mm_file_reg_dp_selector      : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_DP_SELECTOR";
-  CONSTANT c_mm_file_ram_st_sst           : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_ST_SST";
-  CONSTANT c_mm_file_ram_st_bst           : STRING := mmf_unb_file_prefix(c_unb_nr + c_last_unb_nr, c_last_rn_nr MOD c_quad) & "RAM_ST_BST"; --end RN
-  CONSTANT c_mm_file_reg_stat_enable_bst  : STRING := mmf_unb_file_prefix(c_unb_nr + c_last_unb_nr, c_last_rn_nr MOD c_quad) & "REG_STAT_ENABLE_BST"; --end RN
-  CONSTANT c_mm_file_reg_dp_xonoff        : STRING := mmf_unb_file_prefix(c_unb_nr + c_last_unb_nr, c_last_rn_nr MOD c_quad) & "REG_DP_XONOFF"; --end RN
-  CONSTANT c_mm_file_ram_ss_ss_wide       : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_SS_SS_WIDE";
-  CONSTANT c_mm_file_ram_bf_weights       : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_BF_WEIGHTS";
-  CONSTANT c_mm_file_reg_bf_scale         : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BF_SCALE";
-  CONSTANT c_mm_file_reg_sdp_info         : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_SDP_INFO";
-  CONSTANT c_mm_file_reg_hdr_dat          : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_HDR_DAT";  -- c_sdp_N_beamsets = 2 beamsets
+  -- Use c_mm_file_reg_* on c_last_gn for MM accesses to only last node in ring, e.g. for control beamlet output, read BSN, read back
+  -- Use mmf_unb_file_prefix() and range(g_nof_rn) for MM access loop to all nodes in ring, e.g. for control BSN source, BF weights
+  CONSTANT c_mm_file_reg_bsn_scheduler_wg : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "REG_BSN_SCHEDULER";  -- read current BSN
+  CONSTANT c_mm_file_ram_equalizer_gains  : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "RAM_EQUALIZER_GAINS";  -- read default subband weight
+  CONSTANT c_mm_file_reg_dp_selector      : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "REG_DP_SELECTOR";  -- read sst_weighted_subbands_flag
+  CONSTANT c_mm_file_ram_st_sst           : STRING := mmf_unb_file_prefix(c_global_sp_unb_nr, c_global_sp_node_nr) & "RAM_ST_SST";  -- read SST from GN with g_global_sp
+  CONSTANT c_mm_file_ram_st_bst           : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "RAM_ST_BST";  -- read BST
+  CONSTANT c_mm_file_reg_stat_enable_bst  : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "REG_STAT_ENABLE_BST";  -- control BST offload
+  CONSTANT c_mm_file_reg_dp_xonoff        : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "REG_DP_XONOFF";  -- beamlet output
+  CONSTANT c_mm_file_ram_ss_ss_wide       : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "RAM_SS_SS_WIDE";  --readback
+  CONSTANT c_mm_file_ram_bf_weights       : STRING := mmf_unb_file_prefix(c_global_sp_unb_nr, c_global_sp_node_nr) & "RAM_BF_WEIGHTS";  -- readback from GN with g_global_sp
+  CONSTANT c_mm_file_reg_bf_scale         : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "REG_BF_SCALE";  -- readback
+  CONSTANT c_mm_file_reg_hdr_dat          : STRING := mmf_unb_file_prefix(c_last_unb_nr, c_last_node_nr) & "REG_HDR_DAT";  -- control beamlet output
 
   -- Tb
   SIGNAL stimuli_done        : STD_LOGIC := '0';
@@ -382,15 +386,15 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   SIGNAL rd_cep_eth_src_mac  : STD_LOGIC_VECTOR(47 DOWNTO 0);
   SIGNAL rd_cep_eth_dst_mac  : STD_LOGIC_VECTOR(47 DOWNTO 0);
   SIGNAL rd_cep_ip_src_addr  : STD_LOGIC_VECTOR(31 DOWNTO 0);
-  SIGNAL rd_cep_eth_dst_mac  : STD_LOGIC_VECTOR(47 DOWNTO 0);
   SIGNAL rd_cep_ip_dst_addr  : STD_LOGIC_VECTOR(31 DOWNTO 0);
+  SIGNAL rd_cep_udp_src_port : STD_LOGIC_VECTOR(15 DOWNTO 0);
   SIGNAL rd_cep_udp_dst_port : STD_LOGIC_VECTOR(15 DOWNTO 0);
 
   -- WG
   SIGNAL current_bsn_wg      : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
 
   -- FSUB
-  -- . Read sp_ssts_arr2 = SST for one WPFB unit that processes g_sp
+  -- . Read sp_ssts_arr2 = SST for one WPFB unit that processes g_global_sp
   SIGNAL sp_ssts_arr2        : t_slv_64_subbands_arr(c_sdp_N_pol-1 DOWNTO 0);   -- [pol][sub], for X,Y pair of A, B
   SIGNAL sp_sst              : REAL := 0.0;
   SIGNAL stat_data           : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0);
@@ -410,15 +414,15 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   SIGNAL sp_subband_select_arr     : t_natural_arr(0 TO c_sdp_S_sub_bf * c_sdp_N_pol-1) := (OTHERS => 0);  -- Q_fft = N_pol = 2
 
   -- . beamlet X-pol
-  SIGNAL sp_bf_x_weights_re_arr    : t_integer_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
-  SIGNAL sp_bf_x_weights_im_arr    : t_integer_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
-  SIGNAL sp_bf_x_weights_gain_arr  : t_real_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
-  SIGNAL sp_bf_x_weights_phase_arr : t_real_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
+  SIGNAL sp_bf_x_weights_re_arr    : t_integer_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
+  SIGNAL sp_bf_x_weights_im_arr    : t_integer_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
+  SIGNAL sp_bf_x_weights_gain_arr  : t_real_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
+  SIGNAL sp_bf_x_weights_phase_arr : t_real_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
   -- . beamlet Y-pol
-  SIGNAL sp_bf_y_weights_re_arr    : t_integer_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
-  SIGNAL sp_bf_y_weights_im_arr    : t_integer_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
-  SIGNAL sp_bf_y_weights_gain_arr  : t_real_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
-  SIGNAL sp_bf_y_weights_phase_arr : t_real_arr(0 TO c_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
+  SIGNAL sp_bf_y_weights_re_arr    : t_integer_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
+  SIGNAL sp_bf_y_weights_im_arr    : t_integer_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0);
+  SIGNAL sp_bf_y_weights_gain_arr  : t_real_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
+  SIGNAL sp_bf_y_weights_phase_arr : t_real_arr(0 TO g_nof_rn * c_sdp_S_pn-1) := (OTHERS => 0.0);
 
   -- . BST
   SIGNAL bsts_arr2           : t_slv_64_beamlets_arr(c_sdp_N_pol_bf-1 DOWNTO 0);  -- [pol_bf][blet]
@@ -472,13 +476,13 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf_ring IS
   SIGNAL eth_rxp             : STD_LOGIC_VECTOR(c_unb2c_board_nof_eth-1 downto 0);
 
   SIGNAL SA_CLK              : STD_LOGIC := '1';
-  SIGNAL i_QSFP_0_TX         : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
-  SIGNAL i_QSFP_0_RX         : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
-  SIGNAL i_RING_0_TX         : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
-  SIGNAL i_RING_0_RX         : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
-  SIGNAL i_RING_1_TX         : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
-  SIGNAL i_RING_1_RX         : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
-  SIGNAL i_QSFP_1_lpbk       : t_unb2c_board_qsfp_bus_2arr(c_nof_rn -1 DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_QSFP_0_TX         : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_QSFP_0_RX         : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_RING_0_TX         : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_RING_0_RX         : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_RING_1_TX         : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_RING_1_RX         : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
+  SIGNAL i_QSFP_1_lpbk       : t_unb2c_board_qsfp_bus_2arr(c_last_rn DOWNTO 0) := (OTHERS => (OTHERS => '0'));
 
   -- back transceivers
   SIGNAL JESD204B_SERIAL_DATA : STD_LOGIC_VECTOR(c_sdp_S_pn-1 downto 0);
@@ -512,14 +516,14 @@ BEGIN
   ------------------------------------------------------------------------------
   -- DUT
   ------------------------------------------------------------------------------
-  gen_dut : FOR RN IN 0 TO c_nof_rn -1 GENERATE
+  gen_dut : FOR RN IN 0 TO c_last_rn GENERATE
     u_lofar_unb2c_sdp_station_bf : ENTITY lofar2_unb2c_sdp_station_lib.lofar2_unb2c_sdp_station
     GENERIC MAP (
       g_design_name            => "lofar2_unb2c_sdp_station_bf_ring",
       g_design_note            => "",
       g_sim                    => c_sim,
-      g_sim_unb_nr             => c_unb_nr + (RN / c_quad),
-      g_sim_node_nr            => RN MOD c_quad,
+      g_sim_unb_nr             => (g_first_gn + RN) / c_quad,
+      g_sim_node_nr            => (g_first_gn + RN) MOD c_quad,
       g_wpfb                   => c_wpfb_sim,
       g_bsn_nof_clk_per_sync   => c_nof_clk_per_sync,
       g_scope_selected_subband => g_subband
@@ -534,7 +538,7 @@ BEGIN
 
       -- Others
       VERSION      => c_version,
-      ID           => ( TO_UVEC(RN / c_quad, c_unb2c_board_nof_uniboard_w) & TO_UVEC(RN MOD c_quad, c_unb2c_board_nof_chip_w) ),
+      ID           => ( TO_UVEC((g_first_gn + RN) / c_quad, c_unb2c_board_nof_uniboard_w) & TO_UVEC((g_first_gn + RN) MOD c_quad, c_unb2c_board_nof_chip_w) ),
       TESTIO       => open,
 
       -- 1GbE Control Interface
@@ -572,17 +576,16 @@ BEGIN
   END GENERATE;
 
   -- Ring connections
-  gen_ring : FOR I IN 0 TO c_nof_rn -2 GENERATE
+  gen_ring : FOR RN IN 0 TO c_last_rn-1 GENERATE
     -- Connect consecutive nodes with RING interfaces (PCB)
-    i_RING_0_RX(I+1) <= i_RING_1_TX(I);
-    i_RING_1_RX(I)   <= i_RING_0_TX(I+1);
+    i_RING_0_RX(RN + 1) <= i_RING_1_TX(RN);
+    i_RING_1_RX(RN)     <= i_RING_0_TX(RN + 1);
   END GENERATE;
 
   -- Connect first and last nodes with QSFP interface. 
-  i_QSFP_0_RX(0)          <= i_QSFP_0_TX(c_nof_rn-1);
-  i_QSFP_0_RX(c_nof_rn-1) <= i_QSFP_0_TX(0);
+  i_QSFP_0_RX(0)         <= i_QSFP_0_TX(c_last_rn);
+  i_QSFP_0_RX(c_last_rn) <= i_QSFP_0_TX(0);
 
-  
 
   ------------------------------------------------------------------------------
   -- CEP model
@@ -619,7 +622,7 @@ BEGIN
     dp_rst              => dest_rst,
     dp_clk              => ext_clk,
 
-    serial_rx_arr(0)    => i_QSFP_1_lpbk(c_nof_rn-1)(0), -- Last RN must be used as end node.
+    serial_rx_arr(0)    => i_QSFP_1_lpbk(c_last_rn)(0), -- Last RN must be used as end node.
 
     src_out_arr(0)      => tr_10GbE_src_out,
     src_in_arr(0)       => tr_10GbE_src_in
@@ -660,6 +663,7 @@ BEGIN
   tb_clk  <= NOT tb_clk AFTER c_tb_clk_period/2;    -- Testbench MM clock
   
   p_mm_stimuli : PROCESS
+    VARIABLE v_gn                                   : NATURAL;
     VARIABLE v_bsn                                  : NATURAL;
     VARIABLE v_sp_sst                               : REAL := 0.0;
     VARIABLE v_bst                                  : REAL := 0.0;
@@ -722,16 +726,17 @@ BEGIN
     -- . Write
 
 
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  7, TO_UINT(c_exp_sdp_info.station_id), tb_clk);
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  6, TO_UINT(slv(c_exp_sdp_info.antenna_band_index)), tb_clk);
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  5, TO_UINT(c_exp_sdp_info.observation_id), tb_clk);
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  4, TO_UINT(c_exp_sdp_info.nyquist_zone_index), tb_clk);
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  1, TO_UINT(slv(c_exp_sdp_info.beam_repositioning_flag)), tb_clk);
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  7, TO_UINT(c_exp_sdp_info.station_id), tb_clk);
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  6, TO_UINT(slv(c_exp_sdp_info.antenna_band_index)), tb_clk);
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  5, TO_UINT(c_exp_sdp_info.observation_id), tb_clk);
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  4, TO_UINT(c_exp_sdp_info.nyquist_zone_index), tb_clk);
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  1, TO_UINT(slv(c_exp_sdp_info.beam_repositioning_flag)), tb_clk);
       -- . Read
-      mmf_mm_bus_rd(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  3, rd_data, tb_clk); rd_sdp_info.f_adc <= rd_data(0);
-      mmf_mm_bus_rd(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  2, rd_data, tb_clk); rd_sdp_info.fsub_type <= rd_data(0);
-      mmf_mm_bus_rd(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_SDP_INFO",  0, rd_data, tb_clk); rd_sdp_info.block_period <= rd_data(15 DOWNTO 0);
+      mmf_mm_bus_rd(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  3, rd_data, tb_clk); rd_sdp_info.f_adc <= rd_data(0);
+      mmf_mm_bus_rd(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  2, rd_data, tb_clk); rd_sdp_info.fsub_type <= rd_data(0);
+      mmf_mm_bus_rd(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_SDP_INFO",  0, rd_data, tb_clk); rd_sdp_info.block_period <= rd_data(15 DOWNTO 0);
     END LOOP;
 
     proc_common_wait_some_cycles(tb_clk, 1);
@@ -744,13 +749,13 @@ BEGIN
     ---- Set and check BF per beamset
     ------------------------------------------------------------------------------
     FOR bset IN 0 TO c_sdp_N_beamsets-1 LOOP
-      -- MM beamlet_scale
+      ----------------------------------------------------------------------------
+      -- MM beamlet_scale on last node
+      ----------------------------------------------------------------------------
       -- . write
       v_offset := bset * c_mm_span_reg_bf_scale;
-      FOR RN IN 0 TO c_nof_rn-1 LOOP
-        mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BF_SCALE", v_offset + 0, c_exp_beamlet_scale, tb_clk);
-      END LOOP;
-      proc_common_wait_cross_clock_domain_latency(c_mm_clk_period, c_ext_clk_period);
+      mmf_mm_bus_wr(c_mm_file_reg_bf_scale, v_offset + 0, c_exp_beamlet_scale, tb_clk);
+      proc_common_wait_cross_clock_domain_latency(c_mm_clk_period, c_ext_clk_period, c_common_cross_clock_domain_latency*2);
 
       -- . readback
       mmf_mm_bus_rd(c_mm_file_reg_bf_scale, v_offset + 0, rd_data, tb_clk);
@@ -758,6 +763,9 @@ BEGIN
       proc_common_wait_some_cycles(tb_clk, 1);
       ASSERT TO_UINT(rd_beamlet_scale) = c_exp_beamlet_scale REPORT "Wrong MM read beamlet_scale for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;
 
+      ----------------------------------------------------------------------------
+      -- Set CEP beamlets output MAC,IP,UDP port on last node
+      ----------------------------------------------------------------------------
       -- CEP beamlet output header
       --     c_sdp_cep_hdr_field_arr : t_common_field_arr(c_sdp_cep_nof_hdr_fields-1 DOWNTO 0) := (
       --  40   "eth_dst_mac"                        ), "RW", 48, field_default(c_sdp_cep_eth_dst_mac) ),
@@ -827,7 +835,7 @@ BEGIN
       ASSERT UNSIGNED(rd_cep_udp_dst_port) = 0 REPORT "Wrong MM read rd_cep_udp_dst_port != 0 for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;
 
       -- Write tb defaults
-      -- . Use sim default dst and src MAC, IP, UDP port from sdp_pkg.vhd and based on c_gn_index
+      -- . Use sim default dst and src MAC, IP, UDP port from sdp_pkg.vhd and based on c_last_gn
       mmf_mm_bus_wr(c_mm_file_reg_hdr_dat, v_offset + 39, TO_UINT(c_cep_eth_src_mac(47 DOWNTO 32)), tb_clk);
       mmf_mm_bus_wr(c_mm_file_reg_hdr_dat, v_offset + 38, TO_SINT(c_cep_eth_src_mac(31 DOWNTO 0)), tb_clk);  -- use signed to fit 32 b in INTEGER
       mmf_mm_bus_wr(c_mm_file_reg_hdr_dat, v_offset + 26, TO_SINT(c_cep_ip_src_addr), tb_clk);  -- use signed to fit 32 b in INTEGER
@@ -857,13 +865,13 @@ BEGIN
       ASSERT rd_cep_udp_dst_port = c_sdp_cep_udp_dst_port REPORT "Wrong MM read rd_cep_udp_dst_port for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;
 
       ----------------------------------------------------------------------------
-      -- Enable BST offload on end node (not verified here, but only for view in Wave window)
+      -- Enable BST offload on last node (not verified here, but only for view in Wave window)
       ----------------------------------------------------------------------------
       v_offset := bset * c_mm_span_reg_stat_enable_bst;
       mmf_mm_bus_wr(c_mm_file_reg_stat_enable_bst, v_offset + 0, 1, tb_clk);
 
       ----------------------------------------------------------------------------
-      -- Enable beamlet output on end node (dp_xonoff)
+      -- Enable beamlet output on last node (dp_xonoff)
       ----------------------------------------------------------------------------
       v_offset := bset * c_mm_span_reg_dp_xonoff;
       mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff, v_offset + 0, 1, tb_clk);
@@ -872,11 +880,12 @@ BEGIN
     ----------------------------------------------------------------------------
     -- Enable BS
     ----------------------------------------------------------------------------
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BSN_SOURCE_V2", 2,         c_init_bsn, tb_clk);  -- Init BSN
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BSN_SOURCE_V2", 3,                  0, tb_clk);  -- Write high part a
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BSN_SOURCE_V2", 1, c_nof_clk_per_sync, tb_clk);  -- nof_block_per_syn
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BSN_SOURCE_V2", 0,       16#00000003#, tb_clk);  -- Enable BS at PPS
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_BSN_SOURCE_V2", 2,         c_init_bsn, tb_clk);  -- Init BSN
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_BSN_SOURCE_V2", 3,                  0, tb_clk);  -- Write high part a
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_BSN_SOURCE_V2", 1, c_nof_clk_per_sync, tb_clk);  -- nof_block_per_syn
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_BSN_SOURCE_V2", 0,       16#00000003#, tb_clk);  -- Enable BS at PPS
     END LOOP;
 
     -- Release PPS pulser, to get first PPS now and to start BSN source
@@ -887,28 +896,42 @@ BEGIN
     -- Ring config
     ----------------------------------------------------------------------------
     -- Write ring configuration to all nodes.
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_RING_INFO", 2, c_nof_rn, tb_clk); -- N_rn
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_RING_INFO", 3, 0,        tb_clk); -- O_rn
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 2, g_nof_rn, tb_clk); -- N_rn
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 3, g_first_gn, tb_clk); -- O_rn
     END LOOP;
 
-    -- Start node specific settings
-    mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr, 0) & "REG_RING_INFO", 0, 1, tb_clk); -- use_ring_to_previous_rn = 1
-    mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr, 0) & "REG_RING_INFO", 1, 0, tb_clk); -- use_ring_to_next_rn = 0
-  
-    -- End node specific settings
-    mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + c_last_unb_nr, c_last_rn_nr MOD c_quad) & "REG_RING_INFO", 0, 0, tb_clk); -- use_ring_to_previous_rn = 0
-    mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + c_last_unb_nr, c_last_rn_nr MOD c_quad) & "REG_RING_INFO", 1, 1, tb_clk); -- use_ring_to_next_rn = 1
-     
-    -- Access scheme 1. Each RN uses and sends them along the ring.
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      IF v_gn = g_first_gn THEN
+        -- Start node specific settings
+        mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 0, 1, tb_clk); -- use_cable_to_previous_rn = 1
+        mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 1, 0, tb_clk); -- use_cable_to_next_rn = 0
+      ELSIF v_gn = c_last_gn THEN
+        -- End node specific settings
+        mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 0, 0, tb_clk); -- use_cable_to_previous_rn = 0
+        mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 1, 1, tb_clk); -- use_cable_to_next_rn = 1
+      ELSE
+        -- Use same settings for all nodes in between
+        mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 0, 0, tb_clk); -- use_cable_to_previous_rn = 0
+        mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_INFO", 1, 0, tb_clk); -- use_cable_to_next_rn = 0
+      END IF;
+    END LOOP;
+
+    -- Access scheme 1 for beamformer.
+    -- Use transport_nof_hops = 0 on last nodes to stop remote input for first node. The
+    -- alternative is to use transport_nof_hops = 1 on all nodes and let the BSN aligner
+    -- on the first node disable its remote input.
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
       FOR I IN 0 TO c_nof_lanes-1 LOOP
-        IF RN = c_nof_rn-1 THEN
+        IF RN = c_last_rn THEN
           -- End RN, so set transport_nof_hops to 0.
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_RING_LANE_INFO_BF", I*2+1, 0, tb_clk);
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_LANE_INFO_BF", I*2+1, 0, tb_clk);
         ELSE
           -- Set transport_nof_hops to 1 on all nodes.
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_RING_LANE_INFO_BF", I*2+1, 1, tb_clk);
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_RING_LANE_INFO_BF", I*2+1, 1, tb_clk);
         END IF;
       END LOOP;
     END LOOP;
@@ -921,24 +944,26 @@ BEGIN
     --   1 : phase[15:0]
     --   2 : freq[30:0]
     --   3 : ampl[16:0]
-    -- . Put wanted signal on g_sp input and remnant signal on the other inputs
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
-      FOR I IN 0 TO c_sdp_S_pn-1 LOOP
-        IF RN * c_sdp_S_pn + I = g_sp THEN
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 0, 1024*2**16 + 1, tb_clk);  -- nof_samples, mode calc
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 1, INTEGER(c_wg_phase * c_diag_wg_phase_unit), tb_clk);  -- phase offset in degrees
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 2, INTEGER(REAL(g_subband) * c_sdp_wg_subband_freq_unit), tb_clk);  -- freq
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 3, INTEGER(REAL(c_wg_ampl) * c_sdp_wg_ampl_lsb), tb_clk);  -- ampl
+    -- . Put wanted signal on g_global_sp input and remnant signal on all g_nof_rn * S_pn - 1 other inputs
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      FOR S IN 0 TO c_sdp_S_pn-1 LOOP
+        IF RN * c_sdp_S_pn + S = g_global_sp THEN
+          -- Strong WG signal at g_global_sp
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 0, 1024*2**16 + 1, tb_clk);  -- nof_samples, mode calc
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 1, INTEGER(c_wg_phase * c_diag_wg_phase_unit), tb_clk);  -- phase offset in degrees
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 2, INTEGER(REAL(g_subband) * c_sdp_wg_subband_freq_unit), tb_clk);  -- freq
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 3, INTEGER(REAL(c_wg_ampl) * c_sdp_wg_ampl_lsb), tb_clk);  -- ampl
         ELSE
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 0, 1024*2**16 + 1, tb_clk);  -- nof_samples, mode calc
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 1, INTEGER(c_wg_remnant_phase * c_diag_wg_phase_unit), tb_clk);  -- phase offset in degrees
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 2, INTEGER(REAL(g_subband) * c_sdp_wg_subband_freq_unit), tb_clk);  -- freq
-          mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_WG", I*4 + 3, INTEGER(REAL(c_wg_remnant_ampl) * c_sdp_wg_ampl_lsb), tb_clk);  -- ampl
+          -- Weak WG signal on all other (remnant) SP
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 0, 1024*2**16 + 1, tb_clk);  -- nof_samples, mode calc
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 1, INTEGER(c_wg_remnant_phase * c_diag_wg_phase_unit), tb_clk);  -- phase offset in degrees
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 2, INTEGER(REAL(g_subband) * c_sdp_wg_subband_freq_unit), tb_clk);  -- freq
+          mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_WG", S*4 + 3, INTEGER(REAL(c_wg_remnant_ampl) * c_sdp_wg_ampl_lsb), tb_clk);  -- ampl
         END IF;
       END LOOP;
     END LOOP;
 
-
     -- Read current BSN
     mmf_mm_bus_rd(c_mm_file_reg_bsn_scheduler_wg, 0, current_bsn_wg(31 DOWNTO  0), tb_clk);
     mmf_mm_bus_rd(c_mm_file_reg_bsn_scheduler_wg, 1, current_bsn_wg(63 DOWNTO 32), tb_clk);
@@ -948,9 +973,10 @@ BEGIN
     v_bsn := TO_UINT(current_bsn_wg) + 2;
     ASSERT v_bsn <= c_bsn_start_wg REPORT "Too late to start WG: " & int_to_str(v_bsn) & " > " & int_to_str(c_bsn_start_wg) SEVERITY ERROR;
 
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BSN_SCHEDULER", 0, c_bsn_start_wg, tb_clk);  -- first write low then high part
-      mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "REG_BSN_SCHEDULER", 1,              0, tb_clk);  -- assume v_bsn < 2**31-1
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_BSN_SCHEDULER", 0, c_bsn_start_wg, tb_clk);  -- first write low then high part
+      mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "REG_BSN_SCHEDULER", 1,              0, tb_clk);  -- assume v_bsn < 2**31-1
     END LOOP;
 
     ----------------------------------------------------------------------------
@@ -965,7 +991,7 @@ BEGIN
     ----------------------------------------------------------------------------
 
     -- . MM format: (cint16)RAM_EQUALIZER_GAINS[S_pn/Q_fft]_[Q_fft][N_sub] = [S_pn][N_sub]
-    v_addr := g_sp * c_sdp_N_sub + g_subband;
+    v_addr := c_local_sp * c_sdp_N_sub + g_subband;
     -- . read
     mmf_mm_bus_rd(c_mm_file_ram_equalizer_gains, v_addr, rd_data, tb_clk);
     v_re := unpack_complex_re(rd_data, c_sdp_W_sub_weight);
@@ -992,20 +1018,21 @@ BEGIN
         FOR P IN 0 TO c_sdp_N_pol-1 LOOP
           v_addr := P + g_beamlet * c_sdp_N_pol + A * v_span + U * c_mm_span_ram_ss_ss_wide;
           v_sel := P + g_subband * c_sdp_N_pol;
-          FOR RN IN 0 TO c_nof_rn-1 LOOP
-            mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "RAM_SS_SS_WIDE", v_addr, v_sel, tb_clk);
+          FOR RN IN 0 TO c_last_rn LOOP
+            v_gn := g_first_gn + RN;
+            mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "RAM_SS_SS_WIDE", v_addr, v_sel, tb_clk);
           END LOOP;
         END LOOP;
       END LOOP;
     END LOOP;
 
-    -- . read back selection for g_sp = c_pfb_index * c_sdp_N_pol + c_pol_index
+    -- . read back selection for c_local_sp = c_pfb_index * c_sdp_N_pol + c_pol_index
     v_P := c_pol_index;
     v_A := c_pfb_index;
     FOR U IN 0 TO c_sdp_N_beamsets-1 LOOP
       -- Same selection for both beamsets, so fine to use only one sp_subband_select_arr()
       FOR B IN 0 TO c_sdp_S_sub_bf-1 LOOP
-        -- Same selection for all SP, so fine to only read subband selection for g_sp
+        -- Same selection for all SP, so fine to only read subband selection for c_local_sp on last node
         v_addr := v_P + B * c_sdp_N_pol + v_A * v_span + U * c_mm_span_ram_ss_ss_wide;
         mmf_mm_bus_rd(c_mm_file_ram_ss_ss_wide, v_addr, rd_data, tb_clk);
         v_sel := (TO_UINT(rd_data) - v_P) / c_sdp_N_pol;
@@ -1023,16 +1050,18 @@ BEGIN
 
     -- . write BF weights, only for g_beamlet to save sim time
     v_span := true_log_pow2(c_sdp_N_pol * c_sdp_S_sub_bf);  -- = 1024
-    FOR RN IN 0 TO c_nof_rn-1 LOOP
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
       FOR U IN 0 TO c_sdp_N_beamsets-1 LOOP
         -- Same BF weights for both beamsets
         FOR PB IN 0 TO c_sdp_N_pol_bf-1 LOOP
-          -- Same BF weights for both beamlet polarizations
+          -- Different BF weights for both beamlet polarizations
           FOR A IN 0 TO c_sdp_A_pn-1 LOOP
             FOR P IN 0 TO c_sdp_N_pol-1 LOOP
               v_S := RN * c_sdp_S_pn + A * c_sdp_N_pol + P;
-              IF v_S = g_sp THEN
-                -- use generic BF weight for g_sp in g_beamlet
+              -- Different BF weights for g_global_sp and the remnant sp
+              IF v_S = g_global_sp THEN
+                -- use generic BF weight for g_global_sp in g_beamlet
                 IF PB = 0 THEN
                   v_weight := pack_complex(re => c_bf_x_weight_re, im => c_bf_x_weight_im, w => c_sdp_W_bf_weight);
                 ELSE
@@ -1046,12 +1075,13 @@ BEGIN
                   v_weight := pack_complex(re => c_bf_remnant_y_weight_re, im => c_bf_remnant_y_weight_im, w => c_sdp_W_bf_weight);
                 END IF;
               END IF;
+            -- Only need to set BF weight for g_beamlet, keep other beamlet BF weights at zero rst default.
               v_addr := g_beamlet;                              -- beamlet index
               v_addr := v_addr + P * c_sdp_S_sub_bf;            -- antenna input polarization address offset
               v_addr := v_addr + A * v_span;                    -- antenna input address offset
               v_addr := v_addr + PB * c_sdp_A_pn * v_span;      -- beamlet polarization address offset
               v_addr := v_addr + U * c_mm_span_ram_bf_weights;  -- beamset address offset
-              mmf_mm_bus_wr(mmf_unb_file_prefix(c_unb_nr + (RN / c_quad), RN MOD c_quad) & "RAM_BF_WEIGHTS", v_addr, v_weight, tb_clk);
+              mmf_mm_bus_wr(mmf_unb_file_prefix(v_gn / c_quad, v_gn MOD c_quad) & "RAM_BF_WEIGHTS", v_addr, v_weight, tb_clk);
             END LOOP;
           END LOOP;
         END LOOP;
@@ -1059,32 +1089,35 @@ BEGIN
     END LOOP;
 
     -- . read back BF weights for g_beamlet in S_sub_bf
-    FOR U IN 0 TO c_sdp_N_beamsets-1 LOOP
-      FOR PB IN 0 TO c_sdp_N_pol_bf-1 LOOP
-        FOR A IN 0 TO c_sdp_A_pn-1 LOOP
-          FOR P IN 0 TO c_sdp_N_pol-1 LOOP
-            v_addr := g_beamlet;                              -- beamlet index
-            v_addr := v_addr + P * c_sdp_S_sub_bf;            -- antenna input polarization address offset
-            v_addr := v_addr + A * v_span;                    -- antenna input address offset
-            v_addr := v_addr + PB * c_sdp_A_pn * v_span;      -- beamlet polarization address offset
-            v_addr := v_addr + U * c_mm_span_ram_bf_weights;  -- beamset address offset
-            mmf_mm_bus_rd(c_mm_file_ram_bf_weights, v_addr, rd_data, tb_clk);
-            v_re := unpack_complex_re(rd_data, c_sdp_W_bf_weight);
-            v_im := unpack_complex_im(rd_data, c_sdp_W_bf_weight);
-            -- same BF weights for both beamsets and both beamlet polarizations,
-            -- so fine to use only one sp_bf_x_weights_*_arr()
-            v_S := A * c_sdp_N_pol + P;
-            IF PB = 0 THEN
-              sp_bf_x_weights_re_arr(v_S) <= v_re;
-              sp_bf_x_weights_im_arr(v_S) <= v_im;
-              sp_bf_x_weights_gain_arr(v_S) <= COMPLEX_RADIUS(v_re, v_im) / REAL(c_sdp_unit_bf_weight);
-              sp_bf_x_weights_phase_arr(v_S) <= COMPLEX_PHASE(v_re, v_im);
-            ELSE
-              sp_bf_y_weights_re_arr(v_S) <= v_re;
-              sp_bf_y_weights_im_arr(v_S) <= v_im;
-              sp_bf_y_weights_gain_arr(v_S) <= COMPLEX_RADIUS(v_re, v_im) / REAL(c_sdp_unit_bf_weight);
-              sp_bf_y_weights_phase_arr(v_S) <= COMPLEX_PHASE(v_re, v_im);
-            END IF;
+    FOR RN IN 0 TO c_last_rn LOOP
+      v_gn := g_first_gn + RN;
+      FOR U IN 0 TO c_sdp_N_beamsets-1 LOOP
+        FOR PB IN 0 TO c_sdp_N_pol_bf-1 LOOP
+          FOR A IN 0 TO c_sdp_A_pn-1 LOOP
+            FOR P IN 0 TO c_sdp_N_pol-1 LOOP
+              v_addr := g_beamlet;                              -- beamlet index
+              v_addr := v_addr + P * c_sdp_S_sub_bf;            -- antenna input polarization address offset
+              v_addr := v_addr + A * v_span;                    -- antenna input address offset
+              v_addr := v_addr + PB * c_sdp_A_pn * v_span;      -- beamlet polarization address offset
+              v_addr := v_addr + U * c_mm_span_ram_bf_weights;  -- beamset address offset
+              mmf_mm_bus_rd(c_mm_file_ram_bf_weights, v_addr, rd_data, tb_clk);
+              v_re := unpack_complex_re(rd_data, c_sdp_W_bf_weight);
+              v_im := unpack_complex_im(rd_data, c_sdp_W_bf_weight);
+              -- same BF weights for both beamsets and both beamlet polarizations,
+              -- so fine to use only one sp_bf_x_weights_*_arr()
+              v_S := RN * c_sdp_S_pn + A * c_sdp_N_pol + P;
+              IF PB = 0 THEN
+                sp_bf_x_weights_re_arr(v_S) <= v_re;
+                sp_bf_x_weights_im_arr(v_S) <= v_im;
+                sp_bf_x_weights_gain_arr(v_S) <= COMPLEX_RADIUS(v_re, v_im) / REAL(c_sdp_unit_bf_weight);
+                sp_bf_x_weights_phase_arr(v_S) <= COMPLEX_PHASE(v_re, v_im);
+              ELSE
+                sp_bf_y_weights_re_arr(v_S) <= v_re;
+                sp_bf_y_weights_im_arr(v_S) <= v_im;
+                sp_bf_y_weights_gain_arr(v_S) <= COMPLEX_RADIUS(v_re, v_im) / REAL(c_sdp_unit_bf_weight);
+                sp_bf_y_weights_phase_arr(v_S) <= COMPLEX_PHASE(v_re, v_im);
+              END IF;
+            END LOOP;
           END LOOP;
         END LOOP;
       END LOOP;
@@ -1103,13 +1136,13 @@ BEGIN
     stimuli_done <= '1';
 
     ---------------------------------------------------------------------------
-    -- Read subband statistics
+    -- Read subband statistics from node with g_global_sp
     ---------------------------------------------------------------------------   
     -- . the subband statistics are c_stat_data_sz = 2 word power values.
     -- . there are c_sdp_S_pn = 12 signal inputs A, B, C, D, E, F, G, H, I, J, K, L
     -- . there are c_sdp_N_sub = 512 subbands per signal input (SI, = signal path, SP)
     -- . one complex WPFB can process two real inputs A, B, so there are c_sdp_P_pfb = 6 WPFB units,
-    --   but only read for the 1 WPFB unit of the selected g_sp, to save sim time
+    --   but only read for the 1 WPFB unit of the selected g_global_sp, to save sim time
     -- . the outputs for A, B are time multiplexed, c_sdp_Q_fft = 2, assume that they
     --   correspond to the c_sdp_N_pol = 2 signal polarizations
     -- . the subbands are output alternately so A0 B0 A1 B1 ... A511 B511 for input A, B
@@ -1141,13 +1174,13 @@ BEGIN
     END LOOP;
     proc_common_wait_some_cycles(tb_clk, 1);
 
-    -- Subband power of g_subband in g_sp
+    -- Subband power of g_subband in g_global_sp
     sp_sst <= TO_UREAL(sp_ssts_arr2(c_pol_index)(g_subband));
     proc_common_wait_some_cycles(tb_clk, 1);
     proc_common_wait_some_cycles(ext_clk, 100);  -- delay for ease of view in Wave window
  
     ---------------------------------------------------------------------------
-    -- Read beamlet statistics from end node
+    -- Read beamlet statistics from last node
     ---------------------------------------------------------------------------
     -- . the beamlet statistics are c_stat_data_sz = 2 word power values.
     -- . there are c_sdp_S_sub_bf = 488 dual pol beamlets per beamset
@@ -1258,8 +1291,8 @@ BEGIN
     -- Verify SST
     ---------------------------------------------------------------------------
     -- verify expected subband power based on WG power
-    ASSERT sp_sst > c_stat_lo_factor * c_exp_subband_sst REPORT "Wrong subband power for SP " & NATURAL'IMAGE(g_sp) SEVERITY ERROR;
-    ASSERT sp_sst < c_stat_hi_factor * c_exp_subband_sst REPORT "Wrong subband power for SP " & NATURAL'IMAGE(g_sp) SEVERITY ERROR;
+    ASSERT sp_sst > c_stat_lo_factor * c_exp_subband_sst REPORT "Wrong subband power for SP " & NATURAL'IMAGE(g_global_sp) SEVERITY ERROR;
+    ASSERT sp_sst < c_stat_hi_factor * c_exp_subband_sst REPORT "Wrong subband power for SP " & NATURAL'IMAGE(g_global_sp) SEVERITY ERROR;
 
     ---------------------------------------------------------------------------
     -- Verify BST
@@ -1328,7 +1361,7 @@ BEGIN
 
   exp_sdp_cep_header <= func_sdp_compose_cep_header(c_exp_ip_header_checksum,
                                                     c_exp_sdp_info,
-                                                    c_gn_index,
+                                                    c_last_gn,
                                                     c_exp_beamlet_scale,
                                                     c_exp_beamlet_index,
                                                     exp_dp_bsn);
@@ -1412,4 +1445,5 @@ BEGIN
 
   -- 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);
+
 END tb;
-- 
GitLab