Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sdp_statistics_offload.vhd 17.25 KiB
-------------------------------------------------------------------------------
--
-- Copyright 2020
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
--
-- Author: P. Donker, R van der Walle

-- Purpose:
-- . SDP statistics offload
-- Description:
-- https://support.astron.nl/confluence/display/L2M/L5+SDPFW+Design+Document%3A+Subband+filterbank
-- . See figure 4.3
-- https://plm.astron.nl/polarion/#/project/LOFAR2System/wiki/L2%20Interface%20Control%20Documents/SC%20to%20SDP%20ICD
-- . See 2.9.4 Station Control (L3-SC) - SDP Firmware (L4-SDPFW)
--
-------------------------------------------------------------------------------

LIBRARY IEEE, common_lib, mm_lib, dp_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE common_lib.common_field_pkg.ALL;
USE common_lib.common_network_layers_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE work.sdp_pkg.ALL;

ENTITY sdp_statistics_offload IS
  GENERIC (
    g_statistics_type : STRING  := "SST";
    g_offload_time    : NATURAL := c_sdp_offload_time;
    g_beamset_id      : NATURAL := 0;
    g_P_sq            : NATURAL := c_sdp_P_sq -- use generic to support P_sq = 1 for one node and P_sq = c_sdp_P_sq for multiple nodes (with ring)
  );
  PORT (
    -- Clocks and reset
    mm_rst : IN  STD_LOGIC;   -- reset synchronous with mm_clk
    mm_clk : IN  STD_LOGIC;   -- memory-mapped bus clock

    dp_clk : IN  STD_LOGIC;
    dp_rst : IN  STD_LOGIC;

    -- Memory access to statistics values
    master_mosi : OUT  t_mem_mosi;  -- := c_mem_mosi_rst;
    master_miso : IN t_mem_miso;

    -- Memory access to read/write settings
    reg_enable_mosi : IN  t_mem_mosi := c_mem_mosi_rst;
    reg_enable_miso : OUT t_mem_miso;

    reg_hdr_dat_mosi : IN  t_mem_mosi := c_mem_mosi_rst;
    reg_hdr_dat_miso : OUT t_mem_miso;

    -- Input timing regarding the integration interval of the statistics
    in_sosi     : IN t_dp_sosi;
    
    -- Streaming output of offloaded statistics packets
    out_sosi    : OUT t_dp_sosi;
    out_siso    : IN t_dp_siso;

    -- inputs from other blocks
    eth_src_mac             : IN STD_LOGIC_VECTOR(c_network_eth_mac_addr_w-1 DOWNTO 0);
    udp_src_port            : IN STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0);
    ip_src_addr             : IN STD_LOGIC_VECTOR(c_network_ip_addr_w-1 DOWNTO 0);
    sdp_info                : IN t_sdp_info;
    subband_calibrated_flag : IN STD_LOGIC := '0';
    crosslets_info          : IN STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0) := (OTHERS => '0');

    gn_index     : IN NATURAL
  );
END sdp_statistics_offload;


ARCHITECTURE str OF sdp_statistics_offload IS


  CONSTANT c_nof_streams               : NATURAL := 1;
  CONSTANT c_data_size                 : NATURAL := 2; 
  CONSTANT c_nof_data_per_step         : NATURAL := 2; 

  CONSTANT c_step_size                 : NATURAL := sel_a_b(g_statistics_type="BST",  c_data_size,
                                                    sel_a_b(g_statistics_type="XST",  c_data_size, 
                                                                                      c_data_size * c_nof_data_per_step));  -- SST

  CONSTANT c_nof_data                  : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_N_pol_bf * c_sdp_S_sub_bf,
                                                    sel_a_b(g_statistics_type="XST",  c_sdp_S_pn * c_sdp_S_pn * c_nof_complex, 
                                                                                      c_sdp_N_sub));  -- SST
  CONSTANT c_block_size                : NATURAL := c_nof_data * c_step_size;
  
  CONSTANT c_nof_packets               : NATURAL := sel_a_b(g_statistics_type="BST", 1,
                                                    sel_a_b(g_statistics_type="XST", g_P_sq,
                                                                                     c_sdp_S_pn));  -- SST
  
  CONSTANT c_marker                    : NATURAL := sel_a_b(g_statistics_type="BST", c_sdp_marker_bst,
                                                    sel_a_b(g_statistics_type="XST", c_sdp_marker_xst,
                                                                                     c_sdp_marker_sst));
  
  CONSTANT c_nof_signal_inputs         : NATURAL := sel_a_b(g_statistics_type="BST", 0,
                                                    sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
                                                                                     1));  -- SST
  
  CONSTANT c_nof_statistics_per_packet : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_N_pol_bf * c_sdp_S_sub_bf,
                                                    sel_a_b(g_statistics_type="XST", (c_sdp_S_pn * c_sdp_S_pn * c_nof_complex), 
                                                                                      c_sdp_N_sub));  -- SST
  
  CONSTANT c_beamlet_id                : NATURAL := g_beamset_id * c_sdp_S_sub_bf;

  CONSTANT c_app_total_length : NATURAL := c_sdp_stat_app_header_len + c_nof_data * c_longword_sz;
  CONSTANT c_udp_total_length : NATURAL := c_app_total_length + c_network_udp_header_len;
  CONSTANT c_ip_total_length  : NATURAL := c_app_total_length + c_network_udp_header_len + c_network_ip_header_len;

  TYPE t_reg IS RECORD
    block_count          : NATURAL;
    start_address        : NATURAL;
    start_pulse          : STD_LOGIC;
    dp_header_info       : STD_LOGIC_VECTOR(1023 DOWNTO 0);
    data_id              : STD_LOGIC_VECTOR(31 DOWNTO 0);
    nof_cycles_dly       : NATURAL;
    payload_err          : STD_LOGIC;
    interval_cnt         : NATURAL;
    integration_interval : NATURAL; 
  END RECORD;

  CONSTANT c_reg_rst : t_reg := (0, 0, '0', (OTHERS => '0'), (OTHERS => '0'), 0, '0', 0, 0);

  SIGNAL r     : t_reg;
  SIGNAL nxt_r : t_reg;

  SIGNAL trigger                  : STD_LOGIC := '0';
  SIGNAL done                     : STD_LOGIC := '0';
  SIGNAL dp_block_from_mm_src_out : t_dp_sosi;
  SIGNAL dp_block_from_mm_src_in  : t_dp_siso;
  
  SIGNAL dp_offload_snk_in        : t_dp_sosi;
  SIGNAL dp_offload_snk_out       : t_dp_siso;

  SIGNAL dp_header_info           : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0');
  SIGNAL bsn_at_sync              : STD_LOGIC_VECTOR(63 DOWNTO 0) := (OTHERS => '0');

  SIGNAL selected_crosslet        : STD_LOGIC_VECTOR(c_sdp_crosslets_index_w-1 DOWNTO 0);
  --SIGNAL sdp_data_id : STD_LOGIC_VECTOR(31 DOWNTO 0);
  
BEGIN

  bsn_at_sync <= RESIZE_UVEC(in_sosi.bsn, 64) WHEN rising_edge(dp_clk) AND in_sosi.sync = '1';
  selected_crosslet <= crosslets_info(c_sdp_crosslets_index_w-1 DOWNTO 0);
    
  -------------------------------------------------------------------------------
  -- Assemble offload header info
  -------------------------------------------------------------------------------
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "eth_src_mac"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "eth_src_mac"                             )) <= eth_src_mac;
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "udp_src_port"                            )) <= udp_src_port;
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "ip_src_addr"                             )) <= ip_src_addr;
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "ip_total_length"                         ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "ip_total_length"                         )) <= TO_UVEC(c_ip_total_length, 16);
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "udp_total_length"                        ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "udp_total_length"                        )) <= TO_UVEC(c_udp_total_length, 16);
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_marker"                              ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_marker"                              )) <= TO_UVEC(c_marker, 8);
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_observation_id"                      ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_observation_id"                      )) <= sdp_info.observation_id;
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_station_id"                          ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_station_id"                          )) <= sdp_info.station_id;
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id"         ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_antenna_band_id"         )) <= SLV(sdp_info.antenna_band_index);
  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_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_subband_calibrated_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_subband_calibrated_flag" )) <= SLV(subband_calibrated_flag);
  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_reserved"                ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_reserved"                )) <= (OTHERS => '0');
  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_reserved"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_reserved"                            )) <= (OTHERS => '0');
  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_data_id"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_data_id"                             )) <= r.data_id;
  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_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; 

  p_reg : PROCESS(dp_rst, dp_clk)
  BEGIN
    IF dp_rst='1' THEN
      r <= c_reg_rst;
    ELSIF rising_edge(dp_clk) THEN
      r <= nxt_r;
    END IF;
  END PROCESS;

  p_control_packet_offload : PROCESS(r, gn_index, in_sosi, trigger, done, dp_header_info, selected_crosslet)
    VARIABLE v: t_reg;
  BEGIN
    v := r;
    v.start_pulse    := '0';
    v.nof_cycles_dly := gn_index * g_offload_time;
    
    -- Count number of sop's in a sync interval and get payload errors and keep them till next sync.
    IF in_sosi.sync = '1' THEN
      v.integration_interval := r.interval_cnt + 1;  -- count = index + 1
      v.interval_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.interval_cnt := r.interval_cnt + 1;
      END IF;
    END IF;

    -- assign sdp_data_id for different statistic types
    IF g_statistics_type = "SST" THEN
      v.data_id := x"000000" & TO_UVEC(r.block_count + c_sdp_S_pn * gn_index, 8);
    ELSIF g_statistics_type = "BST" THEN
      v.data_id := x"0000" & TO_UVEC(c_beamlet_id, 16);
    ELSIF g_statistics_type = "XST" THEN
      v.data_id := x"0" & "000" & RESIZE_UVEC(selected_crosslet, 9) & TO_UVEC(r.block_count * c_sdp_S_pn, 8) & TO_UVEC(r.block_count * c_sdp_S_pn, 8); -- RW TODO: define for P_sq > 1
    ELSE
      v.data_id := x"00000000";
    END IF;

    -- Issue start_pulse per packet offload
    IF trigger = '1' THEN
      -- Use trigger to start first packet
      v.start_pulse   := '1';
      v.start_address := 0;
      v.block_count   := 0;
    ELSIF done = '1' THEN
      -- Use done to start next packets
      IF r.block_count < c_nof_packets-1 THEN
        IF r.block_count MOD c_nof_data_per_step = 0 THEN
          v.start_address := r.block_count / c_nof_data_per_step * c_block_size;  -- jump to first packet in next block
        ELSE 
          v.start_address := r.start_address + c_data_size;  -- step to next packet within block
        END IF;
        v.start_pulse := '1';
        v.block_count := r.block_count + 1;
      ELSE
        -- Prepare for next trigger interval.
        v.start_address := 0;
        v.block_count   := 0;
      END IF;
    END IF;

    -- Release header info per packet offload
    IF trigger = '1' OR done = '1' THEN
      v.dp_header_info := dp_header_info;
    END IF;
    nxt_r <= v;
  END PROCESS;

  u_mms_common_variable_delay : ENTITY common_lib.mms_common_variable_delay
  PORT MAP (
    mm_rst => mm_rst,
    mm_clk => mm_clk,
    dp_rst => dp_rst,
    dp_clk => dp_clk,

    -- MM interface
    reg_enable_mosi => reg_enable_mosi,
    reg_enable_miso => reg_enable_miso,

    delay           => r.nof_cycles_dly,
    trigger         => in_sosi.sync,
    trigger_dly     => trigger
  );
  
  u_dp_block_from_mm : ENTITY dp_lib.dp_block_from_mm_dc
  GENERIC MAP (
    g_data_size          => c_data_size,
    g_step_size          => c_step_size,
    g_nof_data           => c_nof_data,
    g_reverse_word_order => TRUE -- default word order is MSB after LSB, we need to stream LSB after MSB.
  ) 
  PORT MAP(
    dp_rst        => dp_rst,
    dp_clk        => dp_clk,
    mm_rst        => mm_rst,
    mm_clk        => mm_clk,
    start_pulse   => r.start_pulse,
    start_address => r.start_address,
    done          => done,
    mm_mosi       => master_mosi,
    mm_miso       => master_miso,
    out_sosi      => dp_block_from_mm_src_out,
    out_siso      => dp_block_from_mm_src_in
  );

  u_dp_pipeline_ready : ENTITY dp_lib.dp_pipeline_ready
  PORT MAP(
    rst          => dp_rst,
    clk          => dp_clk,
    -- ST sink
    snk_out      => dp_block_from_mm_src_in,
    snk_in       => dp_block_from_mm_src_out,
    -- ST source
    src_in       => dp_offload_snk_out,
    src_out      => dp_offload_snk_in
  );

  u_dp_offload_tx_v3: ENTITY dp_lib.dp_offload_tx_v3
  GENERIC MAP (
    g_nof_streams    => c_nof_streams,
    g_data_w         => c_word_w,
    g_symbol_w       => c_word_w,
    g_hdr_field_arr  => c_sdp_stat_hdr_field_arr,
    g_hdr_field_sel  => c_sdp_stat_hdr_field_sel,
    g_pipeline_ready => TRUE
  )
  PORT MAP(
    mm_rst               => mm_rst,
    mm_clk               => mm_clk,
    dp_rst               => dp_rst,
    dp_clk               => dp_clk,
    reg_hdr_dat_mosi     => reg_hdr_dat_mosi,
    reg_hdr_dat_miso     => reg_hdr_dat_miso,
    snk_in_arr(0)        => dp_offload_snk_in,
    snk_out_arr(0)       => dp_offload_snk_out,
    src_out_arr(0)       => out_sosi,
    src_in_arr(0)        => out_siso,
    hdr_fields_in_arr(0) => r.dp_header_info
  );
END str;