-
Eric Kooistra authoredEric Kooistra authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
reorder_pkg.vhd 19.78 KiB
-------------------------------------------------------------------------------
--
-- 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;