diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd index b9815e98407b87317b19f63ed24da1f6c0b7fc98..bd5b00ce8c6788396ff993949fd530c6d3c823ab 100644 --- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd +++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd @@ -84,13 +84,13 @@ -- BST (Xh, Xl), (Yh, Yl), 2 keep X, Y parts order -- XST (Rh, Rl), (Ih, Il), 2 keep Re, Im parts order -- --- . g_P_sq and nof_used_P_sq +-- . g_P_sq and p.nof_used_P_sq -- The g_P_sq defines the number of correlator cells that is available in -- the SDPFW. Use generic to support P_sq = 1 for one node and P_sq = -- c_sdp_P_sq for multiple nodes (with ring). --- The nof_used_P_sq is the number of correlator cells that is actually +-- The p.nof_used_P_sq is the number of correlator cells that is actually -- used and that will output XST packets. Unused correlator cells yield --- zero data that should not be output. The nof_used_P_sq is the smallest +-- zero data that should not be output. The p.nof_used_P_sq is the smallest -- of g_P_sq and ring_info.N_rn/2 + 1. In this way the XST offload can work -- with g_P_sq = 1 when N_rn > 1 and also in a ring with N_rn < N_pn when -- g_P_sq = 9. @@ -158,8 +158,8 @@ ENTITY sdp_statistics_offload IS sdp_info : IN t_sdp_info; weighted_subbands_flag : IN STD_LOGIC := '0'; - nof_crosslets : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0'); - crosslets_info_rec : IN t_sdp_crosslets_info := c_sdp_crosslets_info_rst + nof_crosslets : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0'); -- from MM + prev_crosslets_info_rec : IN t_sdp_crosslets_info := c_sdp_crosslets_info_rst ); END sdp_statistics_offload; @@ -185,43 +185,57 @@ ARCHITECTURE str OF sdp_statistics_offload IS CONSTANT c_mm_nof_data : NATURAL := func_sdp_get_stat_from_mm_nof_data(g_statistics_type); CONSTANT c_mm_ram_size : NATURAL := c_mm_nof_data * c_mm_data_size * c_nof_packets_max; - -- offload control + -- Parameters that are fixed per node + TYPE t_parameters IS RECORD + gn_index : NATURAL; -- index of this global node + pn_index : NATURAL; -- index of this node in antenna band + rn_index : NATURAL; -- index of this ring node + local_si_offset : NATURAL; -- index of first signal input on this node + remote_rn : NATURAL; -- index of remote ring node + remote_gn : NATURAL; -- index of remote global node + remote_pn : NATURAL; -- index of remote node in antenna band + remote_si_offset : NATURAL; -- index of first signal input on remote node + nof_cycles_dly : NATURAL; -- trigger_offload delay for this node + offset_rn : NATURAL; -- = ring_info.O_rn, GN index of first ring node + nof_rn : NATURAL; -- = ring_info.N_rn, number of GN in the ring + nof_used_P_sq : NATURAL; -- number of used correlator cells <= g_P_sq (is number of available correlator cells) + END RECORD; + + -- Input capture per sync interval + TYPE t_input IS RECORD + nof_crosslets : NATURAL RANGE 0 TO c_sdp_N_crosslets_max; -- nof_crosslets from MM + nof_packets : NATURAL; -- nof XST offload packets per integration interval dependend on nof_crosslets + bsn_at_sync : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0); + integration_interval : NATURAL; + crosslets_info_rec : t_sdp_crosslets_info; + sop_cnt : NATURAL; + payload_err : STD_LOGIC; + END RECORD; + + -- Offload control TYPE t_reg IS RECORD packet_count : NATURAL RANGE 0 TO c_nof_packets_max; start_address : NATURAL RANGE 0 TO c_mm_ram_size; start_pulse : STD_LOGIC; start_sync : STD_LOGIC; dp_header_info : STD_LOGIC_VECTOR(1023 DOWNTO 0); - payload_err : STD_LOGIC; - in_sop_cnt : NATURAL; - integration_interval : NATURAL; interleave_count : NATURAL RANGE 0 TO c_sdp_Q_fft; interleave_address : NATURAL RANGE 0 TO c_mm_ram_size; crosslet_count : NATURAL RANGE 0 TO c_sdp_N_crosslets_max; instance_count : NATURAL RANGE 0 TO c_sdp_P_sq; instance_address : NATURAL RANGE 0 TO c_mm_ram_size; - nof_crosslets : NATURAL RANGE 0 TO c_sdp_N_crosslets_max; - crosslets_info_rec : t_sdp_crosslets_info; END RECORD; - CONSTANT c_reg_rst : t_reg := (0, 0, '0', '0', (OTHERS => '0'), '0', 0, 0, 0, 0, 0, 0, 0, 0, c_sdp_crosslets_info_rst); - - SIGNAL r : t_reg; - SIGNAL nxt_r : t_reg; - - SIGNAL gn_index_reg : NATURAL; -- index of this global node - SIGNAL pn_index : NATURAL; -- index of this node in antenna band - SIGNAL rn_index : NATURAL; -- index of this ring node - SIGNAL local_si_offset : NATURAL; -- index of first signal input on this node - SIGNAL remote_rn : NATURAL; -- index of remote ring node - SIGNAL remote_gn : NATURAL; -- index of remote global node - SIGNAL remote_pn : NATURAL; -- index of remote node in antenna band - SIGNAL remote_si_offset : NATURAL; -- index of first signal input on remote node - SIGNAL nof_cycles_dly : NATURAL; -- trigger_offload delay for this node - SIGNAL offset_rn : NATURAL; -- = ring_info.O_rn, GN index of first ring node - SIGNAL nof_rn : NATURAL; -- = ring_info.N_rn, number of GN in the ring - SIGNAL nof_used_P_sq : NATURAL; -- number of used correlator cells <= g_P_sq (is number of available correlator cells) - SIGNAL nof_packets : NATURAL; -- nof XST offload packets per integration interval + CONSTANT c_reg_rst : t_reg := (0, 0, '0', '0', (OTHERS => '0'), 0, 0, 0, 0, 0); + + SIGNAL p : t_parameters; + + SIGNAL reg_input : t_input; + SIGNAL prev_input : t_input; + SIGNAL hdr_input : t_input; + + SIGNAL r : t_reg; + SIGNAL nxt_r : t_reg; SIGNAL data_id_rec : t_sdp_stat_data_id; SIGNAL data_id_slv : STD_LOGIC_VECTOR(31 DOWNTO 0) := (OTHERS => '0'); @@ -239,7 +253,6 @@ ARCHITECTURE str OF sdp_statistics_offload IS SIGNAL udp_sosi : t_dp_sosi; - SIGNAL bsn_at_sync : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := (OTHERS => '0'); SIGNAL dp_header_info : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0'); SIGNAL r_dp_header_sop : STD_LOGIC; SIGNAL r_dp_header_rec : t_sdp_stat_header; @@ -258,8 +271,6 @@ ARCHITECTURE str OF sdp_statistics_offload IS BEGIN - bsn_at_sync <= RESIZE_UVEC(in_sosi.bsn, c_dp_stream_bsn_w) WHEN rising_edge(dp_clk) AND in_sosi.sync = '1'; - ------------------------------------------------------------------------------- -- Assemble offload header info, for data path fields that are selected by: -- c_sdp_stat_hdr_field_sel = "1"&"101"&"111011111001"&"0100"&"0100"&"000000010"&"1000000"&"0" @@ -300,17 +311,17 @@ BEGIN dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id" )) <= sdp_info.nyquist_zone_index; dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc" )) <= SLV(sdp_info.f_adc); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type" )) <= SLV(sdp_info.fsub_type); - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" )) <= SLV(r.payload_err); + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error" )) <= SLV(hdr_input.payload_err); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag" )) <= SLV(sdp_info.beam_repositioning_flag); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_weighted_subbands_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_weighted_subbands_flag" )) <= SLV(weighted_subbands_flag); - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" )) <= TO_UVEC(gn_index, 5); - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" )) <= TO_UVEC(r.integration_interval, 24); + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id" )) <= TO_UVEC(p.gn_index, 5); + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_integration_interval" )) <= TO_UVEC(hdr_input.integration_interval, 24); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_data_id" )) <= data_id_slv; dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs" )) <= TO_UVEC(c_nof_signal_inputs, 8); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic" )) <= TO_UVEC(c_sdp_nof_bytes_per_statistic, 8); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet" )) <= TO_UVEC(c_nof_statistics_per_packet, 16); dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_block_period" )) <= sdp_info.block_period; - dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn" )) <= bsn_at_sync; + dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn" )) <= hdr_input.bsn_at_sync; p_reg : PROCESS(dp_rst, dp_clk) BEGIN @@ -321,81 +332,101 @@ BEGIN END IF; END PROCESS; - -- Derive and pipeline dynamic parameters - p_parameters : PROCESS(dp_clk) + -- Derive and pipeline dynamic parameters that depend on node id and MM, but are otherwise fixed + p_reg_parameters : PROCESS(dp_clk) BEGIN IF rising_edge(dp_clk) THEN - gn_index_reg <= gn_index; - pn_index <= func_sdp_gn_index_to_pn_index(gn_index_reg); - offset_rn <= TO_UINT(ring_info.O_rn); - rn_index <= gn_index_reg - offset_rn; - local_si_offset <= pn_index * c_sdp_S_pn; - nof_cycles_dly <= gn_index_reg * g_offload_time; - nof_rn <= TO_UINT(ring_info.N_rn); - nof_used_P_sq <= smallest(nof_rn / 2 + 1, g_P_sq); - nof_packets <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, nof_used_P_sq, r.nof_crosslets); - remote_rn <= func_ring_nof_hops_to_source_rn(r.instance_count, rn_index, nof_rn, g_crosslets_direction); - remote_gn <= offset_rn + remote_rn; - remote_pn <= func_sdp_gn_index_to_pn_index(remote_gn); - remote_si_offset <= remote_pn * c_sdp_S_pn; + p.gn_index <= gn_index; + p.pn_index <= func_sdp_gn_index_to_pn_index(p.gn_index); + p.offset_rn <= TO_UINT(ring_info.O_rn); + p.rn_index <= p.gn_index - p.offset_rn; + p.local_si_offset <= p.pn_index * c_sdp_S_pn; + p.nof_cycles_dly <= p.gn_index * g_offload_time; + p.nof_rn <= TO_UINT(ring_info.N_rn); + p.nof_used_P_sq <= smallest(p.nof_rn / 2 + 1, g_P_sq); + p.remote_rn <= func_ring_nof_hops_to_source_rn(r.instance_count, p.rn_index, p.nof_rn, g_crosslets_direction); + p.remote_gn <= p.offset_rn + p.remote_rn; + p.remote_pn <= func_sdp_gn_index_to_pn_index(p.remote_gn); + p.remote_si_offset <= p.remote_pn * c_sdp_S_pn; + END IF; + END PROCESS; + + -- Determine info from previous sync interval in which the statistics were + -- measured. + -- . hold nof_crosslets from MM (and related nof_packets) per sync interval + -- . capture bsn_at_sync at start of a sync interval + -- . count number of sop that occur in a sync interval, to determine the + -- actual number of sop for the previous integration_interval + -- . track whether payload errors occur in a sync interval, to determine + -- the payload_err flag for the previous integration_interval + p_input_capture : PROCESS(dp_clk) + BEGIN + IF rising_edge(dp_clk) THEN + -- Capture input + IF in_sosi.sync = '1' THEN + reg_input.nof_crosslets <= TO_UINT(nof_crosslets); + reg_input.bsn_at_sync <= in_sosi.bsn; + reg_input.integration_interval <= reg_input.sop_cnt + 1; -- size = index + 1 + reg_input.crosslets_info_rec <= prev_crosslets_info_rec; + reg_input.sop_cnt <= 0; + reg_input.payload_err <= '0'; + ELSIF in_sosi.sop = '1' THEN + reg_input.sop_cnt <= reg_input.sop_cnt + 1; + ELSIF in_sosi.eop = '1' THEN + reg_input.payload_err <= reg_input.payload_err OR in_sosi.err(0); + END IF; + reg_input.nof_packets <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, p.nof_used_P_sq, reg_input.nof_crosslets); + + -- Determine previous sync interval info + -- . just register when the value suits any sync interval + prev_input.nof_crosslets <= reg_input.nof_crosslets; + prev_input.nof_packets <= reg_input.nof_packets; + -- . register at sync to get value for previous sync interval + IF in_sosi.sync = '1' THEN + prev_input.bsn_at_sync <= reg_input.bsn_at_sync; + END IF; + -- . just register when the value already is for the previous sync interval + prev_input.integration_interval <= reg_input.integration_interval; + prev_input.crosslets_info_rec <= reg_input.crosslets_info_rec; + prev_input.sop_cnt <= reg_input.sop_cnt; + prev_input.payload_err <= reg_input.payload_err; + + -- Hold previous sync interval info at trigger_offload, so that + -- hdr_input can be used in p_control_packet_offload and + -- dp_header_info. + IF trigger_offload = '1' THEN + hdr_input <= prev_input; + END IF; END IF; END PROCESS; -- Assign application header data_id for different statistic types, use -- GENERATE to keep unused fields at 0 (all fields are NATURAL, so default to 0). gen_data_id_sst : IF g_statistics_type = "SST" GENERATE - data_id_rec.sst_signal_input_index <= r.packet_count + local_si_offset; + data_id_rec.sst_signal_input_index <= r.packet_count + p.local_si_offset; END GENERATE; gen_data_id_bst : IF g_statistics_type = "BST" GENERATE data_id_rec.bst_beamlet_index <= c_beamlet_id; END GENERATE; gen_data_id_xst : IF g_statistics_type = "XST" GENERATE - data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(r.crosslets_info_rec.offset_arr(r.crosslet_count)); - data_id_rec.xst_signal_input_A_index <= local_si_offset; - data_id_rec.xst_signal_input_B_index <= remote_si_offset; + data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(hdr_input.crosslets_info_rec.offset_arr(r.crosslet_count)); + data_id_rec.xst_signal_input_A_index <= p.local_si_offset; + data_id_rec.xst_signal_input_B_index <= p.remote_si_offset; END GENERATE; data_id_slv <= func_sdp_map_stat_data_id(g_statistics_type, data_id_rec); - p_control_packet_offload : PROCESS(r, in_sosi, nof_crosslets, crosslets_info_rec, trigger_offload, dp_sop, dp_header_info, nof_packets) -- in order of appearance + -- sensitivity list in order of appearance + p_control_packet_offload : PROCESS(r, trigger_offload, dp_sop, dp_header_info, reg_input) VARIABLE v : t_reg; VARIABLE v_index : NATURAL; BEGIN v := r; v.start_pulse := '0'; v.start_sync := '0'; - - -- Count number of sop in a sync interval and get payload errors and keep them till next sync. - IF in_sosi.sync = '1' THEN - v.integration_interval := r.in_sop_cnt + 1; -- count = index + 1 - v.in_sop_cnt := 0; - v.payload_err := '0'; - ELSE - IF in_sosi.eop = '1' THEN - v.payload_err := r.payload_err OR in_sosi.err(0); - END IF; - - IF in_sosi.sop = '1' THEN - v.in_sop_cnt := r.in_sop_cnt + 1; - END IF; - END IF; - - -- For XST offload capture nof_crosslets and crosslets_info_rec at in_sosi.sync, - -- to make sure they do not change during packets offload. - -- . The sdp_crosslets_subband_select.vhd in [2] takes care that - -- nof_crosslets and crosslets_info_rec are valid at the xsel_sosi.sync. The - -- mmp_dp_bsn_align_v2 in [2] then aligns the local xsel_sosi with the - -- remote data and passes on the sync. After some latency the sync - -- arrives at the sdp_statistics_offload. This latency is very short - -- compared to the sync period, so the nof_crosslets and crosslets_info_rec - -- are still valid at the in_sosi.sync. - IF in_sosi.sync = '1' THEN - v.nof_crosslets := TO_UINT(nof_crosslets); - v.crosslets_info_rec := crosslets_info_rec; - END IF; - -- The trigger_offload occurs nof_cycles_dly after the in_sosi.sync and the - -- offload will have finished before the next in_sosi.sync, because + -- The trigger_offload occurs p.nof_cycles_dly after the in_sosi.sync and + -- the offload will have finished before the next in_sosi.sync, because -- c_sdp_offload_time is such that all offload will finish within 100 ms -- and the integration interval (= sync interval) is 1 s for SST and BST -- and minimal 0.1s (= c_sdp_xst_nof_clk_per_sync_min) for XST. @@ -428,7 +459,7 @@ BEGIN v.dp_header_info := dp_header_info; -- Start next packets offload. - IF r.packet_count < nof_packets - 1 THEN + IF r.packet_count < reg_input.nof_packets - 1 THEN IF g_statistics_type = "SST" THEN -- step step step step step step -- start_address : 0, 2, 2048, 2050, 4096, 4098, 6144, 6146, 8192, 8194, 10240, 10242 @@ -448,7 +479,8 @@ BEGIN ELSIF g_statistics_type = "XST" THEN -- start_address: - -- nof_crosslets: 1, 2, 3, 4, 5, 6, 7 + -- reg_input.nof_crosslets: + -- 1, 2, 3, 4, 5, 6, 7 -- X_sq instance: -- 0 0, 576, 1152, 1728, 2304, 2880, 3456 -- 1 4096, 4672, 5248, 5824, 6400, 6976, 7552 @@ -461,7 +493,7 @@ BEGIN -- 8 32768, 33344, 33920, 34496, 35072, 35648, 36224 v.start_address := r.start_address + c_sdp_X_sq * c_nof_complex * c_sdp_W_statistic_sz; -- continue with next packet in this instance v.crosslet_count := r.crosslet_count + 1; - IF r.crosslet_count = TO_UINT(nof_crosslets) - 1 THEN + IF r.crosslet_count = reg_input.nof_crosslets - 1 THEN v.start_address := r.instance_address + 2**c_sdp_ram_st_xsq_addr_w; -- jump to first packet in next instance v.crosslet_count := 0; v.instance_count := r.instance_count + 1; @@ -498,7 +530,7 @@ BEGIN reg_enable_mosi => reg_enable_mosi, reg_enable_miso => reg_enable_miso, - delay => nof_cycles_dly, + delay => p.nof_cycles_dly, trigger => in_trigger, trigger_en => trigger_en, trigger_dly => trigger_offload @@ -522,7 +554,7 @@ BEGIN mm_clk => mm_clk, start_pulse => r.start_pulse, sync_in => r.start_sync, - bsn_at_sync => bsn_at_sync, + bsn_at_sync => hdr_input.bsn_at_sync, start_address => r.start_address, mm_mosi => master_mosi, mm_miso => master_miso, @@ -551,7 +583,7 @@ BEGIN src_out => dp_offload_snk_in ); - -- The bsn_at_sync is passed on via r.dp_header_info so that + -- The hdr_input.bsn_at_sync is passed on via r.dp_header_info so that -- u_dp_offload_tx_v3 can put it in the udp_sosi header. -- The dp_offload_snk_in.bsn that is passed on by u_dp_block_from_mm_dc is -- in fact not used, but useful to see in udp_sosi.bsn in the Wave Window.