------------------------------------------------------------------------------- -- -- Copyright (C) 2011 -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. -- ------------------------------------------------------------------------------- library IEEE, common_lib; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; use common_lib.common_pkg.all; use common_lib.common_mem_pkg.all; package reorder_pkg is type t_reorder_seq is record wr_chunksize : positive; -- := 64; rd_chunksize : positive; -- := 16; rd_nof_chunks : positive; -- := 4; rd_interval : positive; -- := 1; gapsize : natural; -- := 0; nof_blocks : positive; -- := 5; end record; constant c_reorder_seq : t_reorder_seq := (64, 16, 4, 1, 0, 5); constant c_reorder_seq_same : t_reorder_seq := (64, 64, 1, 1, 0, 4); type t_reorder_table is array(integer range 0 to 31, integer range 0 to 31) of natural; constant c_reorder_table: t_reorder_table := ( (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ); ----------------------------------------------------------------------------- -- Reorder transpose -- -- There are several functions that yield the same transpose, but for -- different purposes. -- . func_reorder_transpose_indices() and -- func_reorder_transpose_indices_impl() calculate a lookup table of all -- indices in advance using loops. -- . func_reorder_transpose_packet() applies func_reorder_transpose_indices() -- on a packet list of octet values, for in a test bench. -- . func_reorder_transpose() and func_reorder_transpose_look_up() -- sequentially calculate the next index per clock cycle and use -- t_reorder_transpose, for in a synthesis component. ----------------------------------------------------------------------------- -- Block and data counters to derive select_copi.address for transpose -- 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; 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, 0, 0, 0); -- Input packet has nof_ch = nof_data_per_block * nof_blocks_per_packet of -- 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 function that operates on a packet function func_reorder_transpose_packet(nof_blocks_per_packet : natural; nof_data_per_block : natural; nof_words_per_data : natural; packet_list : t_slv_8_arr) return t_slv_8_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; -- 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) 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; ----------------------------------------------------------------------------- -- Reorder identity -- . so no reordering, same out as in, but delayed due to dual page -- buffering of reorder ----------------------------------------------------------------------------- -- Pass on input to output in same order. type t_reorder_identity is record select_copi : t_mem_copi; addr : natural; end record; 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; end reorder_pkg; package body reorder_pkg is -- Determine transpose index for input packet_index -- . 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; -- Apply func_reorder_transpose_indices() on a packet function func_reorder_transpose_packet(nof_blocks_per_packet : natural; nof_data_per_block : natural; nof_words_per_data : natural; packet_list : t_slv_8_arr) return t_slv_8_arr 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_list : t_slv_8_arr(packet_list'range); begin assert c_nof_ch = packet_list'length report "Wrong packet_list length" severity error; for ch in 0 to c_nof_ch - 1 loop v_list(ch) := packet_list(c_look_up_list(ch)); end loop; return v_list; end func_reorder_transpose_packet; -- 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.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 -- 1 4 7 10 13 -- 2 5 8 11 14 -- v.addr 0 3 6 9 12 1 4 7 10 13 2 5 8 11 14 -- data_out 0 3 6 9 12 1 4 7 10 13 2 5 8 11 14 -- transposed_sosi -- -- and then with swapped parameter values, to get untransposed -- output back (= original input order): -- . g_nof_blocks_per_packet = 5 and -- . g_nof_data_per_block = 3 -- the p_comb_undo_transpose selects: -- -- v.blk_cnt: 0 1 2 3 4 -- 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 -- 3 4 5 -- 6 7 8 -- 9 10 11 -- 12 13 14 -- v.addr: 0 5 10 1 6 11 2 7 12 3 8 13 4 9 14 -- data_out: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -- out_sosi -- -- 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 v.select_copi.address := TO_MEM_ADDRESS(v.addr); v.select_copi.rd := '1'; -- 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; 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; end if; end if; v.addr := v.blk_offset + v.dat_offset + 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_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.word_cnt := 0; end if; v.addr := c_look_up_list(v.word_cnt); return v; 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 v.select_copi.address := TO_MEM_ADDRESS(v.addr); v.select_copi.rd := '1'; -- Prepare next read address if v.addr < nof_ch_per_packet - 1 then v.addr := v.addr + 1; else v.addr := 0; end if; return v; end; end reorder_pkg;