From 86d8befc494938a5dde33d3c9cfb3a05ad311e56 Mon Sep 17 00:00:00 2001 From: Eric Kooistra <kooistra@astron.nl> Date: Thu, 5 Aug 2021 11:30:30 +0200 Subject: [PATCH] Added verify_mon_output_sync_bsn and verifying_sync_equal in tb. --- .../dp/src/vhdl/dp_bsn_sync_scheduler.vhd | 18 +- .../dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd | 201 +++++++++++------- 2 files changed, 139 insertions(+), 80 deletions(-) diff --git a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd index a3bb5f9c11..f9081f32ad 100644 --- a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd +++ b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd @@ -81,7 +81,17 @@ -- Remark: -- * The implementation avoids using division and modulo on signals (e.g. -- ctrl_interval_size / g_block_size) by using counters and fractions. - +-- * The implementation uses the input BSN to calculate when to output the +-- scheduled output syncs. It has to be robust against lost input blocks, +-- therfore it cannot count input valids to determine the scheduled output +-- syncs. +-- * This dp_bsn_sync_scheduler.vhd resembles dp_bsn_source_v2. The +-- similarities are that both: +-- . create the fractional output sync intervals, +-- . start at an input trigger. +-- The differences are that: +-- . dp_bsn_sync_scheduler requires in_sosi.sync and copies the other in_sosi +-- ctrl, info and data, whereas generates bs_sosi.ctrl. LIBRARY IEEE, common_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -89,7 +99,7 @@ USE IEEE.NUMERIC_STD.ALL; USE common_lib.common_pkg.ALL; USE work.dp_stream_pkg.ALL; -ENTITY dp_bsn_sync_interval IS +ENTITY dp_bsn_sync_scheduler IS GENERIC ( g_bsn_w : NATURAL := c_dp_stream_bsn_w; g_block_size : NATURAL := 256; -- = number of data valid per BSN block @@ -115,10 +125,10 @@ ENTITY dp_bsn_sync_interval IS out_start : OUT STD_LOGIC; out_enable : OUT STD_LOGIC ); -END dp_bsn_sync_interval; +END dp_bsn_sync_scheduler; -ARCHITECTURE rtl OF dp_bsn_sync_interval IS +ARCHITECTURE rtl OF dp_bsn_sync_scheduler IS TYPE t_reg IS RECORD enable_init : STD_LOGIC; diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd index 0e976dc16e..d8d240da3a 100644 --- a/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd +++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd @@ -20,17 +20,18 @@ ------------------------------------------------------------------------------- -- Author: Eric Kooistra --- Purpose: Test bench for tb_dp_bsn_sync_interval.vhd +-- Purpose: Test bench for dp_bsn_sync_scheduler.vhd -- Description: -- The tb automatically TODO verifies the following cases: -- * when enabled the out_sosi = in_sosi, except for the output sync -- interval --- * disabling the output and re-enabling the output using c_nof_intervals +-- * disabling the output and re-enabling the output using +-- c_nof_test_intervals -- * c_ctrl_start_bsn must be in the future, else output remains disabled --- * gaps between input blocks using c_input_gap_size > 0 --- * sync intervals that have fractional number of blocks using --- c_nof_samples_per_output_sync --- * output sync interval in case of lost input blocks +-- * gaps between input blocks using g_input_gap_size > 0 +-- * output sync intervals that have fractional number of blocks using +-- ctrl_interval_size = g_nof_samples_per_output_sync +-- * output sync interval recovery in case of lost input blocks -- Usage: -- > as 8 -- > run -all @@ -38,67 +39,80 @@ -- out_sync, out_start -- -- Development steps: --- 1a Draft design description of dp_bsn_sync_interval +-- . Step 1 ~1 day work (idea started earlier, so requirements were clear), +-- steps 2,5 ~1.5 days work (fixing details and verifying all features), +-- steps 3,4 ~0.5 day work, +-- total ~3 days work spent in ~ 1 week. +-- 1a Draft design description of dp_bsn_sync_scheduler -- b Initial implementation that compiles -- 2a Initial tb using u_stimuli to generate in_sosi and using p_stimuli --- to test basic functionality, i.e. to get out_sosi when ctrl_enable is on --- and no output when off. --- b Verify that c_input_gap_size > 0 also works --- c Add verification of out_sosi = in sosi when enabled (proc_dp_verify_sosi_equal) +-- to test basic functionality, i.e. to get out_sosi when ctrl_enable is +-- on and no output when off. +-- b Verify that g_input_gap_size > 0 also works +-- c Add verification of out_sosi = in sosi when enabled +-- (proc_dp_verify_sosi_equal) -- d Add verification of out_sosi (p_verify_out_enable) -- e Add verification of out_start and first out_sync (p_verify_out_start) -- f Add verification of out_sosi.sync using proc_dp_verify_sync() for -- fractional sync intervals -- g Add verification of mon_output_sync_bsn (p_verify_mon_output_sync_bsn) --- 3a For all ASSERTs, verify that the ERROR or FAILURE can occur by e.g. +-- 3a Add tb generics and tb_tb_dp_bsn_sync_scheduler.vhd +-- 4a Add mmc_dp_bsn_sync_scheduler.vhd with MM register and +-- tb_mmc_dp_bsn_sync_scheduler.vhd that verifies only the MM part, +-- because the sync part is already verified by +-- tb_tb_dp_bsn_sync_scheduler.vhd. +-- 5a For all ASSERTs, verify that the ERROR or FAILURE can occur by e.g. -- temporarily changing the condition -- b Initialy used LOOP in p_stimuli to repeat test. Later used list of --- intervals and tb_state to try different stimuli. +-- c_nof_test_intervals and tb_state to try different stimuli. 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_str_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_sync_interval IS -END tb_dp_bsn_sync_interval; +ENTITY tb_dp_bsn_sync_scheduler IS + GENERIC ( + -- Input sync period and sosi ctrl + g_nof_input_sync : NATURAL := 10; + g_nof_block_per_input_sync : NATURAL := 17; + g_block_size : NATURAL := 10; + g_input_gap_size : NATURAL := 3; -ARCHITECTURE tb OF tb_dp_bsn_sync_interval IS + -- Output sync period + g_nof_samples_per_output_sync : NATURAL := 45 -- 45 / g_block_size = 4.5 + ); +END tb_dp_bsn_sync_scheduler; - CONSTANT c_clk_period : TIME := 10 ns; - CONSTANT c_bsn_w : NATURAL := 31; - CONSTANT c_dut_latency : NATURAL := 1; +ARCHITECTURE tb OF tb_dp_bsn_sync_scheduler IS - -- Input sync period - CONSTANT c_block_size : NATURAL := 10; - CONSTANT c_nof_block_per_input_sync : NATURAL := 17; + CONSTANT c_clk_period : TIME := 10 ns; + CONSTANT c_bsn_w : NATURAL := 31; + CONSTANT c_dut_latency : NATURAL := 1; -- Test intervals - CONSTANT c_nof_intervals : NATURAL := 4; -- should be large enough for p_stimuli - CONSTANT c_nof_input_sync : NATURAL := 10; - CONSTANT c_nof_block_per_interval : NATURAL := c_nof_block_per_input_sync * c_nof_input_sync; - CONSTANT c_input_gap_size : NATURAL := 3; - CONSTANT c_nof_clk_per_block : NATURAL := c_block_size + c_input_gap_size; - CONSTANT c_nof_clk_per_interval : NATURAL := c_nof_clk_per_block * c_nof_block_per_interval; - CONSTANT c_start_of_interval : NATURAL := 0 + c_nof_clk_per_block * 4; -- just after start - CONSTANT c_early_in_interval : NATURAL := c_nof_clk_per_interval / 3; - CONSTANT c_mid_of_interval : NATURAL := c_nof_clk_per_interval / 2; - CONSTANT c_end_of_interval : NATURAL := c_nof_clk_per_interval - c_nof_clk_per_block * 3; -- just before end - - CONSTANT c_sim_nof_blocks : NATURAL := c_nof_block_per_interval * c_nof_intervals; - CONSTANT c_nof_lost_input : NATURAL := c_nof_block_per_input_sync * 4 + 3; - - -- Output sync period - --CONSTANT c_nof_samples_per_output_sync : NATURAL := c_block_size * 4; - CONSTANT c_nof_samples_per_output_sync : NATURAL := c_block_size * 9 / 2; + CONSTANT c_nof_test_intervals : NATURAL := 4; -- nof should be large enough for p_stimuli + CONSTANT c_nof_block_per_test_interval : NATURAL := g_nof_block_per_input_sync * g_nof_input_sync; + CONSTANT c_nof_lost_input_blocks : NATURAL := g_nof_block_per_input_sync * 4 + 3; + CONSTANT c_nof_clk_per_block : NATURAL := g_block_size + g_input_gap_size; + CONSTANT c_nof_clk_per_test_interval : NATURAL := c_nof_clk_per_block * c_nof_block_per_test_interval; + CONSTANT c_begin_of_test_interval : NATURAL := 0 + c_nof_clk_per_block * 4; -- just after start + CONSTANT c_early_in_test_interval : NATURAL := c_nof_clk_per_test_interval / 3; -- just after begin and before mid + CONSTANT c_mid_of_test_interval : NATURAL := c_nof_clk_per_test_interval / 2; -- at mid + CONSTANT c_end_of_test_interval : NATURAL := c_nof_clk_per_test_interval - c_nof_clk_per_block * 3; -- just before end + + CONSTANT c_sim_nof_blocks : NATURAL := c_nof_block_per_test_interval * c_nof_test_intervals; + + CONSTANT c_output_nof_blocks_min : NATURAL := g_nof_samples_per_output_sync / g_block_size; SIGNAL clk : STD_LOGIC := '1'; SIGNAL rst : STD_LOGIC := '1'; SIGNAL cnt : INTEGER := 0; - SIGNAL interval : INTEGER := 0; + SIGNAL test_interval : INTEGER := 0; SIGNAL tb_end : STD_LOGIC := '0'; TYPE t_stimuli_state_enum IS ( -- use short names to ease unzoomed view in Wave window @@ -113,12 +127,13 @@ ARCHITECTURE tb OF tb_dp_bsn_sync_interval IS -- Stimuli SIGNAL ctrl_enable : STD_LOGIC := '0'; SIGNAL ctrl_enable_evt : STD_LOGIC := '0'; - SIGNAL ctrl_interval_size : NATURAL := c_nof_samples_per_output_sync; + SIGNAL ctrl_interval_size : NATURAL := g_nof_samples_per_output_sync; SIGNAL ctrl_start_bsn : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); SIGNAL mon_input_current_bsn : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); SIGNAL mon_input_bsn_at_sync : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); SIGNAL mon_output_enable : STD_LOGIC; SIGNAL mon_output_sync_bsn : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); + SIGNAL out_sop_dly : STD_LOGIC; SIGNAL stimuli_sosi : t_dp_sosi := c_dp_sosi_init; SIGNAL stimuli_sync : STD_LOGIC; -- declared next to stimuli_sosi and out_sync for easier comparison in Wave window @@ -137,8 +152,10 @@ ARCHITECTURE tb OF tb_dp_bsn_sync_interval IS -- Verify SIGNAL in_sosi_integer : t_dp_sosi_integer; SIGNAL out_sosi_integer : t_dp_sosi_integer; - SIGNAL verify_sosi_equal : STD_LOGIC := '0'; - SIGNAL verify_sync : STD_LOGIC := '1'; + + SIGNAL verify_sosi_equal : STD_LOGIC := '0'; + SIGNAL verify_sync : STD_LOGIC := '1'; + SIGNAL verify_mon_output_sync_bsn : STD_LOGIC := '1'; SIGNAL prev_out_enable : STD_LOGIC := '0'; SIGNAL out_eop_dly : STD_LOGIC := '0'; @@ -147,12 +164,14 @@ ARCHITECTURE tb OF tb_dp_bsn_sync_interval IS SIGNAL expected_out_enable : STD_LOGIC := '0'; SIGNAL expecting_out_start : STD_LOGIC := '0'; + SIGNAL verifying_sync_equal : STD_LOGIC := '0'; + SIGNAL dbg_out_sosi_sync : STD_LOGIC; SIGNAL dbg_out_sosi_sop : STD_LOGIC; SIGNAL dbg_out_sosi_bsn : NATURAL; - SIGNAL dbg_expected_bsn : NATURAL := 0; SIGNAL dbg_nof_blk : NATURAL := 0; - SIGNAL dbg_extra : NATURAL := 0; + SIGNAL dbg_accumulate : NATURAL := 0; + SIGNAL dbg_expected_bsn : NATURAL := 0; -- Local procedures PROCEDURE proc_output_enable(SIGNAL clk : IN STD_LOGIC; @@ -164,7 +183,7 @@ ARCHITECTURE tb OF tb_dp_bsn_sync_interval IS SIGNAL ctrl_enable_evt : OUT STD_LOGIC) IS BEGIN stimuli_state <= e_en; - ctrl_start_bsn <= ADD_UVEC(mon_input_bsn_at_sync, TO_UVEC(c_nof_block_per_input_sync, c_natural_w)); -- determine BSN in the future + ctrl_start_bsn <= ADD_UVEC(mon_input_bsn_at_sync, TO_UVEC(g_nof_block_per_input_sync, c_natural_w)); -- determine BSN in the future ctrl_enable <= '1'; ctrl_enable_evt <= '1'; proc_common_wait_some_cycles(clk, 1); @@ -211,56 +230,62 @@ BEGIN ------------------------------------------------------------------------------ -- Enable and disable output ------------------------------------------------------------------------------ - interval <= 0; proc_common_wait_some_cycles(clk, 1); + test_interval <= 0; proc_common_wait_some_cycles(clk, 1); - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_start_of_interval, clk, cnt); - -- Start of interval: Enable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_begin_of_test_interval, clk, cnt); + -- Start of test_interval: Enable output proc_output_enable(clk, cnt, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); - -- End of interval: Disable output - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_end_of_interval, clk, cnt); + -- End of test_interval: Disable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_end_of_test_interval, clk, cnt); proc_output_disable(stimuli_state, ctrl_enable); ------------------------------------------------------------------------------ -- Re enable output when already enabled ------------------------------------------------------------------------------ - interval <= interval + 1; proc_common_wait_some_cycles(clk, 1); + test_interval <= test_interval + 1; proc_common_wait_some_cycles(clk, 1); - -- Start of interval: Enable output - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_start_of_interval, clk, cnt); + -- Start of test_interval: Enable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_begin_of_test_interval, clk, cnt); proc_output_enable(clk, cnt, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); - -- Mid of interval: Re-enable output - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_mid_of_interval, clk, cnt); + -- Mid of test_interval: Re-enable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_mid_of_test_interval, clk, cnt); proc_output_re_enable(clk, cnt, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); - -- End of interval: Disable output - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_end_of_interval, clk, cnt); + -- End of test_interval: Disable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_end_of_test_interval, clk, cnt); proc_output_disable(stimuli_state, ctrl_enable); ------------------------------------------------------------------------------ -- Lost input blocks ------------------------------------------------------------------------------ - interval <= interval + 1; proc_common_wait_some_cycles(clk, 1); + test_interval <= test_interval + 1; proc_common_wait_some_cycles(clk, 1); - -- Start of interval: Enable output - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_start_of_interval, clk, cnt); + -- Start of test_interval: Enable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_begin_of_test_interval, clk, cnt); proc_output_enable(clk, cnt, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); - -- Early in interval: Disable input to simulate lost blocks - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_early_in_interval, clk, cnt); + -- Early in test_interval: Disable input to simulate lost blocks + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_early_in_test_interval, clk, cnt); stimuli_state <= e_lost; proc_common_wait_until_high(clk, stimuli_sosi.eop); in_lost <= '1'; -- high after eop, so high at next sop - FOR I IN 0 TO c_nof_lost_input-1 LOOP + FOR I IN 0 TO c_nof_lost_input_blocks-1 LOOP proc_common_wait_some_cycles(clk, 1); proc_common_wait_until_high(clk, stimuli_sosi.eop); END LOOP; in_lost <= '0'; -- low after eop, so low at next sop stimuli_state <= e_en; + -- Wait for some cycles that DUT needs to catch up after lost input (see nxt_r.update_bsn in DUT) + verify_mon_output_sync_bsn <= '0'; + FOR I IN 0 TO c_nof_lost_input_blocks / c_output_nof_blocks_min + 2 LOOP -- + for some extra margin + proc_common_wait_some_cycles(clk, 1); + END LOOP; + verify_mon_output_sync_bsn <= '1'; - -- End of interval: Disable output - proc_common_wait_until_value(interval * c_nof_clk_per_interval + c_end_of_interval, clk, cnt); + -- End of test_interval: Disable output + proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_end_of_test_interval, clk, cnt); proc_output_disable(stimuli_state, ctrl_enable); WAIT; @@ -269,14 +294,14 @@ BEGIN -- Generate data blocks with input sync u_stimuli : ENTITY work.dp_stream_stimuli GENERIC MAP ( - g_sync_period => c_nof_block_per_input_sync, + g_sync_period => g_nof_block_per_input_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 + g_pkt_len => g_block_size, + g_pkt_gap => g_input_gap_size ) PORT MAP ( rst => rst, @@ -429,18 +454,27 @@ BEGIN proc_dp_verify_sosi_equal("valid", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer); proc_dp_verify_sosi_equal( "data", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer); + ----------------------------------------------------------------------------- + -- . Verify out_sosi.sync = in_sosi.sync, when sync interval is not changed + ----------------------------------------------------------------------------- + gen_verify_sync_equal : IF g_nof_samples_per_output_sync = g_nof_block_per_input_sync * g_block_size GENERATE + verifying_sync_equal <= '1'; -- to show in Wave window that this check is active + proc_dp_verify_sosi_equal( "sync", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer); + END GENERATE; + ----------------------------------------------------------------------------- -- . Verify out_sosi.sync interval ----------------------------------------------------------------------------- proc_dp_verify_sync(TO_UINT(ctrl_start_bsn), ctrl_interval_size, - c_block_size, clk, + g_block_size, + clk, out_enable, out_sosi.sync, out_sosi.sop, out_sosi.bsn, dbg_nof_blk, - dbg_extra, + dbg_accumulate, dbg_expected_bsn); dbg_out_sosi_sync <= out_sosi.sync; @@ -451,24 +485,39 @@ BEGIN -- . Verify mon_output_sync_bsn ----------------------------------------------------------------------------- + out_sop_dly <= out_sosi.sop WHEN rising_edge(clk); + p_verify_mon_output_sync_bsn : PROCESS(clk) + VARIABLE v_bsn_min : NATURAL; + VARIABLE v_bsn_max : NATURAL; BEGIN IF rising_edge(clk) THEN - IF out_sosi.sync = '1' THEN - --ASSERT UNSIGNED(mon_output_sync_bsn) = UNSIGNED(out_sosi.bsn) REPORT "Wrong mon_output_sync_bsn" SEVERITY ERROR; + IF verify_mon_output_sync_bsn = '1' THEN + IF out_sop_dly = '1' THEN + v_bsn_min := TO_UINT(mon_input_current_bsn); + v_bsn_max := TO_UINT(mon_input_current_bsn) + c_output_nof_blocks_min + 1; + + ASSERT TO_UINT(mon_output_sync_bsn) >= v_bsn_min + REPORT "Wrong: mon_output_sync_bsn is behind" SEVERITY ERROR; + ASSERT TO_UINT(mon_output_sync_bsn) <= v_bsn_max + REPORT "Wrong: mon_output_sync_bsn is too far ahead (" & int_to_str(TO_UINT(mon_output_sync_bsn)) & " > " & int_to_str(v_bsn_max) & ")" SEVERITY ERROR; + + --Debug report used to investigate v_bsn_min and v_bsn_max assert conditions + --REPORT int_to_str(TO_UINT(mon_output_sync_bsn)) & " : " & int_to_str(TO_UINT(mon_input_current_bsn)) SEVERITY NOTE; + END IF; END IF; END IF; END PROCESS; ----------------------------------------------------------------------------- - -- DUT: dp_bsn_sync_interval + -- DUT: dp_bsn_sync_scheduler ----------------------------------------------------------------------------- - dut : ENTITY work.dp_bsn_sync_interval + dut : ENTITY work.dp_bsn_sync_scheduler GENERIC MAP ( g_bsn_w => c_bsn_w, - g_block_size => c_block_size, + g_block_size => g_block_size, g_pipeline => 0 ) PORT MAP ( -- GitLab