diff --git a/libraries/base/reorder/src/vhdl/reorder_pkg.vhd b/libraries/base/reorder/src/vhdl/reorder_pkg.vhd index fb72f9233ac99fea559c3f26ce8f6b38e14efcf4..71439a5142d0f2f0115a1843362c391411e076cd 100644 --- a/libraries/base/reorder/src/vhdl/reorder_pkg.vhd +++ b/libraries/base/reorder/src/vhdl/reorder_pkg.vhd @@ -82,21 +82,54 @@ package reorder_pkg is ----------------------------------------------------------------------------- -- Block and data counters to derive select_copi.address for transpose - -- reording between nof_blocks_per_packet and nof_data_per_block. + -- reordering between nof_blocks_per_packet and nof_data_per_block. + -- Optionally the data can consist of multiple words at consecutive + -- addresses. If data is one word, then word_cnt record field is not used. type t_reorder_transpose is record select_copi : t_mem_copi; addr : natural; blk_cnt : natural; - data_cnt : natural; + blk_offset : natural; + dat_cnt : natural; + dat_offset : natural; + word_cnt : natural; end record; - constant c_reorder_transpose_rst : t_reorder_transpose := (c_mem_copi_rst, 0, 0, 0); + constant c_reorder_transpose_rst : t_reorder_transpose := (c_mem_copi_rst, 0, 0, 0, 0, 0, 0); -- Input packet has nof_ch = nof_data_per_block * nof_blocks_per_packet of - -- data per packet. - -- The transpose.select_copi.address order will yield transposed output, - -- with nof_blocks_per_packet data per block, and with nof_data_per_block - -- number of blocks per packet. + -- data per packet. The order of the words per nof_words_per_data is + -- preserved. + + -- Transpose functions that operate on list of data indices + function func_reorder_transpose_indices(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural) return t_natural_arr; + + function func_reorder_transpose_indices_impl(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural) return t_natural_arr; + + -- Transpose functions that operate sequentially to determine the read + -- transpose.select_copi.address + -- . The transpose.select_copi.address order will yield transposed output, + -- with nof_blocks_per_packet data per block, and with nof_data_per_block + -- number of blocks per packet. + function func_reorder_transpose(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural; + transpose : t_reorder_transpose) + return t_reorder_transpose; + + -- Alternative implementation using a look up list: + -- func_reorder_transpose_look_up() = func_reorder_transpose() + function func_reorder_transpose_look_up(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural; + transpose : t_reorder_transpose) + return t_reorder_transpose; + + -- Variant with nof_words_per_data = 1 function func_reorder_transpose(nof_blocks_per_packet : natural; nof_data_per_block : natural; transpose : t_reorder_transpose) @@ -116,6 +149,8 @@ package reorder_pkg is constant c_reorder_identity_rst : t_reorder_identity := (c_mem_copi_rst, 0); + -- Identity function that operates sequentially to determine the read + -- identity.select_copi.address, which is an incrementing address range. function func_reorder_identity(nof_ch_per_packet : natural; identity : t_reorder_identity) return t_reorder_identity; @@ -124,15 +159,99 @@ end reorder_pkg; package body reorder_pkg is + -- Determine transpose index for input packet_index + -- . Similar function as func_sdp_bdo_transpose_packet() in tb_sdp_pkg.vhd. + -- . The transpose is between nof_blocks_per_packet and nof_data_per_block. + -- Doing transpose again with swapped nof_blocks_per_packet and + -- nof_data_per_block, yields original order. + -- . The order of the words per nof_words_per_data is preserved. + -- Example for: + -- . blk in range(nof_blocks_per_packet = 4) + -- . dat in range(nof_data_per_block = 488) + -- . wi in range(nof_words_per_data = 2) + -- input packet_index v_in: + -- . blk 0, 1, 2, 3 + -- . dat 0, ... 487, 0, ... 487, 0, ... 487, 0, ... 487 + -- . wi 0,1, ... 0,1, 0,1, ... 0,1, 0,1, ... 0,1, 0,1, ... 0,1 + -- . v_in 0, ... 975, 976, ...1951,1952, ...2927,2928, ...3903 + -- return index v_out: + -- . wi 0,1,0,1,0,1,0,1, ..., 0,1,0,1,0,1,0,1 + -- . blk 0, 1, 2, 3, ..., 0, 1, 2, 3 + -- . dat 0, ..., 487 + -- . v_out 0,1, 976, 977, 1952,1953, 2928,2929, + -- 2,3, 978, 979, 1954,1955, 2930,2931, + -- ..., ..., ..., ..., + -- 972,973, 1948,1949, 2924,2925, 3900,3901, + -- 974,775, 1950,1951, 2926,2927, 3902,3903 + function func_reorder_transpose_indices(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural) return t_natural_arr is + constant c_nof_ch : natural := nof_blocks_per_packet * nof_data_per_block * nof_words_per_data; + variable v_arr : t_natural_arr(0 to c_nof_ch - 1); + variable v_in : natural; + variable v_out : natural; + variable v_ch : natural := 0; + begin + -- Use outer loop blk and inner loop dat to have v_in = v_ch. + -- Use outer loop dat and inner loop blk to have v_out = v_ch. + -- For the return v_arr it does not matter which loop is the outer loop + -- or the inner loop. Choose to have v_out = v_ch, because then the + -- values in v_arr are calculated in output order, similar as if they + -- are output sequentially by reorder_col_select. + for dat in 0 to nof_data_per_block - 1 loop + for blk in 0 to nof_blocks_per_packet - 1 loop + for wi in 0 to nof_words_per_data - 1 loop + -- v_out is the transpose index for index v_in, so output value at + -- index v_out becomes input value at index v_in + v_in := (blk * nof_data_per_block + dat) * nof_words_per_data + wi; + v_out := (dat * nof_blocks_per_packet + blk) * nof_words_per_data + wi; + assert v_out = v_ch report "Wrong index in func_reorder_transpose_indices()" severity failure; + v_arr(v_out) := v_in; + v_ch := v_ch + 1; + end loop; + end loop; + end loop; + return v_arr; + end; + + -- The func_reorder_transpose_indices_impl() yields the same as + -- func_reorder_transpose_indices(), except that it uses only + -- additions to calculate the indices, so no multiplications in + -- the loops. + function func_reorder_transpose_indices_impl(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural) return t_natural_arr is + constant c_nof_ch : natural := nof_blocks_per_packet * nof_data_per_block * nof_words_per_data; + constant c_nof_words_per_block : natural := nof_words_per_data * nof_data_per_block; + variable v_blk_offset : natural := 0; + variable v_dat_offset : natural := 0; + variable v_arr : t_natural_arr(0 to c_nof_ch - 1); + variable v_ch : natural := 0; + begin + for dat in 0 to nof_data_per_block - 1 loop + for blk in 0 to nof_blocks_per_packet - 1 loop + for wi in 0 to nof_words_per_data - 1 loop + v_arr(v_ch) := v_blk_offset + v_dat_offset + wi; + v_ch := v_ch + 1; + end loop; + v_blk_offset := v_blk_offset + c_nof_words_per_block; + end loop; + v_blk_offset := 0; + v_dat_offset := v_dat_offset + nof_words_per_data; + end loop; + return v_arr; + end; + -- A transpose process and an undo transpose process can both use -- func_reorder_transpose(), by swapping the transpose dimensions. -- For example, to get transposed output with: -- . g_nof_blocks_per_packet = 3 and -- . g_nof_data_per_block = 5 + -- . g_nof_words_per_data = 1 -- the p_comb_transpose selects: -- -- v.blk_cnt: 0 1 2 - -- v.data_cnt: 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 + -- v.dat_cnt: 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 -- ch: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- data_in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- in_sosi -- transpose: 0 3 6 9 12 @@ -148,7 +267,7 @@ package body reorder_pkg is -- the p_comb_undo_transpose selects: -- -- v.blk_cnt: 0 1 2 3 4 - -- v.data_cnt: 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 + -- v.dat_cnt: 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 -- ch: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- data_in 0 3 6 9 12 1 4 7 10 13 2 5 8 11 14 -- transposed_sosi -- undo_transpose: 0 1 2 @@ -162,45 +281,102 @@ package body reorder_pkg is -- to restore the original order. function func_reorder_transpose(nof_blocks_per_packet : natural; nof_data_per_block : natural; + nof_words_per_data : natural; transpose : t_reorder_transpose) return t_reorder_transpose is + constant c_nof_words_per_block : natural := nof_words_per_data * nof_data_per_block; + variable v : t_reorder_transpose; begin + -- Implementation derived from func_reorder_transpose_indices_impl(). + -- Instead of using incrementing v_ch and for-loops to return list of all + -- read indices, use sequential calls of this func_reorder_transpose() + -- to return next read address. v := transpose; - -- read at current address + -- Read at current address v.select_copi.address := TO_MEM_ADDRESS(v.addr); v.select_copi.rd := '1'; - -- prepare next read address - if v.blk_cnt <= nof_blocks_per_packet - 1 then - if v.data_cnt < nof_data_per_block - 1 then - v.data_cnt := v.data_cnt + 1; - v.addr := v.addr + nof_blocks_per_packet; - else - v.data_cnt := 0; + -- Prepare next read address + -- . loop word_cnt + if v.word_cnt < nof_words_per_data - 1 then + v.word_cnt := v.word_cnt + 1; + else + -- . end loop word_cnt + v.word_cnt := 0; + -- . loop blk_cnt + if v.blk_cnt < nof_blocks_per_packet - 1 then v.blk_cnt := v.blk_cnt + 1; - if v.blk_cnt = nof_blocks_per_packet then - v.blk_cnt := 0; + v.blk_offset := v.blk_offset + c_nof_words_per_block; + else + -- . end loop blk_cnt + v.blk_cnt := 0; + v.blk_offset := 0; + -- . loop dat_cnt + if v.dat_cnt < nof_data_per_block - 1 then + v.dat_cnt := v.dat_cnt + 1; + v.dat_offset := v.dat_offset + nof_words_per_data; + else + -- . end loop dat_cnt + v.dat_cnt := 0; + v.dat_offset := 0; end if; - v.addr := v.blk_cnt; end if; + end if; + v.addr := v.blk_offset + v.dat_offset + v.word_cnt; + return v; + end; + + function func_reorder_transpose_look_up(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + nof_words_per_data : natural; + transpose : t_reorder_transpose) + return t_reorder_transpose is + constant c_nof_ch : natural := nof_blocks_per_packet * nof_data_per_block * nof_words_per_data; + constant c_look_up_list : t_natural_arr(0 to c_nof_ch - 1) := + func_reorder_transpose_indices(nof_blocks_per_packet, + nof_data_per_block, + nof_words_per_data); + variable v : t_reorder_transpose; + begin + -- Equivalent implementation, so func_reorder_transpose_look_up() = + -- func_reorder_transpose() + v := transpose; + -- Read at current address + v.select_copi.address := TO_MEM_ADDRESS(v.addr); + v.select_copi.rd := '1'; + -- Prepare next read address + -- . use word_cnt as incrementing ch index to count the sequential calls + -- of this func_reorder_transpose() to return next read address + if v.word_cnt < c_nof_ch - 1 then + v.word_cnt := v.word_cnt + 1; else - v.data_cnt := 0; - v.blk_cnt := 0; - v.addr := 0; + v.word_cnt := 0; end if; + v.addr := c_look_up_list(v.word_cnt); return v; end; + function func_reorder_transpose(nof_blocks_per_packet : natural; + nof_data_per_block : natural; + transpose : t_reorder_transpose) + return t_reorder_transpose is + begin + return func_reorder_transpose(nof_blocks_per_packet, + nof_data_per_block, + 1, + transpose); + end; + function func_reorder_identity(nof_ch_per_packet : natural; identity : t_reorder_identity) return t_reorder_identity is variable v : t_reorder_identity; begin v := identity; - -- read at current address + -- Read at current address v.select_copi.address := TO_MEM_ADDRESS(v.addr); v.select_copi.rd := '1'; - -- prepare next read address + -- Prepare next read address if v.addr < nof_ch_per_packet - 1 then v.addr := v.addr + 1; else diff --git a/libraries/base/reorder/tb/vhdl/reorder_pkg_test.vhd b/libraries/base/reorder/tb/vhdl/reorder_pkg_test.vhd new file mode 100644 index 0000000000000000000000000000000000000000..a51252e77b80678d1466155eadf409a303bd5755 --- /dev/null +++ b/libraries/base/reorder/tb/vhdl/reorder_pkg_test.vhd @@ -0,0 +1,124 @@ +------------------------------------------------------------------------------- +-- +-- 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: +-- Test bench to verify functions in reorder_pkg.vhd. +-- Description: +-- +-- Usage: +-- > as 3 +-- > run -all +-- * The tb is self stopping and self checking, tb_end will stop the simulation +-- by stopping the clk and thus all toggling. + +library IEEE, common_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.tb_common_pkg.all; +use dp_lib.dp_stream_pkg.all; +use work.reorder_pkg.all; + +entity reorder_pkg_test is + generic( + g_nof_blocks_per_packet : natural := 4; + g_nof_data_per_block : natural := 488; + g_nof_words_per_data : natural := 1 + ); +end reorder_pkg_test; + + +architecture tb of reorder_pkg_test is + + constant c_clk_period : time := 10 ns; + constant c_nof_data : natural := g_nof_blocks_per_packet * g_nof_data_per_block; + + signal rst : std_logic; + signal clk : std_logic := '1'; + signal tb_end : std_logic := '0'; + + -- Verify default and alternative (lu = look up) implementation of r_transpose + signal r_transpose : t_reorder_transpose := c_reorder_transpose_rst; + signal r_transpose_lu : t_reorder_transpose := c_reorder_transpose_rst; + signal in_address : natural := 0; + signal in_val : std_logic := '0'; + signal out_address : natural; + signal out_address_lu : natural; + signal out_val : std_logic; + signal exp_address : natural; + + constant c_exp_addresses_arr : t_natural_arr := func_reorder_transpose_indices(g_nof_blocks_per_packet, + g_nof_data_per_block, + g_nof_words_per_data); + + constant c_impl_addresses_arr : t_natural_arr := func_reorder_transpose_indices_impl(g_nof_blocks_per_packet, + g_nof_data_per_block, + g_nof_words_per_data); +begin + + assert c_exp_addresses_arr = c_impl_addresses_arr report "Wrong func_reorder_transpose_indices_impl()" severity failure; + + clk <= (not clk) or tb_end after c_clk_period / 2; + rst <= '1', '0' after c_clk_period * 7; + + p_stimuli : process + begin + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 5); + + for I in 0 to c_nof_data - 1 loop + in_val <= '1'; + in_address <= I; + r_transpose <= func_reorder_transpose( + g_nof_blocks_per_packet, g_nof_data_per_block, g_nof_words_per_data, r_transpose); + r_transpose_lu <= func_reorder_transpose_look_up( + g_nof_blocks_per_packet, g_nof_data_per_block, g_nof_words_per_data, r_transpose_lu); + proc_common_wait_some_cycles(clk, 1); + end loop; + in_val <= '0'; + r_transpose <= c_reorder_transpose_rst; + r_transpose_lu <= c_reorder_transpose_rst; + + proc_common_wait_some_cycles(clk, 5); + tb_end <= '1'; + wait; + end process; + + out_address <= TO_UINT(r_transpose.select_copi.address); + out_address_lu <= TO_UINT(r_transpose_lu.select_copi.address); + out_val <= r_transpose.select_copi.rd; + + exp_address <= c_exp_addresses_arr(in_address); + + p_verify : process(clk) + begin + if rising_edge(clk) then + if in_val = '1' then + -- Only when valid expect that out_address = exp_address + assert out_address = exp_address report "Wrong transpose address" severity error; + end if; + -- Always expect that out_address_lu = out_address + assert out_address_lu = out_address report "Wrong transpose_lu address" severity error; + end if; + end process; + +end tb; diff --git a/libraries/base/reorder/tb/vhdl/reorder_pkg_test_test.vhd b/libraries/base/reorder/tb/vhdl/reorder_pkg_test_test.vhd new file mode 100644 index 0000000000000000000000000000000000000000..e8aeb6583dcaa90dcc28494017ed232d43aae2f7 --- /dev/null +++ b/libraries/base/reorder/tb/vhdl/reorder_pkg_test_test.vhd @@ -0,0 +1,53 @@ +------------------------------------------------------------------------------- +-- +-- 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: +-- Multi test bench to verify functions in reorder_pkg.vhd using instances +-- of reorder_pkg_test.vhd. +-- Description: +-- +-- Usage: +-- > as 3 +-- > run -all +-- * The tb is self stopping and self checking, tb_end will stop the simulation +-- by stopping the clk and thus all toggling. +library IEEE; +use IEEE.std_logic_1164.all; + + +entity reorder_pkg_test_test is +end reorder_pkg_test_test; + + +architecture tb of reorder_pkg_test_test is + + signal tb_end : std_logic := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + +begin + -- g_nof_blocks_per_packet : natural := 4; + -- g_nof_data_per_block : natural := 488; + -- g_nof_words_per_data : natural := 1 + + u_4_488_1 : entity work.reorder_pkg_test generic map (4, 488, 1); + u_4_488_2 : entity work.reorder_pkg_test generic map (4, 488, 2); + +end tb;