From 5251bbcb02eca213de7cd92bac1e21aaefcd835a Mon Sep 17 00:00:00 2001 From: Eric Kooistra <kooistra@astron.nl> Date: Thu, 23 Mar 2023 15:31:01 +0100 Subject: [PATCH] Add tb_dp_rsn_source.vhd, it compiles OK but does not verify rs_sosi yet. --- libraries/base/dp/hdllib.cfg | 1 + .../base/dp/tb/vhdl/tb_dp_rsn_source.vhd | 365 ++++++++++++++++++ 2 files changed, 366 insertions(+) create mode 100644 libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg index 945734723b..db256bdb9c 100644 --- a/libraries/base/dp/hdllib.cfg +++ b/libraries/base/dp/hdllib.cfg @@ -221,6 +221,7 @@ test_bench_files = 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_rsn_source.vhd tb/vhdl/tb_dp_bsn_source.vhd tb/vhdl/tb_dp_bsn_source_v2.vhd tb/vhdl/tb_mms_dp_bsn_source.vhd diff --git a/libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd b/libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd new file mode 100644 index 0000000000..774c0fe8f1 --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd @@ -0,0 +1,365 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2022 +-- 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 +-- Purpose: Tb for tb_dp_rsn_source +-- Use dp_bsn_source_v2 to create bs_sosi and extending it with dut = +-- dp_rsn_source to create and verify rs_sosi. +-- Remark: +-- * This tb is made based on tb_dp_bsn_source_v2. + +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.tb_common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE dp_lib.tb_dp_pkg.ALL; + +ENTITY tb_dp_rsn_source IS + GENERIC ( + g_pps_interval : NATURAL := 16; --101; + g_bs_block_size : NATURAL := 5; --23 + g_rs_block_size : NATURAL := 5 --23 + ); +END tb_dp_rsn_source; + +ARCHITECTURE tb OF tb_dp_rsn_source IS + + -- The nof block per sync interval will be the same after every + -- c_min_nof_pps_interval. The c_gcd is the greatest common divider of + -- g_pps_interval and g_bs_block_size, so g_bs_block_size / c_gcd yields an + -- integer. When g_pps_interval and g_bs_block_size are relative prime, + -- then c_gcd = 1, and then it takes g_bs_block_size nof g_pps_interval + -- for the pattern of c_nof_block_per_sync_lo and c_nof_block_per_sync_hi + -- to repeat. If c_gcd = g_bs_block_size, then c_nof_block_per_sync_lo = + -- c_nof_block_per_sync_hi, so then the nof block per sync interval is + -- the same in every pps interval. + CONSTANT c_gcd : NATURAL := gcd(g_pps_interval, g_bs_block_size); + CONSTANT c_min_nof_pps_interval : NATURAL := g_bs_block_size / c_gcd; + + CONSTANT c_nof_block_per_sync_lo : NATURAL := g_pps_interval / g_bs_block_size; + CONSTANT c_nof_block_per_sync_hi : NATURAL := ceil_div(g_pps_interval, g_bs_block_size); + + -- choose c_nof_pps and c_nof_repeat > c_min_nof_pps_interval, because the + -- fractional sync pattern will repeat every c_min_nof_pps_interval number + -- of g_pps_intervals. + CONSTANT c_factor : NATURAL := 3; + CONSTANT c_nof_pps : NATURAL := c_min_nof_pps_interval * c_factor; + CONSTANT c_nof_repeat : NATURAL := c_min_nof_pps_interval * c_factor; + + CONSTANT c_clk_period : TIME := 10 ns; + CONSTANT c_bsn_w : NATURAL := 31; + CONSTANT c_bsn_time_offset_w : NATURAL := ceil_log2(g_bs_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_dp_on, + s_dp_on_pps + ); + + -- 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_bs_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; + + -- BSN source + 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 bs_new_interval : STD_LOGIC; + SIGNAL tb_new_interval : STD_LOGIC := '0'; + 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; + + -- RSN source + SIGNAL rs_restart : STD_LOGIC; + SIGNAL rs_new_interval : STD_LOGIC; + SIGNAL rs_sosi : t_dp_sosi; + + -- Verify + 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_c_nof_pps : NATURAL := c_nof_pps; + SIGNAL dbg_c_nof_repeat : NATURAL := c_nof_repeat; + + SIGNAL dbg_nof_blk : NATURAL; + SIGNAL dbg_accumulate : NATURAL; + SIGNAL dbg_expected_bsn : NATURAL; + +BEGIN + + rst <= '1', '0' AFTER c_clk_period*7; + clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; + + ----------------------------------------------------------------------------- + -- Generate reference time grid + ----------------------------------------------------------------------------- + proc_common_gen_pulse(1, g_pps_interval, '1', sl0, clk, ref_grid.pps); + proc_common_gen_pulse(1, g_bs_block_size, '1', sl0, clk, ref_grid.sop); + ref_grid.eop <= ref_grid.sop'DELAYED((g_bs_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 + 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; + + 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_ssn : NATURAL; + VARIABLE v_bsn_init : NATURAL; + VARIABLE v_bsn_time_offset : NATURAL; + BEGIN + -- Get synchronous to clk + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 10); + + -- Start asynchronously by making dp_on high + verify_en <= '0'; -- only verify visualy in wave window + tb_state <= s_dp_on; + dp_on_pps <= '0'; + dp_on <= '1'; + proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval); + tb_state <= s_disable; + dp_on <= '0'; + dp_on_pps <= '0'; + proc_common_wait_some_cycles(clk, 10); + + -- 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_bs_block_size); -- Equation 3.6 in [1] + v_bsn_time_offset := v_bsn_init * g_bs_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'; + proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval); + tb_state <= s_disable; + dp_on <= '0'; + dp_on_pps <= '0'; + END LOOP; + + 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, 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(0, -- start bsn of PPS grid and BSN grid is 0, see [1] + g_pps_interval, + g_bs_block_size, + clk, + verify_en, + bs_sosi.sync, + bs_sosi.sop, + bs_sosi.bsn, + dbg_nof_blk, + 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 by verifying bs_start + 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'; + + p_verify_bs_restart : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + IF bs_restart = '1' THEN + ASSERT bs_sosi.sync = '1' REPORT "Unexpected bs_start while bs_sosi.sync /= 1" SEVERITY ERROR; + ASSERT prev_bs_valid = '0' REPORT "Unexpected bs_start while prev_bs_valid /= 0" SEVERITY ERROR; + END IF; + END IF; + END PROCESS; + + p_verify_bs_new_interval : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + IF bs_restart = '1' THEN + ASSERT bs_new_interval = '1' REPORT "Wrong begin of bs_new_interval" SEVERITY ERROR; + tb_new_interval <= '1'; + ELSIF bs_sosi.sync = '1' THEN + ASSERT bs_new_interval = '0' REPORT "Wrong end of bs_new_interval" SEVERITY ERROR; + tb_new_interval <= '0'; + ELSIF tb_new_interval = '1' THEN + ASSERT bs_new_interval = '1' REPORT "Wrong level during bs_new_interval" SEVERITY ERROR; + ELSE + ASSERT bs_new_interval = '0' REPORT "Unexpected bs_new_interval" SEVERITY ERROR; + END IF; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- DUT: dp_bsn_source_v2 + ----------------------------------------------------------------------------- + + u_bsn : ENTITY work.dp_bsn_source_v2 + GENERIC MAP ( + g_block_size => g_bs_block_size, + g_nof_clk_per_sync => g_pps_interval, + g_bsn_w => c_bsn_w, + g_bsn_time_offset_w => c_bsn_time_offset_w + ) + PORT MAP ( + rst => rst, + clk => clk, + pps => ref_grid.pps, + -- MM control + 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.sync for first sync after dp_on went high + bs_new_interval => bs_new_interval, -- active during first src_out.sync interval + + bsn_init => bsn_init, + bsn_time_offset => bsn_time_offset, + + -- Streaming + src_out => bs_sosi + ); + + u_rsn : ENTITY work.dp_rsn_source + GENERIC MAP ( + g_block_size => g_rs_block_size, + g_nof_clk_per_sync => g_pps_interval, + g_bsn_w => c_bsn_w + ) + PORT MAP ( + rst => rst, + clk => clk, + + -- Input stream sosi control using BSN + bs_sosi => bs_sosi, -- input reference stream using BSN + + -- Output stream sosi control using RSN + rs_sosi => rs_sosi, -- output stream using RSN and g_rs_block_size, g_nof_clk_per_sync + rs_restart => rs_restart, -- = rs_sosi.sync for first sync after bs_sosi.valid went high + rs_new_interval => rs_new_interval -- = active during first rs_sosi.sync interval + ); + +END tb; -- GitLab