Skip to content
Snippets Groups Projects
Commit dc079f36 authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Added first version of dp_bsn_align_v2.vhd with mmp and tb. This was...

Added first version of dp_bsn_align_v2.vhd with mmp and tb. This was accidentially on L2SDP-495 branch, but should be on this L2SDP-290 branch.
parent a2e24900
No related branches found
No related tags found
1 merge request!156Added first version of dp_bsn_align_v2.vhd with mmp and tb. This was...
......@@ -104,6 +104,8 @@ synth_files =
src/vhdl/dp_bsn_align.vhd
src/vhdl/dp_bsn_align_reg.vhd
src/vhdl/mms_dp_bsn_align.vhd
src/vhdl/dp_bsn_align_v2.vhd
src/vhdl/mmp_dp_bsn_align_v2.vhd
src/vhdl/dp_frame_rd.vhd
src/vhdl/dp_frame_fsn.vhd
src/vhdl/dp_frame_tx.vhd
......@@ -211,6 +213,8 @@ test_bench_files =
tb/vhdl/tb_dp_block_validate_channel.vhd
tb/vhdl/tb_dp_bsn_align.vhd
tb/vhdl/tb_mms_dp_bsn_align.vhd
tb/vhdl/tb_dp_bsn_align_v2.vhd
tb/vhdl/tb_mmp_dp_bsn_align_v2.vhd
tb/vhdl/tb_dp_bsn_monitor.vhd
tb/vhdl/tb_dp_bsn_monitor_v2.vhd
tb/vhdl/tb_dp_bsn_source.vhd
......@@ -295,6 +299,7 @@ test_bench_files =
tb/vhdl/tb_tb_dp_block_from_mm.vhd
tb/vhdl/tb_tb_dp_block_validate_channel.vhd
tb/vhdl/tb_tb_dp_bsn_align.vhd
tb/vhdl/tb_tb_dp_bsn_align_v2.vhd
tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd
tb/vhdl/tb_tb_dp_concat.vhd
......@@ -351,6 +356,7 @@ regression_test_vhdl =
tb/vhdl/tb_dp_latency_adapter.vhd
tb/vhdl/tb_dp_shiftreg.vhd
tb/vhdl/tb_dp_bsn_source.vhd
tb/vhdl/tb_mmp_dp_bsn_align_v2.vhd
tb/vhdl/tb_mms_dp_bsn_source.vhd
tb/vhdl/tb_mms_dp_bsn_source_v2.vhd
tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd
......@@ -364,6 +370,7 @@ regression_test_vhdl =
tb/vhdl/tb_tb_dp_block_gen_valid_arr.vhd
tb/vhdl/tb_tb_dp_block_from_mm.vhd
tb/vhdl/tb_tb_dp_block_validate_channel.vhd
tb/vhdl/tb_tb_dp_bsn_align_v2.vhd
tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd
tb/vhdl/tb_tb_dp_concat.vhd
......
-- --------------------------------------------------------------------------
-- Copyright 2021
-- 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, 3 Sept 2021
-- Purpose :
-- Align frames from multiple input streams
-- Description:
-- The aligner uses a circular buffer to capture the blocks that arrive at
-- the input streams. The blocks have a block sequence number (BSN) that
-- is used to align the inputs. The input stream 0 is treated as local
-- 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
-- filler 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 filler flag and data to replace lost input blocks
-- . output block can be read in arbitrary order
--
-- For more detailed description see:
-- https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+aligner+v2
--
-- Remarks:
-- . This dp_bsn_align_v2.vhd replaces the dp_bsn_align.vhd that was used in
-- APERTIF. Mian differences are that the old component uses FIFO buffers,
-- timeouts and states, and v2 does not, which makes v2 simpler and more
-- robust.
LIBRARY IEEE,common_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
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_bsn_latency_use_node_index : BOOLEAN := FALSE; -- FALSE for align at end node, TRUE for align at every intermediate node
g_node_index_max : NATURAL := 31; -- limit to functional 5 bit range, instead of full 31 bit NATURAL range
g_block_size : NATURAL := 32; -- > 1, g_block_size=1 is not supported
g_buffer_nof_blocks : NATURAL; -- circular buffer size per input, choose ceil_pow2(1 + g_bsn_latency_max)
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_filler_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
);
PORT (
dp_rst : IN STD_LOGIC;
dp_clk : IN STD_LOGIC;
node_index : IN NATURAL RANGE 0 TO g_node_index_max := 0; -- only used when g_bsn_latency_use_node_index is TRUE
-- MM control
stream_en_arr : IN STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1');
-- Streaming input
in_sosi_arr : IN t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
-- Output via local MM interface in dp_clk domain
mm_sosi : OUT t_dp_sosi; -- streaming information that signals that an output block can be read
mm_copi : IN t_mem_copi; -- 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);
-- Output via streaming DP interface
out_sosi_arr : OUT t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0)
);
END dp_bsn_align_v2;
ARCHITECTURE rtl OF dp_bsn_align_v2 IS
-- Circular buffer per stream
CONSTANT c_ram_size : NATURAL := g_buffer_nof_blocks * g_block_size;
CONSTANT c_ram_buf : t_c_mem := (latency => 1,
adr_w => ceil_log2(c_ram_size),
dat_w => g_data_w,
nof_dat => c_ram_size,
init_sl => '0');
CONSTANT c_block_size_w : NATURAL := ceil_log2(g_block_size);
CONSTANT c_block_size_slv : STD_LOGIC_VECTOR(c_block_size_w-1 DOWNTO 0) := TO_UVEC(g_block_size, c_block_size_w);
CONSTANT c_blk_pointer_w : NATURAL := ceil_log2(g_buffer_nof_blocks);
-- Use fixed slv width instead of using naturals for address calculation, to
-- avoid that synthesis may infer a too larger multiplier
CONSTANT c_product_w : NATURAL := c_blk_pointer_w + c_block_size_w;
TYPE t_bsn_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(g_bsn_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(g_buffer_nof_blocks-1 DOWNTO 0);
TYPE t_reg IS RECORD
-- p_write_arr
wr_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);
use_filler_data : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
-- local reference
sync_arr : STD_LOGIC_VECTOR(g_buffer_nof_blocks-1 DOWNTO 0);
bsn_arr : t_bsn_arr(g_buffer_nof_blocks-1 DOWNTO 0);
mm_sosi : t_dp_sosi;
-- p_read
rd_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 filler data
out_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); -- hold BSN for streaming output
END RECORD;
CONSTANT c_reg_rst : t_reg := (0,
(OTHERS=>c_mem_copi_rst),
(OTHERS=>(OTHERS=>'0')),
(OTHERS=>'0'),
(OTHERS=>'0'),
(OTHERS=>(OTHERS=>'0')),
c_dp_sosi_rst,
0,
(OTHERS=>'0'),
c_mem_copi_rst,
(OTHERS=>c_mem_cipo_rst),
(OTHERS=>'0'));
-- State registers
SIGNAL r : t_reg;
SIGNAL nxt_r : t_reg;
-- Wires
SIGNAL dp_done : STD_LOGIC;
SIGNAL dp_done_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL dp_copi : t_mem_copi;
SIGNAL dp_copi_arr : t_mem_copi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL dp_sosi : t_dp_sosi;
SIGNAL rd_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL rd_cipo_arr : t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_mem_cipo_rst);
-- Pipeline registers
SIGNAL in_sosi_arr_p : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL rd_copi_p : t_mem_copi;
BEGIN
mm_sosi <= r.mm_sosi;
p_reg : PROCESS(dp_clk, dp_rst)
BEGIN
IF dp_rst='1' THEN
r <= c_reg_rst;
ELSIF rising_edge(dp_clk) THEN
r <= nxt_r;
END IF;
END PROCESS;
p_comb : PROCESS(r, in_sosi_arr_p, mm_copi, rd_cipo_arr, rd_sosi_arr)
-- State variable
VARIABLE v : t_reg;
-- Auxiliary variables / local wires / no memory
VARIABLE v_ref_sosi : t_dp_sosi;
VARIABLE v_pointer_slv : STD_LOGIC_VECTOR(c_blk_pointer_w-1 DOWNTO 0);
VARIABLE v_product_slv : STD_LOGIC_VECTOR(c_product_w-1 DOWNTO 0);
VARIABLE v_filler_flag : STD_LOGIC;
VARIABLE v_out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
BEGIN
v := r;
v.mm_sosi := func_dp_stream_reset_control(r.mm_sosi);
v.wr_copi_arr := RESET_MEM_COPI_CTRL(r.wr_copi_arr);
----------------------------------------------------------------------------
-- p_write_arr
----------------------------------------------------------------------------
FOR I IN 0 TO g_nof_streams-1 LOOP
-- p_write
IF in_sosi_arr_p(I).valid = '1' THEN
-- . increment address during block
v.wr_copi_arr(I).address := RESIZE_MEM_ADDRESS(INCR_UVEC(r.wr_copi_arr(I).address(c_mem_ram.adr_w-1 DOWNTO 0), 1));
v.wr_copi_arr(I).wr := '1';
v.wr_copi_arr(I).wrdata := RESIZE_MEM_SDATA(in_sosi_arr_p(I).data);
END IF;
IF in_sosi_arr_p(I).sop = '1' THEN
-- . set address at start of block
v_pointer_slv := in_sosi_arr_p(I).bsn(c_blk_pointer_w-1 DOWNTO 0);
v_product_slv := MULT_UVEC(v_pointer_slv, c_block_size_slv);
v.wr_copi_arr(I).address := RESIZE_MEM_ADDRESS(v_product_slv);
-- . set filled flag at sop, so assume rest of block will follow in time
v.filled_arr(I)(TO_UINT(v_pointer_slv)) := '1';
END IF;
END LOOP;
----------------------------------------------------------------------------
-- p_control, all at sop of local reference input 0
----------------------------------------------------------------------------
v_ref_sosi := in_sosi_arr_p(0);
IF v_ref_sosi.sop = '1' THEN
-- . write sync & bsn buffer
v.wr_pointer := TO_UINT(v_ref_sosi.bsn(c_blk_pointer_w-1 DOWNTO 0));
v.sync_arr(v.wr_pointer) := v_ref_sosi.sync;
v.bsn_arr(v.wr_pointer) := v_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_bsn_latency_use_node_index = FALSE 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 + g_buffer_nof_blocks;
END IF;
-- . update read address of read block pointer
v_pointer_slv := TO_UVEC(v.rd_pointer, c_blk_pointer_w);
v_product_slv := MULT_UVEC(v_pointer_slv, c_block_size_slv);
v.rd_offset := RESIZE_UVEC(v_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
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));
-- . pass on filled flags for enabled streams via channel field, and
-- determine whether the ouput has to insert filler data
v.mm_sosi.channel := (OTHERS=>'0');
FOR I IN 0 TO g_nof_streams-1 LOOP
v_filler_flag := NOT v.filled_arr(I)(v.rd_pointer);
IF stream_en_arr(I) = '1' THEN -- use MM bit at sop
v.use_filler_data(I) := v_filler_flag; -- enabled stream
v.mm_sosi.channel(I) := v_filler_flag;
ELSE
v.use_filler_data(I) := '1'; -- disabled stream
END IF;
END LOOP;
END IF;
-- . 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';
END LOOP;
END IF;
----------------------------------------------------------------------------
-- p_read
----------------------------------------------------------------------------
-- Read the data from the buffer, or replace a block by filler data
-- . default use input data from the circular buffer
v.fill_cipo_arr := rd_cipo_arr;
-- . if necessary, replace a stream by filler data
FOR I IN 0 TO g_nof_streams-1 LOOP
IF r.use_filler_data(I) = '1' THEN
v.fill_cipo_arr(I).rddata := TO_MEM_SDATA(g_filler_value);
END IF;
END LOOP;
IF g_use_mm_output THEN
--------------------------------------------------------------------------
-- Do the output via the MM interface
--------------------------------------------------------------------------
-- . adjust the rd address to the current buffer output block
v.rd_copi := mm_copi;
v.rd_copi.address := RESIZE_MEM_ADDRESS(ADD_UVEC(r.rd_offset, mm_copi.address)); -- sum yields c_mem_ram.adr_w bits, because left operand determines width
-- . output via MM interface
mm_cipo_arr <= v.fill_cipo_arr;
ELSE
--------------------------------------------------------------------------
-- Do the output via the DP streaming interface
--------------------------------------------------------------------------
-- . adjust the rd address
v.rd_copi := dp_copi;
v.rd_copi.address := RESIZE_MEM_ADDRESS(ADD_UVEC(r.rd_offset, dp_copi.address)); -- sum yields c_mem_ram.adr_w bits, because left operand determines width
-- . hold mm_sosi.sync, bsn
IF r.mm_sosi.sop = '1' THEN
dp_sosi <= r.mm_sosi;
END IF;
-- apply mm_sosi.sync and bsn at sop to all streams in out_sosi_arr
v_out_sosi_arr := rd_sosi_arr; -- the input data from the buffer or filler data (= v.fill_cipo_arr in streaming format)
IF rd_sosi_arr(0).sop = '1' THEN
v_out_sosi_arr := func_dp_stream_arr_set(v_out_sosi_arr, dp_sosi.sync, "SYNC");
v_out_sosi_arr := func_dp_stream_arr_set(v_out_sosi_arr, dp_sosi.bsn, "BSN");
v.out_bsn := dp_sosi.bsn(g_bsn_w-1 DOWNTO 0); -- hold BSN until next sop, to ease view in wave window
ELSE
-- hold BSN until next sop, to ease view in wave window
v_out_sosi_arr := func_dp_stream_arr_set(v_out_sosi_arr, r.out_bsn, "BSN");
END IF;
-- . output via DP streaming interface
out_sosi_arr <= v_out_sosi_arr;
END IF;
----------------------------------------------------------------------------
-- next state
----------------------------------------------------------------------------
nxt_r <= v;
END PROCESS;
------------------------------------------------------------------------------
-- Circular buffers
------------------------------------------------------------------------------
gen_data_buffer : FOR I IN 0 TO g_nof_streams-1 GENERATE
u_data_buffer : ENTITY common_lib.common_ram_r_w
GENERIC MAP (
g_ram => c_ram_buf
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
wr_en => r.wr_copi_arr(I).wr,
wr_adr => r.wr_copi_arr(I).address(c_ram_buf.adr_w-1 DOWNTO 0),
wr_dat => r.wr_copi_arr(I).wrdata(c_ram_buf.dat_w-1 DOWNTO 0),
rd_en => rd_copi_p.rd,
rd_adr => rd_copi_p.address(c_ram_buf.adr_w-1 DOWNTO 0),
rd_dat => rd_cipo_arr(I).rddata(c_ram_buf.dat_w-1 DOWNTO 0),
rd_val => rd_cipo_arr(I).rdval
);
END GENERATE;
------------------------------------------------------------------------------
-- MM to streaming DP
------------------------------------------------------------------------------
gen_streaming_output : IF NOT g_use_mm_output GENERATE
dp_copi <= dp_copi_arr(0);
dp_done <= dp_done_arr(0); -- for viewing only
gen_mm_to_dp : FOR I IN 0 TO g_nof_streams-1 GENERATE
u_mm_to_dp: ENTITY work.dp_block_from_mm
GENERIC MAP (
g_data_size => 1,
g_step_size => 1,
g_nof_data => g_block_size,
g_data_w => g_data_w,
g_mm_rd_latency => g_rd_latency,
g_reverse_word_order => FALSE
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
start_pulse => r.mm_sosi.sop,
start_address => 0,
mm_done => dp_done_arr(I),
mm_mosi => dp_copi_arr(I),
mm_miso => nxt_r.fill_cipo_arr(I),
out_sosi => rd_sosi_arr(I),
out_siso => c_dp_siso_rdy
);
END GENERATE;
END GENERATE;
------------------------------------------------------------------------------
-- Pipelining
------------------------------------------------------------------------------
-- . input
u_in_sosi_arr_p : ENTITY work.dp_pipeline_arr
GENERIC MAP (
g_nof_streams => g_nof_streams,
g_pipeline => g_pipeline_input -- 0 for wires, > 0 for registers,
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
-- ST sink
snk_in_arr => in_sosi_arr,
-- ST source
src_out_arr => in_sosi_arr_p
);
-- . read RAM
rd_copi_p <= nxt_r.rd_copi WHEN g_rd_latency = 1 ELSE r.rd_copi;
END rtl;
......@@ -298,7 +298,7 @@ PACKAGE dp_stream_pkg Is
-- Functions to set or get a STD_LOGIC field as a STD_LOGIC_VECTOR to or from an siso or an sosi array
FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_siso_arr;
FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr;
FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr; -- also support slv fields
FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; sl : STD_LOGIC; str : STRING) RETURN t_dp_siso_arr;
FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; sl : STD_LOGIC; str : STRING) RETURN t_dp_sosi_arr;
FUNCTION func_dp_stream_arr_get(dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC_VECTOR;
......@@ -889,13 +889,19 @@ PACKAGE BODY dp_stream_pkg IS
FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr IS
VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- default
VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := slv; -- map to ensure same range as for dp
VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := slv(dp'RANGE); -- map to ensure same range as for dp
BEGIN
FOR I IN dp'RANGE LOOP
-- use v_slv(I) to set individual sl field
IF str="VALID" THEN v_dp(I).valid := v_slv(I);
ELSIF str="SOP" THEN v_dp(I).sop := v_slv(I);
ELSIF str="EOP" THEN v_dp(I).eop := v_slv(I);
ELSIF str="SYNC" THEN v_dp(I).sync := v_slv(I);
-- use slv to set individual slv field
ELSIF str="BSN" THEN v_dp(I).bsn := RESIZE_DP_BSN(slv);
ELSIF str="CHANNEL" THEN v_dp(I).channel := RESIZE_DP_CHANNEL(slv);
ELSIF str="EMPTY" THEN v_dp(I).empty := RESIZE_DP_EMPTY(slv);
ELSIF str="ERR" THEN v_dp(I).err := RESIZE_DP_ERROR(slv);
ELSE REPORT "Error in func_dp_stream_arr_set for t_dp_sosi_arr";
END IF;
END LOOP;
......
-- --------------------------------------------------------------------------
-- Copyright 2021
-- 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, 6 Sept 2021
-- Purpose: MMP for dp_bsn_align_v2
-- Description:
-- Add MM interfaces to dp_bsn_align_v2:
--
-- * Instantiates input BSN monitors when g_nof_input_bsn_monitors > 0
-- * Instantiates output BSN monitor g_use_bsn_output_monitor = TRUE
-- * Define MM reg for input enable/disable control for input i:
--
-- wi Bits Access Type Name
-- i [0] RW boolean input_enable
--
-- where i = 0:g_nof_streams-1 and input_enable '1' is on, '0' is off
--
-- For more description see dp_bsn_align_v2 and
-- https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+aligner+v2
LIBRARY IEEE, common_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE work.dp_stream_pkg.ALL;
ENTITY mmp_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_bsn_latency_use_node_index : BOOLEAN := FALSE; -- FALSE for align at end node, TRUE for align at every intermediate node
g_block_size : NATURAL := 32; -- > 1, g_block_size=1 is not supported
g_buffer_nof_blocks : NATURAL; -- circular buffer size per input, choose ceil_pow2(1 + g_bsn_latency_max)
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_filler_value : INTEGER := 0; -- output sosi data value for missing input blocks
g_nof_clk_per_sync : NATURAL := 200*10**6;
g_nof_input_bsn_monitors : NATURAL := 0;
g_use_bsn_output_monitor : BOOLEAN := FALSE
);
PORT (
-- Memory-mapped clock domain
mm_rst : IN STD_LOGIC;
mm_clk : IN STD_LOGIC;
reg_copi : IN t_mem_copi;
reg_cipo : OUT t_mem_cipo;
reg_input_monitor_copi : IN t_mem_copi;
reg_input_monitor_cipo : OUT t_mem_cipo;
reg_output_monitor_copi : IN t_mem_copi;
reg_output_monitor_cipo : OUT t_mem_cipo;
-- Streaming clock domain
dp_rst : IN STD_LOGIC;
dp_clk : IN STD_LOGIC;
node_index : IN NATURAL := 0; -- only used when g_bsn_latency_use_node_index is TRUE
-- 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
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
);
END mmp_dp_bsn_align_v2;
ARCHITECTURE str OF mmp_dp_bsn_align_v2 IS
-- Use one MM word (bit 0) per input_enable bit, similar as in dp_bsn_align_reg.vhd.
-- TYPE t_c_mem IS RECORD
-- latency : NATURAL; -- read latency
-- adr_w : NATURAL;
-- dat_w : NATURAL;
-- nof_dat : NATURAL; -- optional, nof dat words <= 2**adr_w
-- init_sl : STD_LOGIC; -- optional, init all dat words to std_logic '0', '1' or 'X'
CONSTANT c_mm_reg : t_c_mem := (1, ceil_log2(g_nof_streams), 1, g_nof_streams, '0');
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);
BEGIN
u_reg : ENTITY common_lib.common_reg_r_w_dc
GENERIC MAP (
g_cross_clock_domain => TRUE,
g_readback => FALSE,
g_reg => c_mm_reg
)
PORT MAP (
-- Clocks and reset
mm_rst => mm_rst,
mm_clk => mm_clk,
st_rst => dp_rst,
st_clk => dp_clk,
-- Memory Mapped Slave in mm_clk domain
sla_in => reg_copi,
sla_out => reg_cipo,
-- MM registers in st_clk domain
reg_wr_arr => OPEN,
reg_rd_arr => OPEN,
out_reg => reg_wr, -- readback via ST clock domain
in_reg => reg_wr
);
stream_en_arr <= reg_wr;
-- 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
-- . all input streams (g_nof_input_bsn_monitors = g_nof_streams).
gen_bsn_mon_input : IF g_nof_input_bsn_monitors > 0 GENERATE
u_bsn_mon_input : ENTITY work.mms_dp_bsn_monitor_v2
GENERIC MAP (
g_nof_streams => g_nof_input_bsn_monitors,
g_cross_clock_domain => TRUE,
g_sync_timeout => g_nof_clk_per_sync,
g_bsn_w => g_bsn_w,
g_error_bi => 0,
g_cnt_sop_w => c_word_w,
g_cnt_valid_w => c_word_w,
g_cnt_latency_w => c_word_w
)
PORT MAP (
-- Memory-mapped clock domain
mm_rst => mm_rst,
mm_clk => mm_clk,
reg_mosi => reg_input_monitor_copi,
reg_miso => reg_input_monitor_cipo,
-- Streaming clock domain
dp_rst => dp_rst,
dp_clk => dp_clk,
ref_sync => in_sosi_arr(0).sync, -- local reference sync input
in_siso_arr => (OTHERS=>c_dp_siso_rdy),
in_sosi_arr => in_sosi_arr(g_nof_input_bsn_monitors-1 DOWNTO 0)
);
END GENERATE;
gen_bsn_mon_output : IF g_use_bsn_output_monitor GENERATE
u_bsn_mon_output : ENTITY work.mms_dp_bsn_monitor_v2
GENERIC MAP (
g_nof_streams => 1, -- all outputs have same BSN monitor information
g_cross_clock_domain => TRUE,
g_sync_timeout => g_nof_clk_per_sync,
g_bsn_w => g_bsn_w,
g_error_bi => 0,
g_cnt_sop_w => c_word_w,
g_cnt_valid_w => c_word_w,
g_cnt_latency_w => c_word_w
)
PORT MAP (
-- Memory-mapped clock domain
mm_rst => mm_rst,
mm_clk => mm_clk,
reg_mosi => reg_output_monitor_copi,
reg_miso => reg_output_monitor_cipo,
-- Streaming clock domain
dp_rst => dp_rst,
dp_clk => dp_clk,
ref_sync => in_sosi_arr(0).sync, -- local reference sync input
in_siso_arr => (OTHERS=>c_dp_siso_rdy),
in_sosi_arr => mm_sosi_arr
);
END 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_bsn_latency_use_node_index => g_bsn_latency_use_node_index,
g_block_size => g_block_size,
g_buffer_nof_blocks => g_buffer_nof_blocks,
g_bsn_w => g_bsn_w,
g_data_w => g_data_w,
g_filler_value => g_filler_value
)
PORT MAP (
dp_rst => dp_rst,
dp_clk => dp_clk,
node_index => node_index,
-- MM control
stream_en_arr => stream_en_arr,
-- 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_arr(0);
END str;
-- --------------------------------------------------------------------------
-- Copyright 2021
-- 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, 3 Sept 2021
-- Purpose: Verify dp_bsn_align_v2
-- Description:
-- Usage:
-- > as 10
-- > run -all
LIBRARY IEEE, common_lib, dp_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE common_lib.common_lfsr_sequences_pkg.ALL;
USE common_lib.tb_common_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE dp_lib.tb_dp_pkg.ALL;
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 := 1; -- Maximum travel latency of a remote block in number of block periods T_blk
g_bsn_latency_use_node_index : BOOLEAN := FALSE; -- FALSE for align at end node, TRUE for align at every intermediate node
g_block_size : NATURAL := 11; -- > 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 := 16; -- number of bits in sosi data
g_filler_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
g_rd_latency : NATURAL := 2; -- 1 or 2, choose 2 to ease timing closure
-- TB
g_diff_delay : NATURAL := 0;
g_diff_bsn : NATURAL := 0; -- g_diff_bsn = g_bsn_latency_max can just be aligned
g_nof_repeat : NATURAL := 100 -- for constant active stream control using 1 is sufficient, use > 1 to verify longer with random stimuli
);
END tb_dp_bsn_align_v2;
ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
CONSTANT c_rl : NATURAL := 1;
CONSTANT c_pulse_active : NATURAL := 1;
CONSTANT c_pulse_period : NATURAL := 7;
CONSTANT c_data_w : NATURAL := 16;
CONSTANT c_data_init : INTEGER := 0;
CONSTANT c_bsn_w : NATURAL := 16;
CONSTANT c_bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := TO_UVEC(3, c_bsn_w);
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 := 10;
CONSTANT c_block_period : NATURAL := g_block_size + c_gap_size;
CONSTANT c_xoff_timeout : NATURAL := c_block_period * g_bsn_latency_max * 2; -- xoff timeout to recover for next alignment attempt
CONSTANT c_sop_timeout : NATURAL := c_block_period * g_bsn_latency_max; -- sop timeout to end current aligment attempt
CONSTANT c_event_input : NATURAL := smallest(1, g_nof_streams-1); -- select special event input at which the event will apply, use >= g_nof_streams to disable the special events
CONSTANT c_buffer_nof_blocks : NATURAL := ceil_pow2(1 + g_bsn_latency_max); -- circular buffer size per input
TYPE t_data_arr IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
TYPE t_bsn_arr IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
TYPE t_err_arr IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_dp_stream_error_w-1 DOWNTO 0);
TYPE t_channel_arr IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0);
TYPE t_rl_vec_arr IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(0 TO c_rl);
TYPE t_tb_state IS (s_idle, s_bsn_mis_aligned, s_bsn_aligned, s_small_bsn_diff, s_large_bsn_diff, s_restore_bsn, s_disable_one_input, s_enable_inputs);
TYPE t_reg IS RECORD
-- p_write_arr
sync : STD_LOGIC;
bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
END RECORD;
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL clk : STD_LOGIC := '1';
SIGNAL rst : STD_LOGIC := '1';
SIGNAL sl1 : STD_LOGIC := '1';
SIGNAL random : STD_LOGIC_VECTOR(15 DOWNTO 0) := (OTHERS=>'0'); -- use different lengths to have different random sequences
SIGNAL pulse : STD_LOGIC;
SIGNAL pulse_en : STD_LOGIC := '1';
SIGNAL node_index : NATURAL := 0;
SIGNAL in_siso_arr : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_dp_siso_rdy);
SIGNAL in_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
SIGNAL in_bsn : t_bsn_arr;
SIGNAL in_data : t_data_arr;
SIGNAL in_val : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL in_sync : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL in_sop : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL in_eop : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL in_err : t_err_arr;
SIGNAL in_channel : t_channel_arr;
SIGNAL mm_copi_arr : t_mem_copi_arr(g_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(g_nof_streams-1 DOWNTO 0);
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 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_bsn : t_bsn_arr;
SIGNAL out_data : t_data_arr;
SIGNAL out_val : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL out_sync : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL out_sop : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL out_eop : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL out_gap : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1');
SIGNAL out_err : t_err_arr;
SIGNAL out_channel : t_channel_arr;
SIGNAL tb_state : t_tb_state;
SIGNAL verify_done_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL default_end_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL default_end : STD_LOGIC;
SIGNAL verify_dis_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL verify_en_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL hold_out_sop : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
SIGNAL prev_out_ready : t_rl_vec_arr;
SIGNAL prev_out_bsn : t_bsn_arr;
SIGNAL expected_out_bsn : t_bsn_arr;
SIGNAL prev_out_data : t_data_arr;
SIGNAL expected_out_data : t_data_arr;
SIGNAL verify_extra_end : STD_LOGIC := '0';
SIGNAL bsn_diff : INTEGER;
SIGNAL bsn_offset : INTEGER;
SIGNAL bsn_event : STD_LOGIC := '0'; -- pulse '1' triggers a BSN offset for an input
SIGNAL bsn_event_ack_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL bsn_event_ack : STD_LOGIC;
SIGNAL stream_en_event : STD_LOGIC := '0'; -- pulse '1' indicates that the stream enables in stream_en_arr have been updated
SIGNAL stream_en_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1'); -- default all streams are enabled
BEGIN
clk <= (NOT clk) OR tb_end AFTER clk_period/2;
rst <= '1', '0' AFTER clk_period*7;
random <= func_common_random(random) WHEN rising_edge(clk);
proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period+1, '1', rst, clk, pulse_en, pulse);
------------------------------------------------------------------------------
-- DATA GENERATION
------------------------------------------------------------------------------
-- Generate data path input data
gen_input : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
p_stimuli : PROCESS
VARIABLE v_sync : STD_LOGIC := '0';
VARIABLE v_bsn : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := c_bsn_init;
VARIABLE v_data : NATURAL := c_data_init;
VARIABLE v_channel : NATURAL := c_channel_init;
VARIABLE v_err : NATURAL := c_err_init;
VARIABLE v_diff_bsn : NATURAL := 0;
BEGIN
-- Create BSN misalignment between the input streams
v_diff_bsn := I MOD (g_diff_bsn+1);
v_data := v_data + I;
v_bsn := INCR_UVEC(v_bsn, v_diff_bsn);
in_sosi_arr(I) <= c_dp_sosi_rst;
proc_common_wait_until_low(clk, rst);
proc_common_wait_some_cycles(clk, c_xoff_timeout*2);
-- Create latency misalignment between the input streams
proc_common_wait_some_cycles(clk, I*g_diff_delay/g_nof_streams);
-- Begin of stimuli
FOR R IN 0 TO g_nof_repeat-v_diff_bsn-1 LOOP
v_sync := sel_a_b(TO_UINT(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, v_bsn, clk, stream_en_arr(I), in_siso_arr(I), in_sosi_arr(I));
v_bsn := INCR_UVEC(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;
-- End of default stimuli
expected_out_bsn(I) <= INCR_UVEC(v_bsn, -1);
expected_out_data(I) <= TO_UVEC(v_data-1, c_data_w);
proc_common_wait_some_cycles(clk, 100); -- depends on stream control
default_end_arr(I) <= '1';
verify_done_arr(I) <= '1';
proc_common_wait_some_cycles(clk, 1);
verify_done_arr(I) <= '0';
--------------------------------------------------------------------------
-- Extra
--------------------------------------------------------------------------
proc_common_wait_some_cycles(clk, 500);
WHILE verify_extra_end /= '1' LOOP
v_sync := sel_a_b(TO_UINT(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, v_bsn, clk, stream_en_arr(I), in_siso_arr(I), in_sosi_arr(I));
v_bsn := INCR_UVEC(v_bsn, 1);
bsn_event_ack_arr(I) <= '0';
IF I=c_event_input AND bsn_event='1' THEN
v_bsn := INCR_UVEC(v_bsn, bsn_offset);
bsn_event_ack_arr(I) <= '1';
END IF;
v_data := v_data + g_block_size;
proc_common_wait_some_cycles(clk, c_gap_size); -- create gap between frames
END LOOP;
-- End of extra stimuli
expected_out_bsn(I) <= INCR_UVEC(v_bsn, -1);
expected_out_data(I) <= TO_UVEC(v_data-1, c_data_w);
verify_done_arr(I) <= '1';
proc_common_wait_some_cycles(clk, 1);
verify_done_arr(I) <= '0';
WAIT;
END PROCESS;
END GENERATE;
default_end <= vector_and(default_end_arr);
bsn_event_ack <= vector_or(bsn_event_ack_arr);
p_special_stimuli : PROCESS
BEGIN
verify_dis_arr <= (OTHERS=>'0');
tb_state <= s_bsn_mis_aligned;
stream_en_event <= '0';
stream_en_arr <= (OTHERS=>'1');
----------------------------------------------------------------------------
-- Wait until default verify test is done
----------------------------------------------------------------------------
proc_common_wait_until_high(clk, default_end);
verify_dis_arr <= (OTHERS=>'1');
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
tb_state <= s_bsn_aligned;
proc_common_wait_some_cycles(clk, 1000);
----------------------------------------------------------------------------
-- Verify change in input BSN offset
----------------------------------------------------------------------------
-- . enforce small BSN misalignment
tb_state <= s_small_bsn_diff;
verify_dis_arr <= (OTHERS=>'1');
bsn_offset <= -1;
bsn_event <= '1';
proc_common_wait_until_high(clk, bsn_event_ack);
bsn_event <= '0';
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 1000);
verify_dis_arr <= (OTHERS=>'1');
-- . restore original BSN sequence
tb_state <= s_restore_bsn;
bsn_offset <= +1;
bsn_event <= '1';
proc_common_wait_until_high(clk, bsn_event_ack);
bsn_event <= '0';
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 1000);
-- verify_dis_arr <= (OTHERS=>'1');
-- . enforce large BSN misalignment
tb_state <= s_large_bsn_diff;
bsn_offset <= -g_bsn_latency_max-1;
bsn_event <= '1';
proc_common_wait_until_high(clk, bsn_event_ack);
bsn_event <= '0';
-- expect no output, because difference remains too large, so do not restart verify_en here and leave it commented:
-- proc_common_wait_some_cycles(clk, 100);
-- verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 1000);
verify_dis_arr <= (OTHERS=>'1');
-- . restore original BSN sequence
tb_state <= s_restore_bsn;
bsn_offset <= g_bsn_latency_max+1;
bsn_event <= '1';
proc_common_wait_until_high(clk, bsn_event_ack);
bsn_event <= '0';
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 1000);
----------------------------------------------------------------------------
-- Verify change in input enables
----------------------------------------------------------------------------
tb_state <= s_disable_one_input;
verify_dis_arr <= (OTHERS=>'1');
stream_en_event <= '1';
stream_en_arr(c_event_input) <= '0'; -- switch an input off
proc_common_wait_some_cycles(clk, 1);
stream_en_event <= '0';
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 2000); -- keep this input off for a while
tb_state <= s_enable_inputs;
verify_dis_arr <= (OTHERS=>'1');
stream_en_event <= '1';
stream_en_arr(c_event_input) <= '1'; -- switch this input on
proc_common_wait_some_cycles(clk, 1);
stream_en_event <= '0';
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 500);
tb_state <= s_restore_bsn;
verify_dis_arr <= (OTHERS=>'1');
bsn_offset <= bsn_diff; -- use input 0 to restore original BSN sequence for input c_event_input, that got lost due to input disable
bsn_event <= '1';
proc_common_wait_until_high(clk, bsn_event_ack);
bsn_event <= '0';
proc_common_wait_some_cycles(clk, 100);
verify_dis_arr <= (OTHERS=>'0');
proc_common_wait_some_cycles(clk, 2000);
tb_state <= s_idle;
verify_extra_end <= '1';
proc_common_wait_some_cycles(clk, 500);
tb_end <= '1';
WAIT;
END PROCESS;
bsn_diff <= TO_UINT(in_sosi_arr(0).bsn) - TO_UINT(in_sosi_arr(c_event_input).bsn) WHEN rising_edge(clk) AND in_sosi_arr(0).sop='1';
------------------------------------------------------------------------------
-- DATA VERIFICATION
------------------------------------------------------------------------------
gen_verify : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
-- Verification logistics
verify_en_arr(I) <= '1' WHEN rising_edge(clk) AND verify_dis_arr(I)='0' AND stream_en_arr(I)='1' AND out_sosi_arr(I).sop='1' ELSE
'0' WHEN rising_edge(clk) AND verify_dis_arr(I)='1'; -- verify enable after first output sop
-- Ease in_sosi_arr monitoring
in_data(I) <= in_sosi_arr(I).data(c_data_w-1 DOWNTO 0);
in_val(I) <= in_sosi_arr(I).valid;
in_sop(I) <= in_sosi_arr(I).sop;
in_eop(I) <= in_sosi_arr(I).eop;
in_err(I) <= in_sosi_arr(I).err;
in_channel(I) <= in_sosi_arr(I).channel;
in_sync(I) <= in_sosi_arr(I).sync;
in_bsn(I) <= in_sosi_arr(I).bsn(c_bsn_w-1 DOWNTO 0);
-- Ease out_sosi_arr monitoring and verification
out_data(I) <= out_sosi_arr(I).data(c_data_w-1 DOWNTO 0);
out_val(I) <= out_sosi_arr(I).valid;
out_sop(I) <= out_sosi_arr(I).sop;
out_eop(I) <= out_sosi_arr(I).eop;
out_err(I) <= out_sosi_arr(I).err;
out_channel(I) <= out_sosi_arr(I).channel;
out_sync(I) <= out_sosi_arr(I).sync;
out_bsn(I) <= out_sosi_arr(I).bsn(c_bsn_w-1 DOWNTO 0);
-- Actual verification of the output streams
-- . Verify that the output valid fits with the output ready latency
proc_dp_verify_valid(c_rl, clk, verify_en_arr(I), out_siso_arr(I).ready, prev_out_ready(I), out_val(I));
-- . Verify that sop and eop come in pairs
proc_dp_verify_sop_and_eop(clk, out_val(I), out_sop(I), out_eop(I), hold_out_sop(I));
-- . Verify that the output is incrementing, like the input stimuli
proc_dp_verify_data("out_sosi.data", c_rl, clk, verify_en_arr(I), out_siso_arr(I).ready, out_val(I), out_data(I), prev_out_data(I));
proc_dp_verify_data("out_sosi.bsn", c_rl, clk, verify_en_arr(I), out_siso_arr(I).ready, out_sop(I), out_bsn(I), prev_out_bsn(I));
-- . Verify that the stimuli have been applied at all
proc_dp_verify_value(e_equal, clk, verify_done_arr(I), expected_out_data(I), prev_out_data(I));
proc_dp_verify_value(e_equal, clk, verify_done_arr(I), expected_out_bsn(I), prev_out_bsn(I));
END GENERATE;
------------------------------------------------------------------------------
-- DUT
------------------------------------------------------------------------------
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_bsn_latency_use_node_index => g_bsn_latency_use_node_index,
g_node_index_max => 31, -- limit to functional 5 bit range, instead of full 31 bit NATURAL range
g_block_size => g_block_size,
g_buffer_nof_blocks => c_buffer_nof_blocks,
g_bsn_w => g_bsn_w,
g_data_w => g_data_w,
g_filler_value => g_filler_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
)
PORT MAP (
dp_rst => rst,
dp_clk => clk,
-- Control
node_index => node_index,
stream_en_arr => stream_en_arr,
-- Streaming input
in_sosi_arr => in_sosi_arr,
-- 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
);
------------------------------------------------------------------------------
-- MM to streaming DP
------------------------------------------------------------------------------
no_use_mm_output : IF NOT g_use_mm_output GENERATE
out_sosi_arr <= dut_sosi_arr;
END GENERATE;
gen_use_mm_output : IF g_use_mm_output GENERATE
mm_copi <= mm_copi_arr(0);
mm_done <= mm_done_arr(0); -- for viewing only
gen_mm_to_dp : FOR I IN 0 TO g_nof_streams-1 GENERATE
u_mm_to_dp: ENTITY work.dp_block_from_mm
GENERIC MAP (
g_data_size => 1,
g_step_size => 1,
g_nof_data => g_block_size,
g_data_w => g_data_w,
g_mm_rd_latency => g_rd_latency,
g_reverse_word_order => FALSE
)
PORT MAP (
rst => rst,
clk => clk,
start_pulse => mm_sosi.sop,
start_address => 0,
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_siso => c_dp_siso_rdy
);
END GENERATE;
p_comb : PROCESS(r, mm_sosi, tb_sosi_arr)
VARIABLE v : t_reg;
BEGIN
v := r;
-- hold mm_sosi.sync, bsn
IF mm_sosi.sop = '1' THEN
v.sync := mm_sosi.sync;
v.bsn := mm_sosi.bsn(g_bsn_w-1 DOWNTO 0);
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 := 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
-- hold sosi.bsn until next sop, to easy view in wave window
FOR I IN 0 TO g_nof_streams-1 LOOP
v.out_sosi_arr(I).bsn := r.out_sosi_arr(I).bsn;
END LOOP;
END IF;
-- next state
nxt_r <= v;
END PROCESS;
p_reg : PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
r <= nxt_r;
END IF;
END PROCESS;
out_sosi_arr <= nxt_r.out_sosi_arr;
END GENERATE;
END tb;
-- --------------------------------------------------------------------------
-- Copyright 2021
-- 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: E. Kooistra, 6 sept 2021
-- 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.
-- Usage:
-- > as 5
-- > run -all
LIBRARY IEEE, common_lib, technology_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.tb_common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE common_lib.tb_common_mem_pkg.ALL;
USE common_lib.common_str_pkg.ALL;
USE work.dp_stream_pkg.ALL;
USE work.tb_dp_pkg.ALL;
ENTITY tb_mmp_dp_bsn_align_v2 IS
END tb_mmp_dp_bsn_align_v2;
ARCHITECTURE tb OF tb_mmp_dp_bsn_align_v2 IS
CONSTANT c_mm_clk_period : TIME := 40 ns;
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 : NATURAL := 2;
CONSTANT c_bsn_latency_use_node_index : BOOLEAN := FALSE;
CONSTANT c_buffer_nof_blocks : NATURAL := ceil_pow2(1 + c_bsn_latency_max);
CONSTANT c_bsn_w : NATURAL := c_dp_stream_bsn_w;
CONSTANT c_data_w : NATURAL := 16;
CONSTANT c_filler_value : INTEGER := 0;
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;
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL stimuli_end : STD_LOGIC := '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_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;
-- 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
BEGIN
dp_clk <= (NOT dp_clk) OR tb_end AFTER c_dp_clk_period/2;
mm_clk <= (NOT mm_clk) OR tb_end AFTER c_mm_clk_period/2;
dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
------------------------------------------------------------------------------
-- MM stimuli and verification
------------------------------------------------------------------------------
p_stimuli_and_verify_mm : PROCESS
VARIABLE v_bsn : NATURAL;
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);
---------------------------------------------------------------------------
-- End of test
---------------------------------------------------------------------------
proc_common_wait_until_high(dp_clk, stimuli_end);
tb_end <= '1';
WAIT;
END PROCESS;
------------------------------------------------------------------------------
-- 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,
-- End of stimuli
tb_end => stimuli_end
);
in_sosi_arr <= (OTHERS => stimuli_sosi);
------------------------------------------------------------------------------
-- DUT
------------------------------------------------------------------------------
u_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,
g_bsn_latency_use_node_index => c_bsn_latency_use_node_index,
g_block_size => c_block_size,
g_buffer_nof_blocks => c_buffer_nof_blocks,
g_bsn_w => c_bsn_w,
g_data_w => c_data_w,
g_filler_value => c_filler_value,
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
)
PORT MAP (
mm_rst => mm_rst,
mm_clk => mm_clk,
reg_copi => reg_copi,
reg_cipo => reg_cipo,
reg_input_monitor_copi => reg_input_monitor_copi,
reg_input_monitor_cipo => reg_input_monitor_cipo,
reg_output_monitor_copi => reg_output_monitor_copi,
reg_output_monitor_cipo => reg_output_monitor_cipo,
dp_rst => dp_rst,
dp_clk => dp_clk,
node_index => node_index,
-- 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
);
END tb;
-- --------------------------------------------------------------------------
-- Copyright 2021
-- 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: E. Kooistra, 15 sept 2021
-- Purpose: Regression multi tb for dp_bsn_align_v2
-- Description:
-- Usage:
-- > as 3
-- > run -all
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE work.tb_dp_pkg.ALL;
ENTITY tb_tb_dp_bsn_align_v2 IS
END tb_tb_dp_bsn_align_v2;
ARCHITECTURE tb OF tb_tb_dp_bsn_align_v2 IS
CONSTANT c_block_size : NATURAL := 11;
CONSTANT c_diff_delay : NATURAL := 20;
CONSTANT c_diff_bsn : NATURAL := 3; -- g_diff_bsn = g_bsn_latency can just be aligned
CONSTANT c_bsn_latency_max : NATURAL := 1;
CONSTANT c_nof_repeat : NATURAL := 100; -- for constant active stream control using 1 is sufficient, use > 1 to verify longer with random stimuli
SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
BEGIN
-- -- DUT
-- g_nof_streams : NATURAL := 2; -- number of input and output streams
-- g_bsn_latency_max : NATURAL := 1; -- Maximum travel latency of a remote block in number of block periods T_blk
-- g_bsn_latency_use_node_index : BOOLEAN := FALSE; -- FALSE for align at end node, TRUE for align at every intermediate node
-- g_block_size : NATURAL := 11; -- > 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 := 16; -- number of bits in sosi data
-- g_filler_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
-- g_rd_latency : NATURAL := 2; -- 1 or 2, choose 2 to ease timing closure
--
-- -- TB
-- g_diff_delay : NATURAL := 0;
-- g_diff_bsn : NATURAL := 0; -- g_diff_bsn = g_bsn_latency_max can just be aligned
-- g_nof_repeat : NATURAL := 100 -- for constant active stream control using 1 is sufficient, use > 1 to verify longer with random stimuli
u_mm_output : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, FALSE, 11, 32, 16, 0, TRUE, 0, 1, 0, 9, c_nof_repeat);
u_dp_output : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, FALSE, 11, 32, 16, 0, FALSE, 0, 1, 0, 9, c_nof_repeat);
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment