-
Eric Kooistra authoredEric Kooistra authored
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;