diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd index 7f15f845fc365470ac223203310aa98097df13eb..669522d47e037d52d3ec6f18bfedf652f93fc080 100644 --- a/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd +++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd @@ -19,22 +19,28 @@ -- ------------------------------------------------------------------------------- --- Author: P. Donker --- Verify if eop and sop come in pairs and if sync is at sop and at expected_sync puls. --- The tb is using a SSN (second sample number) and BSN (block sample number) generator as reference --- for the test, it uses g_pps_interval and g_block_size for generator timing settings. --- Start/Stop BSN source tests: --- 1) test 1x asynchronously (dp_on_pps='0') without automatic check, check visualy in wave window. --- 2) test 3x synchronously (dp_on_pps='1') with automatic check. +-- Author: P. Donker, E. Kooistra +-- Purpose: Tb to verify that the BS source can be started in any PPS interval +-- to create the BSN grid and sync interval as defined in Fig. 3.1 in +-- [1]. +-- Decsription: +-- * Start/Stop BSN source tests: +-- 1) test 1x asynchronously (dp_on_pps='0') without automatic check, check +-- visualy in wave window. +-- 2) test 3x synchronously (dp_on_pps='1') with automatic check. +-- . Verify if bs_sosi.eop and bs_sosi.sop come in pairs +-- . Verify that bs_sosi.sync is at bs_sosi.sop +-- . Verify that bs_sosi has fixed latency with respect to ref_grid +-- +-- References: +-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station +-- [2] https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset -- --- [doc] = https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset - -- Usage: -- > as 10 -- > run -all -- . sop, eop are verified automatically --- . sync and bsn are verified automatically --- and then manually verify on/off in Wave window +-- . sync and bsn are verified automatically using the ref_grid LIBRARY IEEE, common_lib, dp_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -46,193 +52,196 @@ USE dp_lib.tb_dp_pkg.ALL; ENTITY tb_dp_bsn_source_v2 IS GENERIC ( - g_nof_pps : NATURAL := 20; - g_pps_interval : NATURAL := 230; - g_block_size : NATURAL := 32 + g_nof_pps : NATURAL := 10; + g_pps_interval : NATURAL := 10; --101; + g_block_size : NATURAL := 7 --23 ); END tb_dp_bsn_source_v2; ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS - CONSTANT c_clk_period : TIME := 10 ns; - CONSTANT c_bsn_w : NATURAL := 31; - CONSTANT c_dut_latency : NATURAL := 2; + CONSTANT c_nof_repeat : NATURAL := 3; + + CONSTANT c_clk_period : TIME := 10 ns; + CONSTANT c_bsn_w : NATURAL := 31; + CONSTANT c_bsn_time_offset_w : NATURAL := ceil_log2(g_block_size); + + -- Minimum latency between sync and PPS, due to logic in DUT + CONSTANT c_dut_latency : NATURAL := 3; -- The state name tells what kind of test is being done TYPE t_state_enum IS ( s_disable, - s_start, - s_pps_start + s_dp_on, + s_dp_on_pps ); - SIGNAL tb_state : t_state_enum; + -- Define the PPS (SSN) and BSN grid that both start at 0 according to Figure 3.1 in [1]: + TYPE t_time_grid IS RECORD + pps : STD_LOGIC; -- pulse per second, g_pps_interval clk per pps interval + ssn : NATURAL; -- seconds sequence number + bsn : NATURAL; -- block sequence number, g_block_size clk per block + sync : STD_LOGIC; -- active at sop when pps is active or was active + sop : STD_LOGIC; -- start of block + eop : STD_LOGIC; -- end of block + END RECORD; + CONSTANT c_time_grid_rst : t_time_grid := ('0', 0, 0, '0', '0', '0'); + + -- Reference grid + SIGNAL ref_grid : t_time_grid := c_time_grid_rst; + SIGNAL ssn_eop : STD_LOGIC := '0'; + SIGNAL hold_pps : STD_LOGIC := '0'; + SIGNAL nxt_hold_pps : STD_LOGIC := '0'; + + -- Tb SIGNAL tb_end : STD_LOGIC := '0'; SIGNAL rst : STD_LOGIC := '1'; SIGNAL clk : STD_LOGIC := '1'; + SIGNAL tb_state : t_state_enum := s_disable; -- DUT - SIGNAL dp_on : STD_LOGIC := '0'; - SIGNAL dp_on_pps : STD_LOGIC := '0'; - SIGNAL bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); - - SIGNAL bs_sosi : t_dp_sosi; + SIGNAL dp_on : STD_LOGIC := '0'; + SIGNAL dp_on_pps : STD_LOGIC := '0'; + SIGNAL dp_on_status : STD_LOGIC; + SIGNAL bs_restart : STD_LOGIC; + SIGNAL bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL bs_sosi : t_dp_sosi; -- Verify - SIGNAL verify_sync : STD_LOGIC := '0'; - SIGNAL hold_bs_sop : STD_LOGIC; - - SIGNAL tb_bsn_cnt : INTEGER := 0; - - -- Define the PPS grid and the BSN grid that both start at 0 according to Figure 3.1 in [doc]: - SIGNAL SSN : NATURAL := 0; - SIGNAL BSN : NATURAL := 0; - - SIGNAL pps_sop : STD_LOGIC := '0'; - SIGNAL nxt_pps_sop : STD_LOGIC := '0'; - SIGNAL pps_eop : STD_LOGIC := '0'; - SIGNAL bsn_sop : STD_LOGIC := '0'; - SIGNAL nxt_bsn_sop : STD_LOGIC := '0'; - SIGNAL bsn_eop : STD_LOGIC := '0'; - SIGNAL expected_sync : STD_LOGIC := '0'; - SIGNAL expected_sync_dly : STD_LOGIC := '0'; - SIGNAL expected_bsn : NATURAL := 0; - SIGNAL expected_offset_bsn : NATURAL := 0; - SIGNAL dbg_nof_blk : NATURAL := 0; - SIGNAL dbg_accumulate : NATURAL := 0; - SIGNAL dbg_expected_bsn : NATURAL := 0; + SIGNAL exp_grid : t_time_grid; + SIGNAL unexpected_bs_sync : STD_LOGIC; + SIGNAL sl0 : STD_LOGIC := '0'; + SIGNAL verify_en : STD_LOGIC := '0'; + SIGNAL verify_sync : STD_LOGIC := '0'; + SIGNAL hold_bs_sop : STD_LOGIC; + SIGNAL prev_bs_valid : STD_LOGIC; + SIGNAL bs_starts_cnt : NATURAL := 0; + + SIGNAL dbg_nof_blk : NATURAL; + SIGNAL dbg_accumulate : NATURAL; + SIGNAL dbg_expected_bsn : NATURAL; BEGIN - ----------------------------------------------------------------------------- - -- Stimuli - ----------------------------------------------------------------------------- rst <= '1', '0' AFTER c_clk_period*7; clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; - - -- SSN/BSN generator - SSN <= SSN + 1 WHEN rising_edge(clk) AND pps_eop='1'; -- Seconds Sequence Number - BSN <= BSN + 1 WHEN rising_edge(clk) AND bsn_eop='1'; -- Block Sequence Number - - proc_common_gen_pulse(1, g_pps_interval, '1', rst, clk, nxt_pps_sop); -- make PPS grid with pps_sop at start of interval - pps_sop <= nxt_pps_sop AFTER c_clk_period; - pps_eop <= pps_sop'DELAYED((g_pps_interval-1)*c_clk_period); -- make PPS grid with pps_eop at end of intervals - - proc_common_gen_pulse(1, g_block_size, '1', rst, clk, nxt_bsn_sop); -- make BSN grid with bsn_sop at start of interval - bsn_sop <= nxt_bsn_sop AFTER c_clk_period; - bsn_eop <= bsn_sop'DELAYED((g_block_size-1)*c_clk_period); -- make BSN grid with bsn_eop at end of interval - - -- Define the expected sync that occurs when pps_sop = bsn sop, or else at the first bsn_sop after the pps_sop, see Figure 3.1 in [doc]. - p_expected_sync : PROCESS + ----------------------------------------------------------------------------- + -- Generate reference time grid + ----------------------------------------------------------------------------- + proc_common_gen_pulse(1, g_pps_interval, '1', sl0, clk, ref_grid.pps); + proc_common_gen_pulse(1, g_block_size, '1', sl0, clk, ref_grid.sop); + ref_grid.eop <= ref_grid.sop'DELAYED((g_block_size - 1) * c_clk_period); + ssn_eop <= ref_grid.pps'DELAYED((g_pps_interval - 1) * c_clk_period); + ref_grid.ssn <= ref_grid.ssn + 1 WHEN rising_edge(clk) AND ssn_eop = '1'; + ref_grid.bsn <= ref_grid.bsn + 1 WHEN rising_edge(clk) AND ref_grid.eop = '1'; + + -- Issue sync at start of block + p_ref_grid_sync : PROCESS(ref_grid, hold_pps) BEGIN - WAIT UNTIL rising_edge(clk); - expected_sync <= '0'; - proc_common_wait_until_high(clk, pps_sop); - IF bsn_sop = '1' THEN - expected_sync <= '1'; - expected_bsn <= BSN+1; - expected_offset_bsn <= 0; - ELSE - proc_common_wait_until_high(clk, bsn_sop); - expected_sync <= '1'; - expected_bsn <= BSN+1; - expected_offset_bsn <= (BSN+1) * g_block_size - SSN * g_pps_interval; + ref_grid.sync <= '0'; + nxt_hold_pps <= hold_pps; + + IF ref_grid.pps = '1' THEN + IF ref_grid.sop = '1' THEN + ref_grid.sync <= '1'; -- immediately issue sync + ELSE + nxt_hold_pps <= '1'; -- wait until next block + END IF; + END IF; + + IF hold_pps = '1' THEN + IF ref_grid.sop = '1' THEN + ref_grid.sync <= '1'; -- issue pending sync + nxt_hold_pps <= '0'; + END IF; END IF; END PROCESS; - - expected_sync_dly <= expected_sync'DELAYED(c_dut_latency*c_clk_period); - -- MM control + hold_pps <= nxt_hold_pps WHEN rising_edge(clk); + + exp_grid <= ref_grid'DELAYED(c_dut_latency * c_clk_period); + + ----------------------------------------------------------------------------- + -- Stimuli + ----------------------------------------------------------------------------- p_mm : PROCESS - VARIABLE v_bsn_time_offset : NATURAL; + VARIABLE v_ssn : NATURAL; VARIABLE v_bsn_init : NATURAL; + VARIABLE v_bsn_time_offset : NATURAL; BEGIN - tb_end <= '0'; - tb_state <= s_disable; - --pps <= '0'; - - dp_on <= '0'; - dp_on_pps <= '0'; - -- Get synchronous to clk proc_common_wait_until_low(clk, rst); - proc_common_wait_some_cycles(clk, 500); + proc_common_wait_some_cycles(clk, 10); -- Start asynchronously by making dp_on high - proc_common_wait_until_high(clk, expected_sync_dly); - tb_state <= s_pps_start; + verify_en <= '0'; -- only verify visualy in wave window + tb_state <= s_dp_on; dp_on_pps <= '0'; - dp_on <= '1'; - verify_sync <= '0'; -- only verify visualy in wave window + dp_on <= '1'; proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval); - verify_sync <= '0'; - -- Stop by making dp_on low tb_state <= s_disable; dp_on <= '0'; dp_on_pps <= '0'; - - -- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop) - proc_common_wait_until_high(clk, pps_sop); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - WHILE v_bsn_time_offset > 0 LOOP - proc_common_wait_some_cycles(clk, g_pps_interval); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - END LOOP; + proc_common_wait_some_cycles(clk, 10); - -- Start synchronously by making dp_on high at pps - FOR i IN 0 TO 2 LOOP - -- Now start on PPS - proc_common_wait_until_high(clk, expected_sync); - v_bsn_time_offset := ((SSN + 1) * g_pps_interval) MOD g_block_size; - v_bsn_init := ((SSN + 1) * g_pps_interval) / g_block_size; - IF v_bsn_time_offset = 0 THEN - bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); - ELSE - bsn_init <= TO_UVEC(v_bsn_init+1, c_bsn_w); - END IF; - tb_state <= s_pps_start; + -- Start synchronously by making dp_on and dp_on_pps high + verify_en <= '1'; -- verify automatically in test bench + + FOR I IN 0 TO c_nof_repeat-1 LOOP + -- Wait some variable time between tests, to enforce testing different + -- bsn_time_offset values + proc_common_wait_some_cycles(clk, 20); + proc_common_wait_some_cycles(clk, I*g_pps_interval); + + -- Wait until in the beginning of PPS interval + proc_common_wait_until_hi_lo(clk, ref_grid.pps); + proc_common_wait_some_cycles(clk, c_dut_latency); + + -- Determine bsn_init and bsn_time_offset for BSN source start + -- . bsn_init = BSN at sync + -- . bsn_time_offset = number of clk that sync occurs after PPS + v_ssn := ref_grid.ssn + 1; -- +1 to prepare start in next PPS interval + v_bsn_init := ceil_div(v_SSN * g_pps_interval, g_block_size); -- Equation 3.6 in [1] + v_bsn_time_offset := v_bsn_init * g_block_size - v_SSN * g_pps_interval; -- Equation 3.7 in [1] + bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); -- + bsn_time_offset <= TO_UVEC(v_bsn_time_offset, c_bsn_time_offset_w); + -- Start synchronously by making dp_on and dp_on_pps high + tb_state <= s_dp_on_pps; dp_on_pps <= '1'; - dp_on <= '1'; - verify_sync <= '1'; -- verify automatically in test bench + dp_on <= '1'; proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval); - verify_sync <= '0'; - -- Stop by making dp_on low tb_state <= s_disable; dp_on <= '0'; dp_on_pps <= '0'; - - -- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop) - proc_common_wait_until_high(clk, pps_sop); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - WHILE v_bsn_time_offset > 0 LOOP - proc_common_wait_some_cycles(clk, g_pps_interval); - v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; - END LOOP; - END LOOP; - tb_end <= '1'; + proc_common_wait_some_cycles(clk, 10); + ASSERT bs_starts_cnt = 1 + c_nof_repeat REPORT "Wrong number of BSN source starts." SEVERITY ERROR; + + tb_end <= '1'; WAIT; END PROCESS; ----------------------------------------------------------------------------- -- Verification + -- . Some aspects of bs_sosi are verified multiple times in different ways, + -- this overlap is fine, because the tb and DUT are rather complicated, so + -- using different approaches also helpt to verify the tb itself. ----------------------------------------------------------------------------- + verify_sync <= verify_en AND bs_sosi.valid; + proc_dp_verify_sop_and_eop(clk, bs_sosi.valid, bs_sosi.sop, bs_sosi.eop, hold_bs_sop); -- Verify that sop and eop come in pairs - proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, bs_sosi.sop, expected_sync_dly); -- Verify sync at sop and at expected_sync - - -- Verify sync at sop and at expected_sync again: - -- . now using the proc_dp_verify_sync() variant for dp_bsn_source_v2 that - -- can verify fractional sync periods. - -- . the proc_dp_verify_sync() v2 variant was made later, so in fact - -- this tb_dp_bsn_source_v2 verifies this new v2 procedure. - proc_dp_verify_sync(TO_UINT(bsn_init), - g_pps_interval, + --proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, exp_grid.sop, exp_grid.sync); -- Verify sync at sop and at expected_sync + + -- Verify sync at sop and at expected_sync + proc_dp_verify_sync(g_pps_interval, g_block_size, clk, - verify_sync, + verify_en, bs_sosi.sync, bs_sosi.sop, bs_sosi.bsn, @@ -240,6 +249,28 @@ BEGIN dbg_accumulate, dbg_expected_bsn); + -- Verify bs_sosi by comparing with exp_grid, this again verifies bs_sosi.sync, sop and bsn + p_verify_bs_sosi_grid : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + unexpected_bs_sync <= '0'; + IF verify_en = '1' AND bs_sosi.valid = '1' THEN + ASSERT TO_UINT(bs_sosi.bsn) = exp_grid.bsn REPORT "Wrong bs_sosi.bsn /= exp_grid.bsn" SEVERITY ERROR; + ASSERT bs_sosi.sync = exp_grid.sync REPORT "Wrong bs_sosi.sync /= exp_grid.sync" SEVERITY ERROR; + ASSERT bs_sosi.sop = exp_grid.sop REPORT "Wrong bs_sosi.sop /= exp_grid.sop" SEVERITY ERROR; + ASSERT bs_sosi.eop = exp_grid.eop REPORT "Wrong bs_sosi.eop /= exp_grid.eop" SEVERITY ERROR; + -- Mark error in Wave window + IF bs_sosi.sync = '1' AND bs_sosi.sync /= exp_grid.sync THEN + unexpected_bs_sync <= '1'; + END IF; + END IF; + END IF; + END PROCESS; + + -- Verify that bs_sosi.valid = '1' did happen after dp_on + prev_bs_valid <= bs_sosi.valid WHEN rising_edge(clk); + bs_starts_cnt <= bs_starts_cnt + 1 WHEN rising_edge(clk) AND bs_sosi.valid = '1' AND prev_bs_valid = '0'; + ----------------------------------------------------------------------------- -- DUT: dp_bsn_source_v2 ----------------------------------------------------------------------------- @@ -248,18 +279,25 @@ BEGIN GENERIC MAP ( g_block_size => g_block_size, g_nof_clk_per_sync => g_pps_interval, - g_bsn_w => c_bsn_w + g_bsn_w => c_bsn_w, + g_bsn_time_offset_w => c_bsn_time_offset_w ) PORT MAP ( - rst => rst, - clk => clk, - pps => pps_sop, + rst => rst, + clk => clk, + pps => ref_grid.pps, -- MM control - dp_on => dp_on, - dp_on_pps => dp_on_pps, - bsn_init => bsn_init, + dp_on => dp_on, + dp_on_pps => dp_on_pps, + + dp_on_status => dp_on_status, -- = src_out.valid + bs_restart => bs_restart, -- = src_out.sop for first sop after dp_on went high + + bsn_init => bsn_init, + bsn_time_offset => bsn_time_offset, + -- Streaming - src_out => bs_sosi + src_out => bs_sosi ); END tb;