Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sdp_bdo_pkg.vhd 17.28 KiB
-------------------------------------------------------------------------------
--
-- Copyright 2023
-- 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: E. Kooistra
-- Purpose:
-- . This package contains sdp beamlet data output (BDO) specific constants.
-- Description: See [1]
-- References:
-- . [1] https://support.astron.nl/confluence/display/L2M/L4+SDPFW+Decision%3A+Multiple+beamlet+output+destinations
-------------------------------------------------------------------------------
library IEEE, common_lib;
use IEEE.std_logic_1164.all;
use common_lib.common_pkg.all;
use work.sdp_pkg.all;

package sdp_bdo_pkg is
  -- Beamlet data output (BDO) for multiple destinations

  -- Define the maximum number of destination to size the address span of the
  -- MM register in sdp_bdo_destinations_reg. The actual nof_destinations_max
  -- <= c_sdp_bdo_mm_nof_destinations_max is defined as revision constant, so
  -- that it can differ per design revision.
  constant c_sdp_bdo_mm_nof_destinations_max : natural := 32;

  -- Define the maximum number of blocks (= time samples per beamlet) here as
  -- a package constant, because it can be the same for all design revisions.
  -- The actual reorder_nof_blocks_max depends slightly on
  -- nof_destinations_max, because the number of blocks has to fit in a jumbo
  -- frame. Therefore func_sdp_bdo_reorder_nof_blocks_look_up_table()
  -- determines the actual reorder_nof_blocks.
  -- The nof_blocks_per_packet = reorder_nof_blocks. The beamlet packets will
  -- have the same nof_blocks_per_packet for each destination, because the
  -- blocks represent beamlet time samples that have to be kept together per
  -- destination. The beamlets are distributed to the different destinations
  -- based on their beamlet index as defined by
  -- func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table().
  -- The minimum value is c_sdp_cep_nof_blocks_per_packet = 4 to fill a jumbo
  -- frame when nof_destinations = 1.
  -- The maximum value is a balance between having sufficiently large packets
  -- nof_destinations > 1 and how many block RAM resources are available for
  -- the reordering. Therefore c_sdp_bdo_reorder_nof_blocks_max = 16 is a
  -- suitable compromise value.
  constant c_sdp_bdo_reorder_nof_blocks_max : natural := 16;
  constant c_sdp_bdo_reorder_nof_blocks_w   : natural := ceil_log2(c_sdp_bdo_reorder_nof_blocks_max + 1);
  constant c_sdp_bdo_reorder_nof_ch_max     : natural := c_sdp_bdo_reorder_nof_blocks_max *
                                                         c_sdp_nof_beamlets_per_block *
                                                         c_sdp_nof_words_per_beamlet;  -- = 7808

  -- 32 * 3 + 4 = 100 fields
  constant c_sdp_bdo_destinations_info_nof_hdr_fields : natural := c_sdp_bdo_mm_nof_destinations_max * 3 + 4;
  type t_sdp_bdo_destinations_info is record
    eth_destination_mac_arr     : t_slv_48_arr(c_sdp_bdo_mm_nof_destinations_max - 1 downto 0);
    ip_destination_address_arr  : t_slv_32_arr(c_sdp_bdo_mm_nof_destinations_max - 1 downto 0);
    udp_destination_port_arr    : t_slv_16_arr(c_sdp_bdo_mm_nof_destinations_max - 1 downto 0);
    nof_destinations            : natural;
    nof_destinations_act        : natural;
    nof_destinations_max        : natural;
    nof_blocks_per_packet       : natural;
  end record;

  constant c_sdp_bdo_destinations_info_rst : t_sdp_bdo_destinations_info :=
    ( (others => (others => '0')),
      (others => (others => '0')),
      (others => (others => '0')),
      1,
      1,
      1,
      c_sdp_cep_nof_blocks_per_packet);

  -- Parse user input to determine actual nof_destinations
  function func_sdp_bdo_parse_nof_destinations(nof_destinations, c_nof_destinations_max : natural) return natural;

  -- Use functions that return look up tables to precalculate the values as
  -- constant arrays
  -- . One ch (channel) = one 32b word = one dual polarization beamlet (Xre, Xim, Yre, Yim)

  -- . Look up table arrays for: t_natural_arr(1 to c_nof_destinations_max)
  function func_sdp_bdo_reorder_nof_blocks_look_up_table(c_nof_destinations_max : natural) return t_natural_arr;
  function func_sdp_bdo_reorder_nof_ch_look_up_table(c_nof_destinations_max : natural) return t_natural_arr;
  function func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table(c_nof_destinations_max : natural) return t_natural_arr;
  function func_sdp_bdo_nof_beamlets_per_block_last_destination_look_up_table(c_nof_destinations_max : natural) return t_natural_arr;
  function func_sdp_bdo_nof_ch_per_packet_first_destinations_look_up_table(c_nof_destinations_max : natural) return t_natural_arr;
  function func_sdp_bdo_nof_ch_per_packet_last_destination_look_up_table(c_nof_destinations_max : natural) return t_natural_arr;

  -- Look up table matrix for:
  --   t_natural_matrix(1 to c_nof_destinations_max,      -- N_destinations
  --                    0 to c_nof_destinations_max - 1)  -- destination index
  function func_sdp_bdo_beamlet_index_per_destination_look_up_matrix(c_nof_destinations_max : natural) return t_natural_matrix;
end package sdp_bdo_pkg;

package body sdp_bdo_pkg is
  function func_sdp_bdo_parse_nof_destinations(nof_destinations, c_nof_destinations_max : natural) return natural is
  begin
    -- Parse input nof_destinations value
    if nof_destinations = 0 then
      return 1;
    elsif nof_destinations > c_nof_destinations_max then
      return c_nof_destinations_max;
    else
      return nof_destinations;
    end if;
  end func_sdp_bdo_parse_nof_destinations;

  function func_sdp_bdo_reorder_nof_blocks_look_up_table(c_nof_destinations_max : natural) return t_natural_arr is
    variable v_arr : t_natural_arr(1 to c_nof_destinations_max);
  begin
    -- Determine reorder_nof_blocks = nof_blocks_per_packet as function of
    -- c_sdp_bdo_reorder_nof_blocks_max and the number of destinations DN.
    -- . With DN = 1 destination c_sdp_cep_nof_blocks_per_packet = 4 can fit
    --   in a jumbo frame.
    -- . With DN destinations DN * c_sdp_cep_nof_blocks_per_packet can fit in
    --   a jumbo frame, because the number of beamlet indices per destination
    --   reduces by DN.
    --     DN = 1:16 --> 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64
    -- . In total there can be maximum c_sdp_bdo_reorder_nof_blocks_max = 16
    --   blocks per packet, due to the size of the reorder buffer. Taking
    --   smallest yields the actual number of blocks per packet, as function
    --   of number of destinations DN:
    --     DN = 1:16 --> 4, 8, 12, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
    for DN in 1 to c_nof_destinations_max loop
      v_arr(DN) := smallest(DN * c_sdp_cep_nof_blocks_per_packet, c_sdp_bdo_reorder_nof_blocks_max);
    end loop;
    return v_arr;
  end func_sdp_bdo_reorder_nof_blocks_look_up_table;

  function func_sdp_bdo_reorder_nof_ch_look_up_table(c_nof_destinations_max : natural) return t_natural_arr is
    constant c_arr : t_natural_arr(1 to c_nof_destinations_max) :=
                       func_sdp_bdo_reorder_nof_blocks_look_up_table(c_nof_destinations_max);
    variable v_arr : t_natural_arr(1 to c_nof_destinations_max);
  begin
    -- Determine reorder nof_ch as function of number of destinations DN.
    -- . The number of blocks to reorder is given by c_arr, so the number of
    --   ch (channels = words = dual pol, complex beamlets) that need to be
    --   reordered is c_sdp_S_sub_bf * c_arr(DN):
    --     DN = 1:16 --> 1952, 3904, 5856, 7808, 7808, 7808, 7808, 7808
    --                   7808, 7808, 7808, 7808, 7808, 7808, 7808, 7808
    for DN in 1 to c_nof_destinations_max loop
      v_arr(DN) := c_sdp_S_sub_bf * c_arr(DN);
    end loop;
    return v_arr;
  end func_sdp_bdo_reorder_nof_ch_look_up_table;

  function func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table(c_nof_destinations_max : natural) return t_natural_arr is
    variable v_first_arr : t_natural_arr(1 to c_nof_destinations_max);
  begin
    -- Determine nof_beamlets_per_block for the first 1:DN-1 destinations, as
    -- function of number of destinations DN.
    -- . In total there are c_sdp_S_sub_bf = 488 dual polarization beamlets to
    --   distribute over DN destinations, so ceil(488 / DN) yields the number of
    --   blocks for the first 1:DN-1 destinations:
    --     DN = 1:16 --> v_first_arr = 488, 244, 163, 122, 98, 82, 70, 61, 55, 49, 45, 41, 38, 35, 33, 31
    for DN in 1 to c_nof_destinations_max loop
      v_first_arr(DN) := ceil_div(c_sdp_S_sub_bf, DN);
    end loop;
    return v_first_arr;
  end func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table;

  function func_sdp_bdo_nof_beamlets_per_block_last_destination_look_up_table(c_nof_destinations_max : natural) return t_natural_arr is
    variable v_first_arr : t_natural_arr(1 to c_nof_destinations_max) :=
                             func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table(c_nof_destinations_max);
    variable v_last_arr  : t_natural_arr(1 to c_nof_destinations_max);
  begin
    -- Determine remaining nof_beamlets_per_block for the last destination
    -- with index DN, as function of number of destinations DN.
    -- . In total there are c_sdp_S_sub_bf = 488 dual polarization beamlets to
    --   distribute over DN destinations, so 488 - (DN-1) * ceil(488 / DN)
    --   beamlets remain for the last destination:
    --     DN = 1:16 --> v_first_arr = 488, 244, 163, 122, 98, 82, 70, 61, 55, 49, 45, 41, 38, 35, 33, 31
    --     DN = 1:16 --> v_last_arr  = 488, 244, 162, 122, 96, 78, 68, 61, 48, 47, 38, 37, 32, 33, 26, 23
    --
    -- Remark:
    -- . The v_last_arr(DN) may be < v_first_arr(DN) - 1, so the last
    --   destination may contain much less beamlets than the others. In
    --   combination with dp_packet_unmerge it is not feasible to distribute
    --   the beamlets evenly over all destinations, using v_hi beamlets for
    --   some first destinations and v_lo = v_hi - 1 for the remaining
    --   destinations. This is because dp_packet_unmerge can only unmerge the
    --   same packet length for N - 1 blocks and then unmerge the remaining
    --   data in the last block until the eop.
    for DN in 1 to c_nof_destinations_max loop
      v_last_arr(DN) := c_sdp_S_sub_bf - (DN - 1) * v_first_arr(DN);
    end loop;
    return v_last_arr;
  end func_sdp_bdo_nof_beamlets_per_block_last_destination_look_up_table;

  function func_sdp_bdo_nof_ch_per_packet_first_destinations_look_up_table(c_nof_destinations_max : natural) return t_natural_arr is
    constant c_nof_blocks_arr   : t_natural_arr(1 to c_nof_destinations_max) :=
                                    func_sdp_bdo_reorder_nof_blocks_look_up_table(c_nof_destinations_max);
    constant c_nof_beamlets_arr : t_natural_arr(1 to c_nof_destinations_max) :=
                                    func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table(c_nof_destinations_max);
    variable v_len_arr : t_natural_arr(1 to c_nof_destinations_max);
  begin
    -- Determine nof_ch per packet for the first 1:DN-1 destinations, as
    -- function of number of destinations DN.
    -- The packet lengths follow from c_nof_blocks_arr * c_nof_beamlets_arr:
    -- . c_nof_blocks_arr   =    4,   8,  12,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16
    -- . c_nof_beamlets_arr =  488, 244, 163, 122,  98,  82,  70,  61,  55,  49,  45,  41,  38,  35,  33,  31
    -- . v_len_arr          = 1952,1952,1956,1952,1568,1312,1120, 976, 880, 784, 720, 656, 608, 560, 528, 496
    -- . nof octets         = 7808,7808,7824,7808,6272,5248,4480,3904,3520,3136,2880,2624,2432,2240,2112,1984
    for DN in 1 to c_nof_destinations_max loop
      v_len_arr(DN) := c_nof_blocks_arr(DN) * c_nof_beamlets_arr(DN);
    end loop;
    return v_len_arr;
  end func_sdp_bdo_nof_ch_per_packet_first_destinations_look_up_table;

  function func_sdp_bdo_nof_ch_per_packet_last_destination_look_up_table(c_nof_destinations_max : natural) return t_natural_arr is
    constant c_nof_blocks_arr   : t_natural_arr(1 to c_nof_destinations_max) :=
                                    func_sdp_bdo_reorder_nof_blocks_look_up_table(c_nof_destinations_max);
    constant c_nof_beamlets_arr : t_natural_arr(1 to c_nof_destinations_max) :=
                                    func_sdp_bdo_nof_beamlets_per_block_last_destination_look_up_table(c_nof_destinations_max);
    variable v_len_arr : t_natural_arr(1 to c_nof_destinations_max);
  begin
    -- Determine nof_ch per packet for the first 1:DN-1 destinations, as
    -- function of number of destinations DN.
    -- The packet lengths follow from c_nof_blocks_arr * c_nof_beamlets_arr:
    -- . c_nof_blocks_arr   =    4,   8,  12,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16
    -- . c_nof_beamlets_arr =  488, 244, 162, 122,  96,  78,  68,  61,  48,  47,  38,  37,  32,  33,  26,  23
    -- . v_len_arr          = 1952,1952,1944,1952,1536,1248,1088, 976, 768, 752, 608, 592, 512, 528, 416, 368
    -- . nof octets         = 7808,7808,7776,7808,6144,4992,4352,3904,3072,3008,2432,2368,2048,2112,1664,1472
    for DN in 1 to c_nof_destinations_max loop
      v_len_arr(DN) := c_nof_blocks_arr(DN) * c_nof_beamlets_arr(DN);
    end loop;
    return v_len_arr;
  end func_sdp_bdo_nof_ch_per_packet_last_destination_look_up_table;

  function func_sdp_bdo_beamlet_index_per_destination_look_up_matrix(c_nof_destinations_max : natural) return t_natural_matrix is
    constant c_len_arr   : t_natural_arr(1 to c_nof_destinations_max) :=
                             func_sdp_bdo_nof_beamlets_per_block_first_destinations_look_up_table(c_nof_destinations_max);
    variable v_index_mat : t_natural_matrix(1 to c_nof_destinations_max,
                                            0 to c_nof_destinations_max - 1);
    variable v_beamlet_index : natural;
    variable v_step          : natural;
  begin
    -- Determine beamlet index of first beamlet in packet per destination with
    -- index DI, as function of number of destinations DN.
    -- . Beamlet index for first destination starts at 0
    -- . Beamlet index for the other destinations increments with number of
    --   beamlets per previous destination given by c_len_arr.
    --
    -- * rows: nof_destinations DN
    -- * columns: destination index DI
    --
    -- DI:   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
    -- DN:
    --  1    0,   0.....................................................................0
    --  2    0, 244,   0                                                                .
    --  3    0, 163, 326,   0                                                           .
    --  4    0, 122, 244, 366,   0                                                      .
    --  5    0,  98, 196, 294, 392,   0                                                 .
    --  6    0,  82, 164, 246, 328, 410,   0                                            .
    --  7    0,  70, 140, 210, 280, 350, 420,   0                                       .
    --  8    0,  61, 122, 183, 244, 305, 366, 427,   0                                  .
    --  9    0,  55, 110, 165, 220, 275, 330, 385, 440,   0                             .
    -- 10    0,  49,  98, 147, 196, 245, 294, 343, 392, 441,   0                        .
    -- 11    0,  45,  90, 135, 180, 225, 270, 315, 360, 405, 450,   0                   .
    -- 12    0,  41,  82, 123, 164, 205, 246, 287, 328, 369, 410, 451,   0              .
    -- 13    0,  38,  76, 114, 152, 190, 228, 266, 304, 342, 380, 418, 456,   0         .
    -- 14    0,  35,  70, 105, 140, 175, 210, 245, 280, 315, 350, 385, 420, 455,   0    .
    -- 15    0,  33,  66,  99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462,   0
    -- 16    0,  31,  62,  93, 124, 155, 186, 217, 248, 279, 310, 341, 372, 403, 434, 465
    --
    -- Equivalent Python code to produce matrix:
    --
    --   c_len_arr = [488, 244, 163, 122, 98, 82, 70, 61, 55, 49, 45, 41, 38, 35, 33, 31]
    --   for DN in range(16):
    --       lineStr = '%2d ' % (DN + 1)
    --       v_beamlet_index = 0
    --       v_step = c_len_arr[DN]
    --       for DI in range(16):
    --           if v_beamlet_index < 488:
    --               lineStr += '%4d,' % v_beamlet_index
    --           v_beamlet_index += v_step
    --       print(lineStr)
    --
    for DN in 1 to c_nof_destinations_max loop
      v_beamlet_index := 0;
      v_step := c_len_arr(DN);
      for DI in 0 to c_nof_destinations_max - 1 loop
        if v_beamlet_index < c_sdp_S_sub_bf then
          v_index_mat(DN, DI) := v_beamlet_index;
        end if;
        v_beamlet_index := v_beamlet_index + v_step;
      end loop;
    end loop;
    return v_index_mat;
  end func_sdp_bdo_beamlet_index_per_destination_look_up_matrix;
end sdp_bdo_pkg;