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 67939303a128f1791853c3c7ea1314f5296d2f48..930588f18e0c8b0114dd064c864f1449a19ea81a 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 @@ -34,7 +34,7 @@ -- > as 4 -- > run -all -- View in Wave window u_dut: r, nxt_r, and tb: in_sosi, out_sosi, --- out_sync, out_start +-- out_sync, out_start, out_start_interval -- -- Development steps: -- . Step 1 ~1 day work (idea started earlier, so requirements were clear), @@ -76,13 +76,14 @@ USE dp_lib.tb_dp_pkg.ALL; ENTITY tb_dp_bsn_sync_scheduler IS GENERIC ( -- Input sync period and sosi ctrl - g_nof_input_sync : NATURAL := 10; + g_nof_input_sync : NATURAL := 10; -- Use c_nof_input_sync > g_block_size, see tb_tb_dp_bsn_sync_scheduler g_nof_block_per_input_sync : NATURAL := 17; g_block_size : NATURAL := 2; g_input_gap_size : NATURAL := 0; -- Output sync period - g_nof_samples_per_output_sync : NATURAL := 45 -- 45 / g_block_size = 4.5 + g_nof_samples_per_output_sync : NATURAL := 45; -- 45 / g_block_size = 4.5 + g_pipeline : NATURAL := 0 -- 0 or 1 ); END tb_dp_bsn_sync_scheduler; @@ -108,6 +109,8 @@ ARCHITECTURE tb OF tb_dp_bsn_sync_scheduler IS CONSTANT c_output_nof_blocks_min : NATURAL := g_nof_samples_per_output_sync / g_block_size; CONSTANT c_enable_init_nof_bsn : NATURAL := ceil_value(c_output_nof_blocks_min / g_block_size + 10, g_nof_block_per_input_sync); + CONSTANT c_out_enable_margin : NATURAL := g_block_size; + SIGNAL clk : STD_LOGIC := '1'; SIGNAL rst : STD_LOGIC := '1'; SIGNAL cnt : INTEGER := 0; @@ -145,28 +148,36 @@ ARCHITECTURE tb OF tb_dp_bsn_sync_scheduler IS -- Output SIGNAL out_sync : STD_LOGIC; -- declared next to in_sync, out_start and out_sosi for easier comparison in Wave window SIGNAL out_start : STD_LOGIC; + SIGNAL out_start_interval : STD_LOGIC; + SIGNAL exp_start_interval : STD_LOGIC := '0'; + SIGNAL first_interval : STD_LOGIC := '0'; SIGNAL out_enable : STD_LOGIC; + SIGNAL expected_out_enable : STD_LOGIC := '0'; SIGNAL out_sosi : t_dp_sosi := c_dp_sosi_init; -- Verify + SIGNAL in_sosi_integer_comb : t_dp_sosi_integer; + SIGNAL in_sosi_integer_pipe : t_dp_sosi_integer; 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 recover_from_in_lost : STD_LOGIC := '0'; + SIGNAL verify_sosi_equal : STD_LOGIC := '0'; + SIGNAL verify_sosi_equal_at_sop : STD_LOGIC := '0'; + SIGNAL verify_sosi_equal_at_valid : STD_LOGIC := '0'; + SIGNAL verify_sync : STD_LOGIC := '1'; + SIGNAL recover_from_in_lost : STD_LOGIC := '0'; - SIGNAL verifying_sync_equal : STD_LOGIC := '0'; + SIGNAL verifying_sync_equal : STD_LOGIC := '0'; - SIGNAL prev_out_enable : STD_LOGIC := '0'; - SIGNAL pending_out_disable : STD_LOGIC := '0'; - SIGNAL expected_out_enable : STD_LOGIC := '0'; - SIGNAL expected_out_enable1 : STD_LOGIC := '0'; - SIGNAL expected_out_enable2 : STD_LOGIC := '0'; - SIGNAL expecting_out_start : STD_LOGIC := '0'; - SIGNAL hold_out_eop : STD_LOGIC := '0'; - SIGNAL hold_out_sop : STD_LOGIC := '0'; - SIGNAL out_sop_cnt : NATURAL := 0; + SIGNAL prev_ctrl_enable : STD_LOGIC := '0'; + SIGNAL prev_out_enable : STD_LOGIC := '0'; + SIGNAL out_enable_cnt : NATURAL := 0; + SIGNAL nxt_out_enable_cnt : NATURAL := 0; + SIGNAL expected_out_enable_comb : STD_LOGIC := '0'; + SIGNAL expected_out_enable_pipe : STD_LOGIC := '0'; + SIGNAL expecting_out_start : STD_LOGIC := '0'; + SIGNAL hold_out_sop : STD_LOGIC := '0'; + SIGNAL out_sop_cnt : NATURAL := 0; SIGNAL dbg_out_sosi_sync : STD_LOGIC; SIGNAL dbg_out_sosi_sop : STD_LOGIC; @@ -346,75 +357,53 @@ BEGIN ----------------------------------------------------------------------------- -- . Verify out_enable ----------------------------------------------------------------------------- - p_hold_out_eop : PROCESS(clk) + -- The expected out_enable is difficult to determine cycle exact, because + -- it depends on g_block_size = 2 or > 2 and on g_pipeline. Therfore use + -- expected_out_enable_comb = '-' to define dont care. + -- * For g_block_size = 2 the use of r.enable (instead of v.enable) in + -- dp_bsn_sync_scheduler.vhd causes that the output can stay enabled 2 + -- cycles longer, which is ok. Using v.enable does avoid these extra + -- cycles, but for timing closure it is preferred to use r.enable. + + -- Verify out_enable + p_expected_out_enable : PROCESS(ctrl_enable, in_sosi, ctrl_start_bsn, ctrl_enable_evt, out_enable_cnt, ctrl_enable, in_sosi, ctrl_start_bsn) BEGIN - IF rising_edge(clk) THEN - IF out_sosi.eop = '1' THEN - hold_out_eop <= '1'; - ELSIF out_sosi.sop = '1' THEN - hold_out_eop <= '0'; - END IF; - END IF; - END PROCESS; + -- Default + expected_out_enable_comb <= '0'; - -- Determine expected out_enable - p_expected_out_enable : PROCESS(ctrl_enable, ctrl_enable_evt, in_sosi, ctrl_start_bsn, out_enable, pending_out_disable, hold_out_eop) - BEGIN - -- Expect output disable after ctrl_enable_evt - IF ctrl_enable_evt = '1' THEN - IF out_enable = '0' THEN - -- Output is already disabled - expected_out_enable <= '0'; - ELSE - -- Output is enabled, so this is a re-enable event. - IF hold_out_eop = '1' THEN - expected_out_enable <= '0'; -- end of block, so output can disable immediately - ELSE - pending_out_disable <= '1'; -- plan output disable before re-enable - END IF; - END IF; - END IF; - - IF pending_out_disable <= '1' THEN - IF hold_out_eop = '1' THEN - expected_out_enable <= '0'; -- end of block, so output can disable - pending_out_disable <= '0'; - END IF; - END IF; - - -- Expect output enable at start BSN + -- Expect output enable '1' when ctrl_enable is active, but after ctrl_start_bsn IF ctrl_enable = '1' THEN IF UNSIGNED(in_sosi.bsn) >= UNSIGNED(ctrl_start_bsn) THEN - expected_out_enable <= '1'; + expected_out_enable_comb <= '1'; END IF; END IF; + + -- Introduce some dont care margin in case of ctrl_enable_evt when ctrl_enable was active and may change. + -- The ctrl_enable may go inactive (= stop) or remain active (= restart). + IF ctrl_enable_evt = '1' AND prev_ctrl_enable = '1' THEN + nxt_out_enable_cnt <= 0; + expected_out_enable_comb <= '-'; + ELSIF out_enable_cnt < c_out_enable_margin THEN + nxt_out_enable_cnt <= out_enable_cnt + 1; + expected_out_enable_comb <= '-'; + END IF; END PROCESS; - expected_out_enable1 <= expected_out_enable WHEN rising_edge(clk); - expected_out_enable2 <= expected_out_enable1 WHEN rising_edge(clk); + prev_ctrl_enable <= ctrl_enable WHEN rising_edge(clk); + + out_enable_cnt <= nxt_out_enable_cnt WHEN rising_edge(clk); + + expected_out_enable_pipe <= expected_out_enable_comb WHEN rising_edge(clk); + + expected_out_enable <= expected_out_enable_comb WHEN g_pipeline = 0 ELSE expected_out_enable_pipe; p_verify_out_enable : PROCESS(clk) BEGIN - -- Use registered values to compare, to avoid combinatorial differences - -- that can occur during a simulation delta cycle. These combinatorial - -- differences are not relevant, because they get resolved after a few - -- delta cycles. IF rising_edge(clk) THEN - IF out_enable /= expected_out_enable THEN - IF out_enable = '1' THEN - IF g_block_size > 2 THEN - REPORT "Unexpected enabled out_enable" SEVERITY ERROR; - ELSIF out_enable /= expected_out_enable2 THEN - -- For g_block_size = 2 the use of r.enable (instead of v.enable) - -- in dp_bsn_sync_scheduler.vhd causes that the output can stay - -- enabled 2 cycles longer, which is ok. Using v.enable does - -- avoid this need to use expected_out_enable2, but for timing - -- closure it is preferred to use r.enable. - REPORT "Unexpected enabled out_enable2" SEVERITY ERROR; - END IF; - ELSE - REPORT "Unexpected disabled out_enable" SEVERITY ERROR; - END IF; + IF expected_out_enable = '1' THEN + ASSERT out_enable = '1' REPORT "Wrong out_enable, should be active" SEVERITY ERROR; + ELSIF expected_out_enable = '0' THEN + ASSERT out_enable = '0' REPORT "Wrong out_enable, should be inactive" SEVERITY ERROR; END IF; END IF; END PROCESS; @@ -471,6 +460,23 @@ BEGIN END IF; END PROCESS; + exp_start_interval <= out_start OR (first_interval AND NOT out_sync) WHEN rst = '0' ELSE '0'; + + p_verify_out_start_interval : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + -- Create first_interval for exp_start_interval + IF out_start = '1' THEN + first_interval <= '1'; + ELSIF out_sync = '1' THEN + first_interval <= '0'; + END IF; + + -- Verify exp_start_interval + ASSERT exp_start_interval = out_start_interval REPORT "Wrong out_start_interval" SEVERITY ERROR; + END IF; + END PROCESS; + ----------------------------------------------------------------------------- -- . Verify out_sosi = in_sosi, for all fields except out_sosi.sync ----------------------------------------------------------------------------- @@ -480,14 +486,19 @@ BEGIN -- declaration is not sufficient. verify_sosi_equal <= out_enable WHEN rising_edge(clk); - in_sosi_integer <= func_dp_stream_slv_to_integer(in_sosi, c_natural_w) WHEN rising_edge(clk); - out_sosi_integer <= func_dp_stream_slv_to_integer(out_sosi, c_natural_w) WHEN rising_edge(clk); + verify_sosi_equal_at_sop <= verify_sosi_equal AND in_sosi_integer.sop; + verify_sosi_equal_at_valid <= verify_sosi_equal AND in_sosi_integer.valid; + + in_sosi_integer_comb <= func_dp_stream_slv_to_integer(in_sosi, c_natural_w) WHEN rising_edge(clk); + in_sosi_integer_pipe <= in_sosi_integer_comb WHEN rising_edge(clk); + in_sosi_integer <= in_sosi_integer_comb WHEN g_pipeline = 0 ELSE in_sosi_integer_pipe; + out_sosi_integer <= func_dp_stream_slv_to_integer(out_sosi, c_natural_w) WHEN rising_edge(clk); - proc_dp_verify_sosi_equal( "bsn", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer); + proc_dp_verify_sosi_equal( "bsn", clk, verify_sosi_equal_at_sop, out_sosi_integer, in_sosi_integer); proc_dp_verify_sosi_equal( "sop", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer); proc_dp_verify_sosi_equal( "eop", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer); 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); + proc_dp_verify_sosi_equal( "data", clk, verify_sosi_equal_at_valid, out_sosi_integer, in_sosi_integer); -- Verify that out_sosi blocks have sop and eop proc_dp_verify_sop_and_eop(clk, out_sosi.valid, out_sosi.sop, out_sosi.eop, hold_out_sop); @@ -558,7 +569,7 @@ BEGIN GENERIC MAP ( g_bsn_w => c_bsn_w, g_block_size => g_block_size, - g_pipeline => 0 + g_pipeline => g_pipeline ) PORT MAP ( rst => rst, @@ -578,6 +589,7 @@ BEGIN in_sosi => in_sosi, out_sosi => out_sosi, out_start => out_start, + out_start_interval => out_start_interval, out_enable => out_enable );