From 7460dbf635b61e36d3d46624ba947cfc3d78ec0f Mon Sep 17 00:00:00 2001
From: Eric Kooistra <kooistra@astron.nl>
Date: Wed, 15 Feb 2023 13:49:36 +0100
Subject: [PATCH] Add two variants of component that can reverse serial data in
 a block.

---
 libraries/base/common/hdllib.cfg              |   1 +
 .../common/src/vhdl/common_reverse_n_data.vhd | 251 ++++++++++++++
 libraries/base/dp/hdllib.cfg                  |   8 +
 .../base/dp/src/vhdl/dp_reverse_n_data.vhd    | 129 +++++++
 .../base/dp/src/vhdl/dp_reverse_n_data_fc.vhd | 108 ++++++
 .../base/dp/tb/vhdl/tb_dp_reverse_n_data.vhd  | 320 ++++++++++++++++++
 .../dp/tb/vhdl/tb_dp_reverse_n_data_fc.vhd    | 276 +++++++++++++++
 .../dp/tb/vhdl/tb_tb_dp_reverse_n_data.vhd    |  59 ++++
 .../dp/tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd |  73 ++++
 9 files changed, 1225 insertions(+)
 create mode 100644 libraries/base/common/src/vhdl/common_reverse_n_data.vhd
 create mode 100644 libraries/base/dp/src/vhdl/dp_reverse_n_data.vhd
 create mode 100644 libraries/base/dp/src/vhdl/dp_reverse_n_data_fc.vhd
 create mode 100644 libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data.vhd
 create mode 100644 libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data_fc.vhd
 create mode 100644 libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data.vhd
 create mode 100644 libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd

diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg
index a5aa29dd84..a9a72853c4 100644
--- a/libraries/base/common/hdllib.cfg
+++ b/libraries/base/common/hdllib.cfg
@@ -88,6 +88,7 @@ synth_files =
     src/vhdl/common_reorder_symbol.vhd
     src/vhdl/common_multiplexer.vhd
     src/vhdl/common_demultiplexer.vhd
+    src/vhdl/common_reverse_n_data.vhd
     src/vhdl/common_transpose_symbol.vhd
     src/vhdl/common_transpose.vhd
     src/vhdl/common_peak.vhd
diff --git a/libraries/base/common/src/vhdl/common_reverse_n_data.vhd b/libraries/base/common/src/vhdl/common_reverse_n_data.vhd
new file mode 100644
index 0000000000..61c3b21077
--- /dev/null
+++ b/libraries/base/common/src/vhdl/common_reverse_n_data.vhd
@@ -0,0 +1,251 @@
+--------------------------------------------------------------------------------
+--
+-- 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.
+--
+--------------------------------------------------------------------------------
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE work.common_pkg.ALL;
+
+-- Author:
+-- . Eric Kooistra, 14 Feb 2023
+-- Purpose:
+-- . Reverse the order of time multiplexed, serial data per set of every
+--   g_reverse_len data values in time.
+-- Description:
+-- . The implementation does:
+--     serial in_data -->
+--     parallel demux_data_vec -->
+--     reversed parallel reverse_data_vec -->
+--     serial out_data
+-- . The first in_val after rst release is treated as start of the time
+--   multiplexed sets of data.
+-- . For example with g_reverse_len = 3, then input in_data 012_345_678
+--   becomes 210_543_876 output out_data.
+-- . If in_eop is used to mark the end of in_data blocks, then the in_data
+--   blocks size should be an integer multiple of g_reverse_len. If after rst
+--   release, the in_val is always marking all in_data block data, then it is
+--   fine to leave in_eop = '0', so not connected.
+-- Remark:
+-- . Typically g_reverse_len should not be too large (~< 4), because then the
+--   implementation takes relatively too much logic.
+-- . This common_reverse_n_data.vhd is used in dp_reverse_n.vhd and verified
+--   in tb_dp_reverse_n.vhd.
+
+ENTITY common_reverse_n_data IS
+  GENERIC (
+    -- Pipeline: 0 for combinatorial, > 0 for registers
+    g_pipeline_demux_in  : NATURAL := 1;  -- serial to parallel demux
+    g_pipeline_demux_out : NATURAL := 0;
+    g_pipeline_mux_in    : NATURAL := 0;  -- parallel to serial mux
+    g_pipeline_mux_out   : NATURAL := 1;
+    g_reverse_len        : NATURAL := 2;
+    g_data_w             : NATURAL := 16
+  );
+  PORT (
+    rst         : IN  STD_LOGIC;
+    clk         : IN  STD_LOGIC;
+
+    in_data     : IN  STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+    in_val      : IN  STD_LOGIC;
+    in_eop      : IN  STD_LOGIC := '0';
+    out_data    : OUT STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+    out_val     : OUT STD_LOGIC
+  );
+END common_reverse_n_data;
+
+
+ARCHITECTURE str OF common_reverse_n_data IS
+
+  CONSTANT c_pipeline_total : NATURAL := g_pipeline_demux_in + g_pipeline_demux_out +
+                                         g_reverse_len-1 +
+                                         g_pipeline_mux_in + g_pipeline_mux_out;
+
+  CONSTANT c_sel_w          : NATURAL := ceil_log2(g_reverse_len);
+
+  SIGNAL in_sel             : STD_LOGIC_VECTOR(c_sel_w-1 DOWNTO 0);
+  SIGNAL nxt_in_sel         : STD_LOGIC_VECTOR(c_sel_w-1 DOWNTO 0);
+
+  SIGNAL demux_data         : STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+  SIGNAL demux_data_vec     : STD_LOGIC_VECTOR(g_reverse_len*g_data_w-1 DOWNTO 0);
+  SIGNAL demux_val_vec      : STD_LOGIC_VECTOR(g_reverse_len-1 DOWNTO 0);
+
+  SIGNAL reverse_sel        : STD_LOGIC_VECTOR(c_sel_w-1 DOWNTO 0);
+  SIGNAL reverse_data_vec   : STD_LOGIC_VECTOR(g_reverse_len*g_data_w-1 DOWNTO 0);
+  SIGNAL reverse_val_vec    : STD_LOGIC_VECTOR(g_reverse_len-1 DOWNTO 0);
+  SIGNAL reverse_val        : STD_LOGIC;
+
+BEGIN
+
+  p_clk : PROCESS(rst, clk)
+  BEGIN
+    IF rst = '1' THEN
+      in_sel <= (OTHERS=>'0');
+    ELSIF rising_edge(clk) THEN
+      in_sel <= nxt_in_sel;
+    END IF;
+  END PROCESS;
+
+  p_in_sel : PROCESS(in_sel, in_val, in_eop)
+  BEGIN
+    nxt_in_sel <= in_sel;
+
+    -- Map multiplexed input to parallel demultiplexed signal index in_sel
+    IF in_val = '1' THEN
+      IF UNSIGNED(in_sel) < g_reverse_len-1 THEN
+        nxt_in_sel <= INCR_UVEC(in_sel, 1);
+      ELSE
+        nxt_in_sel <= (OTHERS=>'0');
+      END IF;
+    END IF;
+
+    -- Force that serial to parallel mapping restarts at next sop, in case
+    -- in_data block length is not an integer multiple of g_reverse_len
+    IF in_eop = '1' THEN
+      nxt_in_sel <= (OTHERS=>'0');
+    END IF;
+  END PROCESS;
+
+  u_common_demultiplexer : ENTITY work.common_demultiplexer
+  GENERIC MAP (
+    g_pipeline_in  => g_pipeline_demux_in,
+    g_pipeline_out => g_pipeline_demux_out,
+    g_nof_out      => g_reverse_len,
+    g_dat_w        => g_data_w
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    in_dat      => in_data,
+    in_val      => in_val,
+
+    out_sel     => in_sel,
+    out_dat     => demux_data_vec,
+    out_val     => demux_val_vec
+  );
+
+  -- All g_reverse_len parts in demux_data_vec carry the same data, the
+  -- demux_val_vec determines for which demux stream it is. Use demux_data
+  -- only for view in wave window.
+  demux_data <= demux_data_vec(g_data_w-1 DOWNTO 0);
+
+  -- Reverse is done by delaying the demultiplexed signal and reversing their
+  -- indices. The delay per parallel signal is 2*I to reverse them in time.
+  -- The latency of gen_reverse from in_val to out_val is g_reverse_len-1.
+  -- The timing diagram shows how gen_reverse and u_mux_n_to_one work, without
+  -- additional demux and mux pipelining to focus on the reverse function.
+  --
+  -- in_data               a b c d e f g h i j k l m
+  --                       _________________________
+  -- in_val              _|
+  --
+  -- in_sel                0 1 2 0 1 2 0 1 2 0 1 2 0
+  --
+  -- demux_data            a b c d e f g h i j k l m   = same in each demux_data_vec[]
+  -- demux_data_vec[0]     a b c d e f g h i j k l m
+  -- demux_data_vec[1]     a b c d e f g h i j k l m
+  -- demux_data_vec[2]     a b c d e f g h i j k l m
+  --                       _     _     _     _     _
+  -- demux_val_vec[0]    _| |___| |___| |___| |___| |
+  --                         _     _     _     _
+  -- demux_val_vec[1]    ___| |___| |___| |___| |___
+  --                           _     _     _     _
+  -- demux_val_vec[2]    _____| |___| |___| |___| |_
+  --
+  -- reverse_data_vec[0]   a b c d e f g h i j k l m    = demux_data_vec[2] with 0 delay
+  -- reverse_data_vec[1]       a b c d e f g h i j k l  = demux_data_vec[1] with 2 delay
+  -- reverse_data_vec[2]           a b c d e f g h i j  = demux_data_vec[0] with 4 delay
+  --                           _     _     _     _
+  -- reverse_val_vec[0]  _____| |___| |___| |___| |___  = demux_val_vec[2] with 0 delay
+  --                             _     _     _     _
+  -- reverse_val_vec[1]  _______| |___| |___| |___| |_  = demux_val_vec[1] with 2 delay
+  --                               _     _     _     _
+  -- reverse_val_vec[2]  _________| |___| |___| |___| | = demux_val_vec[0] with 4 delay
+  --                           ________________________
+  -- reverse_val         _____|                         = vector_or(reverse_val_vec)
+  --
+  -- reverse_sel               0 1 2 0 1 2 0 1 2 0 1 2  = in_sel delayed by g_reverse_len-1
+  --
+  -- out_data                  c b a f e d i h g l k j
+  --                           ________________________
+  -- out_val             _____|
+  --
+  gen_reverse : FOR I IN 0 TO g_reverse_len-1 GENERATE
+    u_reverse_data : ENTITY work.common_pipeline
+    GENERIC MAP (
+      g_pipeline   => 2*I,
+      g_in_dat_w   => g_data_w,
+      g_out_dat_w  => g_data_w
+    )
+    PORT MAP (
+      rst     => rst,
+      clk     => clk,
+      in_dat  => demux_data_vec((g_reverse_len-1 - I + 1)*g_data_w-1 DOWNTO (g_reverse_len-1 - I)*g_data_w),
+      out_dat => reverse_data_vec((I+1)*g_data_w-1 DOWNTO I*g_data_w)
+    );
+
+    u_reverse_val : ENTITY work.common_pipeline_sl
+    GENERIC MAP (
+      g_pipeline  => 2*I
+    )
+    PORT MAP (
+      rst     => rst,
+      clk     => clk,
+      in_dat  => demux_val_vec(g_reverse_len-1 - I),
+      out_dat => reverse_val_vec(I)
+    );
+  END GENERATE;
+
+  reverse_val <= vector_or(reverse_val_vec);
+
+  -- pipeline in_sel to align reverse_sel to reverse_data_vec and reverse_val_vec
+  u_pipe_sel : ENTITY work.common_pipeline
+  GENERIC MAP (
+    g_pipeline  => g_pipeline_demux_in + g_pipeline_demux_out + g_reverse_len - 1,
+    g_in_dat_w  => c_sel_w,
+    g_out_dat_w => c_sel_w
+  )
+  PORT MAP (
+    rst     => rst,
+    clk     => clk,
+    in_dat  => in_sel,
+    out_dat => reverse_sel
+  );
+
+  u_common_multiplexer : ENTITY work.common_multiplexer
+  GENERIC MAP (
+    g_pipeline_in  => g_pipeline_mux_in,
+    g_pipeline_out => g_pipeline_mux_out,
+    g_nof_in       => g_reverse_len,
+    g_dat_w        => g_data_w
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    in_sel      => reverse_sel,
+    in_dat      => reverse_data_vec,
+    in_val      => reverse_val,
+
+    out_dat     => out_data,
+    out_val     => out_val  -- = in_val delayed by c_pipeline_total
+  );
+
+END str;
diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg
index 4dae8aaf5b..bd4c013312 100644
--- a/libraries/base/dp/hdllib.cfg
+++ b/libraries/base/dp/hdllib.cfg
@@ -158,6 +158,8 @@ synth_files =
     src/vhdl/dp_offload_rx.vhd
     src/vhdl/dp_deinterleave_one_to_n.vhd
     src/vhdl/dp_interleave_n_to_one.vhd
+    src/vhdl/dp_reverse_n_data.vhd
+    src/vhdl/dp_reverse_n_data_fc.vhd
     src/vhdl/dp_deinterleave.vhd
     src/vhdl/dp_reinterleave.vhd
     src/vhdl/mms_dp_gain_arr.vhd
@@ -231,6 +233,8 @@ test_bench_files =
     tb/vhdl/tb_dp_deinterleave_one_to_n_to_one.vhd
     tb/vhdl/tb_dp_deinterleave_interleave_to_one.vhd
     tb/vhdl/tb_dp_deinterleave.vhd
+    tb/vhdl/tb_dp_reverse_n_data.vhd
+    tb/vhdl/tb_dp_reverse_n_data_fc.vhd
     tb/vhdl/tb_dp_distribute.vhd
     tb/vhdl/tb_dp_example_dut.vhd
     tb/vhdl/tb_dp_fifo_fill.vhd
@@ -311,6 +315,8 @@ test_bench_files =
     tb/vhdl/tb_tb_dp_distribute.vhd
     tb/vhdl/tb_tb_dp_deinterleave_one_to_n_to_one.vhd
     tb/vhdl/tb_tb_dp_deinterleave_interleave_to_one.vhd
+    tb/vhdl/tb_tb_dp_reverse_n_data.vhd
+    tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd
     tb/vhdl/tb_tb_dp_example_dut.vhd
     tb/vhdl/tb_tb_dp_flush.vhd
     tb/vhdl/tb_tb_dp_fifo_info.vhd
@@ -383,6 +389,8 @@ regression_test_vhdl =
     tb/vhdl/tb_tb_dp_distribute.vhd
     tb/vhdl/tb_tb_dp_deinterleave_one_to_n_to_one.vhd
     tb/vhdl/tb_tb_dp_deinterleave_interleave_to_one.vhd
+    tb/vhdl/tb_tb_dp_reverse_n_data.vhd
+    tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd
     tb/vhdl/tb_tb_dp_example_dut.vhd
     tb/vhdl/tb_tb_dp_fifo_dc.vhd  
     tb/vhdl/tb_tb_dp_fifo_dc_mixed_widths.vhd  
diff --git a/libraries/base/dp/src/vhdl/dp_reverse_n_data.vhd b/libraries/base/dp/src/vhdl/dp_reverse_n_data.vhd
new file mode 100644
index 0000000000..0b310e901e
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/dp_reverse_n_data.vhd
@@ -0,0 +1,129 @@
+--------------------------------------------------------------------------------
+--
+-- 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.
+--
+--------------------------------------------------------------------------------
+
+LIBRARY IEEE,common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+
+-- Author:
+-- . Eric Kooistra, 14 Feb 2023
+-- Purpose:
+-- . Reverse the order of multiplexed data per every g_reverse_len values in
+--   time.
+-- Description:
+-- . The snk_in blocks size must be an integer multiple of g_reverse_len.
+-- . For example with g_reverse_len = 3, then snk_in input data 012_345_678
+--   becomes 210_543_876 at src_out output.
+-- . See common_reverse_n_data.vhd for more detailed description.
+-- Remark:
+-- . The dp_reverse_n_data.vhd and dp_reverse_n_data_fc.vhd are functionally
+--   equivalent. Use dp_reverse_n_data_fc.vhd flow control (fc) is needed.
+--   A difference is that dp_reverse_n_data_fc causes extra gaps in the data
+--   valid after each g_reverse_len. Therefore use dp_reverse_n_data when no
+--   flow control is needed.
+-- . Typically g_reverse_len should not be too large (~< 4), because then the
+--   implementation takes relatively too much logic.
+
+ENTITY dp_reverse_n_data IS
+  GENERIC (
+    -- Pipeline: 0 for combinatorial, > 0 for registers
+    g_pipeline_demux_in  : NATURAL := 1;  -- serial to parallel section
+    g_pipeline_demux_out : NATURAL := 0;
+    g_pipeline_mux_in    : NATURAL := 0;  -- parallel to serial section
+    g_pipeline_mux_out   : NATURAL := 1;
+    g_reverse_len        : NATURAL := 2;
+    g_data_w             : NATURAL := 16;
+    g_signed             : BOOLEAN := TRUE
+  );
+  PORT (
+    rst         : IN  STD_LOGIC;
+    clk         : IN  STD_LOGIC;
+
+    snk_in      : IN  t_dp_sosi;
+    src_out     : OUT t_dp_sosi
+  );
+END dp_reverse_n_data;
+
+
+ARCHITECTURE str OF dp_reverse_n_data IS
+
+  CONSTANT c_pipeline_total : NATURAL := g_pipeline_demux_in + g_pipeline_demux_out +
+                                         g_reverse_len-1 +
+                                         g_pipeline_mux_in + g_pipeline_mux_out;
+
+  SIGNAL in_data            : STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+
+  SIGNAL reversed_data      : STD_LOGIC_VECTOR(g_data_w-1 DOWNTO 0);
+  SIGNAL reversed_val       : STD_LOGIC;
+
+  SIGNAL snk_in_delayed     : t_dp_sosi;
+
+BEGIN
+
+  in_data <= snk_in.data(g_data_w-1 DOWNTO 0);
+
+  u_common_reverse_n : ENTITY common_lib.common_reverse_n_data
+  GENERIC MAP (
+    -- Pipeline: 0 for combinatorial, > 0 for registers
+    g_pipeline_demux_in  => g_pipeline_demux_in,  -- serial to parallel demux
+    g_pipeline_demux_out => g_pipeline_demux_out,
+    g_pipeline_mux_in    => g_pipeline_mux_in,    -- parallel to serial mux
+    g_pipeline_mux_out   => g_pipeline_mux_out,
+    g_reverse_len        => g_reverse_len,
+    g_data_w             => g_data_w
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    in_data     => in_data,
+    in_val      => snk_in.valid,
+    in_eop      => snk_in.eop,
+    out_data    => reversed_data,
+    out_val     => reversed_val  -- = snk_in_delayed.valid
+  );
+
+  -- Pipeline other sosi fields
+  u_pipe_input : ENTITY work.dp_pipeline
+  GENERIC MAP (
+    g_pipeline   => c_pipeline_total
+  )
+  PORT MAP (
+    rst          => rst,
+    clk          => clk,
+    -- ST sink
+    snk_in       => snk_in,
+    -- ST source
+    src_out      => snk_in_delayed
+  );
+
+  p_src_out : PROCESS(snk_in_delayed, reversed_data)
+  BEGIN
+    src_out <= snk_in_delayed;
+    IF g_signed = TRUE THEN
+      src_out.data <= RESIZE_DP_SDATA(reversed_data);
+    ELSE
+      src_out.data <= RESIZE_DP_DATA(reversed_data);
+    END IF;
+  END PROCESS;
+
+END str;
diff --git a/libraries/base/dp/src/vhdl/dp_reverse_n_data_fc.vhd b/libraries/base/dp/src/vhdl/dp_reverse_n_data_fc.vhd
new file mode 100644
index 0000000000..d29f7bd895
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/dp_reverse_n_data_fc.vhd
@@ -0,0 +1,108 @@
+--------------------------------------------------------------------------------
+--
+-- 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.
+--
+--------------------------------------------------------------------------------
+
+LIBRARY IEEE,common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+
+-- Author:
+-- . Eric Kooistra, 14 Feb 2023
+-- Purpose:
+-- . Reverse the order of multiplexed data per every g_reverse_len values in
+--   time.
+-- Description:
+-- . The snk_in blocks size must be an integer multiple of g_reverse_len.
+-- . For example with g_reverse_len = 3, then snk_in input data 012_345_678
+--   becomes 210_543_876 at src_out output.
+-- Remark:
+-- . The dp_reverse_n_data.vhd and dp_reverse_n_data_fc.vhd are functionally
+--   equivalent. Use dp_reverse_n_data_fc.vhd flow control (fc) is needed.
+--   A difference is that dp_reverse_n_data_fc causes extra gaps in the data
+--   valid after each g_reverse_len. Therefore use dp_reverse_n_data when no
+--   flow control is needed.
+-- . Typically g_reverse_len should not be too large (~< 4), because then the
+--   implementation takes relatively too much logic.
+
+ENTITY dp_reverse_n_data_fc IS
+  GENERIC (
+    -- Pipeline: 0 for combinatorial, > 0 for registers
+    g_pipeline_in   : NATURAL := 1;  -- serial to parallel section
+    g_pipeline_out  : NATURAL := 0;  -- parallel to serial section
+    g_reverse_len   : NATURAL
+  );
+  PORT (
+    rst         : IN  STD_LOGIC;
+    clk         : IN  STD_LOGIC;
+
+    snk_out     : OUT t_dp_siso;
+    snk_in      : IN  t_dp_sosi;
+    src_in      : IN  t_dp_siso := c_dp_siso_rdy;
+    src_out     : OUT t_dp_sosi
+  );
+END dp_reverse_n_data_fc;
+
+
+ARCHITECTURE str OF dp_reverse_n_data_fc IS
+
+  SIGNAL demux_siso_arr   : t_dp_siso_arr(g_reverse_len-1 DOWNTO 0);
+  SIGNAL demux_sosi_arr   : t_dp_sosi_arr(g_reverse_len-1 DOWNTO 0);
+  SIGNAL reverse_siso_arr : t_dp_siso_arr(g_reverse_len-1 DOWNTO 0);
+  SIGNAL reverse_sosi_arr : t_dp_sosi_arr(g_reverse_len-1 DOWNTO 0);
+
+BEGIN
+
+  u_demux_one_to_n : ENTITY work.dp_deinterleave_one_to_n
+  GENERIC MAP (
+    g_pipeline     => g_pipeline_in,
+    g_nof_outputs  => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_out     => snk_out,
+    snk_in      => snk_in,
+    src_in_arr  => demux_siso_arr,
+    src_out_arr => demux_sosi_arr
+  );
+
+  gen_reverse : FOR I IN 0 TO g_reverse_len-1 GENERATE
+    demux_siso_arr(g_reverse_len-1 - I) <= reverse_siso_arr(I);
+    reverse_sosi_arr(I) <= demux_sosi_arr(g_reverse_len-1 - I);
+  END GENERATE;
+
+  u_mux_n_to_one : ENTITY work.dp_interleave_n_to_one
+  GENERIC MAP (
+    g_pipeline   => g_pipeline_out,
+    g_nof_inputs => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_out_arr => reverse_siso_arr,
+    snk_in_arr  => reverse_sosi_arr,
+    src_in      => src_in,
+    src_out     => src_out
+  );
+
+END str;
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data.vhd b/libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data.vhd
new file mode 100644
index 0000000000..313153ac26
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data.vhd
@@ -0,0 +1,320 @@
+-------------------------------------------------------------------------------
+--
+-- 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:
+--   Eric Kooistra, 14 feb 2023
+-- Purpose:
+-- . Test bench for dp_reverse_n_data and dp_reverse_n_data_fc.
+-- Description:
+--   Block diagram:
+--
+--                reverse               reverse back
+--    stimuli --> dp_reverse_n_data --> dp_reverse_n_data --> verify
+--
+--   The dp_reverse_n_data has no flow control. The tb does also verify
+--   the dp_reverse_n_data_fc, but without using flow control. To
+--   verify that dp_reverse_n_data and dp_reverse_n_data_fc with
+--   c_dp_siso_rdy then yield the same results.
+-- Remark:
+-- . Stimuli and verification copied from tb_dp_deinterleave_one_to_n_to_one.
+--
+-- Usage:
+-- . as 5
+-- . run -all
+
+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_str_pkg.ALL;
+USE common_lib.common_lfsr_sequences_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+USE work.tb_dp_pkg.ALL;
+
+ENTITY tb_dp_reverse_n_data IS
+  GENERIC (
+    -- specific
+    g_pipeline               : NATURAL := 1;  -- 0 for combinatorial, > 0 for registers
+    g_nof_repeat             : NATURAL := 5;
+    g_reverse_len            : NATURAL := 2;
+    g_pkt_gap                : NATURAL := 10
+  );
+END tb_dp_reverse_n_data;
+
+
+ARCHITECTURE tb OF tb_dp_reverse_n_data IS
+                                                                                                
+  CONSTANT c_pkt_len                  : NATURAL := g_reverse_len * 5;
+
+  -- dp_stream_stimuli
+  CONSTANT c_stimuli_pulse_active     : NATURAL := 3;
+  CONSTANT c_stimuli_pulse_period     : NATURAL := 7;
+
+  CONSTANT c_data_w                   : NATURAL := 16;
+  CONSTANT c_data_init                : NATURAL := 0;
+  CONSTANT c_re_init                  : NATURAL := 7;
+  CONSTANT c_im_init                  : NATURAL := 17;
+  CONSTANT c_bsn_init                 : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := X"0000000000000000";  -- X"0877665544332211"
+  CONSTANT c_err_init                 : NATURAL := 247;
+  CONSTANT c_channel_init             : NATURAL := 5;  -- fixed
+
+  -- dp_stream_verify
+  CONSTANT c_flow_control_latency     : NATURAL := c_pkt_len;
+                                                           
+  CONSTANT c_data_max                 : UNSIGNED(c_data_w-1 DOWNTO 0) := (OTHERS=>'1');
+  CONSTANT c_dsp_max                  : UNSIGNED(c_data_w-1 DOWNTO 0) := (OTHERS=>'1');
+
+  --CONSTANT c_verify_snk_in_cnt_max    : t_dp_sosi_unsigned := c_dp_sosi_unsigned_rst;  -- default 0 is no wrap
+  CONSTANT c_verify_snk_in_cnt_max    : t_dp_sosi_unsigned := TO_DP_SOSI_UNSIGNED('0', '0', '0', '0', c_data_max, c_dsp_max, c_dsp_max, c_unsigned_0, c_unsigned_0, c_unsigned_0, c_unsigned_0);
+  CONSTANT c_verify_snk_in_cnt_gap    : t_dp_sosi_unsigned := c_dp_sosi_unsigned_ones; -- default only accept increment +1
+
+  -- both
+  CONSTANT c_sync_period              : NATURAL := 10;
+  CONSTANT c_sync_offset              : NATURAL := 7;
+
+  SIGNAL clk                        : STD_LOGIC := '1';
+  SIGNAL rst                        : STD_LOGIC := '1';
+  SIGNAL tb_end                     : STD_LOGIC := '0';
+
+  SIGNAL stimuli_src_out            : t_dp_sosi;
+  SIGNAL stimuli_src_out_data       : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+  SIGNAL reverse_src_out            : t_dp_sosi;
+  SIGNAL reverse_src_out_data       : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+  SIGNAL reverse_fc_src_out         : t_dp_sosi;
+  SIGNAL reverse_fc_src_out_data    : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+  SIGNAL verify_snk_in_enable       : t_dp_sosi_sl := c_dp_sosi_sl_rst;
+  SIGNAL last_snk_in                : t_dp_sosi;
+  SIGNAL last_snk_in_evt            : STD_LOGIC;
+  SIGNAL verify_last_snk_in_evt     : t_dp_sosi_sl := c_dp_sosi_sl_rst;
+
+  SIGNAL verify_snk_in              : t_dp_sosi;
+  SIGNAL verify_snk_in_data         : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+  SIGNAL verify_fc_snk_in           : t_dp_sosi;
+  SIGNAL verify_fc_snk_in_data      : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+BEGIN
+
+  clk <= (NOT clk) OR tb_end AFTER clk_period/2;
+  rst <= '1', '0' AFTER clk_period*7;
+
+  ------------------------------------------------------------------------------
+  -- DATA GENERATION
+  ------------------------------------------------------------------------------
+
+  u_dp_stream_stimuli : ENTITY work.dp_stream_stimuli
+  GENERIC MAP (
+    g_instance_nr    => 0,                        -- only one stream so choose index 0
+    -- flow control
+    g_flow_control   => e_active,   -- always active, no flow control
+    -- initializations
+    g_sync_period    => c_sync_period,
+    g_sync_offset    => c_sync_offset,
+    g_use_complex    => FALSE,
+    g_data_init      => c_data_init,
+    g_re_init        => c_re_init,
+    g_im_init        => c_im_init,
+    g_bsn_init       => c_bsn_init,
+    g_err_init       => c_err_init,
+    g_channel_init   => c_channel_init,
+    -- specific
+    g_in_dat_w       => c_data_w,
+    g_nof_repeat     => g_nof_repeat,
+    g_pkt_len        => c_pkt_len,
+    g_pkt_gap        => g_pkt_gap,
+    g_wait_last_evt  => c_flow_control_latency   -- number of clk cycles to wait with last_snk_in_evt after finishing the stimuli
+  )
+  PORT MAP (
+    rst                 => rst,
+    clk                 => clk,
+
+    -- Generate stimuli
+    src_in              => c_dp_siso_rdy,
+    src_out             => stimuli_src_out,
+
+    -- End of stimuli
+    last_snk_in         => last_snk_in,      -- expected verify_snk_in after end of stimuli
+    last_snk_in_evt     => last_snk_in_evt,  -- trigger verify to verify the last_snk_in
+    tb_end              => tb_end            -- signal end of tb as far as this dp_stream_stimuli is concerned
+  );
+
+
+  ------------------------------------------------------------------------------
+  -- DATA VERIFICATION
+  ------------------------------------------------------------------------------
+
+  -- Select fields that need to be verified
+  -- . during the test
+  verify_snk_in_enable.sync    <= '1';   -- or '0'
+  verify_snk_in_enable.bsn     <= '1';
+  verify_snk_in_enable.data    <= '1';
+  verify_snk_in_enable.valid   <= '1';
+  verify_snk_in_enable.sop     <= '1';
+  verify_snk_in_enable.eop     <= '1';
+  verify_snk_in_enable.empty   <= '0';
+  verify_snk_in_enable.channel <= '1';
+  verify_snk_in_enable.err     <= '1';
+
+  -- . after the test
+  verify_last_snk_in_evt.sync    <= last_snk_in_evt;
+  verify_last_snk_in_evt.bsn     <= last_snk_in_evt;
+  verify_last_snk_in_evt.data    <= last_snk_in_evt;
+  verify_last_snk_in_evt.valid   <= last_snk_in_evt;
+  verify_last_snk_in_evt.sop     <= last_snk_in_evt;
+  verify_last_snk_in_evt.eop     <= last_snk_in_evt;
+  verify_last_snk_in_evt.empty   <= '0';
+  verify_last_snk_in_evt.channel <= last_snk_in_evt;
+  verify_last_snk_in_evt.err     <= last_snk_in_evt;
+
+  u_dp_stream_verify : ENTITY work.dp_stream_verify
+  GENERIC MAP (
+    g_instance_nr    => 0,                        -- only one stream so choose index 0
+    -- flow control
+    g_flow_control   => e_active,   -- always active, no flow control
+    -- initializations
+    g_sync_period    => c_sync_period,
+    g_sync_offset    => c_sync_offset,
+    g_snk_in_cnt_max => c_verify_snk_in_cnt_max,
+    g_snk_in_cnt_gap => c_verify_snk_in_cnt_gap,
+    -- specific
+    g_in_dat_w       => c_data_w,
+    g_pkt_len        => c_pkt_len
+  )
+  PORT MAP (
+    rst                        => rst,
+    clk                        => clk,
+
+    -- Verify data
+    snk_out                    => OPEN,
+    snk_in                     => verify_snk_in,
+
+    -- During stimuli
+    verify_snk_in_enable       => verify_snk_in_enable,  -- enable verify to verify that the verify_snk_in fields are incrementing
+
+    -- End of stimuli
+    expected_snk_in            => last_snk_in,            -- expected verify_snk_in after end of stimuli
+    verify_expected_snk_in_evt => verify_last_snk_in_evt  -- trigger verify to verify the last_snk_in
+  );
+
+  ------------------------------------------------------------------------------
+  -- DUT: Using dp_reverse_n_data
+  ------------------------------------------------------------------------------
+
+  -- Use g_pipeline_demux_out = 0 and g_pipeline_mux_in = 0, to have that
+  -- u_reverse and u_reverse_fc, and u_unreverse and u_unreverse_fc use the
+  -- same total pipeline, so that their outputs can easily be compared.
+
+  -- Reverse
+  u_reverse : ENTITY work.dp_reverse_n_data
+  GENERIC MAP (
+    g_pipeline_demux_in  => g_pipeline,
+    g_pipeline_demux_out => 0,
+    g_pipeline_mux_in    => 0,
+    g_pipeline_mux_out   => g_pipeline,
+    g_reverse_len        => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_in      => stimuli_src_out,
+    src_out     => reverse_src_out
+  );
+
+  -- Reverse again to unreverse
+  u_unreverse : ENTITY work.dp_reverse_n_data
+  GENERIC MAP (
+    g_pipeline_demux_in  => g_pipeline,
+    g_pipeline_demux_out => 0,
+    g_pipeline_mux_in    => 0,
+    g_pipeline_mux_out   => g_pipeline,
+    g_reverse_len        => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_in      => reverse_src_out,
+    src_out     => verify_snk_in
+  );
+
+  ------------------------------------------------------------------------------
+  -- DUT: Using dp_reverse_n_data_fc, but with c_dp_siso_rdy so no flow control
+  ------------------------------------------------------------------------------
+
+  -- Reverse
+  u_reverse_fc : ENTITY work.dp_reverse_n_data_fc
+  GENERIC MAP (
+    g_pipeline_in  => g_pipeline,
+    g_pipeline_out => g_pipeline,
+    g_reverse_len  => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_in      => stimuli_src_out,
+    src_out     => reverse_fc_src_out
+  );
+
+  -- Reverse again to unreverse
+  u_unreverse_fc : ENTITY work.dp_reverse_n_data_fc
+  GENERIC MAP (
+    g_pipeline_in  => g_pipeline,
+    g_pipeline_out => g_pipeline,
+    g_reverse_len  => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_in      => reverse_fc_src_out,
+    src_out     => verify_fc_snk_in
+  );
+
+  -- Verify that dp_reverse_n_data_fc is equivalent to dp_reverse_n_data, when
+  -- no flow control is used.
+  p_verify_fc : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF verify_snk_in.valid = '1' THEN
+        ASSERT verify_snk_in = verify_fc_snk_in REPORT "Unexpected verify_fc_snk_in at " & TIME'IMAGE(NOW) SEVERITY ERROR;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  ------------------------------------------------------------------------------
+  -- Auxiliary
+  ------------------------------------------------------------------------------
+
+  -- Map to slv to ease monitoring in wave window
+  stimuli_src_out_data    <= stimuli_src_out.data(c_data_w-1 DOWNTO 0);
+
+  reverse_src_out_data    <= reverse_src_out.data(c_data_w-1 DOWNTO 0);
+  reverse_fc_src_out_data <= reverse_fc_src_out.data(c_data_w-1 DOWNTO 0);
+
+  verify_snk_in_data      <= verify_snk_in.data(c_data_w-1 DOWNTO 0);
+  verify_fc_snk_in_data   <= verify_fc_snk_in.data(c_data_w-1 DOWNTO 0);
+
+END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data_fc.vhd b/libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data_fc.vhd
new file mode 100644
index 0000000000..fc4ade544f
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_dp_reverse_n_data_fc.vhd
@@ -0,0 +1,276 @@
+-------------------------------------------------------------------------------
+--
+-- 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:
+--   Eric Kooistra, 14 feb 2023
+-- Purpose:
+-- . Test bench for dp_reverse_n_data_fc.
+-- Description:
+--   Block diagram:
+--
+--                reverse               reverse back
+--    stimuli --> dp_reverse_n_data_fc --> dp_reverse_n_data_fc --> verify
+--
+-- Remark:
+-- . Stimuli and verification copied from tb_dp_deinterleave_one_to_n_to_one.
+--
+-- Usage:
+-- . as 5
+-- . run -all
+
+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_str_pkg.ALL;
+USE common_lib.common_lfsr_sequences_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+USE work.tb_dp_pkg.ALL;
+
+ENTITY tb_dp_reverse_n_data_fc IS
+  GENERIC (
+    -- general
+    g_flow_control_stimuli   : t_dp_flow_control_enum := e_active;  -- always e_active, e_random or e_pulse flow control
+    g_flow_control_verify    : t_dp_flow_control_enum := e_active;  -- always e_active, e_random or e_pulse flow control
+    -- specific
+    g_pipeline               : NATURAL := 1;  -- 0 for combinatorial, > 0 for registers
+    g_nof_repeat             : NATURAL := 5;
+    g_reverse_len            : NATURAL := 7;
+    g_pkt_gap                : NATURAL := 10
+  );
+END tb_dp_reverse_n_data_fc;
+
+
+ARCHITECTURE tb OF tb_dp_reverse_n_data_fc IS
+                                                                                                
+  CONSTANT c_pkt_len                  : NATURAL := g_reverse_len * 5;
+
+  -- dp_stream_stimuli
+  CONSTANT c_stimuli_pulse_active     : NATURAL := 3;
+  CONSTANT c_stimuli_pulse_period     : NATURAL := 7;
+
+  CONSTANT c_data_w                   : NATURAL := 16;
+  CONSTANT c_data_init                : NATURAL := 0;
+  CONSTANT c_re_init                  : NATURAL := 7;
+  CONSTANT c_im_init                  : NATURAL := 17;
+  CONSTANT c_bsn_init                 : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := X"0000000000000000";  -- X"0877665544332211"
+  CONSTANT c_err_init                 : NATURAL := 247;
+  CONSTANT c_channel_init             : NATURAL := 5;  -- fixed
+
+  -- dp_stream_verify
+  CONSTANT c_verify_pulse_active      : NATURAL := 1;
+  CONSTANT c_verify_pulse_period      : NATURAL := 5;
+
+  CONSTANT c_flow_control_latency_pls : NATURAL := g_nof_repeat * c_pkt_len * (c_verify_pulse_period*c_stimuli_pulse_period) / (c_stimuli_pulse_active*c_verify_pulse_active);
+  CONSTANT c_flow_control_latency_rnd : NATURAL := g_nof_repeat * c_pkt_len;
+  CONSTANT c_flow_control_latency     : NATURAL := sel_a_b(g_flow_control_stimuli=e_pulse OR g_flow_control_verify=e_pulse,
+                                                           c_flow_control_latency_pls,
+                                                           c_flow_control_latency_rnd);  -- worst case value
+                                                           
+  CONSTANT c_data_max                 : UNSIGNED(c_data_w-1 DOWNTO 0) := (OTHERS=>'1');
+  CONSTANT c_dsp_max                  : UNSIGNED(c_data_w-1 DOWNTO 0) := (OTHERS=>'1');
+
+  --CONSTANT c_verify_snk_in_cnt_max    : t_dp_sosi_unsigned := c_dp_sosi_unsigned_rst;  -- default 0 is no wrap
+  CONSTANT c_verify_snk_in_cnt_max    : t_dp_sosi_unsigned := TO_DP_SOSI_UNSIGNED('0', '0', '0', '0', c_data_max, c_dsp_max, c_dsp_max, c_unsigned_0, c_unsigned_0, c_unsigned_0, c_unsigned_0);
+  CONSTANT c_verify_snk_in_cnt_gap    : t_dp_sosi_unsigned := c_dp_sosi_unsigned_ones; -- default only accept increment +1
+
+  -- both
+  CONSTANT c_sync_period              : NATURAL := 10;
+  CONSTANT c_sync_offset              : NATURAL := 7;
+
+  SIGNAL clk                        : STD_LOGIC := '1';
+  SIGNAL rst                        : STD_LOGIC := '1';
+  SIGNAL tb_end                     : STD_LOGIC := '0';
+
+  SIGNAL stimuli_src_in             : t_dp_siso := c_dp_siso_rdy;
+  SIGNAL stimuli_src_out            : t_dp_sosi;
+  SIGNAL stimuli_src_out_data       : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+  SIGNAL reverse_src_in             : t_dp_siso;
+  SIGNAL reverse_src_out            : t_dp_sosi;
+  SIGNAL reverse_src_out_data       : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+  SIGNAL verify_snk_in_enable       : t_dp_sosi_sl := c_dp_sosi_sl_rst;
+  SIGNAL last_snk_in                : t_dp_sosi;
+  SIGNAL last_snk_in_evt            : STD_LOGIC;
+  SIGNAL verify_last_snk_in_evt     : t_dp_sosi_sl := c_dp_sosi_sl_rst;
+
+  SIGNAL verify_snk_out             : t_dp_siso := c_dp_siso_rdy;
+  SIGNAL verify_snk_in              : t_dp_sosi;
+  SIGNAL verify_snk_in_data         : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+
+BEGIN
+
+  clk <= (NOT clk) OR tb_end AFTER clk_period/2;
+  rst <= '1', '0' AFTER clk_period*7;
+
+  ------------------------------------------------------------------------------
+  -- DATA GENERATION
+  ------------------------------------------------------------------------------
+
+  u_dp_stream_stimuli : ENTITY work.dp_stream_stimuli
+  GENERIC MAP (
+    g_instance_nr    => 0,                        -- only one stream so choose index 0
+    -- flow control
+    g_random_w       => 15,                       -- use different random width for stimuli and for verify to have different random sequences
+    g_pulse_active   => c_stimuli_pulse_active,
+    g_pulse_period   => c_stimuli_pulse_period,
+    g_flow_control   => g_flow_control_stimuli,   -- always active, random or pulse flow control
+    -- initializations
+    g_sync_period    => c_sync_period,
+    g_sync_offset    => c_sync_offset,
+    g_use_complex    => FALSE,
+    g_data_init      => c_data_init,
+    g_re_init        => c_re_init,
+    g_im_init        => c_im_init,
+    g_bsn_init       => c_bsn_init,
+    g_err_init       => c_err_init,
+    g_channel_init   => c_channel_init,
+    -- specific
+    g_in_dat_w       => c_data_w,
+    g_nof_repeat     => g_nof_repeat,
+    g_pkt_len        => c_pkt_len,
+    g_pkt_gap        => g_pkt_gap,
+    g_wait_last_evt  => c_flow_control_latency   -- number of clk cycles to wait with last_snk_in_evt after finishing the stimuli
+  )
+  PORT MAP (
+    rst                 => rst,
+    clk                 => clk,
+
+    -- Generate stimuli
+    src_in              => stimuli_src_in,
+    src_out             => stimuli_src_out,
+
+    -- End of stimuli
+    last_snk_in         => last_snk_in,      -- expected verify_snk_in after end of stimuli
+    last_snk_in_evt     => last_snk_in_evt,  -- trigger verify to verify the last_snk_in
+    tb_end              => tb_end            -- signal end of tb as far as this dp_stream_stimuli is concerned
+  );
+
+
+  ------------------------------------------------------------------------------
+  -- DATA VERIFICATION
+  ------------------------------------------------------------------------------
+
+  -- Select fields that need to be verified
+  -- . during the test
+  verify_snk_in_enable.sync    <= '1';   -- or '0'
+  verify_snk_in_enable.bsn     <= '1';
+  verify_snk_in_enable.data    <= '1';
+  verify_snk_in_enable.valid   <= '1';
+  verify_snk_in_enable.sop     <= '1';
+  verify_snk_in_enable.eop     <= '1';
+  verify_snk_in_enable.empty   <= '0';
+  verify_snk_in_enable.channel <= '1';
+  verify_snk_in_enable.err     <= '1';
+
+  -- . after the test
+  verify_last_snk_in_evt.sync    <= last_snk_in_evt;
+  verify_last_snk_in_evt.bsn     <= last_snk_in_evt;
+  verify_last_snk_in_evt.data    <= last_snk_in_evt;
+  verify_last_snk_in_evt.valid   <= last_snk_in_evt;
+  verify_last_snk_in_evt.sop     <= last_snk_in_evt;
+  verify_last_snk_in_evt.eop     <= last_snk_in_evt;
+  verify_last_snk_in_evt.empty   <= '0';
+  verify_last_snk_in_evt.channel <= last_snk_in_evt;
+  verify_last_snk_in_evt.err     <= last_snk_in_evt;
+
+  u_dp_stream_verify : ENTITY work.dp_stream_verify
+  GENERIC MAP (
+    g_instance_nr    => 0,                        -- only one stream so choose index 0
+    -- flow control
+    g_random_w       => 14,                       -- use different random width for stimuli and for verify to have different random sequences
+    g_pulse_active   => c_verify_pulse_active,
+    g_pulse_period   => c_verify_pulse_period,
+    g_flow_control   => g_flow_control_verify,    -- always active, random or pulse flow control
+    -- initializations
+    g_sync_period    => c_sync_period,
+    g_sync_offset    => c_sync_offset,
+    g_snk_in_cnt_max => c_verify_snk_in_cnt_max,
+    g_snk_in_cnt_gap => c_verify_snk_in_cnt_gap,
+    -- specific
+    g_in_dat_w       => c_data_w,
+    g_pkt_len        => c_pkt_len
+  )
+  PORT MAP (
+    rst                        => rst,
+    clk                        => clk,
+
+    -- Verify data
+    snk_out                    => verify_snk_out,
+    snk_in                     => verify_snk_in,
+
+    -- During stimuli
+    verify_snk_in_enable       => verify_snk_in_enable,  -- enable verify to verify that the verify_snk_in fields are incrementing
+
+    -- End of stimuli
+    expected_snk_in            => last_snk_in,            -- expected verify_snk_in after end of stimuli
+    verify_expected_snk_in_evt => verify_last_snk_in_evt  -- trigger verify to verify the last_snk_in
+  );
+
+  ------------------------------------------------------------------------------
+  -- DUT
+  ------------------------------------------------------------------------------
+
+  -- Reverse
+  u_reverse_fc : ENTITY work.dp_reverse_n_data_fc
+  GENERIC MAP (
+    g_pipeline_in  => g_pipeline,
+    g_pipeline_out => g_pipeline,
+    g_reverse_len  => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_out     => stimuli_src_in,
+    snk_in      => stimuli_src_out,
+    src_in      => reverse_src_in,
+    src_out     => reverse_src_out
+  );
+
+  -- Reverse again to unreverse
+  u_unreverse_fc : ENTITY work.dp_reverse_n_data_fc
+  GENERIC MAP (
+    g_pipeline_in  => g_pipeline,
+    g_pipeline_out => g_pipeline,
+    g_reverse_len  => g_reverse_len
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    snk_out     => reverse_src_in,
+    snk_in      => reverse_src_out,
+    src_in      => verify_snk_out,
+    src_out     => verify_snk_in
+  );
+
+  ------------------------------------------------------------------------------
+  -- Auxiliary
+  ------------------------------------------------------------------------------
+
+  -- Map to slv to ease monitoring in wave window
+  stimuli_src_out_data <= stimuli_src_out.data(c_data_w-1 DOWNTO 0);
+  reverse_src_out_data <= reverse_src_out.data(c_data_w-1 DOWNTO 0);
+  verify_snk_in_data   <= verify_snk_in.data(c_data_w-1 DOWNTO 0);
+
+END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data.vhd
new file mode 100644
index 0000000000..832d3e08d8
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data.vhd
@@ -0,0 +1,59 @@
+-------------------------------------------------------------------------------
+--
+-- 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: Eric Kooistra, 14 feb 2023
+-- Purpose: Verify multiple variations of tb_dp_reverse_n_data
+-- Description:
+-- Usage:
+-- > as 3
+-- > run -all
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE work.tb_dp_pkg.ALL;
+
+ENTITY tb_tb_dp_reverse_n_data IS
+END tb_tb_dp_reverse_n_data;
+
+
+ARCHITECTURE tb OF tb_tb_dp_reverse_n_data IS
+
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+  
+  CONSTANT c_rep_act   : NATURAL :=   3;
+  
+BEGIN
+
+-- g_pipeline               : NATURAL := 1;  -- 0 for combinatorial, > 0 for registers
+-- g_nof_repeat             : NATURAL := 5;
+-- g_reverse_len            : NATURAL := 4;
+-- g_pkt_gap                : NATURAL := 0
+
+--    g_pipeline
+--    |  g_reverse_len
+--    |  | g_pkt_gap
+--    |  | |
+  u_pipe_1_0  : ENTITY work.tb_dp_reverse_n_data GENERIC MAP(1, c_rep_act, 1, 0);
+  u_comb_2_0  : ENTITY work.tb_dp_reverse_n_data GENERIC MAP(0, c_rep_act, 2, 0);
+  u_pipe_2_0  : ENTITY work.tb_dp_reverse_n_data GENERIC MAP(1, c_rep_act, 2, 0);
+  u_comb_3_5  : ENTITY work.tb_dp_reverse_n_data GENERIC MAP(0, c_rep_act, 3, 5);
+  u_pipe_3_5  : ENTITY work.tb_dp_reverse_n_data GENERIC MAP(1, c_rep_act, 3, 5);
+
+END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd
new file mode 100644
index 0000000000..d34661cc68
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_reverse_n_data_fc.vhd
@@ -0,0 +1,73 @@
+-------------------------------------------------------------------------------
+--
+-- 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: Eric Kooistra, 14 feb 2023
+-- Purpose: Verify multiple variations of tb_dp_reverse_n_data
+-- Description:
+-- Usage:
+-- > as 3
+-- > run -all
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE work.tb_dp_pkg.ALL;
+
+ENTITY tb_tb_dp_reverse_n_data_fc IS
+END tb_tb_dp_reverse_n_data_fc;
+
+
+ARCHITECTURE tb OF tb_tb_dp_reverse_n_data_fc IS
+
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+  
+  CONSTANT c_rep_act   : NATURAL :=   3;
+  CONSTANT c_rep_rnd   : NATURAL := 100;
+  
+BEGIN
+
+-- -- general
+-- g_flow_control_stimuli   : t_dp_flow_control_enum := e_active;   -- always e_active, e_random or e_pulse flow control
+-- g_flow_control_verify    : t_dp_flow_control_enum := e_active;  -- always e_active, e_random or e_pulse flow control
+-- -- specific
+-- g_pipeline               : NATURAL := 1;  -- 0 for combinatorial, > 0 for registers
+-- g_nof_repeat             : NATURAL := 5;
+-- g_reverse_len            : NATURAL := 4;
+-- g_pkt_gap                : NATURAL := 0
+
+--            g_pipeline
+--            |  g_reverse_len
+--            |  | g_pkt_gap
+--            |  | |
+  u_act_act_pipe_1_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_active, e_active, 1, c_rep_act, 1, 0);
+  u_act_act_pipe_2_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_active, e_active, 1, c_rep_act, 2, 0);
+  u_act_act_pipe_4_7  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_active, e_active, 1, c_rep_act, 4, 7);
+  u_act_act_comb_4_7  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_active, e_active, 0, c_rep_act, 4, 7);
+
+  u_rnd_rnd_pipe_1_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_random, e_random, 1, c_rep_rnd, 1, 0);
+  u_rnd_rnd_pipe_2_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_random, e_random, 1, c_rep_rnd, 2, 0);
+  u_rnd_rnd_pipe_4_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_random, e_random, 1, c_rep_rnd, 4, 0);
+  u_rnd_rnd_pipe_4_7  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_random, e_random, 1, c_rep_rnd, 4, 7);
+
+  u_pls_pls_comb_3_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_pulse,  e_pulse,  0, c_rep_rnd, 3, 0);
+  u_pls_pls_pipe_3_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_pulse,  e_pulse,  1, c_rep_rnd, 3, 0);
+  u_rnd_rnd_comb_3_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_random, e_random, 0, c_rep_rnd, 3, 0);
+  u_rnd_rnd_pipe_3_0  : ENTITY work.tb_dp_reverse_n_data_fc GENERIC MAP(e_random, e_random, 1, c_rep_rnd, 3, 0);
+
+END tb;
-- 
GitLab