Skip to content
Snippets Groups Projects
Commit 8a0bf9f5 authored by Reinier van der Walle's avatar Reinier van der Walle
Browse files

Merge branch 'L2SDP-502' into 'master'

Use v.busy, instead of r.busy, to allow start_pulse at mm_done, to support...

Closes L2SDP-502

See merge request desp/hdl!159
parents 27e2016f 21f43b38
Branches
No related tags found
1 merge request!159Use v.busy, instead of r.busy, to allow start_pulse at mm_done, to support...
......@@ -111,10 +111,16 @@ Implementation steps:
SIGNAL r : t_reg;
SIGNAL nxt_r : t_reg;
. -- Memoryless signals in p_comb (wires used as local auxiliary variables)
SIGNAL s : t_comb;
. -- Memoryless signals and auxiliary variables in p_comb
-- . For unique representation of variables in p_comb as signal wires, the p_comb
-- should assign each field in t_comb only once to a variable. It is allowed to
-- reasign a t_comb variable in p_comb, so use it as a temporary auxiliary
-- variable, but then only the last assignment value will be visible via the
-- signal dbg_wires in the Wave window.
SIGNAL dbg_wires : t_comb;
. -- Structural signals (wires used to connect components and IO)
. -- Structural signals (wires used to connect r, nxt_r to other components and to
-- the entity IO)
. -- Pipeline registers
SIGNAL in_data_p : ...
......@@ -135,8 +141,9 @@ Implementation steps:
-- State variable
VARIABLE v : t_reg;
-- Auxiliary variables
VARIABLE v_* -- optional, use to improve code readability
-- use v. only on left side? use separate v_* to clearly indicate when we use it also on the right side of assignments ?
--VARIABLE v_* -- optional, use to improve code readability
-- -- use v. only on left side? use separate v_* to clearly indicate when we use it also on the right side of assignments ?
VARIABLE w : t_comb;
BEGIN
v := r; -- default keep existing state
v.* := ...; -- default force specific values, e.g. set strobes to '0',
......@@ -152,7 +159,7 @@ Implementation steps:
nxt_r <= v;
-- memory less signals, only for view in wave window
s <= d;
dbg_wires <= w;
END PROCESS;
. -- Pipelining
......
......@@ -124,7 +124,11 @@ BEGIN
IF r.busy = '0' AND start_pulse = '1' THEN
-- initiate next block
v.busy := '1';
ELSIF r.busy = '1' THEN
END IF;
-- use v.busy, instead of r.busy, to allow start_pulse at mm_done, to
-- support zero gaps between output blocks
IF v.busy = '1' THEN
IF out_siso.ready = '1' THEN
-- continue with block
mm_mosi.rd <= '1';
......
......@@ -26,13 +26,17 @@
-- input stream that is ahead of the other remote input streams. After a
-- certain number of blocks on input 0, the same block on all remote
-- inputs should also have arrived. If not then they are replaced by
-- replacement data. The output streams are paced by the block rate of input 0.
-- The user has to read the block within the block period.
-- replacement data. The output streams are paced by the block rate of
-- input 0. The user has to read the block within the block period.
--
-- Features:
-- . uses lost_data flag and replacement data to replace lost input blocks
-- . The g_block_size <= block period, so supports input blocks arriving
-- with or without data valid gaps
-- . uses replacement data to replace lost input blocks and channel bit 0 as
-- lost_data flag
-- . uses replacement data to replace disabled input streams
-- . output block can be read in arbitrary order
-- . output block can be read in arbitrary order via g_use_mm_output = TRUE
-- . output block can be streamed via g_use_mm_output = FALSE
--
-- For more detailed description see:
-- https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+aligner+v2
......@@ -53,22 +57,23 @@ USE work.dp_stream_pkg.ALL;
ENTITY dp_bsn_align_v2 IS
GENERIC (
g_nof_streams : NATURAL; -- number of input and output streams
g_bsn_latency_max : NATURAL; -- Maximum travel latency of a remote block in number of block periods T_blk
g_nof_streams : NATURAL; -- >= 2, number of input and output streams
g_bsn_latency_max : NATURAL; -- maximum travel latency of a remote block in number of block periods T_blk
g_nof_aligners_max : POSITIVE := 1; -- 1 when only align at last node, > 1 when align at every intermediate node
g_block_size : NATURAL := 32; -- > 1, g_block_size=1 is not supported
g_bsn_w : NATURAL := c_dp_stream_bsn_w; -- number of bits in sosi BSN
g_data_w : NATURAL; -- number of bits in sosi data
g_replacement_value : INTEGER := 0; -- output sosi data value for missing input blocks
g_data_replacement_value : INTEGER := 0; -- output sosi data value for missing input blocks
g_use_mm_output : BOOLEAN := FALSE; -- output via MM or via streaming DP
g_pipeline_input : NATURAL := 0; -- >= 0, choose 0 for wires, choose 1 to ease timing closure
g_rd_latency : NATURAL := 1 -- 1 or 2, choose 2 to ease timing closure
g_pipeline_input : NATURAL := 1; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of in_sosi_arr
g_pipeline_output : NATURAL := 1; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of out_sosi_arr
g_rd_latency : NATURAL := 2 -- 1 or 2, choose 2 to ease timing closure
);
PORT (
dp_rst : IN STD_LOGIC;
dp_clk : IN STD_LOGIC;
node_index : IN NATURAL RANGE 0 TO g_nof_aligners_max := 0; -- only used when g_nof_aligners_max > 1
node_index : IN NATURAL RANGE 0 TO g_nof_aligners_max-1 := 0; -- only used when g_nof_aligners_max > 1
-- MM control
stream_en_arr : IN STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1');
......@@ -89,8 +94,8 @@ END dp_bsn_align_v2;
ARCHITECTURE rtl OF dp_bsn_align_v2 IS
-- Circular buffer per stream
CONSTANT c_buffer_nof_blocks : NATURAL := ceil_pow2(1 + g_nof_aligners_max * g_bsn_latency_max);
-- Circular buffer per stream, size is next power of 2 that fits
CONSTANT c_buffer_nof_blocks : NATURAL := true_log_pow2(1 + g_nof_aligners_max * g_bsn_latency_max);
CONSTANT c_ram_size : NATURAL := c_buffer_nof_blocks * g_block_size;
CONSTANT c_ram_buf : t_c_mem := (latency => 1,
......@@ -107,13 +112,18 @@ ARCHITECTURE rtl OF dp_bsn_align_v2 IS
-- avoid that synthesis may infer a too larger multiplier
CONSTANT c_product_w : NATURAL := c_blk_pointer_w + c_block_size_w;
-- Output on lost data flag via out_sosi_arr().channel bit 0
CONSTANT c_channel_w : NATURAL := 1;
TYPE t_bsn_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
TYPE t_channel_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_channel_w-1 DOWNTO 0);
TYPE t_adr_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_mem_ram.adr_w-1 DOWNTO 0);
TYPE t_filled_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_buffer_nof_blocks-1 DOWNTO 0);
-- State
TYPE t_reg IS RECORD
-- p_write_arr
wr_pointer : NATURAL;
wr_blk_pointer : NATURAL;
wr_copi_arr : t_mem_copi_arr(g_nof_streams-1 DOWNTO 0);
-- all streams
filled_arr : t_filled_arr(g_nof_streams-1 DOWNTO 0);
......@@ -124,18 +134,24 @@ ARCHITECTURE rtl OF dp_bsn_align_v2 IS
mm_sosi : t_dp_sosi;
dp_sosi : t_dp_sosi;
-- p_read
rd_pointer : INTEGER; -- use integer to detect need to wrap to natural
rd_blk_pointer : INTEGER; -- use integer to detect need to wrap to natural
rd_offset : STD_LOGIC_VECTOR(c_mem_ram.adr_w-1 DOWNTO 0);
rd_copi : t_mem_copi;
fill_cipo_arr : t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0); -- used combinatorial to contain rd_cipo_arr from buffer or replacement data
out_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); -- hold BSN for streaming output
out_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); -- hold BSN until next sop, for easy view in Wave window
out_channel_arr : t_channel_arr(g_nof_streams-1 DOWNTO 0); -- hold channel until next sop per stream, for easy view in Wave window
END RECORD;
-- Wires and auxiliary variables in p_comb
-- . For unique representation as signal wire, the p_comb should assign each
-- field in t_comb only once to a variable. It is allowed to reasign a
-- t_comb variable in p_comb, but then only the last assignment value will
-- be visible via the signal dbg_wires in the Wave window.
TYPE t_comb IS RECORD
ref_sosi : t_dp_sosi;
pointer_slv : STD_LOGIC_VECTOR(c_blk_pointer_w-1 DOWNTO 0);
blk_pointer_slv : STD_LOGIC_VECTOR(c_blk_pointer_w-1 DOWNTO 0);
product_slv : STD_LOGIC_VECTOR(c_product_w-1 DOWNTO 0);
lost_data_flag : STD_LOGIC;
lost_data_flags_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
END RECORD;
......@@ -151,14 +167,15 @@ ARCHITECTURE rtl OF dp_bsn_align_v2 IS
(OTHERS=>'0'),
c_mem_copi_rst,
(OTHERS=>c_mem_cipo_rst),
(OTHERS=>'0'));
(OTHERS=>'0'),
(OTHERS=>(OTHERS=>'0')));
-- State registers for p_comb
SIGNAL r : t_reg;
SIGNAL nxt_r : t_reg;
-- Memoryless signals in p_comb (wires used as local auxiliary variables)
SIGNAL s : t_comb;
-- Memoryless signals in p_comb (wires used as local variables)
SIGNAL dbg_wires : t_comb;
-- Structural signals (wires used to connect components and IO)
SIGNAL dp_done : STD_LOGIC;
......@@ -172,6 +189,7 @@ ARCHITECTURE rtl OF dp_bsn_align_v2 IS
-- Pipeline registers
SIGNAL in_sosi_arr_p : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL rd_copi : t_mem_copi;
SIGNAL comb_out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
-- Debug signals
SIGNAL dbg_nof_streams : NATURAL := g_nof_streams;
......@@ -180,7 +198,7 @@ ARCHITECTURE rtl OF dp_bsn_align_v2 IS
SIGNAL dbg_block_size : NATURAL := g_block_size;
SIGNAL dbg_bsn_w : NATURAL := g_bsn_w;
SIGNAL dbg_data_w : NATURAL := g_data_w;
SIGNAL dbg_replacement_value : INTEGER := g_replacement_value;
SIGNAL dbg_data_replacement_value : INTEGER := g_data_replacement_value;
SIGNAL dbg_use_mm_output : BOOLEAN := g_use_mm_output;
SIGNAL dbg_pipeline_input : NATURAL := g_pipeline_input;
SIGNAL dbg_rd_latency : NATURAL := g_rd_latency;
......@@ -189,7 +207,8 @@ ARCHITECTURE rtl OF dp_bsn_align_v2 IS
BEGIN
mm_sosi <= r.mm_sosi WHEN g_use_mm_output = TRUE ELSE c_dp_sosi_rst;
-- Output mm_sosi, also when g_use_mm_output = FALSE.
mm_sosi <= r.mm_sosi;
p_reg : PROCESS(dp_clk, dp_rst)
BEGIN
......@@ -202,7 +221,7 @@ BEGIN
p_comb : PROCESS(r, in_sosi_arr_p, mm_copi, dp_copi, rd_cipo_arr, rd_sosi_arr)
VARIABLE v : t_reg; -- State variable
VARIABLE d : t_comb; -- Memoryless auxiliary variables, local wires
VARIABLE w : t_comb; -- Local wires = memoryless auxiliary variables
BEGIN
v := r; -- state signals
v.mm_sosi := func_dp_stream_reset_control(r.mm_sosi);
......@@ -222,56 +241,57 @@ BEGIN
IF in_sosi_arr_p(I).sop = '1' THEN
-- . set address at start of block
d.pointer_slv := in_sosi_arr_p(I).bsn(c_blk_pointer_w-1 DOWNTO 0);
d.product_slv := MULT_UVEC(d.pointer_slv, c_block_size_slv);
v.wr_copi_arr(I).address := RESIZE_MEM_ADDRESS(d.product_slv);
w.blk_pointer_slv := in_sosi_arr_p(I).bsn(c_blk_pointer_w-1 DOWNTO 0);
w.product_slv := MULT_UVEC(w.blk_pointer_slv, c_block_size_slv);
v.wr_copi_arr(I).address := RESIZE_MEM_ADDRESS(w.product_slv);
-- . set filled flag at sop, so assume rest of block will follow in time
v.filled_arr(I)(TO_UINT(d.pointer_slv)) := '1';
v.filled_arr(I)(TO_UINT(w.blk_pointer_slv)) := '1';
END IF;
END LOOP;
----------------------------------------------------------------------------
-- p_control, all at sop of local reference input 0
----------------------------------------------------------------------------
d.ref_sosi := in_sosi_arr_p(0);
IF d.ref_sosi.sop = '1' THEN
w.ref_sosi := in_sosi_arr_p(0);
IF w.ref_sosi.sop = '1' THEN
-- . write sync & bsn buffer
v.wr_pointer := TO_UINT(d.ref_sosi.bsn(c_blk_pointer_w-1 DOWNTO 0));
v.sync_arr(v.wr_pointer) := d.ref_sosi.sync;
v.bsn_arr(v.wr_pointer) := d.ref_sosi.bsn(g_bsn_w-1 DOWNTO 0);
-- . update read block pointer at g_bsn_latency_max blocks behind the reference write pointer
IF g_nof_aligners_max = 1 THEN
v.rd_pointer := v.wr_pointer - g_bsn_latency_max;
ELSE
v.rd_pointer := v.wr_pointer - g_bsn_latency_max * node_index;
END IF;
IF v.rd_pointer < 0 THEN
v.rd_pointer := v.rd_pointer + c_buffer_nof_blocks;
v.wr_blk_pointer := TO_UINT(w.ref_sosi.bsn(c_blk_pointer_w-1 DOWNTO 0));
v.sync_arr(v.wr_blk_pointer) := w.ref_sosi.sync;
v.bsn_arr(v.wr_blk_pointer) := w.ref_sosi.bsn(g_bsn_w-1 DOWNTO 0);
-- . update read block pointer at g_bsn_latency_max blocks behind the
-- reference write pointer, dependent on the node_index. For
-- g_bsn_latency_max = 1 the node_index = 0 fixed. For
-- g_bsn_latency_max > 1, node_index is the first BSN aligner in a
-- chain. Each subsequent node in the chain then has to account for
-- g_bsn_latency_max additional block latency.
v.rd_blk_pointer := v.wr_blk_pointer - g_bsn_latency_max * (1 + node_index);
IF v.rd_blk_pointer < 0 THEN
v.rd_blk_pointer := v.rd_blk_pointer + c_buffer_nof_blocks;
END IF;
-- . update read address of read block pointer
d.pointer_slv := TO_UVEC(v.rd_pointer, c_blk_pointer_w);
d.product_slv := MULT_UVEC(d.pointer_slv, c_block_size_slv);
v.rd_offset := RESIZE_UVEC(d.product_slv, c_mem_ram.adr_w);
w.blk_pointer_slv := TO_UVEC(v.rd_blk_pointer, c_blk_pointer_w);
w.product_slv := MULT_UVEC(w.blk_pointer_slv, c_block_size_slv);
v.rd_offset := RESIZE_UVEC(w.product_slv, c_mem_ram.adr_w);
-- . issue mm_sosi, if there is output ready to be read, indicated by filled reference block
IF r.filled_arr(0)(v.rd_pointer) = '1' THEN
IF r.filled_arr(0)(v.rd_blk_pointer) = '1' THEN
v.mm_sosi.sop := '1';
v.mm_sosi.eop := '1';
v.mm_sosi.valid := '1';
-- . pass on timestamp information
v.mm_sosi.sync := v.sync_arr(v.rd_pointer);
v.mm_sosi.bsn := RESIZE_DP_BSN(v.bsn_arr(v.rd_pointer));
v.mm_sosi.sync := v.sync_arr(v.rd_blk_pointer);
v.mm_sosi.bsn := RESIZE_DP_BSN(v.bsn_arr(v.rd_blk_pointer));
-- . pass on lost data flags for enabled streams via channel field, and
-- determine whether the ouput has to insert replacement data
v.mm_sosi.channel := (OTHERS=>'0');
FOR I IN 0 TO g_nof_streams-1 LOOP
d.lost_data_flag := NOT v.filled_arr(I)(v.rd_pointer);
w.lost_data_flags_arr(I) := NOT v.filled_arr(I)(v.rd_blk_pointer);
IF stream_en_arr(I) = '1' THEN -- use MM bit at sop
v.use_replacement_data(I) := d.lost_data_flag; -- enabled stream, so replace the data if the data was lost
v.mm_sosi.channel(I) := d.lost_data_flag; -- enabled stream, so flag the data if the data was lost
v.use_replacement_data(I) := w.lost_data_flags_arr(I); -- enabled stream, so replace the data if the data was lost
v.mm_sosi.channel(I) := w.lost_data_flags_arr(I); -- enabled stream, so flag the data if the data was lost
ELSE
v.use_replacement_data(I) := '1'; -- disabled stream, so replace the data, but do not flag the data as lost
END IF;
......@@ -280,7 +300,7 @@ BEGIN
-- . clear filled flags, after mm_sosi was issued, or could have been issued
FOR I IN 0 TO g_nof_streams-1 LOOP
v.filled_arr(I)(v.rd_pointer) := '0';
v.filled_arr(I)(v.rd_blk_pointer) := '0';
END LOOP;
END IF;
......@@ -294,7 +314,7 @@ BEGIN
-- . if necessary, replace a stream by replacement data
FOR I IN 0 TO g_nof_streams-1 LOOP
IF r.use_replacement_data(I) = '1' THEN
v.fill_cipo_arr(I).rddata := TO_MEM_SDATA(g_replacement_value);
v.fill_cipo_arr(I).rddata := TO_MEM_SDATA(g_data_replacement_value);
END IF;
END LOOP;
......@@ -311,7 +331,7 @@ BEGIN
mm_cipo_arr <= v.fill_cipo_arr;
-- . no output via DP streaming interface
out_sosi_arr <= (OTHERS => c_dp_sosi_rst);
comb_out_sosi_arr <= (OTHERS => c_dp_sosi_rst);
ELSE
--------------------------------------------------------------------------
-- Do the output via the DP streaming interface
......@@ -327,26 +347,33 @@ BEGIN
END IF;
-- . pass on input data from the buffer
d.out_sosi_arr := rd_sosi_arr; -- = v.fill_cipo_arr in streaming format, contains the
w.out_sosi_arr := rd_sosi_arr; -- = v.fill_cipo_arr in streaming format, contains the
-- input data from the buffer or replacement data
IF rd_sosi_arr(0).sop = '1' THEN
-- . at sop pass on input info from r.dp_sosi to all streams in out_sosi_arr
d.out_sosi_arr := func_dp_stream_arr_set(d.out_sosi_arr, r.dp_sosi.sync, "SYNC");
d.out_sosi_arr := func_dp_stream_arr_set(d.out_sosi_arr, r.dp_sosi.bsn, "BSN");
w.out_sosi_arr := func_dp_stream_arr_set(w.out_sosi_arr, r.dp_sosi.sync, "SYNC");
w.out_sosi_arr := func_dp_stream_arr_set(w.out_sosi_arr, r.dp_sosi.bsn, "BSN");
FOR I IN 0 TO g_nof_streams-1 LOOP
-- . pass on the lost flag per stream
d.out_sosi_arr(I).channel := RESIZE_DP_CHANNEL(slv(r.dp_sosi.channel(I)));
w.out_sosi_arr(I).channel := RESIZE_DP_CHANNEL(slv(r.dp_sosi.channel(I)));
END LOOP;
-- . hold BSN until next sop, to ease view in wave window
-- . hold sop info fields until next sop, to ease view in wave window
v.out_bsn := r.dp_sosi.bsn(g_bsn_w-1 DOWNTO 0);
FOR I IN 0 TO g_nof_streams-1 LOOP
v.out_channel_arr(I) := w.out_sosi_arr(I).channel(c_channel_w-1 DOWNTO 0);
END LOOP;
ELSE
-- . until next sop pass on BSN, to ease view in wave window
d.out_sosi_arr := func_dp_stream_arr_set(d.out_sosi_arr, r.out_bsn, "BSN");
-- . until next sop pass on BSN to all streams, to ease view in wave window
w.out_sosi_arr := func_dp_stream_arr_set(w.out_sosi_arr, r.out_bsn, "BSN");
FOR I IN 0 TO g_nof_streams-1 LOOP
-- . until next sop pass on channel bit 0 per stream, to ease view in wave window
w.out_sosi_arr(I).channel := RESIZE_DP_CHANNEL(r.out_channel_arr(I));
END LOOP;
END IF;
-- . output via DP streaming interface
out_sosi_arr <= d.out_sosi_arr;
comb_out_sosi_arr <= w.out_sosi_arr;
-- . no output via MM interface
mm_cipo_arr <= (OTHERS => c_mem_cipo_rst);
......@@ -357,8 +384,8 @@ BEGIN
----------------------------------------------------------------------------
nxt_r <= v;
-- memory less signals, only for view in wave window
s <= d;
-- local wires, only for view in wave window
dbg_wires <= w;
END PROCESS;
------------------------------------------------------------------------------
......@@ -421,7 +448,7 @@ BEGIN
-- Pipelining
------------------------------------------------------------------------------
-- . input
-- . input streams
u_in_sosi_arr_p : ENTITY work.dp_pipeline_arr
GENERIC MAP (
g_nof_streams => g_nof_streams,
......@@ -439,4 +466,19 @@ BEGIN
-- . read RAM
rd_copi <= nxt_r.rd_copi WHEN g_rd_latency = 1 ELSE r.rd_copi;
-- . output streams
u_out_sosi_arr_p : ENTITY work.dp_pipeline_arr
GENERIC MAP (
g_nof_streams => g_nof_streams,
g_pipeline => g_pipeline_output
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
-- ST sink
snk_in_arr => comb_out_sosi_arr,
-- ST source
src_out_arr => out_sosi_arr
);
END rtl;
......@@ -43,13 +43,19 @@ USE work.dp_stream_pkg.ALL;
ENTITY mmp_dp_bsn_align_v2 IS
GENERIC (
-- for dp_bsn_align_v2
g_nof_streams : NATURAL; -- number of input and output streams
g_bsn_latency_max : NATURAL; -- Maximum travel latency of a remote block in number of block periods T_blk
g_nof_aligners_max : NATURAL := 1; -- 1 when only align at last node, > 1 when align at every intermediate node
g_block_size : NATURAL := 32; -- > 1, g_block_size=1 is not supported
g_bsn_w : NATURAL := c_dp_stream_bsn_w; -- number of bits in sosi BSN
g_data_w : NATURAL; -- number of bits in sosi data
g_replacement_value : INTEGER := 0; -- output sosi data value for missing input blocks
g_data_replacement_value : INTEGER := 0; -- output sosi data value for missing input blocks
g_use_mm_output : BOOLEAN := FALSE; -- output via MM or via streaming DP
g_pipeline_input : NATURAL := 1; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of in_sosi_arr
g_pipeline_output : NATURAL := 1; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of out_sosi_arr
g_rd_latency : NATURAL := 2; -- 1 or 2, choose 2 to ease timing closure
-- for mms_dp_bsn_monitor_v2
g_nof_clk_per_sync : NATURAL := 200*10**6;
g_nof_input_bsn_monitors : NATURAL := 0;
g_use_bsn_output_monitor : BOOLEAN := FALSE
......@@ -59,8 +65,8 @@ ENTITY mmp_dp_bsn_align_v2 IS
mm_rst : IN STD_LOGIC;
mm_clk : IN STD_LOGIC;
reg_copi : IN t_mem_copi;
reg_cipo : OUT t_mem_cipo;
reg_bsn_align_copi : IN t_mem_copi;
reg_bsn_align_cipo : OUT t_mem_cipo;
reg_input_monitor_copi : IN t_mem_copi;
reg_input_monitor_cipo : OUT t_mem_cipo;
......@@ -72,15 +78,18 @@ ENTITY mmp_dp_bsn_align_v2 IS
dp_rst : IN STD_LOGIC;
dp_clk : IN STD_LOGIC;
node_index : IN NATURAL := 0; -- only used when g_nof_aligners_max > 1
node_index : IN NATURAL RANGE 0 TO g_nof_aligners_max-1 := 0; -- only used when g_nof_aligners_max > 1
-- Streaming input
in_sosi_arr : IN t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
-- Output via local MM in dp_clk domain
mm_copi : IN t_mem_copi; -- read access to output block, all output streams share same mm_copi
-- Output via local MM interface in dp_clk domain, when g_use_mm_output = TRUE.
mm_sosi : OUT t_dp_sosi; -- streaming information that signals that an output block can be read
mm_copi : IN t_mem_copi := c_mem_copi_rst; -- read access to output block, all output streams share same mm_copi
mm_cipo_arr : OUT t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0);
mm_sosi : OUT t_dp_sosi -- streaming information that signals that an output block can be read
-- Output via streaming DP interface, when g_use_mm_output = TRUE.
out_sosi_arr : OUT t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0)
);
END mmp_dp_bsn_align_v2;
......@@ -100,10 +109,14 @@ ARCHITECTURE str OF mmp_dp_bsn_align_v2 IS
SIGNAL reg_wr : STD_LOGIC_VECTOR(c_mm_reg.nof_dat*c_mm_reg.dat_w-1 DOWNTO 0);
SIGNAL stream_en_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL mm_sosi_arr : t_dp_sosi_arr(0 DOWNTO 0);
SIGNAL ref_sync : STD_LOGIC;
SIGNAL mon_out_sosi_arr : t_dp_sosi_arr(0 DOWNTO 0);
SIGNAL i_out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL i_mm_sosi : t_dp_sosi;
BEGIN
-- MM control of BSN aligner
u_reg : ENTITY common_lib.common_reg_r_w_dc
GENERIC MAP (
g_cross_clock_domain => TRUE,
......@@ -118,8 +131,8 @@ BEGIN
st_clk => dp_clk,
-- Memory Mapped Slave in mm_clk domain
sla_in => reg_copi,
sla_out => reg_cipo,
sla_in => reg_bsn_align_copi,
sla_out => reg_bsn_align_cipo,
-- MM registers in st_clk domain
reg_wr_arr => OPEN,
......@@ -130,6 +143,9 @@ BEGIN
stream_en_arr <= reg_wr;
-- Use local sync as reference sync input for the BSN monitors
ref_sync <= in_sosi_arr(0).sync;
-- Use input BSN monitors for the first g_nof_input_bsn_monitors input
-- streams, e.g. to support:
-- . only one input stream (g_nof_input_bsn_monitors = 1), or
......@@ -156,9 +172,8 @@ BEGIN
-- Streaming clock domain
dp_rst => dp_rst,
dp_clk => dp_clk,
ref_sync => in_sosi_arr(0).sync, -- local reference sync input
ref_sync => ref_sync,
in_siso_arr => (OTHERS=>c_dp_siso_rdy),
in_sosi_arr => in_sosi_arr(g_nof_input_bsn_monitors-1 DOWNTO 0)
);
END GENERATE;
......@@ -185,13 +200,19 @@ BEGIN
-- Streaming clock domain
dp_rst => dp_rst,
dp_clk => dp_clk,
ref_sync => in_sosi_arr(0).sync, -- local reference sync input
ref_sync => ref_sync,
in_siso_arr => (OTHERS=>c_dp_siso_rdy),
in_sosi_arr => mm_sosi_arr
in_sosi_arr => mon_out_sosi_arr
);
END GENERATE;
-- Use mm_sosi or out_sosi_arr(0) from BSN aligner for output BSN monitor
mon_out_sosi_arr(0) <= i_mm_sosi WHEN g_use_mm_output = TRUE ELSE i_out_sosi_arr(0);
-- wire to output
mm_sosi <= i_mm_sosi;
out_sosi_arr <= i_out_sosi_arr;
u_bsn_align : ENTITY work.dp_bsn_align_v2
GENERIC MAP (
g_nof_streams => g_nof_streams,
......@@ -200,7 +221,11 @@ BEGIN
g_block_size => g_block_size,
g_bsn_w => g_bsn_w,
g_data_w => g_data_w,
g_replacement_value => g_replacement_value
g_data_replacement_value => g_data_replacement_value,
g_use_mm_output => g_use_mm_output,
g_pipeline_input => g_pipeline_input,
g_pipeline_output => g_pipeline_output,
g_rd_latency => g_rd_latency
)
PORT MAP (
dp_rst => dp_rst,
......@@ -211,12 +236,12 @@ BEGIN
-- Streaming input
in_sosi_arr => in_sosi_arr,
-- Output via local MM in dp_clk domain
mm_sosi => i_mm_sosi,
mm_copi => mm_copi,
mm_cipo_arr => mm_cipo_arr,
mm_sosi => mm_sosi
-- Output via streaming DP interface, when g_use_mm_output = TRUE.
out_sosi_arr => i_out_sosi_arr
);
mm_sosi <= mm_sosi_arr(0);
END str;
......@@ -19,6 +19,54 @@
-- Author: Eric Kooistra, 3 Sept 2021
-- Purpose: Verify dp_bsn_align_v2
-- Description:
-- The tb verifies:
-- . DUT alignment of g_nof_streams >= 2
-- . DUT restart via g_tb_nof_restart > 1
-- . gaps or no gaps between blocks via g_block_period >= g_block_size
-- . g_bsn_latency_max in combination with g_tb_diff_delay
-- . g_use_mm_output using DUT MM to DP or external MM to DP in tb
-- . g_data_replacement_value for a remote g_disable_stream_id (it is
-- sufficient to verify one stream)
-- . g_data_replacement_value and lost flag channel(0) bit for a lost
-- remote g_lost_stream_id
-- . the local stream cannot be disabled or lost, because if it does,
-- then there is no output, which is verified by g_tb_nof_restart >= 2
-- . g_lost_bsn_id to loose a single block in stream 1 and verify that
-- it gets replaced and flagged.
-- . array of one or more BSN aligners via g_nof_aligners_max >= 1,
-- using node_index_arr, only support tb for g_use_mm_output = FALSE
-- Remark:
-- For this BSN aligner component it was essential to have an almost
-- complete, reviewed, detailed design document, because it is a complex
-- component. Main difference after review was addding build in support
-- for streaming output via g_use_mm_output. The clear design made it
-- much easier to achieve a draft implementation that was almost correct.
-- For the DUT it implementation it was also essential to use the p_reg,
-- p_comb coding template.
-- The initial DUT implementation did not change much anymore after the
-- first tb tests, expect for some small (but important) corrections.
-- Each feature of the tb took several hours or up to a day to add. Much
-- time was also spent to regularly clean up and simply the code. The
-- last tb feature that verifies a chain of DUTs is realy nice to see,
-- e.g. by expanding dut_out_sosi_2_arr in the Wave window. The ascii
-- drawing at gen_bsn_align_chain was used to more easily connect and
-- clarify the wiring for the chain of DUTs.
-- Implementing the tb took more time than the DUT, but the design
-- document and the especially the design decisions took the most time.
-- The design decision to use a circular buffer instead of FIFOs and the
-- design decision to rely on a local reference and fixed latencies were
-- important. Initial prestudy end 2020 was done based on experience with
-- aligning streams in LOFAR1 and with the bsn_aligner.vhd in APERTIF.
-- About a in autumn 2021 the detailed design and implementation were done.
-- . prestudy ~ 1 week
-- . design decisions doc ~ 1 week (based on prestudy)
-- . detailed design doc ~ 1 week
-- . review and process review ~ 2 days
-- . implement DUT ~ 2 days (core in 1 day, features one more day)
-- . implement tb (and adding to tb_tb) ~ 1 week
-- . implement tb_mmp ~ 1 day
-- Total: ~ 6 weeks
--
-- Usage:
-- > as 10
-- > run -all
......@@ -38,19 +86,24 @@ ENTITY tb_dp_bsn_align_v2 IS
GENERIC (
-- DUT
g_nof_streams : NATURAL := 2; -- number of input and output streams
g_bsn_latency_max : NATURAL := 2; -- Maximum travel latency of a remote block in number of block periods T_blk
g_bsn_latency_max : NATURAL := 1; -- Maximum travel latency of a remote block in number of block periods T_blk
g_nof_aligners_max : POSITIVE := 1; -- 1 when only align at last node, > 1 when align at every intermediate node
g_block_size : NATURAL := 11; -- > 1, g_block_size=1 is not supported
g_block_period : NATURAL := 20; -- >= g_block_size, = g_block_size + c_gap_size
g_bsn_w : NATURAL := c_dp_stream_bsn_w; -- number of bits in sosi BSN
g_data_w : NATURAL := 16; -- number of bits in sosi data
g_replacement_value : INTEGER := 17; -- output sosi data replacement value for missing input blocks
g_data_replacement_value : INTEGER := 17; -- output sosi data replacement value for missing input blocks
g_disable_stream_id : NATURAL := 0; -- default 0 to enable all streams, > 0 selects stream that will be disabled
g_lost_stream_id : NATURAL := 0; -- default 0 to have all streams, > 0 selects stream that will be lost
g_lost_bsn_id : NATURAL := 0; -- for stream 1 the block with bsn = g_lost_bsn_id will be lost
g_use_mm_output : BOOLEAN := FALSE; -- output via MM or via streaming DP
g_pipeline_input : NATURAL := 1; -- >= 0, choose 0 for wires, choose 1 to ease timing closure
g_rd_latency : NATURAL := 2; -- 1 or 2, choose 2 to ease timing closure
g_pipeline_input : NATURAL := 0; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of in_sosi_arr
g_pipeline_output : NATURAL := 0; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of out_sop_arr
g_rd_latency : NATURAL := 1; -- 1 or 2, choose 2 to ease timing closure
-- TB
g_tb_diff_delay_max : NATURAL := 10; -- maximum nof clk delay between any inputs, <= c_align_latency_nof_clk
g_tb_diff_delay : INTEGER := 0; -- 0 = aligned inputs, -1 = max input delay for no loss,
-- >~ g_bsn_latency_max * g_block_period will give loss
g_tb_nof_restart : NATURAL := 2; -- number of times to restart the input stimuli
g_tb_nof_blocks : NATURAL := 20 -- number of input blocks per restart
);
......@@ -64,17 +117,42 @@ ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
CONSTANT c_data_w : NATURAL := 16;
CONSTANT c_data_init : INTEGER := 0;
CONSTANT c_bsn_w : NATURAL := 16; -- use <= 31 bit to fit NATURAL
CONSTANT c_bsn_init : NATURAL := 3;
CONSTANT c_bsn_init : NATURAL := 3; -- use > 0 to have no lost data for g_lost_bsn_id = 0
CONSTANT c_channel_init : INTEGER := 0;
CONSTANT c_err_init : NATURAL := 247;
CONSTANT c_sync_period : NATURAL := 7;
CONSTANT c_sync_offset : NATURAL := 2;
-- maximum nof clk delay between any inputs, <= c_align_latency_nof_clk
-- . the -1 is due to some acceptable pipeline detail related to dp_block_from_mm
CONSTANT c_diff_delay_max : NATURAL := g_bsn_latency_max * g_block_period - sel_a_b(g_rd_latency > 1, 0, 1);
CONSTANT c_diff_delay : NATURAL := sel_a_b(g_tb_diff_delay < 0, c_diff_delay_max, g_tb_diff_delay);
-- Return input delay as function of inputs stream index I
FUNCTION func_input_delay(I : NATURAL) RETURN NATURAL IS
BEGIN
RETURN c_diff_delay * I / (g_nof_streams - 1);
END;
CONSTANT c_gap_size : NATURAL := g_block_period - g_block_size;
CONSTANT c_dut_latency : NATURAL := g_pipeline_input + g_rd_latency + 2;
CONSTANT c_align_latency_nof_blocks : NATURAL := g_bsn_latency_max; -- DUT buffer latency in number blocks
CONSTANT c_align_latency_nof_valid : NATURAL := g_bsn_latency_max * g_block_size; -- DUT buffer latency in number of data samples
CONSTANT c_align_latency_nof_clk : NATURAL := g_bsn_latency_max * g_block_period; -- DUT buffer latency in number clk cycles
CONSTANT c_lost_bsn_stream_id : NATURAL := 1; -- fixed use stream 1 to verify g_lost_bsn_id
-- In the tb only support MM interface verification for c_nof_aligners_max = 1
CONSTANT c_nof_aligners_max : POSITIVE := sel_a_b(g_use_mm_output, 1, g_nof_aligners_max);
-- DUT latency of chain of DUTs is same as DUT latency of one DUT, so
-- independent c_nof_aligners_max. This is because the c_dut_latency of the
-- other DUTs is covered by the buffer latency.
CONSTANT c_mm_to_dp_latency : NATURAL := 1;
CONSTANT c_dut_latency : NATURAL := g_pipeline_input + g_rd_latency + c_mm_to_dp_latency + g_pipeline_output;
-- DUT buffer latency for chain of DUTs
CONSTANT c_align_latency_nof_blocks : NATURAL := g_bsn_latency_max * c_nof_aligners_max; -- in number blocks
CONSTANT c_align_latency_nof_valid : NATURAL := g_bsn_latency_max * c_nof_aligners_max * g_block_size; -- in number of data samples
CONSTANT c_align_latency_nof_clk : NATURAL := g_bsn_latency_max * c_nof_aligners_max * g_block_period; -- in number clk cycles
-- Total DUT chain latency
CONSTANT c_total_latency : NATURAL := c_dut_latency + c_align_latency_nof_clk;
CONSTANT c_verify_nof_blocks : NATURAL := g_tb_nof_blocks - c_align_latency_nof_blocks; -- skip last blocks that are still in the DUT buffer
......@@ -92,18 +170,26 @@ ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
END RECORD;
TYPE t_dut_sosi_2arr IS ARRAY (NATURAL RANGE <>) OF t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL tb_end_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL tb_end : STD_LOGIC;
SIGNAL clk : STD_LOGIC := '1';
SIGNAL rst : STD_LOGIC := '1';
SIGNAL sl1 : STD_LOGIC := '1';
SIGNAL node_index : NATURAL := 0;
SIGNAL node_index_arr : t_nat_natural_arr(0 TO c_nof_aligners_max-1) := array_init(0, c_nof_aligners_max, 1);
SIGNAL stream_en_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '1'); -- default all streams are enabled
SIGNAL stream_lost_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- default no streams are lost
SIGNAL in_bsn_lost_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- default no blocks are lost
SIGNAL in_bsn_lost : STD_LOGIC; -- = in_bsn_lost_arr(c_lost_bsn_stream_id)
SIGNAL exp_bsn_lost_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- default no blocks are lost
SIGNAL ref_siso_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_siso_rdy);
SIGNAL ref_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL in_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst);
SIGNAL ref_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); -- generated stimuli
SIGNAL rx_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst); -- received stimuli
SIGNAL in_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst); -- input stimuli
SIGNAL in_sync_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL in_sop_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
......@@ -120,12 +206,13 @@ ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
SIGNAL mm_sosi : t_dp_sosi; -- streaming information that signals that an output block can be read
SIGNAL mm_done_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL mm_done : STD_LOGIC;
SIGNAL dut_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL tb_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL mm_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL dut_in_sosi_2arr : t_dut_sosi_2arr(0 TO c_nof_aligners_max-1);
SIGNAL dut_out_sosi_2arr : t_dut_sosi_2arr(0 TO c_nof_aligners_max-1);
SIGNAL dut_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); -- last BSN aligner output
SIGNAL r : t_reg;
SIGNAL nxt_r : t_reg;
SIGNAL out_siso_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_siso_rdy);
SIGNAL out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL out_sosi : t_dp_sosi;
......@@ -136,11 +223,11 @@ ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
SIGNAL out_data_arr : t_data_arr;
SIGNAL hold_data_arr : t_data_arr;
SIGNAL out_bsn_arr : t_bsn_arr;
SIGNAL out_bsn : INTEGER;
SIGNAL out_channel_arr : t_channel_arr;
SIGNAL out_err_arr : t_err_arr;
SIGNAL tb_state : t_tb_state;
SIGNAL tb_bsn : INTEGER;
SIGNAL restart_cnt_arr : t_nat_integer_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => -1);
SIGNAL restart_cnt : INTEGER := 0;
SIGNAL ref_sosi_arr_dly : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst);
......@@ -152,16 +239,25 @@ ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
SIGNAL hold_out_sop_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL expected_out_bsn_arr : t_bsn_arr;
SIGNAL expected_out_data_arr : t_data_arr;
SIGNAL expected_out_channel_arr : t_channel_arr;
-- Return input delay as function of inputs stream index I
FUNCTION func_input_delay(I : NATURAL) RETURN NATURAL IS
BEGIN
RETURN g_tb_diff_delay_max * I / (g_nof_streams - 1);
END;
SIGNAL dbg_tb_diff_delay_max : NATURAL := g_tb_diff_delay_max;
-- Debug signals to view in Wave window
SIGNAL dbg_func_delay_max : NATURAL := func_input_delay(g_nof_streams - 1);
SIGNAL dbg_c_align_latency_nof_blocks : NATURAL := c_align_latency_nof_blocks;
SIGNAL dbg_c_align_latency_nof_valid : NATURAL := c_align_latency_nof_valid;
SIGNAL dbg_c_align_latency_nof_clk : NATURAL := c_align_latency_nof_clk;
SIGNAL dbg_c_total_latency : NATURAL := c_total_latency;
SIGNAL dbg_c_verify_nof_blocks : NATURAL := c_verify_nof_blocks;
-- Debug signals to view that verification conditions actually occur
SIGNAL dbg_verify_sosi_control_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- '1' when sosi control is verified
SIGNAL dbg_verify_passed_on_data_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- '1' when passed on data is verified
SIGNAL dbg_verify_replaced_data_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- '1' when replaced data is verified
SIGNAL dbg_verify_bsn_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- '1' when bsn in all streams is verified
SIGNAL dbg_verify_no_lost_flag_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- '1' when lost data flag = 0 is verified
SIGNAL dbg_verify_lost_flag_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS => '0'); -- '1' when lost data flag = 1 is verified
BEGIN
clk <= (NOT clk) OR tb_end AFTER clk_period/2;
......@@ -172,7 +268,7 @@ BEGIN
------------------------------------------------------------------------------
-- Generate data path input data
gen_input : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
gen_stimuli : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
p_stimuli : PROCESS
VARIABLE v_sync : STD_LOGIC := '0';
VARIABLE v_bsn : NATURAL;
......@@ -189,23 +285,38 @@ BEGIN
-- Begin of stimuli
FOR S IN 0 TO g_tb_nof_restart-1 LOOP
v_bsn := c_bsn_init;
IF S = 2 THEN
stream_en_arr(1) <= '0';
END IF;
FOR R IN 0 TO g_tb_nof_blocks-1 LOOP
v_sync := sel_a_b(v_bsn MOD c_sync_period = c_sync_offset, '1', '0');
proc_dp_gen_block_data(c_rl, TRUE, c_data_w, c_data_w, v_data, 0, 0, g_block_size, v_channel, v_err, v_sync, TO_UVEC(v_bsn, c_bsn_w), clk, stream_en_arr(I), ref_siso_arr(I), ref_sosi_arr(I));
proc_dp_gen_block_data(c_rl, TRUE, c_data_w, c_data_w, v_data, 0, 0, g_block_size, v_channel, v_err, v_sync, TO_UVEC(v_bsn, c_bsn_w), clk, sl1, ref_siso_arr(I), ref_sosi_arr(I));
v_bsn := v_bsn + 1;
v_data := v_data + g_block_size;
proc_common_wait_some_cycles(clk, c_gap_size); -- create gap between frames
END LOOP;
-- no gap between restarts, to ease verification by maintaining fixed latency of out_sosi_arr_exp
-- no gap between restarts, to ease verification by maintaining fixed
-- latency of out_sosi_arr_exp
restart_cnt_arr(I) <= restart_cnt_arr(I) + 1;
END LOOP;
-- End of stimuli, g_bsn_latency_max blocks remain in DUT buffer
-- End of stimuli
-- . default g_bsn_latency_max blocks remain in DUT buffer
expected_out_bsn_arr(I) <= TO_UVEC(v_bsn-1 - c_align_latency_nof_blocks, c_bsn_w);
expected_out_data_arr(I) <= TO_UVEC(v_data-1 - c_align_latency_nof_valid, c_data_w);
-- . default no data is lost, so all channel(0) lost data flags are 0
expected_out_channel_arr(I) <= TO_DP_CHANNEL(0);
-- Account for disturbed remote input streams
IF I > 0 THEN
IF I = g_disable_stream_id THEN
-- Expected stream disable replacement data value
expected_out_data_arr(I) <= TO_UVEC(g_data_replacement_value, c_data_w);
END IF;
IF I = g_lost_stream_id THEN
-- Expected stream lost replacement data value and expected lost
-- flag channel(0) value
expected_out_data_arr(I) <= TO_UVEC(g_data_replacement_value, c_data_w);
expected_out_channel_arr(I) <= TO_DP_CHANNEL(1);
END IF;
END IF;
proc_common_wait_some_cycles(clk, 100);
verify_done_arr(I) <= '1';
......@@ -217,6 +328,39 @@ BEGIN
END PROCESS;
END GENERATE;
tb_end <= vector_and(tb_end_arr);
-- Model misalignment latency between the input streams.
-- . it is sufficient to only model misalignment for the first DUT in case
-- c_nof_aligners_max > 1.
gen_rx_sosi_arr : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
rx_sosi_arr(I) <= TRANSPORT ref_sosi_arr(I) AFTER func_input_delay(I) * clk_period;
END GENERATE;
-- Model enable/disable remote input stream
stream_en_arr(g_disable_stream_id) <= '0' WHEN g_disable_stream_id > 0;
-- Model lost remote input stream
stream_lost_arr(g_lost_stream_id) <= '1' WHEN g_lost_stream_id > 0;
-- Model lost block on one remote input stream c_lost_bsn_stream_id
in_bsn_lost_arr(c_lost_bsn_stream_id) <= '1' WHEN TO_UINT(rx_sosi_arr(c_lost_bsn_stream_id).bsn) = g_lost_bsn_id ELSE '0';
in_bsn_lost <= in_bsn_lost_arr(c_lost_bsn_stream_id);
p_in_sosi_arr : PROCESS(rx_sosi_arr, stream_lost_arr, in_bsn_lost_arr)
BEGIN
in_sosi_arr <= rx_sosi_arr;
-- Model entirely lost remote input stream
IF stream_lost_arr(g_lost_stream_id) = '1' THEN
in_sosi_arr(g_lost_stream_id) <= RESET_DP_SOSI_CTRL(rx_sosi_arr(g_lost_stream_id));
END IF;
-- Model single lost block in a stream (stream c_lost_bsn_stream_id = 1)
IF in_bsn_lost_arr(c_lost_bsn_stream_id) = '1' THEN
in_sosi_arr(c_lost_bsn_stream_id) <= RESET_DP_SOSI_CTRL(rx_sosi_arr(c_lost_bsn_stream_id));
END IF;
END PROCESS;
-- Use tb_state to view tb progress in Wave window
restart_cnt <= restart_cnt_arr(0);
......@@ -228,14 +372,6 @@ BEGIN
IF restart_cnt > 1 THEN tb_state <= s_restart; END IF;
END PROCESS;
-- Create latency misalignment between the input streams
gen_in_sosi_arr : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
in_sosi_arr(I) <= TRANSPORT ref_sosi_arr(I) AFTER func_input_delay(I) * clk_period;
END GENERATE;
tb_end <= vector_and(tb_end_arr);
mon_sosi : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
-- Ease in_sosi_arr monitoring
in_sync_arr(I) <= in_sosi_arr(I).sync;
......@@ -260,14 +396,14 @@ BEGIN
out_sosi <= out_sosi_arr(0); -- take out_sosi control and info from out_sosi_arr(0)
out_bsn <= TO_UINT(out_sosi.bsn); -- = out_bsn_arr().bsn = out_sosi_arr(I).bsn
------------------------------------------------------------------------------
-- DATA VERIFICATION, use multiple ways to increase coverage
-- a) Use proc_dp_verify_*() to verify output compared to prev output
-- b) Use delayed in_sosi_arr as expected out_sosi_arr
-- a) Use proc_dp_verify_*() to verify output sosi format
-- b) Use delayed in_sosi_arr as expected out_sosi_arr to verify output sosi
------------------------------------------------------------------------------
tb_bsn <= TO_UINT(out_sosi.bsn);
ref_sosi_arr_dly <= TRANSPORT ref_sosi_arr AFTER c_total_latency * clk_period;
out_sosi_arr_exp <= ref_sosi_arr_dly WHEN rising_edge(clk);
out_sosi_exp <= out_sosi_arr_exp(0); -- take out_sosi_exp control and info from out_sosi_arr_exp(0)
......@@ -280,16 +416,16 @@ BEGIN
-- . Verify that the stimuli have been applied at all
hold_data_arr(I) <= out_data_arr(I) WHEN out_val_arr(I) = '1'; -- hold last valid data
proc_dp_verify_value("out_data_arr", e_equal, clk, verify_done_arr(I), expected_out_data_arr(I), hold_data_arr(I));
proc_dp_verify_value("out_bsn_arr", e_equal, clk, verify_done_arr(I), expected_out_bsn_arr(I), out_bsn_arr(I));
proc_dp_verify_value("out_channel_arr", e_equal, clk, verify_done_arr(I), expected_out_channel_arr(I), out_channel_arr(I));
END GENERATE;
-- . Use delayed in_sosi_arr as expected out_sosi_arr, this is possible
-- because the DUT has no flow control and has a fixed latency.
p_verify_sosi_en_arr : PROCESS(out_sosi_exp)
BEGIN
IF g_tb_diff_delay_max <= c_align_latency_nof_clk THEN
IF c_diff_delay <= c_align_latency_nof_clk THEN
verify_sosi_en_arr <= (OTHERS => '1');
IF TO_UINT(out_sosi_exp.bsn) - c_bsn_init >= c_verify_nof_blocks THEN
verify_sosi_en_arr <= (OTHERS => '0');
......@@ -297,23 +433,57 @@ BEGIN
END IF;
END PROCESS;
exp_bsn_lost_arr(c_lost_bsn_stream_id) <= '1' WHEN TO_UINT(out_sosi_arr_exp(c_lost_bsn_stream_id).bsn) = g_lost_bsn_id ELSE '0';
gen_verify_streams : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
p_verify_sosi : PROCESS(clk)
p_verify_stream : PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
dbg_verify_sosi_control_arr(I) <= '0';
dbg_verify_passed_on_data_arr(I) <= '0';
dbg_verify_replaced_data_arr(I) <= '0';
dbg_verify_bsn_arr(I) <= '0';
dbg_verify_no_lost_flag_arr(I) <= '0';
dbg_verify_lost_flag_arr(I) <= '0';
IF verify_sosi_en_arr(I) = '1' AND out_sosi_arr_exp(I).valid = '1' THEN
-- Verify sosi control fields
dbg_verify_sosi_control_arr(I) <= '1';
ASSERT out_sosi_arr(I).sync = out_sosi_arr_exp(I).sync REPORT "Wrong sync for output " & int_to_str(I) SEVERITY ERROR;
ASSERT out_sosi_arr(I).sop = out_sosi_arr_exp(I).sop REPORT "Wrong sop for output " & int_to_str(I) SEVERITY ERROR;
ASSERT out_sosi_arr(I).eop = out_sosi_arr_exp(I).eop REPORT "Wrong eop for output " & int_to_str(I) SEVERITY ERROR;
ASSERT out_sosi_arr(I).valid = out_sosi_arr_exp(I).valid REPORT "Wrong valid for output " & int_to_str(I) SEVERITY ERROR;
IF stream_en_arr(I) = '1' THEN
ASSERT out_sosi_arr(I).data = out_sosi_arr_exp(I).data REPORT "Wrong data for output " & int_to_str(I) & " : "
-- Verify data field
IF stream_en_arr(I) = '1' AND stream_lost_arr(I) = '0' AND exp_bsn_lost_arr(I) = '0' THEN
-- verify passed on data
dbg_verify_passed_on_data_arr(I) <= '1';
ASSERT out_sosi_arr(I).data = out_sosi_arr_exp(I).data REPORT "Wrong data for output stream " & int_to_str(I) & " : "
& int_to_str(TO_UINT(out_sosi_arr(I).data)) & " /= "
& int_to_str(TO_UINT(out_sosi_arr_exp(I).data)) SEVERITY ERROR;
ELSE
ASSERT TO_UINT(out_sosi_arr(I).data) = g_replacement_value REPORT "Wrong data for output " & int_to_str(I) & " : "
-- verify lost data stream at g_disable_stream_id or g_lost_stream_id or g_lost_bsn_id
dbg_verify_replaced_data_arr(I) <= '1';
ASSERT TO_UINT(out_sosi_arr(I).data) = g_data_replacement_value REPORT "Wrong replacement data for output stream " & int_to_str(I) & " : "
& int_to_str(TO_UINT(out_sosi_arr(I).data)) & " /= "
& int_to_str(g_replacement_value) SEVERITY ERROR;
& int_to_str(g_data_replacement_value) SEVERITY ERROR;
END IF;
-- Verify sop info fields
IF out_sosi_arr_exp(I).sop = '1' THEN
-- bsn field
dbg_verify_bsn_arr(I) <= '1';
ASSERT out_sosi_arr(I).bsn = out_sosi_arr_exp(I).bsn REPORT "Wrong bsn for output " & int_to_str(I) SEVERITY ERROR;
-- channel field with lost flag bit 0
IF stream_lost_arr(I) = '0' AND exp_bsn_lost_arr(I) = '0' THEN
-- verify no lost stream
dbg_verify_no_lost_flag_arr(I) <= '1';
ASSERT out_sosi_arr(I).channel = TO_DP_CHANNEL(0) REPORT "Wrong lost flag bit in channel /= 0 for output " & int_to_str(I) SEVERITY ERROR;
ELSE
-- verify lost stream g_lost_stream_id or lost block g_lost_bsn_id
dbg_verify_lost_flag_arr(I) <= '1';
ASSERT out_sosi_arr(I).channel = TO_DP_CHANNEL(1) REPORT "Wrong lost flag bit channel /= 1 for output " & int_to_str(I) SEVERITY ERROR;
END IF;
END IF;
END IF;
END IF;
......@@ -324,36 +494,101 @@ BEGIN
-- DUT
------------------------------------------------------------------------------
-- Connect all inputs to first DUT
dut_in_sosi_2arr(0) <= in_sosi_arr;
u_bsn_align : ENTITY work.dp_bsn_align_v2
GENERIC MAP (
g_nof_streams => g_nof_streams,
g_bsn_latency_max => g_bsn_latency_max,
g_nof_aligners_max => g_nof_aligners_max,
g_nof_aligners_max => c_nof_aligners_max,
g_block_size => g_block_size,
g_bsn_w => g_bsn_w,
g_data_w => g_data_w,
g_replacement_value => g_replacement_value,
g_use_mm_output => g_use_mm_output, -- output via MM or via streaming DP
g_pipeline_input => g_pipeline_input, -- >= 0, choose 0 for wires, choose 1 to ease timing closure
g_rd_latency => g_rd_latency -- 1 or 2, choose 2 to ease timing closure
g_data_replacement_value => g_data_replacement_value,
g_use_mm_output => g_use_mm_output,
g_pipeline_input => g_pipeline_input,
g_pipeline_output => g_pipeline_output,
g_rd_latency => g_rd_latency
)
PORT MAP (
dp_rst => rst,
dp_clk => clk,
-- Control
node_index => node_index,
node_index => node_index_arr(0),
stream_en_arr => stream_en_arr,
-- Streaming input
in_sosi_arr => in_sosi_arr,
in_sosi_arr => dut_in_sosi_2arr(0),
-- Output via local MM interface in dp_clk domain
mm_copi => mm_copi,
mm_cipo_arr => mm_cipo_arr,
mm_sosi => mm_sosi,
-- Output via streaming DP interface
out_sosi_arr => dut_sosi_arr
out_sosi_arr => dut_out_sosi_2arr(0)
);
-- Simulate series of DUT, when g_use_mm_output = FALSE and
-- g_nof_aligners_max > 1. Use same local in_sosi_arr(0) input for all BSN
-- aligners, because all DUT have same local reference. Connect the remote
-- in_sosi_arr(> 0) inputs via the BSN aligners:
--
-- remote
-- in_sosi_arr(> 0) ------> DUT --------> DUT --------> DUT --> dut_sosi_arr()(> 0)
-- /-> 0 --X /-> 1 --X /-> 2 --> dut_sosi_arr()(= 0)
-- local / / / . .
-- in_sosi_arr(= 0) --/-------------/-------------/ . .
-- . . . . . .
-- . . . . . .
-- v . . . . .
-- dut_in_sosi_2arr(0) v . . . .
-- dut_out_sosi_2arr(0) v . . .
-- dut_in_sosi_2arr(1) v . .
-- dut_out_sosi_2arr(1) v .
-- dut_in_sosi_2arr(2) v
-- dut_out_sosi_2arr(2)
--
gen_bsn_align_chain : FOR I IN 1 TO c_nof_aligners_max-1 GENERATE
u_bsn_align : ENTITY work.dp_bsn_align_v2
GENERIC MAP (
g_nof_streams => g_nof_streams,
g_bsn_latency_max => g_bsn_latency_max,
g_nof_aligners_max => c_nof_aligners_max,
g_block_size => g_block_size,
g_bsn_w => g_bsn_w,
g_data_w => g_data_w,
g_data_replacement_value => g_data_replacement_value,
g_use_mm_output => g_use_mm_output,
g_pipeline_input => g_pipeline_input,
g_pipeline_output => g_pipeline_output,
g_rd_latency => g_rd_latency
)
PORT MAP (
dp_rst => rst,
dp_clk => clk,
-- Control
node_index => node_index_arr(I),
stream_en_arr => stream_en_arr,
-- Streaming input
in_sosi_arr => dut_in_sosi_2arr(I),
-- Output via streaming DP interface
out_sosi_arr => dut_out_sosi_2arr(I)
);
-- Connect remote and local between DUTs in the chain of DUTs
p_connect : PROCESS(dut_out_sosi_2arr, in_sosi_arr)
BEGIN
-- connect the remote inputs, by connecting all inputs from previous DUT
dut_in_sosi_2arr(I) <= dut_out_sosi_2arr(I-1);
-- connect the local input, same for all DUTs
dut_in_sosi_2arr(I)(0) <= in_sosi_arr(0);
END PROCESS;
END GENERATE;
-- Connect output from last DUT, so only verify last output
dut_sosi_arr <= dut_out_sosi_2arr(c_nof_aligners_max-1);
------------------------------------------------------------------------------
-- MM to streaming DP
------------------------------------------------------------------------------
......@@ -383,12 +618,12 @@ BEGIN
mm_done => mm_done_arr(I),
mm_mosi => mm_copi_arr(I),
mm_miso => mm_cipo_arr(I),
out_sosi => tb_sosi_arr(I),
out_sosi => mm_sosi_arr(I),
out_siso => c_dp_siso_rdy
);
END GENERATE;
p_comb : PROCESS(r, mm_sosi, tb_sosi_arr)
p_comb : PROCESS(r, mm_sosi, mm_sosi_arr)
VARIABLE v : t_reg;
BEGIN
v := r;
......@@ -400,8 +635,8 @@ BEGIN
END IF;
-- apply mm_sosi.sync, bsn at sop to all streams in out_sosi_arr
v.out_sosi_arr := tb_sosi_arr;
IF tb_sosi_arr(0).sop = '1' THEN
v.out_sosi_arr := mm_sosi_arr;
IF mm_sosi_arr(0).sop = '1' THEN
v.out_sosi_arr := func_dp_stream_arr_set(v.out_sosi_arr, r.sync, "SYNC");
v.out_sosi_arr := func_dp_stream_arr_set(v.out_sosi_arr, r.bsn, "BSN");
ELSE
......
......@@ -20,6 +20,15 @@
-- Purpose: Verify MM part of mmp_dp_bsn_align_v2
-- Description:
-- The functional part is already verified by tb_tb_dp_bsn_align_v2.vhd.
-- Tb features:
-- . verify expected end values for output data and bsn in gen_verify_ctrl
-- to ensure that test has ran
-- . verify MM access to input stream enable in p_mm_verify_bsn_align
-- . verify MM access to input BSN monitors by verifiying expected input
-- latencies for different input delays
-- . verify MM access to output BSN monitor by verifiying expected output
-- latencies.
--
-- Usage:
-- > as 5
-- > run -all
......@@ -45,48 +54,138 @@ ARCHITECTURE tb OF tb_mmp_dp_bsn_align_v2 IS
CONSTANT c_dp_clk_period : TIME := 10 ns;
CONSTANT c_cross_clock_domain_latency : NATURAL := 20;
CONSTANT c_report_note : BOOLEAN := FALSE; -- Use TRUE for tb debugging, else FALSE to keep Transcript window more empty
CONSTANT c_nof_input_sync : NATURAL := 10;
CONSTANT c_nof_block_per_sync : NATURAL := 32;
CONSTANT c_block_size : NATURAL := 10;
CONSTANT c_input_gap_size : NATURAL := 3;
CONSTANT c_sim_nof_blocks : NATURAL := c_nof_block_per_sync * c_nof_input_sync;
CONSTANT c_nof_streams : NATURAL := 2;
CONSTANT c_bsn_latency_max : POSITIVE := 2;
CONSTANT c_nof_aligners_max : NATURAL := 1;
CONSTANT c_rl : NATURAL := 1;
CONSTANT c_tb_nof_restart : NATURAL := 2; -- number of times to restart the input stimuli
CONSTANT c_tb_nof_blocks : NATURAL := 50; -- number of input blocks per restart
-- Fixed dut generics
-- . for dp_bsn_align_v2
CONSTANT c_nof_streams : NATURAL := 3;
CONSTANT c_bsn_latency_max : NATURAL := 1;
CONSTANT c_nof_aligners_max : POSITIVE := 1; -- fixed in this tb
CONSTANT c_block_size : NATURAL := 11;
CONSTANT c_block_period : NATURAL := 11;
CONSTANT c_bsn_w : NATURAL := c_dp_stream_bsn_w;
CONSTANT c_data_w : NATURAL := 16;
CONSTANT c_replacement_value : INTEGER := 0;
CONSTANT c_data_replacement_value : INTEGER := 17;
CONSTANT c_use_mm_output : BOOLEAN := FALSE;
CONSTANT c_pipeline_input : NATURAL := 1;
CONSTANT c_pipeline_output : NATURAL := 1;
CONSTANT c_rd_latency : NATURAL := 2;
-- . for mms_dp_bsn_monitor_v2
CONSTANT c_nof_clk_per_sync : NATURAL := 200*10**6;
CONSTANT c_nof_input_bsn_monitors : NATURAL := 0;
CONSTANT c_use_bsn_output_monitor : BOOLEAN := FALSE;
CONSTANT c_nof_input_bsn_monitors : NATURAL := c_nof_streams;
CONSTANT c_use_bsn_output_monitor : BOOLEAN := TRUE;
CONSTANT c_reg_bsn_monitor_adr_w : NATURAL := ceil_log2(7);
CONSTANT c_reg_bsn_monitor_span : NATURAL := 2**c_reg_bsn_monitor_adr_w;
-- maximum nof clk delay between any inputs, <= c_align_latency_nof_clk
-- . the -1 is due to some acceptable pipeline detail related to dp_block_from_mm
CONSTANT c_diff_delay_max : NATURAL := c_bsn_latency_max * c_block_period - sel_a_b(c_rd_latency > 1, 0, 1);
CONSTANT c_diff_delay : NATURAL := c_diff_delay_max;
-- Return input delay as function of inputs stream index I
FUNCTION func_input_delay(I : NATURAL) RETURN NATURAL IS
BEGIN
RETURN c_diff_delay * I / (c_nof_streams - 1);
END;
-- Input stream settings
CONSTANT c_data_init : INTEGER := 0;
CONSTANT c_bsn_init : NATURAL := 3;
CONSTANT c_channel_init : INTEGER := 0;
CONSTANT c_err_init : NATURAL := 247;
CONSTANT c_sync_period : NATURAL := 7;
CONSTANT c_sync_offset : NATURAL := 2;
CONSTANT c_gap_size : NATURAL := c_block_period - c_block_size;
-- DUT latency
CONSTANT c_mm_to_dp_latency : NATURAL := 1;
CONSTANT c_dut_latency : NATURAL := c_pipeline_input + c_rd_latency + c_mm_to_dp_latency + c_pipeline_output;
CONSTANT c_align_latency_nof_blocks : NATURAL := c_bsn_latency_max * c_nof_aligners_max; -- in number blocks
CONSTANT c_align_latency_nof_valid : NATURAL := c_bsn_latency_max * c_nof_aligners_max * c_block_size; -- in number of data samples
CONSTANT c_align_latency_nof_clk : NATURAL := c_bsn_latency_max * c_nof_aligners_max * c_block_period; -- in number clk cycles
-- Total DUT chain latency
CONSTANT c_total_latency : NATURAL := c_dut_latency + c_align_latency_nof_clk;
CONSTANT c_verify_nof_blocks : NATURAL := c_tb_nof_blocks - c_align_latency_nof_blocks; -- skip last blocks that are still in the DUT buffer
-- Signal monitoring and verification
TYPE t_data_arr IS ARRAY (c_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
TYPE t_bsn_arr IS ARRAY (c_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
TYPE t_err_arr IS ARRAY (c_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_dp_stream_error_w-1 DOWNTO 0);
TYPE t_channel_arr IS ARRAY (c_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0);
SIGNAL sl1 : STD_LOGIC := '1';
SIGNAL mm_end : STD_LOGIC := '0';
SIGNAL dp_end_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL dp_end : STD_LOGIC := '0';
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL stimuli_end : STD_LOGIC := '0';
SIGNAL streams_enabled : STD_LOGIC := '0';
SIGNAL restart_cnt_arr : t_nat_integer_arr(c_nof_streams-1 DOWNTO 0) := (OTHERS => -1);
SIGNAL restart_cnt : INTEGER := 0;
-- MM clock domain
SIGNAL mm_clk : STD_LOGIC := '1';
SIGNAL mm_rst : STD_LOGIC := '1';
SIGNAL reg_copi : t_mem_copi := c_mem_copi_rst;
SIGNAL reg_cipo : t_mem_cipo;
SIGNAL reg_bsn_align_copi : t_mem_copi := c_mem_copi_rst;
SIGNAL reg_bsn_align_cipo : t_mem_cipo;
SIGNAL reg_input_monitor_copi : t_mem_copi := c_mem_copi_rst;
SIGNAL reg_input_monitor_cipo : t_mem_cipo;
SIGNAL reg_output_monitor_copi : t_mem_copi := c_mem_copi_rst;
SIGNAL reg_output_monitor_cipo : t_mem_cipo;
SIGNAL mon_latency_input_arr : t_nat_natural_arr(c_nof_streams-1 DOWNTO 0);
SIGNAL mon_latency_output : NATURAL;
-- DP clock domain
SIGNAL dp_clk : STD_LOGIC := '1';
SIGNAL dp_rst : STD_LOGIC := '1';
SIGNAL node_index : NATURAL := 0; -- only used when g_bsn_latency_use_node_index is TRUE
SIGNAL stimuli_sosi : t_dp_sosi;
SIGNAL in_sosi_arr : t_dp_sosi_arr(c_nof_streams-1 DOWNTO 0);
SIGNAL mm_copi : t_mem_copi; -- read access to output block, all output streams share same mm_copi
SIGNAL mm_cipo_arr : t_mem_cipo_arr(c_nof_streams-1 DOWNTO 0);
SIGNAL mm_sosi : t_dp_sosi; -- streaming information that signals that an output block can be read
SIGNAL node_index : NATURAL := 0;
SIGNAL ref_siso_arr : t_dp_siso_arr(c_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_siso_rdy);
SIGNAL ref_sosi_arr : t_dp_sosi_arr(c_nof_streams-1 DOWNTO 0); -- generated stimuli
SIGNAL in_sosi_arr : t_dp_sosi_arr(c_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst); -- input stimuli
SIGNAL in_sync_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL in_sop_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL in_eop_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL in_val_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL in_data_arr : t_data_arr;
SIGNAL in_bsn_arr : t_bsn_arr;
SIGNAL in_channel_arr : t_channel_arr;
SIGNAL in_err_arr : t_err_arr;
SIGNAL out_sosi_arr : t_dp_sosi_arr(c_nof_streams-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst); -- output
SIGNAL out_sosi : t_dp_sosi;
SIGNAL out_sync_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL out_sop_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL out_eop_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL out_val_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0);
SIGNAL out_data_arr : t_data_arr;
SIGNAL hold_data_arr : t_data_arr;
SIGNAL out_bsn_arr : t_bsn_arr;
SIGNAL out_bsn : INTEGER;
SIGNAL out_channel_arr : t_channel_arr;
SIGNAL out_err_arr : t_err_arr;
SIGNAL verify_done_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL verify_done : STD_LOGIC;
SIGNAL hold_out_sop_arr : STD_LOGIC_VECTOR(c_nof_streams-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL expected_out_bsn_arr : t_bsn_arr;
SIGNAL expected_out_data_arr : t_data_arr;
SIGNAL expected_out_channel_arr : t_channel_arr;
-- Debug signals for view in Wave window
SIGNAL dbg_c_align_latency_nof_blocks : NATURAL := c_align_latency_nof_blocks;
SIGNAL dbg_c_align_latency_nof_valid : NATURAL := c_align_latency_nof_valid;
SIGNAL dbg_c_align_latency_nof_clk : NATURAL := c_align_latency_nof_clk;
SIGNAL dbg_c_total_latency : NATURAL := c_total_latency;
SIGNAL dbg_c_verify_nof_blocks : NATURAL := c_verify_nof_blocks;
BEGIN
......@@ -95,23 +194,70 @@ BEGIN
dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
tb_end <= mm_end AND dp_end;
------------------------------------------------------------------------------
-- MM stimuli
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- MM stimuli and verification
------------------------------------------------------------------------------
p_stimuli_and_verify_mm : PROCESS
VARIABLE v_bsn : NATURAL;
p_mm_verify_bsn_align : PROCESS
BEGIN
proc_common_wait_until_low(dp_clk, mm_rst);
proc_common_wait_until_low(dp_clk, dp_rst);
proc_common_wait_some_cycles(mm_clk, 5);
-- Read stream enable bits, default '0' after power up
FOR I IN 0 TO c_nof_streams-1 LOOP
proc_mem_mm_bus_rd(I, mm_clk, reg_bsn_align_cipo, reg_bsn_align_copi);
proc_mem_mm_bus_rd_latency(1, mm_clk);
ASSERT reg_bsn_align_cipo.rddata(0) = '0' REPORT "Wrong stream disable for output " & int_to_str(I) SEVERITY ERROR;
END LOOP;
-- Write stream enable bits for stream_en_arr
FOR I IN 0 TO c_nof_streams-1 LOOP
proc_mem_mm_bus_wr(I, 1, mm_clk, reg_bsn_align_cipo, reg_bsn_align_copi);
END LOOP;
proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency);
-- Read stream enable bits, should now be '1'
FOR I IN 0 TO c_nof_streams-1 LOOP
proc_mem_mm_bus_rd(I, mm_clk, reg_bsn_align_cipo, reg_bsn_align_copi);
proc_mem_mm_bus_rd_latency(1, mm_clk);
ASSERT reg_bsn_align_cipo.rddata(0) = '1' REPORT "Wrong BSN align stream enable for output " & int_to_str(I) SEVERITY ERROR;
END LOOP;
-- End of MM test
streams_enabled <= '1';
WAIT;
END PROCESS;
---------------------------------------------------------------------------
-- End of test
---------------------------------------------------------------------------
proc_common_wait_until_high(dp_clk, stimuli_end);
tb_end <= '1';
p_mm_verify_bsn_monitors : PROCESS
BEGIN
proc_common_wait_until_high(mm_clk, verify_done);
-- Read input BSN monitors
FOR I IN 0 TO c_nof_streams-1 LOOP
proc_mem_mm_bus_rd(6 + I*c_reg_bsn_monitor_span, mm_clk, reg_input_monitor_cipo, reg_input_monitor_copi);
proc_mem_mm_bus_rd_latency(1, mm_clk);
mon_latency_input_arr(I) <= TO_UINT(reg_input_monitor_cipo.rddata);
proc_common_wait_some_cycles(mm_clk, 1);
ASSERT mon_latency_input_arr(I) = func_input_delay(I) REPORT "Wrong input BSN monitor latency for input " & int_to_str(I) SEVERITY ERROR;
END LOOP;
-- Read output BSN monitor
proc_mem_mm_bus_rd(6, mm_clk, reg_output_monitor_cipo, reg_output_monitor_copi);
proc_mem_mm_bus_rd_latency(1, mm_clk);
mon_latency_output <= TO_UINT(reg_output_monitor_cipo.rddata);
proc_common_wait_some_cycles(mm_clk, 1);
ASSERT mon_latency_output = c_total_latency REPORT "Wrong output BSN monitor latency" SEVERITY ERROR;
-- End of MM test
mm_end <= '1';
WAIT;
END PROCESS;
......@@ -119,36 +265,113 @@ BEGIN
-- Streaming stimuli
------------------------------------------------------------------------------
-- Generate data blocks with input sync
u_stimuli : ENTITY work.dp_stream_stimuli
GENERIC MAP (
g_sync_period => c_nof_block_per_sync,
g_err_init => 0,
g_err_incr => 0, -- do not increment, to not distract from viewing of BSN in Wave window
g_channel_init => 0,
g_channel_incr => 0, -- do not increment, to not distract from viewing of BSN in Wave window
g_nof_repeat => c_sim_nof_blocks,
g_pkt_len => c_block_size,
g_pkt_gap => c_input_gap_size
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
-- Generate stimuli
src_out => stimuli_sosi,
-- Generate data path input data (similar as in tb_mmp_dp_bsn_align_v2.vhd)
gen_input : FOR I IN c_nof_streams-1 DOWNTO 0 GENERATE
p_stimuli : PROCESS
VARIABLE v_sync : STD_LOGIC := '0';
VARIABLE v_bsn : NATURAL;
VARIABLE v_data : NATURAL := c_data_init;
VARIABLE v_channel : NATURAL := c_channel_init;
VARIABLE v_err : NATURAL := c_err_init;
BEGIN
v_data := v_data + I;
ref_sosi_arr(I) <= c_dp_sosi_rst;
proc_common_wait_until_low(dp_clk, dp_rst);
proc_common_wait_until_high(dp_clk, streams_enabled);
proc_common_wait_some_cycles(dp_clk, 10);
restart_cnt_arr(I) <= restart_cnt_arr(I) + 1;
-- Begin of stimuli
FOR S IN 0 TO c_tb_nof_restart-1 LOOP
v_bsn := c_bsn_init;
FOR R IN 0 TO c_tb_nof_blocks-1 LOOP
v_sync := sel_a_b(v_bsn MOD c_sync_period = c_sync_offset, '1', '0');
proc_dp_gen_block_data(c_rl, TRUE, c_data_w, c_data_w, v_data, 0, 0, c_block_size, v_channel, v_err, v_sync, TO_UVEC(v_bsn, c_bsn_w), dp_clk, sl1, ref_siso_arr(I), ref_sosi_arr(I));
v_bsn := v_bsn + 1;
v_data := v_data + c_block_size;
proc_common_wait_some_cycles(dp_clk, c_gap_size); -- create gap between frames
END LOOP;
-- Create gap between restarts
proc_common_wait_some_cycles(dp_clk, 100);
restart_cnt_arr(I) <= restart_cnt_arr(I) + 1;
END LOOP;
-- End of stimuli
tb_end => stimuli_end
);
-- . default c_bsn_latency_max blocks remain in DUT buffer
expected_out_bsn_arr(I) <= TO_UVEC(v_bsn-1 - c_align_latency_nof_blocks, c_bsn_w);
expected_out_data_arr(I) <= TO_UVEC(v_data-1 - c_align_latency_nof_valid, c_data_w);
-- . default no data is lost, so all channel(0) lost data flags are 0
expected_out_channel_arr(I) <= TO_DP_CHANNEL(0);
proc_common_wait_some_cycles(dp_clk, 100);
verify_done_arr(I) <= '1';
proc_common_wait_some_cycles(dp_clk, 1);
verify_done_arr(I) <= '0';
-- Simulate some more to easy recognizing verify_done in Wave window
proc_common_wait_some_cycles(dp_clk, 100);
dp_end_arr(I) <= '1';
WAIT;
END PROCESS;
END GENERATE;
verify_done <= verify_done_arr(0);
restart_cnt <= restart_cnt_arr(0);
dp_end <= vector_and(dp_end_arr);
-- Model misalignment latency between the input streams to have different
-- input BSN monitor latencies
gen_rx_sosi_arr : FOR I IN c_nof_streams-1 DOWNTO 0 GENERATE
in_sosi_arr(I) <= TRANSPORT ref_sosi_arr(I) AFTER func_input_delay(I) * c_dp_clk_period;
END GENERATE;
------------------------------------------------------------------------------
-- Data verification
------------------------------------------------------------------------------
in_sosi_arr <= (OTHERS => stimuli_sosi);
mon_sosi : FOR I IN c_nof_streams-1 DOWNTO 0 GENERATE
-- Ease in_sosi_arr monitoring
in_sync_arr(I) <= in_sosi_arr(I).sync;
in_sop_arr(I) <= in_sosi_arr(I).sop;
in_eop_arr(I) <= in_sosi_arr(I).eop;
in_val_arr(I) <= in_sosi_arr(I).valid;
in_data_arr(I) <= in_sosi_arr(I).data(c_data_w-1 DOWNTO 0);
in_bsn_arr(I) <= in_sosi_arr(I).bsn(c_bsn_w-1 DOWNTO 0);
in_channel_arr(I) <= in_sosi_arr(I).channel;
in_err_arr(I) <= in_sosi_arr(I).err;
-- Ease out_sosi_arr monitoring and verification
out_sync_arr(I) <= out_sosi_arr(I).sync;
out_sop_arr(I) <= out_sosi_arr(I).sop;
out_eop_arr(I) <= out_sosi_arr(I).eop;
out_val_arr(I) <= out_sosi_arr(I).valid;
out_data_arr(I) <= out_sosi_arr(I).data(c_data_w-1 DOWNTO 0);
out_bsn_arr(I) <= out_sosi_arr(I).bsn(c_bsn_w-1 DOWNTO 0);
out_channel_arr(I) <= out_sosi_arr(I).channel;
out_err_arr(I) <= out_sosi_arr(I).err;
END GENERATE;
out_sosi <= out_sosi_arr(0); -- take out_sosi control and info from out_sosi_arr(0)
out_bsn <= TO_UINT(out_sosi.bsn); -- = out_bsn_arr().bsn = out_sosi_arr(I).bsn
gen_verify_ctrl : FOR I IN c_nof_streams-1 DOWNTO 0 GENERATE
-- . Verify that sop and eop come in pairs
proc_dp_verify_sop_and_eop(dp_clk, out_val_arr(I), out_sop_arr(I), out_eop_arr(I), hold_out_sop_arr(I));
-- . Verify that the stimuli have been applied at all
hold_data_arr(I) <= out_data_arr(I) WHEN out_val_arr(I) = '1'; -- hold last valid data
proc_dp_verify_value("out_data_arr", e_equal, dp_clk, verify_done_arr(I), expected_out_data_arr(I), hold_data_arr(I));
proc_dp_verify_value("out_bsn_arr", e_equal, dp_clk, verify_done_arr(I), expected_out_bsn_arr(I), out_bsn_arr(I));
proc_dp_verify_value("out_channel_arr", e_equal, dp_clk, verify_done_arr(I), expected_out_channel_arr(I), out_channel_arr(I));
END GENERATE;
------------------------------------------------------------------------------
-- DUT
------------------------------------------------------------------------------
u_bsn_align : ENTITY work.mmp_dp_bsn_align_v2
u_mmp_dp_bsn_align : ENTITY work.mmp_dp_bsn_align_v2
GENERIC MAP (
g_nof_streams => c_nof_streams,
g_bsn_latency_max => c_bsn_latency_max,
......@@ -156,7 +379,11 @@ BEGIN
g_block_size => c_block_size,
g_bsn_w => c_bsn_w,
g_data_w => c_data_w,
g_replacement_value => c_replacement_value,
g_data_replacement_value => c_data_replacement_value,
g_use_mm_output => c_use_mm_output,
g_pipeline_input => c_pipeline_input,
g_pipeline_output => c_pipeline_output,
g_rd_latency => c_rd_latency,
g_nof_clk_per_sync => c_nof_clk_per_sync,
g_nof_input_bsn_monitors => c_nof_input_bsn_monitors,
g_use_bsn_output_monitor => c_use_bsn_output_monitor
......@@ -165,8 +392,8 @@ BEGIN
mm_rst => mm_rst,
mm_clk => mm_clk,
reg_copi => reg_copi,
reg_cipo => reg_cipo,
reg_bsn_align_copi => reg_bsn_align_copi,
reg_bsn_align_cipo => reg_bsn_align_cipo,
reg_input_monitor_copi => reg_input_monitor_copi,
reg_input_monitor_cipo => reg_input_monitor_cipo,
......@@ -181,9 +408,11 @@ BEGIN
-- Streaming input
in_sosi_arr => in_sosi_arr,
-- Output via local MM in dp_clk domain
mm_copi => mm_copi,
mm_cipo_arr => mm_cipo_arr,
mm_sosi => mm_sosi
--mm_sosi => mm_sosi,
--mm_copi => mm_copi,
--mm_cipo_arr => mm_cipo_arr,
-- Output via streaming DP interface, when g_use_mm_output = TRUE.
out_sosi_arr => out_sosi_arr
);
END tb;
......@@ -34,10 +34,9 @@ END tb_tb_dp_bsn_align_v2;
ARCHITECTURE tb OF tb_tb_dp_bsn_align_v2 IS
CONSTANT c_bsn_latency_max : POSITIVE := 1;
CONSTANT c_block : NATURAL := 11;
CONSTANT c_period : NATURAL := 20;
CONSTANT c_delay_max : NATURAL := c_bsn_latency_max * c_period;
CONSTANT c_nof_blk : NATURAL := 30;
SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
......@@ -51,20 +50,35 @@ BEGIN
-- g_block_period : NATURAL := 20; -- >= g_block_size, = g_block_size + c_gap_size
-- g_bsn_w : NATURAL := c_dp_stream_bsn_w; -- number of bits in sosi BSN
-- g_data_w : NATURAL := 16; -- number of bits in sosi data
-- c_replacement_value : INTEGER := 0; -- output sosi data replacement value for missing input blocks
-- g_data_replacement_value : INTEGER := 0; -- output sosi data replacement value for missing input blocks
-- g_disable_stream_id : NATURAL := 0; -- default 0 to enable all streams, > 0 selects stream that will be disabled
-- g_lost_stream_id : NATURAL := 0; -- default 0 to have all streams, > 0 selects stream that will be lost
-- g_lost_bsn_id : NATURAL := 10; -- for stream 1 the block with bsn = g_lost_bsn_id will be lost
-- g_use_mm_output : BOOLEAN := FALSE; -- output via MM or via streaming DP
-- g_pipeline_input : NATURAL := 1; -- >= 0, choose 0 for wires, choose 1 to ease timing closure
-- g_pipeline_input : NATURAL := 0; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of in_sosi_arr
-- g_pipeline_output : NATURAL := 0; -- >= 0, choose 0 for wires, choose 1 to ease timing closure of out_sop_arr
-- g_rd_latency : NATURAL := 2; -- 1 or 2, choose 2 to ease timing closure
--
-- -- TB
-- g_tb_diff_delay_max : NATURAL := 45; -- maximum nof clk delay between any inputs, <= c_align_latency
-- g_tb_diff_delay : INTEGER := 0; -- 0 = aligned inputs, -1 = max input delay for no loss,
-- -- >~ g_bsn_latency_max * g_block_period will give loss
-- g_tb_nof_restart : NATURAL := 1; -- number of times to restart the input stimuli
-- g_tb_nof_blocks : NATURAL := 10 -- number of input blocks per restart
u_mm_output : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, 1, c_block, c_period, 32, 16, 17, TRUE, 0, 1, 0, 1, 50);
u_dp_output : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, 1, c_block, c_period, 32, 16, 17, FALSE, 0, 1, 0, 1, 50);
u_diff_delay_no_loss : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, 1, c_block, c_period, 32, 16, 17, FALSE, 0, 1, c_delay_max, 1, 50);
--u_loss_replacement : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, 1, c_block, c_period, 32, 16, 17, FALSE, 0, 1, 40 + c_delay_max, 1, 50);
u_mm_output : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, TRUE, 0, 0, 1, 0, 2, c_nof_blk);
u_dp_output : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_dp_output_p1 : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 1, 1, 1, 0, 2, c_nof_blk);
u_bsn_lat_max_2 : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 2, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_bsn_lat_max_3 : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 3, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_p1_rd2 : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 1, 0, 2, 0, 2, c_nof_blk);
u_zero_gap : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 1, c_block, c_block, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_zero_gap_p1_rd2 : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 1, c_block, c_block, 32, 16, 17, 0, 0, 0, FALSE, 1, 1, 2, 0, 2, c_nof_blk);
u_stream_disable : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (3, 1, 1, c_block, c_period, 32, 16, 17, 2, 0, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_stream_lost : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (3, 1, 1, c_block, c_period, 32, 16, 17, 0, 2, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_stream_disable_lost : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (4, 1, 1, c_block, c_period, 32, 16, 17, 1, 2, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_bsn_lost : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (3, 1, 1, c_block, c_period, 32, 16, 17, 0, 0, 10, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_diff_delay : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (3, 1, 1, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, -1, 2, c_nof_blk);
u_nof_aligners : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, 1, 8, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, 0, 2, c_nof_blk);
u_nof_aligners_diff_delay : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (4, 1, 3, c_block, c_period, 32, 16, 17, 0, 0, 0, FALSE, 0, 0, 1, -1, 2, c_nof_blk);
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment