Skip to content
Snippets Groups Projects
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;