diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index ba09970a6f98b5f6896ef7036894ca27fa5b024f..3931d107704f9417e9ce3156c27593ebe36fb8a3 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -195,7 +195,11 @@ PACKAGE common_pkg IS FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR; -- standard logic to 1 element standard logic vector FUNCTION sl( n: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- 1 element standard logic vector to standard logic - + + FUNCTION to_sl( n: IN BOOLEAN) RETURN STD_LOGIC; -- if TRUE then return '1' else '0' + FUNCTION to_bool(n: IN STD_LOGIC) RETURN BOOLEAN; -- if '1' or 'H' then return TRUE else FALSE + FUNCTION to_bool(n: IN INTEGER) RETURN BOOLEAN; -- if 0 then return FALSE else TRUE + FUNCTION to_natural_arr(n : t_integer_arr; to_zero : BOOLEAN) RETURN t_natural_arr; -- if to_zero=TRUE then negative numbers are forced to zero, otherwise they will give a compile range error FUNCTION to_natural_arr(n : t_nat_natural_arr) RETURN t_natural_arr; FUNCTION to_integer_arr(n : t_natural_arr) RETURN t_integer_arr; @@ -646,7 +650,28 @@ PACKAGE BODY common_pkg IS r := n(n'LOW); RETURN r; END; - + + + FUNCTION to_sl(n: IN BOOLEAN) RETURN STD_LOGIC IS + BEGIN + IF n = TRUE THEN + RETURN '1'; + ELSE + RETURN '0'; + END IF; + END; + + FUNCTION to_bool(n: IN STD_LOGIC) RETURN BOOLEAN IS + BEGIN + RETURN n = '1' OR n = 'H'; + END; + + FUNCTION to_bool(n: IN INTEGER) RETURN BOOLEAN IS + BEGIN + RETURN NOT (n = 0); + END; + + FUNCTION to_natural_arr(n : t_integer_arr; to_zero : BOOLEAN) RETURN t_natural_arr IS VARIABLE vN : t_integer_arr(n'LENGTH-1 DOWNTO 0); VARIABLE vR : t_natural_arr(n'LENGTH-1 DOWNTO 0); diff --git a/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd b/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd index 8878d751ee3a09cd3975cf2039658f0d787ca785..d29acbb51aeb1345b3895b2c8a7e4cbea30c3570 100644 --- a/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd +++ b/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd @@ -44,6 +44,12 @@ -- In fact g_readback could better be called g_st_readback. An alternative -- g_mm_readback could define direct read back in the MM clock domain and -- would allow leaving the in_reg not connected. +-- . reg_wr_arr +-- Provides write access pulse in dp_clk domain. However the pulse may arrive +-- before the out_reg data, due to that the pulse and data do not cross the +-- clock domain in the same way. A solution would be to use a single +-- common_reg_cross_domain instance to transfer both the wr access info and +-- the data. LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; diff --git a/libraries/base/common/src/vhdl/common_str_pkg.vhd b/libraries/base/common/src/vhdl/common_str_pkg.vhd index 492bb411ab2341adce4ea2331c759fd56706ed47..cda0f3672e1b164d7156a455166ae428c340b609 100644 --- a/libraries/base/common/src/vhdl/common_str_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_str_pkg.vhd @@ -46,6 +46,7 @@ PACKAGE common_str_pkg IS FUNCTION time_to_str(in_time : TIME) RETURN STRING; FUNCTION str_to_time(in_str : STRING) RETURN TIME; FUNCTION slv_to_str(slv : STD_LOGIC_VECTOR) RETURN STRING; + FUNCTION sl_to_str(sl : STD_LOGIC) RETURN STRING; FUNCTION str_to_hex(str : STRING) RETURN STRING; FUNCTION slv_to_hex(slv : STD_LOGIC_VECTOR) RETURN STRING; FUNCTION hex_to_slv(str : STRING) RETURN STD_LOGIC_VECTOR; @@ -140,6 +141,11 @@ PACKAGE BODY common_str_pkg IS RETURN v_str; END; + FUNCTION sl_to_str(sl : STD_LOGIC) RETURN STRING IS + BEGIN + RETURN slv_to_str(slv(sl)); + END; + FUNCTION str_to_hex(str : STRING) RETURN STRING IS CONSTANT c_nof_nibbles : NATURAL := ceil_div(str'LENGTH, c_nibble_w); VARIABLE v_nibble_arr : t_str_4_arr(0 TO c_nof_nibbles-1) := (OTHERS=>(OTHERS=>'0')); diff --git a/libraries/base/dp/dp.peripheral.yaml b/libraries/base/dp/dp.peripheral.yaml index d24c787541f44ac1301163617119582a3484ccdb..0459fbc4916460613c60f6805f6fefb61974a36f 100644 --- a/libraries/base/dp/dp.peripheral.yaml +++ b/libraries/base/dp/dp.peripheral.yaml @@ -206,6 +206,59 @@ peripherals: access_mode: RW + - peripheral_name: dp_bsn_sync_scheduler # pi_dp_bsn_sync_scheduler.py + peripheral_description: "Disable the output, or enable the output with a programmable sync interval." + mm_ports: + # MM port for mmp_dp_bsn_sync_scheduler.vhd + - mm_port_name: REG_DP_BSN_SYNC_SCHEDULER + mm_port_type: REG + mm_port_span: 16 * MM_BUS_SIZE + mm_port_description: "" + fields: + - - field_name: ctrl_enable + field_description: "Enable the output when 1, disable the output when 0. To first disable output to re-enable output." + address_offset: 0 * MM_BUS_SIZE # = 0x0 + mm_width: 1 + access_mode: RW + - - field_name: ctrl_interval_size + field_description: "Number of samples in output sync interval" + address_offset: 1 * MM_BUS_SIZE # = 0x4 + access_mode: RW + - - field_name: ctrl_start_bsn + field_description: + "Schedule start BSN for the output. The start BSN needs to be in the future, + if the start BSN is in the past or if the input stream is not active, then + ctrl_enable = '1' will have no effect." + address_offset: 2 * MM_BUS_SIZE # = 0x8 + user_width: 64 + radix: uint64 + access_mode: RW + - - field_name: mon_current_input_bsn + field_description: "Current input BSN. This can be read to determine a start BSN in the future." + address_offset: 4 * MM_BUS_SIZE # = 0x10 + user_width: 64 + radix: uint64 + access_mode: RO + - - field_name: mon_output_enable + field_description: + "Is 1 when output is enabled, 0 when output is disabled. The output stream gets + enabled when ctrl_enable is set '1' and when the BSN of the input stream has + reached the ctrl_start_bsn." + address_offset: 6 * MM_BUS_SIZE # = 0x18 + mm_width: 1 + access_mode: RO + - - field_name: mon_output_sync_bsn + field_description: "Output BSN at sync. This yields the number of blocks per output sync interval." + address_offset: 7 * MM_BUS_SIZE # = 0x1C + user_width: 64 + radix: uint64 + access_mode: RO + - - field_name: block_size + field_description: "Number of samples per BSN block, same for input stream and output stream." + address_offset: 9 * MM_BUS_SIZE # = 0x24 + access_mode: RO + + - peripheral_name: dp_bsn_monitor # pi_dp_bsn_monitor.py peripheral_description: "Monitor the Block Sequence Number (BSN) status of streaming data." parameters: diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg index c28801a8d2b92f1666074aa671d2f2c6aab0a5ab..9cc984efdde5e083ce57a73815e6cb4d062c570b 100644 --- a/libraries/base/dp/hdllib.cfg +++ b/libraries/base/dp/hdllib.cfg @@ -91,6 +91,8 @@ synth_files = src/vhdl/dp_bsn_scheduler.vhd src/vhdl/dp_bsn_scheduler_reg.vhd src/vhdl/mms_dp_bsn_scheduler.vhd + src/vhdl/dp_bsn_sync_scheduler.vhd + src/vhdl/mmp_dp_bsn_sync_scheduler.vhd src/vhdl/dp_bsn_delay.vhd src/vhdl/dp_bsn_align.vhd src/vhdl/dp_bsn_align_reg.vhd @@ -214,6 +216,8 @@ test_bench_files = tb/vhdl/tb_dp_bsn_source_v2.vhd tb/vhdl/tb_mms_dp_bsn_source.vhd tb/vhdl/tb_mms_dp_bsn_source_v2.vhd + tb/vhdl/tb_dp_bsn_sync_scheduler.vhd + tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd tb/vhdl/tb_dp_demux.vhd tb/vhdl/tb2_dp_demux.vhd tb/vhdl/tb3_dp_demux.vhd @@ -291,6 +295,7 @@ test_bench_files = tb/vhdl/tb_tb_dp_block_validate_channel.vhd tb/vhdl/tb_tb_dp_bsn_align.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 tb/vhdl/tb_tb_dp_demux.vhd tb/vhdl/tb_tb2_dp_demux.vhd @@ -347,7 +352,7 @@ regression_test_vhdl = tb/vhdl/tb_dp_bsn_source.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 tb/vhdl/tb_tb_dp_block_select.vhd tb/vhdl/tb_tb_dp_block_validate_length.vhd tb/vhdl/tb_tb_dp_block_validate_err.vhd @@ -360,6 +365,7 @@ regression_test_vhdl = tb/vhdl/tb_tb_dp_block_validate_channel.vhd tb/vhdl/tb_tb_dp_bsn_align.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 tb/vhdl/tb_tb_dp_demux.vhd tb/vhdl/tb_tb2_dp_demux.vhd diff --git a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd new file mode 100644 index 0000000000000000000000000000000000000000..9c888558625dbd66a51de1eb75c8cb7ca028d70b --- /dev/null +++ b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd @@ -0,0 +1,388 @@ +-- -------------------------------------------------------------------------- +-- 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, 30 July 2021 +-- Purpose : +-- Create programmable sync interval for input stream. +-- Description: +-- * ctrl_start_bsn: +-- The output sync interval starts at an in_sosi.bsn that is programmable via +-- ctrl_start_bsn. The c_ctrl_start_bsn must be in the future to enable +-- output, otherwise the output is not enabled. The alternative to enable the +-- output immediately in case the BSN is in the passed is easy for a user, +-- because it then starts at any BSN, but that is not synchronous between +-- FPGAs. +-- * ctrl_interval_size: +-- The output sync interval is programmable via ctrl_interval_size. The +-- ctrl_interval_size is the number data samples per output sync interval, +-- so an integer multiple sample periods. The g_block_size is the number of +-- data samples per block. +-- The output sync intervals are controlled such that on average the number +-- of blocks per sync interval is nof_blk = ctrl_interval_size / +-- g_block_size, also when they are not integer dividable. +-- * ctrl_enable: +-- The output is enabled at the ctrl_start_bsn when ctrl_enable = '1' and the +-- output is disable after an in_sosi.eop when ctrl_enable = '0'. If the +-- output is diabled, then the sosai control fields are forced to '0', the +-- other sosi fields of the in_sosi are passed on to the out_sosi. +-- * mon_current_input_bsn: +-- The user can read mon_current_input_bsn to determine a suitable +-- ctrl_start_bsn in the future. +-- * mon_input_bsn_at_sync: +-- The user can read mon_current_input_bsn to determine a suitable +-- ctrl_start_bsn in the future to create a output sync interval that is +-- aligned with the in_sosi.sync. +-- * mon_output_enable: +-- The user can read mon_output_enable to check whether the output is indeed +-- enabled or not (mon_output_enable = out_enable). +-- * mon_output_sync_bsn: +-- The sync interval calculation is robust to lost in_sosi blocks. As soon +-- as it receives a new in_sosi block it will try to determine the next +-- output_sync_bsn, even if blocks were lost for multiple output sync +-- intervals. If mon_output_sync_bsn - mon_current_input_bsn < 0 then the +-- output sync interval calculation should catch up after some in_sosi +-- blocks. If mon_output_sync_bsn - mon_current_input_bsn > nof_blk then +-- something went wrong and then it may be necessary to recover using +-- ctrl_enable. If mon_output_sync_bsn - mon_current_input_bsn < nof_blk and +-- > 0 then that yields the number of blocks until the next output sync. +-- * out_start: +-- Pulse at out_sosi.sync with out_sosi.bsn = ctrl_start_bsn. The first +-- out_sosi.sync interval will have nof_blk_max blocks. +-- * out_enable: +-- Goes high at first out_sosi.sync. In case of a restart when ctrl_enable +-- was already '1', then the out_enable will go low and high to ensure that +-- the restart out_sosi.sync interval will have nof_blk_max blocks. When +-- ctrl_enable goes low or in case of a restart then out_enable will go +-- low after an out_sosi.eop, to preserve complete out_sosi blocks. The +-- out_enable is monitored via mon_output_enable, so that the user can +-- check via MM whether an ctrl_enable access was applied. Typically the +-- out_enable OUT can be left OPEN in an VHDL application, and only used for +-- verification purposes in the test bench (tb). +-- For example: +-- . sample period Ts = 5 ns +-- . g_block_size = 1024 samples +-- . ctrl_start_bsn = 0 +-- . ctrl_interval_size = 200M +-- ==> +-- One block is g_block_size * Ts = 5.12 us +-- nof_blk = ctrl_interval_size / g_block_size = 195312.5 +-- nof_blk_max = ceil(nof_blk) = 195313 = 1.00000256 s +-- nof_blk_min = floor(nof_blk) = 195312 = 0.99999744 s +-- The output sync interval is exactly 1 s on average, the even output sync +-- periods (starting from inex 0 is even) will use nof_blk_max and the odd +-- output sync periods will use nof_blk_min. +-- If all FPGAs are started at the same ctrl_start_bsn, then the output sync +-- interval is synchonous in the entire array of FPGAs. +-- 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; +USE IEEE.NUMERIC_STD.ALL; +USE common_lib.common_pkg.ALL; +USE work.dp_stream_pkg.ALL; + +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, must be >= 2 + g_pipeline : NATURAL := 1 -- use '1' on HW, use '0' for easier debugging in Wave window + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- M&C + ctrl_enable : IN STD_LOGIC; + ctrl_enable_evt : IN STD_LOGIC; + ctrl_interval_size : IN NATURAL; + ctrl_start_bsn : IN STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); + mon_current_input_bsn : OUT STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + mon_input_bsn_at_sync : OUT STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + mon_output_enable : OUT STD_LOGIC; + mon_output_sync_bsn : OUT STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + + -- Streaming + in_sosi : IN t_dp_sosi; + out_sosi : OUT t_dp_sosi; + out_start : OUT STD_LOGIC; -- pulse at out_sosi.sync at ctrl_start_bsn + out_enable : OUT STD_LOGIC -- for tb verification purposes + ); +END dp_bsn_sync_scheduler; + + +ARCHITECTURE rtl OF dp_bsn_sync_scheduler IS + + TYPE t_reg IS RECORD + enable_init : STD_LOGIC; + enable : STD_LOGIC; + blk_cnt : NATURAL; + interval_size : NATURAL; + start_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + input_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + nof_blk_min : NATURAL; + nof_blk_max : NATURAL; + nof_blk : NATURAL; + extra : NATURAL RANGE 0 TO g_block_size; + accumulate : NATURAL RANGE 0 TO g_block_size*2; + hold_eop : STD_LOGIC; + update_bsn : STD_LOGIC; + output_enable : STD_LOGIC; + output_sync_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + END RECORD; + + CONSTANT c_reg_rst : t_reg := ('0', '0', 0, 0, (OTHERS=>'0'), (OTHERS=>'0'), 0, 0, 0, 0, 0, '1', '0', '0', (OTHERS=>'0')); + + -- Local registers + SIGNAL r : t_reg; + SIGNAL nxt_r : t_reg; + + SIGNAL output_start : STD_LOGIC; + SIGNAL output_sync : STD_LOGIC; + SIGNAL output_sosi : t_dp_sosi; + +BEGIN + + ASSERT g_block_size >= 2 REPORT "g_block_size must be >= 2." SEVERITY FAILURE; + + -- Capture monitoring info + mon_current_input_bsn <= in_sosi.bsn(g_bsn_w-1 DOWNTO 0) WHEN rising_edge(clk) AND in_sosi.sop = '1'; + mon_input_bsn_at_sync <= in_sosi.bsn(g_bsn_w-1 DOWNTO 0) WHEN rising_edge(clk) AND in_sosi.sync = '1'; + mon_output_enable <= r.output_enable; + mon_output_sync_bsn <= r.output_sync_bsn; + + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + r <= c_reg_rst; + ELSIF rising_edge(clk) THEN + r <= nxt_r; + END IF; + END PROCESS; + + p_comb : PROCESS(r, ctrl_enable, ctrl_enable_evt, ctrl_interval_size, ctrl_start_bsn, in_sosi) + VARIABLE v : t_reg; + VARIABLE v_size : NATURAL; + BEGIN + v := r; + output_start <= '0'; + output_sync <= '0'; + + -- Detect ctrl_enable rising event + IF ctrl_enable = '1' AND ctrl_enable_evt = '1' THEN + v.enable_init := '1'; + END IF; + + -- Initialization: calculate number of blocks per output sync interval + -- . use r.enable_init instead of v.enable_init to easy timing closure and + -- because functionally it makes no difference. + IF r.enable_init = '1' THEN + -- Assume ctrl_start_bsn is scheduled more than nof_blk block clk cycles + -- after the ctrl_enable_evt, so there is sufficient time until + -- v.output_enable = '1', to perform the calculation of nof_blk_min and + -- nof_blk_max sequentially. This avoids using division in logic to + -- calculate ctrl_interval_size / g_block_size. + v_size := r.blk_cnt * g_block_size; + IF v_size = ctrl_interval_size THEN + -- Support integer number of blocks per output sync interval + v.interval_size := ctrl_interval_size; -- hold ctrl_interval_size + v.start_bsn := ctrl_start_bsn; -- hold ctrl_start_bsn + -- Fixed sync interval control, effectively disable fractional sync interval control: + v.nof_blk_min := r.blk_cnt; + v.nof_blk_max := r.blk_cnt; + v.nof_blk := r.blk_cnt; -- nof_blk = nof_blk_max = nof_blk_min + v.extra := 0; + v.accumulate := 0; + v.enable_init := '0'; -- enable initialization is done + ELSIF v_size > ctrl_interval_size THEN + -- Support fractional number of blocks per output sync interval + v.interval_size := ctrl_interval_size; -- hold ctrl_interval_size + v.start_bsn := ctrl_start_bsn; -- hold ctrl_start_bsn + -- Fractional sync interval control: + v.nof_blk_min := r.blk_cnt - 1; + v.nof_blk_max := r.blk_cnt; + v.nof_blk := r.blk_cnt; -- start with nof_blk_max sync interval + v.extra := v_size - ctrl_interval_size; -- number of extra samples in nof_blk_max compared to ctrl_interval_size + v.accumulate := v.extra; -- start with nof_blk_max sync interval + v.enable_init := '0'; -- enable initialization is done + ELSE + v.blk_cnt := r.blk_cnt + 1; + END IF; + ELSE + v.blk_cnt := 0; + END IF; + + -- Enable / disable control + IF ctrl_enable = '0' THEN + -- Disable output when ctrl_enable requests disable. + v.enable := '0'; + ELSIF ctrl_enable_evt = '1' THEN + -- ctrl_enable is on, so this is a re-enable. First disable output. + v.enable := '0'; + ELSIF r.enable_init = '0' THEN + -- ctrl_enable is still on, so now enable the output. In case of an + -- enable, then the output was already disabled. In case of a re-enable, + -- then wait until the output got disabled. + -- Enabling the output from disabled state ensures that the output will + -- start or restart with nof_blk_max in the first sync interval. + -- Otherwise with re-enabling from enabled state, it is undefined + -- whether the first sync interval after the restart will have + -- nof_blk_min or nof_blk_max blocks. + IF r.output_enable = '0' THEN + v.enable := '1'; + END IF; + END IF; + + -- Hold input eop to detect when input has finished a block and to detect + -- gaps between in_sosi.eop and in_sosi.sop + IF in_sosi.sop = '1' THEN + v.hold_eop := '0'; + END IF; + IF in_sosi.eop = '1' THEN + v.hold_eop := '1'; + END IF; + + IF v.enable = '1' THEN + -- Output enable at in_sosi.sop start_bsn + IF in_sosi.sop = '1' THEN + IF UNSIGNED(in_sosi.bsn) = UNSIGNED(r.start_bsn) THEN + v.output_enable := '1'; + output_start <= '1'; -- Pulse at start of output enable at start BSN of output sync intervals + v.output_sync_bsn := r.start_bsn; -- Initialize output sync at start BSN of output sync intervals + END IF; + END IF; + ELSE + -- Output disable after in_sosi.eop + IF r.hold_eop = '1' THEN + v.output_enable := '0'; + END IF; + END IF; + + -- Generate output sync interval based on input BSN and ctrl_interval_size + IF v.output_enable = '1' THEN + IF in_sosi.sop = '1' THEN + IF UNSIGNED(in_sosi.bsn) = UNSIGNED(v.output_sync_bsn) THEN + -- Matching input block + output_sync <= '1'; -- The output sync interval + v.update_bsn := '1'; + ELSIF UNSIGNED(in_sosi.bsn) > UNSIGNED(v.output_sync_bsn) THEN + -- Missed one or more input blocks, so cannot output sync, look for + -- next opportunity + v.update_bsn := '1'; + END IF; + END IF; + END IF; + + -- Determine BSN for next output sync + IF r.update_bsn = '1' THEN + -- Similar code as in proc_dp_verify_sync(), the difference is that: + -- . Here r.extra is number of extra samples in nof_blk_max compared to + -- ctrl_interval_size, + -- . in proc_dp_verify_sync() r.extra is number of extra samples in + -- ctrl_interval_size compared to nof_blk_min. + -- Both schemes are valid, by using different schemes here and in tb the + -- verification coverage improves. + v.output_sync_bsn := ADD_UVEC(r.output_sync_bsn, TO_UVEC(r.nof_blk, c_natural_w)); -- next BSN + + v.nof_blk := r.nof_blk_max; + v.accumulate := r.accumulate + r.extra; -- account for nof_blk_max + IF v.accumulate >= g_block_size THEN + v.nof_blk := r.nof_blk_min; + v.accumulate := v.accumulate - g_block_size; -- adjust for nof_blk_min + END IF; + + -- Assume output_sync_bsn is in future + v.update_bsn := '0'; + + -- else: last r.input_bsn will be used to keep update_bsn active for + -- more clk cycles to catch up for lost input blocks. + END IF; + + -- Hold input bsn + IF in_sosi.sop = '1' THEN + v.input_bsn := in_sosi.bsn(g_bsn_w-1 DOWNTO 0); + END IF; + + -- Catch up with output_sync_bsn in case of lost input blocks. If many + -- input blocks were lost (e.g. > nof_blk_max), then catching up the + -- output_sync_bsn to the current input_bsn can cause that one or more + -- output_sync will not be made active, but that is acceptable in case + -- recovery from of lost input blocks. + IF v.output_enable = '1' THEN + IF UNSIGNED(r.input_bsn) > UNSIGNED(v.output_sync_bsn) THEN + -- Missed one or more input blocks, fast forward to look for next + -- output_sync_bsn in every clk cycle. + v.update_bsn := '1'; + END IF; + END IF; + + nxt_r <= v; + END PROCESS; + + -- Output in_sosi with programmed sync interval or disable the output + p_output_sosi : PROCESS(in_sosi, nxt_r, output_sync) + BEGIN + output_sosi <= in_sosi; + IF nxt_r.output_enable = '1' THEN + output_sosi.sync <= output_sync; + ELSE + output_sosi.sync <= '0'; + output_sosi.sop <= '0'; + output_sosi.eop <= '0'; + output_sosi.valid <= '0'; + END IF; + END PROCESS; + + -- Pipeline output to avoid timing closure problems due to use of nxt_r.output_enable + u_out_sosi : ENTITY work.dp_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_in => output_sosi, + -- ST source + src_out => out_sosi + ); + + gen_pipe_out_start : IF g_pipeline = 1 GENERATE + out_start <= output_start WHEN rising_edge(clk); + out_enable <= r.output_enable; + END GENERATE; + no_pipe_out_start : IF g_pipeline = 0 GENERATE + out_start <= output_start; + out_enable <= nxt_r.output_enable; + END GENERATE; + +END rtl; + + diff --git a/libraries/base/dp/src/vhdl/mmp_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/mmp_dp_bsn_sync_scheduler.vhd new file mode 100644 index 0000000000000000000000000000000000000000..842fa3175e688d1c4048b4417abd2a526e9e6579 --- /dev/null +++ b/libraries/base/dp/src/vhdl/mmp_dp_bsn_sync_scheduler.vhd @@ -0,0 +1,216 @@ +-- -------------------------------------------------------------------------- +-- 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 aug 2021 +-- Purpose : MM peripheral interface for dp_bsn_sync_scheduler.vhd +-- Description: MM port register logic +-- +-- wi Bits Access Type Name +-- 0 [0] RW boolean ctrl_enable, '1' is on, '0' is FALSE is off +-- 1 [31:0] RW uint32 ctrl_interval_size +-- 2 [31:0] RW uint64 ctrl_start_bsn[31:0] +-- 3 [31:0] RW ctrl_start_bsn[63:32] +-- 4 [31:0] RO uint64 mon_current_input_bsn[31:0] +-- 5 [31:0] RO mon_current_input_bsn[63:32] +-- 6 [31:0] RO uint64 mon_input_bsn_at_sync[31:0] +-- 7 [31:0] RO mon_input_bsn_at_sync[63:32] +-- 8 [0] RO boolean mon_output_enable, '1' is on, '0' is FALSE is off +-- 9 [31:0] RO uint64 mon_output_sync_bsn[31:0] +-- 10 [31:0] RO mon_output_sync_bsn[63:32] +-- 11 [31:0] RO uint32 block_size[31:0] +-- +-- * Do not use reg_wr_arr[0] in dp_clk domain to detect ctrl_enable_evt, +-- because reg_wr_arr pulses occur before the reg_wr data arrives in the +-- dp_clk domain. Therefore instead use change in wr_ctrl_enable value +-- to detect ctrl_enable_evt. Hence a re-enable via MM now requires a +-- disable via MM first. +-- +-- Remarks: +-- * The actual bsn width is constrained by g_bsn_w. +-- * The MM interface accesses 64 bit words in two 32 bit MM word accesses. +-- This can result in a BSN value that is wrong if it happens to have +-- an increment carry from the low word to the high word. The BSN carry +-- occurs after every 2**32 blocks, so e.g. with a block period of 5.12 us +-- this is once every 6.1 hours. It is very unlikely that this will cause +-- a problem and if it does then a next attempt will succeed. Therefore +-- it is not necessary to ensure that the 64 values are accesses more +-- robustly (e.g. by only accepting them when the high word is accessed), +-- and therefore it is fine to use common_reg_r_w_dc for 64 bit BSN words. +-- + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE work.dp_stream_pkg.ALL; + + +ENTITY mmp_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, must be >= 2 + ); + PORT ( + -- Clocks and reset + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + dp_rst : IN STD_LOGIC; + dp_clk : IN STD_LOGIC; + + -- MM control + reg_mosi : IN t_mem_mosi := c_mem_mosi_rst; + reg_miso : OUT t_mem_miso; + + -- Streaming + in_sosi : IN t_dp_sosi; + out_sosi : OUT t_dp_sosi; + out_start : OUT STD_LOGIC; + out_enable : OUT STD_LOGIC + ); +END mmp_dp_bsn_sync_scheduler; + +ARCHITECTURE str OF mmp_dp_bsn_sync_scheduler IS + + -- 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, 4, c_word_w, 12, '0'); + + SIGNAL reg_wr_arr : STD_LOGIC_VECTOR(c_mm_reg.nof_dat -1 DOWNTO 0); + SIGNAL reg_wr : STD_LOGIC_VECTOR(c_mm_reg.nof_dat*c_mm_reg.dat_w-1 DOWNTO 0); + SIGNAL reg_rd : STD_LOGIC_VECTOR(c_mm_reg.nof_dat*c_mm_reg.dat_w-1 DOWNTO 0) := (OTHERS=>'0'); + + SIGNAL wr_ctrl_enable : STD_LOGIC; + SIGNAL wr_ctrl_enable_evt : STD_LOGIC; + SIGNAL ctrl_enable : STD_LOGIC := '0'; + SIGNAL ctrl_enable_evt : STD_LOGIC := '0'; + SIGNAL ctrl_interval_size : NATURAL; + SIGNAL ctrl_start_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL mon_current_input_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + SIGNAL mon_input_bsn_at_sync : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + SIGNAL mon_output_enable : STD_LOGIC; + SIGNAL mon_output_sync_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0); + + -- Resize BSN values to 64 bit + SIGNAL wr_start_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + SIGNAL rd_current_input_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + SIGNAL rd_input_bsn_at_sync_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + SIGNAL rd_output_sync_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + +BEGIN + + ctrl_start_bsn <= wr_start_bsn_64(g_bsn_w-1 DOWNTO 0); + + rd_current_input_bsn_64 <= RESIZE_UVEC(mon_current_input_bsn, 2*c_word_w); + rd_input_bsn_at_sync_64 <= RESIZE_UVEC(mon_input_bsn_at_sync, 2*c_word_w); + rd_output_sync_bsn_64 <= RESIZE_UVEC(mon_output_sync_bsn, 2*c_word_w); + + -- Register mapping + -- . Write + wr_ctrl_enable <= reg_wr( 0); + ctrl_interval_size <= TO_UINT(reg_wr( 2*c_word_w-1 DOWNTO 1*c_word_w)); + wr_start_bsn_64( c_word_w-1 DOWNTO 0) <= reg_wr( 3*c_word_w-1 DOWNTO 2*c_word_w); -- low word + wr_start_bsn_64(2*c_word_w-1 DOWNTO 1*c_word_w) <= reg_wr( 4*c_word_w-1 DOWNTO 3*c_word_w); -- high word + + -- Derive ctrl_enable_evt from change in wr_ctrl_enable, instead of using + -- reg_wr_arr(0), see description + u_common_evt : ENTITY common_lib.common_evt + GENERIC MAP ( + g_evt_type => "BOTH", + g_out_reg => TRUE + ) + PORT MAP ( + rst => dp_rst, + clk => dp_clk, + in_sig => wr_ctrl_enable, + out_evt => wr_ctrl_enable_evt + ); + + ctrl_enable <= wr_ctrl_enable WHEN rising_edge(dp_clk) AND wr_ctrl_enable_evt = '1'; + ctrl_enable_evt <= wr_ctrl_enable_evt WHEN rising_edge(dp_clk); + + -- . Read + reg_rd( 0) <= ctrl_enable; -- read back internal ctrl_enable + reg_rd( 2*c_word_w-1 DOWNTO 1*c_word_w) <= TO_UVEC(ctrl_interval_size, c_word_w); + reg_rd( 3*c_word_w-1 DOWNTO 2*c_word_w) <= wr_start_bsn_64( c_word_w-1 DOWNTO 0); -- low word + reg_rd( 4*c_word_w-1 DOWNTO 3*c_word_w) <= wr_start_bsn_64( 2*c_word_w-1 DOWNTO c_word_w); -- high word + reg_rd( 5*c_word_w-1 DOWNTO 4*c_word_w) <= rd_current_input_bsn_64( c_word_w-1 DOWNTO 0); -- low word + reg_rd( 6*c_word_w-1 DOWNTO 5*c_word_w) <= rd_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w); -- high word + reg_rd( 7*c_word_w-1 DOWNTO 6*c_word_w) <= rd_input_bsn_at_sync_64( c_word_w-1 DOWNTO 0); -- low word + reg_rd( 8*c_word_w-1 DOWNTO 7*c_word_w) <= rd_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w); -- high word + reg_rd( 8*c_word_w) <= mon_output_enable; + reg_rd(10*c_word_w-1 DOWNTO 9*c_word_w) <= rd_output_sync_bsn_64( c_word_w-1 DOWNTO 0); -- low word + reg_rd(11*c_word_w-1 DOWNTO 10*c_word_w) <= rd_output_sync_bsn_64( 2*c_word_w-1 DOWNTO c_word_w); -- high word + reg_rd(12*c_word_w-1 DOWNTO 11*c_word_w) <= TO_UVEC(g_block_size, c_word_w); + + u_common_reg_r_w_dc : 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_mosi, + sla_out => reg_miso, + + -- MM registers in st_clk domain + reg_wr_arr => reg_wr_arr, + reg_rd_arr => OPEN, + out_reg => reg_wr, -- readback via ST clock domain + in_reg => reg_rd + ); + + u_dp_bsn_sync_scheduler : ENTITY work.dp_bsn_sync_scheduler + GENERIC MAP ( + g_bsn_w => g_bsn_w, + g_block_size => g_block_size, + g_pipeline => 1 + ) + PORT MAP ( + rst => dp_rst, + clk => dp_clk, + + -- M&C + ctrl_enable => ctrl_enable, + ctrl_enable_evt => ctrl_enable_evt, + ctrl_interval_size => ctrl_interval_size, + ctrl_start_bsn => ctrl_start_bsn, + mon_current_input_bsn => mon_current_input_bsn, + mon_input_bsn_at_sync => mon_input_bsn_at_sync, + mon_output_enable => mon_output_enable, + mon_output_sync_bsn => mon_output_sync_bsn, + + -- Streaming + in_sosi => in_sosi, + out_sosi => out_sosi, + out_start => out_start, + out_enable => out_enable + ); + +END str; + 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 2e701d81502e0eb51d9ced01318b5187a8fde452..7f15f845fc365470ac223203310aa98097df13eb 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 @@ -46,7 +46,8 @@ USE dp_lib.tb_dp_pkg.ALL; ENTITY tb_dp_bsn_source_v2 IS GENERIC ( - g_pps_interval : NATURAL := 240; + g_nof_pps : NATURAL := 20; + g_pps_interval : NATURAL := 230; g_block_size : NATURAL := 32 ); END tb_dp_bsn_source_v2; @@ -97,6 +98,9 @@ ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS 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; BEGIN @@ -161,7 +165,7 @@ BEGIN dp_on_pps <= '0'; dp_on <= '1'; verify_sync <= '0'; -- only verify visualy in wave window - proc_common_wait_some_cycles(clk, 10*g_pps_interval); + 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; @@ -191,7 +195,7 @@ BEGIN dp_on_pps <= '1'; dp_on <= '1'; verify_sync <= '1'; -- verify automatically in test bench - proc_common_wait_some_cycles(clk, 10*g_pps_interval); + 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; @@ -219,6 +223,23 @@ BEGIN 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, + g_block_size, + clk, + verify_sync, + bs_sosi.sync, + bs_sosi.sop, + bs_sosi.bsn, + dbg_nof_blk, + dbg_accumulate, + dbg_expected_bsn); + ----------------------------------------------------------------------------- -- DUT: dp_bsn_source_v2 ----------------------------------------------------------------------------- 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 new file mode 100644 index 0000000000000000000000000000000000000000..a94570c92e294b528d12f0d07819f6dad441fb7e --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd @@ -0,0 +1,570 @@ +------------------------------------------------------------------------------- +-- -------------------------------------------------------------------------- +-- 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 +-- 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_test_intervals +-- * c_ctrl_start_bsn must be in the future, else output remains disabled +-- * 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 4 +-- > run -all +-- View in Wave window u_dut: r, nxt_r, and tb: in_sosi, out_sosi, +-- out_sync, out_start +-- +-- Development steps: +-- . Step 1 ~1 day work (idea started earlier, so requirements were clear), +-- steps 2,5 ~2 days work (fixing details and verifying all features), +-- steps 3,4 ~1 day work, +-- total ~4 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 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 Add tb generics and tb_tb_dp_bsn_sync_scheduler.vhd +-- 4a Add mmp_dp_bsn_sync_scheduler.vhd with MM register and +-- tb_mmp_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 ASSERT condition +-- b Initialy used LOOP in p_stimuli to repeat test. Later used list of +-- 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_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; + + -- Output sync period + g_nof_samples_per_output_sync : NATURAL := 45 -- 45 / g_block_size = 4.5 + ); +END tb_dp_bsn_sync_scheduler; + +ARCHITECTURE tb OF tb_dp_bsn_sync_scheduler IS + + CONSTANT c_clk_period : TIME := 10 ns; + CONSTANT c_bsn_w : NATURAL := 31; + CONSTANT c_dut_latency : NATURAL := 1; + + -- Test intervals + 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; + CONSTANT c_enable_init_nof_bsn : NATURAL := ceil_value(c_output_nof_blocks_min / g_block_size + 10, g_nof_block_per_input_sync); + + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC := '1'; + SIGNAL cnt : 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 + e_dis, -- disable + e_en, -- enable + e_re, -- re-enable + e_lost, -- lost input blocks + e_old -- start bsn in passed + ); + SIGNAL stimuli_state : t_stimuli_state_enum := e_dis; -- to show what tb does in Wave window + + -- Stimuli + SIGNAL ctrl_enable : STD_LOGIC := '0'; + SIGNAL ctrl_enable_evt : STD_LOGIC := '0'; + 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_current_input_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 + + -- Input stimuli + SIGNAL in_lost : STD_LOGIC := '0'; + SIGNAL in_sosi : t_dp_sosi := c_dp_sosi_init; -- = stimuli_sosi, with option for lost blocks + SIGNAL in_sync : STD_LOGIC; + + -- 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_enable : STD_LOGIC; + SIGNAL out_sosi : t_dp_sosi := c_dp_sosi_init; + + -- 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 recover_from_in_lost : 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 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 dbg_out_sosi_sync : STD_LOGIC; + SIGNAL dbg_out_sosi_sop : STD_LOGIC; + SIGNAL dbg_out_sosi_bsn : NATURAL; + SIGNAL dbg_nof_blk : NATURAL := 0; + SIGNAL dbg_accumulate : NATURAL := 0; + SIGNAL dbg_expected_bsn : NATURAL := 0; + + -- Local procedures + PROCEDURE proc_output_enable(SIGNAL clk : IN STD_LOGIC; + SIGNAL cnt : IN INTEGER; + SIGNAL sync : IN STD_LOGIC; + SIGNAL mon_input_bsn_at_sync : IN STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); + SIGNAL stimuli_state : OUT t_stimuli_state_enum; + SIGNAL ctrl_start_bsn : OUT STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); + SIGNAL ctrl_enable : OUT STD_LOGIC; + SIGNAL ctrl_enable_evt : OUT STD_LOGIC) IS + BEGIN + proc_common_wait_until_hi_lo(clk, sync); -- (re)enable at begin of sync interval + stimuli_state <= e_en; + ctrl_start_bsn <= ADD_UVEC(mon_input_bsn_at_sync, TO_UVEC(c_enable_init_nof_bsn, c_natural_w)); -- determine BSN in the future + ctrl_enable <= '1'; + ctrl_enable_evt <= '1'; + proc_common_wait_some_cycles(clk, 1); + ctrl_enable_evt <= '0'; + END proc_output_enable; + + PROCEDURE proc_output_re_enable(SIGNAL clk : IN STD_LOGIC; + SIGNAL cnt : IN INTEGER; + SIGNAL mon_input_bsn_at_sync : IN STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); + SIGNAL stimuli_state : OUT t_stimuli_state_enum; + SIGNAL ctrl_start_bsn : OUT STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0); + SIGNAL ctrl_enable : OUT STD_LOGIC; + SIGNAL ctrl_enable_evt : OUT STD_LOGIC) IS + BEGIN + proc_output_enable(clk, cnt, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); + stimuli_state <= e_re; + END proc_output_re_enable; + + PROCEDURE proc_output_disable(SIGNAL stimuli_state : OUT t_stimuli_state_enum; + SIGNAL ctrl_enable : OUT STD_LOGIC; + SIGNAL ctrl_enable_evt : OUT STD_LOGIC) IS + BEGIN + stimuli_state <= e_dis; + ctrl_enable <= '0'; + ctrl_enable_evt <= '1'; + proc_common_wait_some_cycles(clk, 1); + ctrl_enable_evt <= '0'; + END proc_output_disable; + +BEGIN + + ------------------------------------------------------------------------------ + -- Clock & reset + ------------------------------------------------------------------------------ + clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; + rst <= '1', '0' AFTER c_clk_period*7; + cnt <= cnt + 1 WHEN rising_edge(clk); + + ------------------------------------------------------------------------------ + -- Stimuli: + ------------------------------------------------------------------------------ + + p_stimuli : PROCESS + BEGIN + stimuli_state <= e_dis; + proc_common_wait_until_low(clk, rst); + + ------------------------------------------------------------------------------ + -- Enable and disable output + ------------------------------------------------------------------------------ + test_interval <= 0; proc_common_wait_some_cycles(clk, 1); + + 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, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); + + -- 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, ctrl_enable_evt); + + ------------------------------------------------------------------------------ + -- Re enable output when already enabled + ------------------------------------------------------------------------------ + test_interval <= test_interval + 1; proc_common_wait_some_cycles(clk, 1); + + -- 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, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); + + -- 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 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, ctrl_enable_evt); + + ------------------------------------------------------------------------------ + -- Lost input blocks + ------------------------------------------------------------------------------ + test_interval <= test_interval + 1; proc_common_wait_some_cycles(clk, 1); + + -- 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, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt); + + -- 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 + recover_from_in_lost <= '1'; + 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) + FOR I IN 0 TO c_nof_lost_input_blocks / c_output_nof_blocks_min + 5 LOOP -- + for some extra margin + proc_common_wait_some_cycles(clk, 1); + END LOOP; + recover_from_in_lost <= '0'; + + -- 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, ctrl_enable_evt); + + WAIT; + END PROCESS; + + -- Generate data blocks with input sync + u_stimuli : ENTITY work.dp_stream_stimuli + GENERIC MAP ( + 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 => g_block_size, + g_pkt_gap => g_input_gap_size + ) + PORT MAP ( + rst => rst, + clk => clk, + + -- Generate stimuli + src_out => stimuli_sosi, + + -- End of stimuli + tb_end => tb_end + ); + + -- Input with option to loose data blocks + p_in_sosi : PROCESS(stimuli_sosi, in_lost) + BEGIN + in_sosi <= stimuli_sosi; + IF in_lost = '1' THEN + in_sosi.sync <= '0'; + in_sosi.sop <= '0'; + in_sosi.eop <= '0'; + in_sosi.valid <= '0'; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- Verification + ----------------------------------------------------------------------------- + + -- separate signal for easier viewing in Wave window + stimuli_sync <= stimuli_sosi.sync; + in_sync <= in_sosi.sync; + out_sync <= out_sosi.sync; + + ----------------------------------------------------------------------------- + -- . Verify out_enable + ----------------------------------------------------------------------------- + p_hold_out_eop : PROCESS(clk) + 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; + + -- 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 + IF ctrl_enable = '1' THEN + IF UNSIGNED(in_sosi.bsn) >= UNSIGNED(ctrl_start_bsn) THEN + expected_out_enable <= '1'; + END IF; + END IF; + END PROCESS; + + 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 + REPORT "Unexpected enabled out_enable" SEVERITY ERROR; + ELSE + REPORT "Unexpected disabled out_enable" SEVERITY ERROR; + END IF; + END IF; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- . Verify that there was valid output + ----------------------------------------------------------------------------- + + out_sop_cnt <= out_sop_cnt + 1 WHEN rising_edge(clk) AND out_sosi.sop = '1'; + + p_verify_output : PROCESS + BEGIN + WAIT UNTIL tb_end = '1'; + ASSERT out_sop_cnt > 0 REPORT "There was no output." SEVERITY ERROR; + WAIT; + END PROCESS; + + ----------------------------------------------------------------------------- + -- . Verify out_start + ----------------------------------------------------------------------------- + + p_expecting_out_start : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + IF ctrl_enable = '1' AND ctrl_enable_evt = '1' THEN + expecting_out_start <= '1'; + ELSIF out_start = '1' THEN + expecting_out_start <= '0'; + END IF; + END IF; + END PROCESS; + + prev_out_enable <= out_enable WHEN rising_edge(clk); + + p_verify_out_start : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + -- Check that out_sync is active at out_start + IF out_start = '1' THEN + IF out_sync = '0' THEN + REPORT "Missing out_sync at out_start" SEVERITY ERROR; + END IF; + END IF; + + -- Check unexpected out_start + IF out_start = '1' AND expecting_out_start = '0' THEN + REPORT "Unexpected out_start" SEVERITY ERROR; + END IF; + + -- Check that out_start is active when out_enable goes high + IF out_start = '0' AND out_enable = '1' AND prev_out_enable = '0' THEN + REPORT "Missed out_start" SEVERITY ERROR; + END IF; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- . Verify out_sosi = in_sosi, for all fields except out_sosi.sync + ----------------------------------------------------------------------------- + -- Use registered values for func_dp_stream_slv_to_integer(), to avoid + -- Warning: NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0 at + -- Time: 0 fs in Transcript window. Using only c_dp_sosi_init at signal + -- 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); + + proc_dp_verify_sosi_equal( "bsn", clk, verify_sosi_equal, 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); + + -- 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); + + ----------------------------------------------------------------------------- + -- . 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 + ----------------------------------------------------------------------------- + verify_sync <= NOT recover_from_in_lost; + + proc_dp_verify_sync(TO_UINT(ctrl_start_bsn), + ctrl_interval_size, + g_block_size, + clk, + verify_sync, + out_sosi.sync, + out_sosi.sop, + out_sosi.bsn, + dbg_nof_blk, + dbg_accumulate, + dbg_expected_bsn); + + dbg_out_sosi_sync <= out_sosi.sync; + dbg_out_sosi_sop <= out_sosi.sop; + dbg_out_sosi_bsn <= TO_UINT(out_sosi.bsn); + + ----------------------------------------------------------------------------- + -- . 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 recover_from_in_lost = '0' THEN + IF out_sop_dly = '1' THEN + v_bsn_min := TO_UINT(mon_current_input_bsn) - 1; + v_bsn_max := TO_UINT(mon_current_input_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 (" & int_to_str(TO_UINT(mon_output_sync_bsn)) & " < " & int_to_str(v_bsn_min) & ")" 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_current_input_bsn)) SEVERITY NOTE; + END IF; + END IF; + END IF; + END PROCESS; + + + ----------------------------------------------------------------------------- + -- DUT: dp_bsn_sync_scheduler + ----------------------------------------------------------------------------- + + dut : ENTITY work.dp_bsn_sync_scheduler + GENERIC MAP ( + g_bsn_w => c_bsn_w, + g_block_size => g_block_size, + g_pipeline => 0 + ) + PORT MAP ( + rst => rst, + clk => clk, + + -- M&C + ctrl_enable => ctrl_enable, + ctrl_enable_evt => ctrl_enable_evt, + ctrl_interval_size => ctrl_interval_size, + ctrl_start_bsn => ctrl_start_bsn, + mon_current_input_bsn => mon_current_input_bsn, + mon_input_bsn_at_sync => mon_input_bsn_at_sync, + mon_output_enable => mon_output_enable, + mon_output_sync_bsn => mon_output_sync_bsn, + + -- Streaming + in_sosi => in_sosi, + out_sosi => out_sosi, + out_start => out_start, + out_enable => out_enable + ); + +END tb; diff --git a/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd b/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd index 381a32262b5abb01aeb880676d4fe1a6f74bdbbb..6e60576827b6b82a8cf5868f626744dfcf28322a 100644 --- a/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd +++ b/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd @@ -444,6 +444,12 @@ PACKAGE tb_dp_pkg IS SIGNAL verify_en : IN STD_LOGIC; SIGNAL res_data : IN STD_LOGIC_VECTOR); + PROCEDURE proc_dp_verify_sosi_equal(CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL dut_sosi : IN t_dp_sosi_integer; -- use func_dp_stream_slv_to_integer for conversion + SIGNAL exp_sosi : IN t_dp_sosi_integer); -- use func_dp_stream_slv_to_integer for conversion + PROCEDURE proc_dp_verify_valid(CONSTANT c_ready_latency : IN NATURAL; SIGNAL clk : IN STD_LOGIC; SIGNAL verify_en : IN STD_LOGIC; @@ -458,30 +464,43 @@ PACKAGE tb_dp_pkg IS SIGNAL out_val : IN STD_LOGIC); -- Verify the DUT output sync - PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN NATURAL; - CONSTANT c_sync_offset : IN NATURAL; - SIGNAL clk : IN STD_LOGIC; + PROCEDURE proc_dp_verify_sync(SIGNAL clk : IN STD_LOGIC; SIGNAL verify_en : IN STD_LOGIC; SIGNAL sync : IN STD_LOGIC; SIGNAL sop : IN STD_LOGIC; - SIGNAL bsn : IN STD_LOGIC_VECTOR); + expected_sync : IN STD_LOGIC); --- Verify the DUT output sync PROCEDURE proc_dp_verify_sync(SIGNAL clk : IN STD_LOGIC; SIGNAL verify_en : IN STD_LOGIC; SIGNAL sync : IN STD_LOGIC; SIGNAL sop : IN STD_LOGIC; - SIGNAL expected_sync : IN STD_LOGIC); + bsn : IN NATURAL; -- for reporting + expected_bsn : IN NATURAL; -- for reporting + expected_sync : IN STD_LOGIC); + -- Note: A SIGNAL IN can only connect a SIGNAL. Therefore define IN as + -- default (= CONSTANT) instead of SIGNAL to be able to connect + -- VARIABLE or SIGNAL. - -- Verify the DUT output sync - PROCEDURE proc_dp_verify_sync_v2(CONSTANT c_sync_period : IN NATURAL; - CONSTANT c_sync_offset : IN NATURAL; - SIGNAL clk : IN STD_LOGIC; - SIGNAL verify_en : IN STD_LOGIC; - SIGNAL sync : IN STD_LOGIC; - SIGNAL sop : IN STD_LOGIC; - SIGNAL bsn : IN STD_LOGIC_VECTOR; - SIGNAL tb_bsn_cnt : INOUT INTEGER); + PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN NATURAL; + CONSTANT c_sync_offset : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL bsn : IN STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_sync(CONSTANT c_start_bsn : IN NATURAL; + CONSTANT c_sync_period : IN NATURAL; + CONSTANT c_block_size : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL bsn : IN STD_LOGIC_VECTOR; + -- for debug purposes + SIGNAL dbg_nof_blk : OUT NATURAL; + SIGNAL dbg_accumulate : OUT NATURAL; + SIGNAL dbg_expected_bsn : OUT NATURAL); -- Verify the DUT output sop and eop PROCEDURE proc_dp_verify_sop_and_eop(CONSTANT c_ready_latency : IN NATURAL; @@ -2155,6 +2174,78 @@ PACKAGE BODY tb_dp_pkg IS END IF; END proc_dp_verify_other_sosi; + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify per field whether DUT output SOSI is equal to expected SOSI + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_sosi_equal(CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL dut_sosi : IN t_dp_sosi_integer; -- use func_dp_stream_slv_to_integer for conversion + SIGNAL exp_sosi : IN t_dp_sosi_integer) IS -- use func_dp_stream_slv_to_integer for conversion + BEGIN + -- Use sosi integers, instead of sosi slv, for easier comparision. This + -- implies that only integer low part of sosi slv fields is checked, so + -- NATURAL'width = 31 bit for control and info and c_dat_w <= 32 bits for + -- data, re, im (c_dat_w is a parameter of func_dp_stream_slv_to_integer). + IF rising_edge(clk) THEN + IF verify_en='1' THEN + -- sosi ctrl fields + IF c_str="sync" THEN + IF dut_sosi.sync /= exp_sosi.sync THEN + REPORT "DP : Wrong dut_sosi.sync (" & sl_to_str(dut_sosi.sync) & " /= " & sl_to_str(exp_sosi.sync) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="sop" THEN + IF dut_sosi.sop /= exp_sosi.sop THEN + REPORT "DP : Wrong dut_sosi.sop (" & sl_to_str(dut_sosi.sop) & " /= " & sl_to_str(exp_sosi.sop) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="eop" THEN + IF dut_sosi.eop /= exp_sosi.eop THEN + REPORT "DP : Wrong dut_sosi.eop (" & sl_to_str(dut_sosi.eop) & " /= " & sl_to_str(exp_sosi.eop) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="valid" THEN + IF dut_sosi.valid /= exp_sosi.valid THEN + REPORT "DP : Wrong dut_sosi.valid (" & sl_to_str(dut_sosi.valid) & " /= " & sl_to_str(exp_sosi.valid) & ")" SEVERITY ERROR; + END IF; + + -- sosi info fields + ELSIF c_str="bsn" THEN + IF dut_sosi.bsn /= exp_sosi.bsn THEN + REPORT "DP : Wrong dut_sosi.bsn (" & int_to_str(dut_sosi.bsn) & " /= " & int_to_str(exp_sosi.bsn) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="empty" THEN + IF dut_sosi.empty /= exp_sosi.empty THEN + REPORT "DP : Wrong dut_sosi.empty (" & int_to_str(dut_sosi.empty) & " /= " & int_to_str(exp_sosi.empty) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="channel" THEN + IF dut_sosi.channel /= exp_sosi.channel THEN + REPORT "DP : Wrong dut_sosi.channel (" & int_to_str(dut_sosi.channel) & " /= " & int_to_str(exp_sosi.channel) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="err" THEN + IF dut_sosi.err /= exp_sosi.err THEN + REPORT "DP : Wrong dut_sosi.err (" & int_to_str(dut_sosi.err) & " /= " & int_to_str(exp_sosi.err) & ")" SEVERITY ERROR; + END IF; + + -- sosi data fields + ELSIF c_str="data" THEN + IF dut_sosi.data /= exp_sosi.data THEN + REPORT "DP : Wrong dut_sosi.data (" & int_to_str(dut_sosi.data) & " /= " & int_to_str(exp_sosi.data) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="re" THEN + IF dut_sosi.re /= exp_sosi.re THEN + REPORT "DP : Wrong dut_sosi.re (" & int_to_str(dut_sosi.re) & " /= " & int_to_str(exp_sosi.re) & ")" SEVERITY ERROR; + END IF; + ELSIF c_str="im" THEN + IF dut_sosi.im /= exp_sosi.im THEN + REPORT "DP : Wrong dut_sosi.im (" & int_to_str(dut_sosi.im) & " /= " & int_to_str(exp_sosi.im) & ")" & ")" SEVERITY ERROR; + END IF; + + -- unknown sosi field + ELSE + REPORT "proc_dp_verify_sosi_equal : Unknown sosi." & c_str & "field" SEVERITY FAILURE; + END IF; + END IF; + END IF; + END proc_dp_verify_sosi_equal; ------------------------------------------------------------------------------ -- PROCEDURE: Verify the DUT output valid @@ -2208,30 +2299,25 @@ PACKAGE BODY tb_dp_pkg IS ------------------------------------------------------------------------------ -- PROCEDURE: Verify the DUT output sync -- . sync is defined such that it can only be active at sop - -- . assume that the sync occures priodically at bsn MOD c_sync_period = c_sync_offset + -- . report expected_sync from input ------------------------------------------------------------------------------ - PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN NATURAL; -- BSN sync period - CONSTANT c_sync_offset : IN NATURAL; -- BSN sync offset - SIGNAL clk : IN STD_LOGIC; + PROCEDURE proc_dp_verify_sync(SIGNAL clk : IN STD_LOGIC; SIGNAL verify_en : IN STD_LOGIC; SIGNAL sync : IN STD_LOGIC; SIGNAL sop : IN STD_LOGIC; - SIGNAL bsn : IN STD_LOGIC_VECTOR) IS - CONSTANT c_bsn_w : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH); -- use maximally 31 bit of BSN slv to allow calculations with integers - VARIABLE v_expected_sync : BOOLEAN; + expected_sync : IN STD_LOGIC) IS BEGIN IF rising_edge(clk) THEN IF verify_en='1' THEN - v_expected_sync := (TO_UINT(bsn(c_bsn_w-1 DOWNTO 0))-c_sync_offset) MOD c_sync_period = 0; -- Check for unexpected sync IF sync='1' THEN - ASSERT v_expected_sync = TRUE + ASSERT expected_sync='1' REPORT "Error: Unexpected sync at BSN" SEVERITY ERROR; ASSERT sop = '1' REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR; END IF; -- Check for missing sync - IF sop='1' AND v_expected_sync=TRUE THEN + IF sop='1' AND expected_sync='1' THEN ASSERT sync = '1' REPORT "Error: Missing sync" SEVERITY ERROR; END IF; @@ -2239,23 +2325,20 @@ PACKAGE BODY tb_dp_pkg IS END IF; END proc_dp_verify_sync; - - ------------------------------------------------------------------------------ - -- PROCEDURE: Verify the DUT output sync - -- . sync is defined such that it can only be active at sop - ------------------------------------------------------------------------------ PROCEDURE proc_dp_verify_sync(SIGNAL clk : IN STD_LOGIC; SIGNAL verify_en : IN STD_LOGIC; SIGNAL sync : IN STD_LOGIC; SIGNAL sop : IN STD_LOGIC; - SIGNAL expected_sync : IN STD_LOGIC) IS + bsn : IN NATURAL; -- for reporting + expected_bsn : IN NATURAL; -- for reporting + expected_sync : IN STD_LOGIC) IS BEGIN IF rising_edge(clk) THEN IF verify_en='1' THEN -- Check for unexpected sync IF sync='1' THEN ASSERT expected_sync='1' - REPORT "Error: Unexpected sync at BSN" SEVERITY ERROR; + REPORT "Error: Unexpected sync at BSN (" & int_to_str(bsn) & " /= " & int_to_str(expected_bsn) & ")" SEVERITY ERROR; ASSERT sop = '1' REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR; END IF; @@ -2268,79 +2351,87 @@ PACKAGE BODY tb_dp_pkg IS END IF; END proc_dp_verify_sync; - ------------------------------------------------------------------------------ -- PROCEDURE: Verify the DUT output sync -- . sync is defined such that it can only be active at sop -- . assume that the sync occures priodically at bsn MOD c_sync_period = c_sync_offset ------------------------------------------------------------------------------ - PROCEDURE proc_dp_verify_sync_v2(CONSTANT c_sync_period : IN NATURAL; -- BSN sync period - CONSTANT c_sync_offset : IN NATURAL; -- BSN sync offset - SIGNAL clk : IN STD_LOGIC; - SIGNAL verify_en : IN STD_LOGIC; - SIGNAL sync : IN STD_LOGIC; - SIGNAL sop : IN STD_LOGIC; - SIGNAL bsn : IN STD_LOGIC_VECTOR; - SIGNAL tb_bsn_cnt : INOUT INTEGER) IS - CONSTANT c_bsn_w : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH); -- use maximally 31 bit of BSN slv to allow calculations with integers - + PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN NATURAL; -- BSN sync period + CONSTANT c_sync_offset : IN NATURAL; -- BSN sync offset + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL bsn : IN STD_LOGIC_VECTOR) IS + CONSTANT c_bsn_w : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH); -- use maximally c_natural_w = 31 bit of BSN slv to allow calculations with integers + VARIABLE v_bsn : NATURAL := TO_UINT(bsn(c_bsn_w-1 DOWNTO 0)); VARIABLE v_expected_sync : BOOLEAN; - VARIABLE v_tb_bsn_cnt : INTEGER; BEGIN - IF rising_edge(clk) THEN - IF verify_en='1' THEN - -- Determine whether sync is expected at this bsn - - v_expected_sync := FALSE; - v_tb_bsn_cnt := tb_bsn_cnt; -- assign signal to variable - - -- on sync check if tb_bsn_cnt is a valid value - -- valid is c_sync_period or c_sync_period-1 - IF sync='1' THEN - IF v_tb_bsn_cnt=c_sync_period OR v_tb_bsn_cnt=c_sync_period-1 THEN - v_expected_sync := TRUE; - --REPORT "bsn count valid " & int_to_str(v_tb_bsn_cnt); - ELSE - v_expected_sync := FALSE; - END IF; - - v_tb_bsn_cnt := 0; -- reset tb_bsn_cnt - END IF; - - -- on sop increment tb_bsn_cnt by 1 - IF sop='1' THEN - v_tb_bsn_cnt := v_tb_bsn_cnt + 1; - END IF; - - -- if bsn = 0 (when in dp_off state) set tb_bsn_cnt also to 0 - IF TO_UINT(bsn(c_bsn_w-1 DOWNTO 0)) = c_sync_offset THEN - v_tb_bsn_cnt := 0; - END IF; - - tb_bsn_cnt <= v_tb_bsn_cnt; -- assign variable to signal + -- Determine v_expected_sync + v_expected_sync := (v_bsn - c_sync_offset) MOD c_sync_period = 0; + -- Report sync and v_expected_sync + proc_dp_verify_sync(clk, verify_en, sync, sop, to_sl(v_expected_sync)); + END proc_dp_verify_sync; - -- Check for unexpected sync - IF sync='1' THEN - ASSERT v_expected_sync = TRUE - REPORT "Error: Unexpected BSN count " & int_to_str(v_tb_bsn_cnt) SEVERITY ERROR; - ASSERT sop = '1' - REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR; - END IF; - -- Check for missing sync - IF sop='1' AND v_expected_sync=TRUE THEN - ASSERT sync = '1' - REPORT "Error: Missing sync" SEVERITY ERROR; - END IF; - -- Check for missing sync - IF sop='1' THEN - ASSERT v_tb_bsn_cnt <= c_sync_period - REPORT "Error: bsn count " & int_to_str(v_tb_bsn_cnt) & " > " & int_to_str(c_sync_period) SEVERITY ERROR; + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output sync + -- . sync is defined such that it can only be active at sop + -- . assume that the fractional sync period varies between N and N-1 blocks + -- . the fractional sync period starts with N blocks and fits e.g. + -- dp_bsn_source_v2, dp_bsn_sync_scheduler. + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_sync(CONSTANT c_start_bsn : IN NATURAL; -- BSN of first sync, start of fractional periods + CONSTANT c_sync_period : IN NATURAL; -- number of sample per sync period + CONSTANT c_block_size : IN NATURAL; -- number of sample per block + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL bsn : IN STD_LOGIC_VECTOR; + -- for debug purposes + SIGNAL dbg_nof_blk : OUT NATURAL; + SIGNAL dbg_accumulate : OUT NATURAL; + SIGNAL dbg_expected_bsn : OUT NATURAL) IS + CONSTANT c_bsn_w : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH); -- use maximally c_natural_w = 31 bit of BSN slv to allow calculations with integers + CONSTANT c_nof_blk_min : NATURAL := c_sync_period / c_block_size; -- minimum number of blocks in sync period + CONSTANT c_extra : NATURAL := c_sync_period MOD c_block_size; -- number of extra samples in sync period + VARIABLE v_bsn : NATURAL := TO_UINT(bsn(c_bsn_w-1 DOWNTO 0)); + VARIABLE v_expected_sync : BOOLEAN := FALSE; -- default FALSE, e.g. when bsn < c_start_bsn is in the past + VARIABLE v_expected_bsn : NATURAL := c_start_bsn; -- BSN that is expected to have a sync, intialize with start BSN + VARIABLE v_nof_blk : NATURAL := c_nof_blk_min + 1; -- number of blocks in period, first sync period will be 1 block longer to achieve the fraction part + VARIABLE v_accumulate : INTEGER := c_block_size - c_extra; -- number of extra samples in period, first sync period will have v_accumulate more + BEGIN + -- Determine v_expected_sync + IF c_extra = 0 THEN + -- The sync period contains an integer number of blocks (c_extra = 0) + -- Determine directly whether the input bsn is expected to have a sync + v_expected_sync := ((v_bsn - c_start_bsn) MOD c_nof_blk_min = 0); + ELSE + -- The sync period contains a fractional number of blocks + -- Determine next expected BSN with sync until the input bsn is reached using a loop + WHILE v_expected_bsn < v_bsn LOOP + v_expected_bsn := v_expected_bsn + v_nof_blk; -- next expected BSN to have a sync + + v_nof_blk := c_nof_blk_min; + v_accumulate := v_accumulate - c_extra; + IF v_accumulate < 0 THEN + v_nof_blk := v_nof_blk + 1; + v_accumulate := v_accumulate + c_block_size; END IF; + END LOOP; + v_expected_sync := (v_bsn = v_expected_bsn); + END IF; - END IF; + IF verify_en = '1' THEN + -- Debug signals, for view in Wave window + dbg_nof_blk <= v_nof_blk; + dbg_accumulate <= v_accumulate; + dbg_expected_bsn <= v_expected_bsn; END IF; - END proc_dp_verify_sync_v2; + -- Report sync and v_expected_sync + proc_dp_verify_sync(clk, verify_en, sync, sop, v_bsn, v_expected_bsn, to_sl(v_expected_sync)); + END proc_dp_verify_sync; ------------------------------------------------------------------------------ -- PROCEDURE: Verify the DUT output sop and eop diff --git a/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd new file mode 100644 index 0000000000000000000000000000000000000000..ce4e5f605e7efbba13cb3ec7a972a5d9b9548805 --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd @@ -0,0 +1,405 @@ +-- -------------------------------------------------------------------------- +-- 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 aug 2021 +-- Purpose: Verify MM part of mmp_dp_bsn_sync_scheduler +-- Description: +-- The functional part is already verified by tb_tb_dp_bsn_sync_scheduler.vhd. +-- Usage: +-- > as 5 +-- > run -all +-- . View *_64 BSN values as radix hex in Wave window to recognize BSN hi word. + +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_sync_scheduler IS +END tb_mmp_dp_bsn_sync_scheduler; + + +ARCHITECTURE tb OF tb_mmp_dp_bsn_sync_scheduler 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_input_sync : NATURAL := 17; + CONSTANT c_nof_block_per_output_sync : NATURAL := 5; + CONSTANT c_block_size : NATURAL := 10; + CONSTANT c_input_gap_size : NATURAL := 3; + CONSTANT c_sim_nof_blocks : NATURAL := c_nof_block_per_input_sync * c_nof_input_sync; + + -- DUT settings + CONSTANT c_bsn_w : NATURAL := 40; + CONSTANT c_bsn_hi_value : NATURAL := 23; + CONSTANT c_ctrl_interval_size : NATURAL := c_nof_block_per_output_sync * c_block_size; + CONSTANT c_ctrl_start_bsn_lo : NATURAL := 19; + CONSTANT c_ctrl_start_bsn_hi : NATURAL := 17; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL stimuli_end : STD_LOGIC := '0'; + SIGNAL mm_clk : STD_LOGIC := '1'; + SIGNAL mm_rst : STD_LOGIC := '1'; + SIGNAL dp_clk : STD_LOGIC := '1'; + SIGNAL dp_rst : STD_LOGIC := '1'; + + SIGNAL reg_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL reg_miso : t_mem_miso; + + SIGNAL ctrl_start_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + + SIGNAL mon_output_enable : STD_LOGIC; + SIGNAL mon_current_input_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + SIGNAL mon_input_bsn_at_sync_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + SIGNAL mon_output_sync_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0); + SIGNAL mon_block_size : NATURAL; + + SIGNAL stimuli_sosi : t_dp_sosi; + SIGNAL in_sosi : t_dp_sosi; + SIGNAL out_sosi : t_dp_sosi; + SIGNAL out_start : STD_LOGIC; + SIGNAL out_enable : STD_LOGIC; + + SIGNAL verify_bsn_hi : STD_LOGIC := '0'; + +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); + + --------------------------------------------------------------------------- + -- Initial check + --------------------------------------------------------------------------- + -- . Read mon_block_size + proc_mem_mm_bus_rd(11, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_block_size <= TO_UINT(reg_miso.rddata(c_word_w-1 DOWNTO 0)); + + -- . Verify mon_block_size + proc_common_wait_some_cycles(mm_clk, 1); + ASSERT mon_block_size = c_block_size REPORT "Wrong block_size." SEVERITY ERROR; + + -- . Read mon_output_enable + proc_mem_mm_bus_rd(8, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_enable <= reg_miso.rddata(0); + + -- . Verify output is off + proc_common_wait_some_cycles(mm_clk, 1); + ASSERT mon_output_enable = '0' REPORT "DUT output is enabled." SEVERITY ERROR; + + --------------------------------------------------------------------------- + -- Verify c_ctrl_start_bsn_hi + --------------------------------------------------------------------------- + + -- . Write ctrl_start_bsn + proc_mem_mm_bus_wr(2, c_ctrl_start_bsn_lo, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_wr(3, c_ctrl_start_bsn_hi, mm_clk, reg_miso, reg_mosi); + proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency); + proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency); + + -- . Read back ctrl_start_bsn + proc_mem_mm_bus_rd(2, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + ctrl_start_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(3, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + ctrl_start_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + proc_common_wait_some_cycles(mm_clk, 1); + ASSERT c_ctrl_start_bsn_lo = TO_UINT(ctrl_start_bsn_64( c_word_w-1 DOWNTO 0)) REPORT "Wrong ctrl_start_bsn low word." SEVERITY ERROR; + ASSERT c_ctrl_start_bsn_hi = TO_UINT(ctrl_start_bsn_64(2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong ctrl_start_bsn high word." SEVERITY ERROR; + + --------------------------------------------------------------------------- + -- Setup, enable and verify DUT output + --------------------------------------------------------------------------- + -- . Read mon_current_input_bsn_64 + proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_common_wait_some_cycles(mm_clk, 1); + + -- . Select start BSN in the future + v_bsn := TO_UINT(mon_current_input_bsn_64) + 20; + + -- . Setup output sync interval + proc_mem_mm_bus_wr(1, c_ctrl_interval_size, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_wr(2, v_bsn, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_wr(3, 0, mm_clk, reg_miso, reg_mosi); + proc_common_wait_some_cycles(dp_clk, c_block_size*10); + + -- . Enable output + proc_mem_mm_bus_wr(0, 1, mm_clk, reg_miso, reg_mosi); + proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency); + proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency); + + proc_common_wait_some_cycles(dp_clk, c_block_size*10); + + -- . Read back ctrl_start_bsn + proc_mem_mm_bus_rd(2, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + ctrl_start_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(3, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + ctrl_start_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + -- . Read mon_current_input_bsn_64 + proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + -- . Read mon_input_bsn_at_sync_64 + proc_mem_mm_bus_rd(6, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_input_bsn_at_sync_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(7, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + -- . Read mon_output_sync_bsn_64 + proc_mem_mm_bus_rd(9, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_sync_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(10, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_sync_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + -- . Read mon_output_enable + proc_mem_mm_bus_rd(8, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_enable <= reg_miso.rddata(0); + + -- Verify output is on and running + proc_common_wait_some_cycles(mm_clk, 1); + ASSERT mon_output_enable = '1' REPORT "mon_output_enable is not enabled." SEVERITY ERROR; + ASSERT out_enable = '1' REPORT "output_enable is not enabled." SEVERITY ERROR; + + --------------------------------------------------------------------------- + -- Check that monitor BSN are incrementing + --------------------------------------------------------------------------- + proc_common_wait_some_cycles(mm_clk, c_ctrl_interval_size * 3); + + -- . Check mon_current_input_bsn_64 + v_bsn := TO_UINT(mon_current_input_bsn_64); + proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_common_wait_some_cycles(mm_clk, 1); + IF c_report_note THEN + REPORT "mon_current_input_bsn : " & int_to_str(v_bsn) & ", " & int_to_str(TO_UINT(mon_current_input_bsn_64)) SEVERITY NOTE; + END IF; + ASSERT v_bsn < TO_UINT(mon_current_input_bsn_64) REPORT "DUT mon_current_input_bsn is not incrementing." SEVERITY ERROR; + + -- . Check mon_input_bsn_at_sync_64 + v_bsn := TO_UINT(mon_input_bsn_at_sync_64); + proc_mem_mm_bus_rd(6, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_input_bsn_at_sync_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(7, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_common_wait_some_cycles(mm_clk, 1); + IF c_report_note THEN + REPORT "mon_input_bsn_at_sync : " & int_to_str(v_bsn) & ", " & int_to_str(TO_UINT(mon_input_bsn_at_sync_64)) SEVERITY NOTE; + END IF; + ASSERT v_bsn < TO_UINT(mon_input_bsn_at_sync_64) REPORT "DUT mon_input_bsn_at_sync is not incrementing." SEVERITY ERROR; + ASSERT (TO_UINT(mon_input_bsn_at_sync_64) - v_bsn) MOD c_nof_block_per_input_sync = 0 REPORT "TB input_sync interval is not correct." SEVERITY ERROR; + + -- . Check mon_output_sync_bsn_64 + v_bsn := TO_UINT(mon_output_sync_bsn_64); + proc_mem_mm_bus_rd(9, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_sync_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(10, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_sync_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_common_wait_some_cycles(mm_clk, 1); + IF c_report_note THEN + REPORT "mon_output_sync_bsn : " & int_to_str(v_bsn) & ", " & int_to_str(TO_UINT(mon_output_sync_bsn_64)) SEVERITY NOTE; + END IF; + ASSERT v_bsn < TO_UINT(mon_output_sync_bsn_64) REPORT "DUT mon_output_sync_bsn is not incrementing." SEVERITY ERROR; + ASSERT (TO_UINT(mon_output_sync_bsn_64) - v_bsn) MOD c_nof_block_per_output_sync = 0 REPORT "DUT output_sync interval is not correct." SEVERITY ERROR; + + --------------------------------------------------------------------------- + -- Verify BSN hi word + --------------------------------------------------------------------------- + -- . wait until input block boundary + proc_common_wait_until_high(dp_clk, stimuli_sosi.sop); + verify_bsn_hi <= '1'; + proc_common_wait_some_cycles(dp_clk, c_block_size*c_nof_block_per_input_sync * 2); + + -- . Read mon_current_input_bsn_64 + proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + -- . Read mon_input_bsn_at_sync_64 + proc_mem_mm_bus_rd(6, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_input_bsn_at_sync_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(7, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + + -- . Read mon_output_sync_bsn_64 + proc_mem_mm_bus_rd(9, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_sync_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_mem_mm_bus_rd(10, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_sync_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0); + proc_common_wait_some_cycles(mm_clk, 1); + + -- The out_sosi.bsn will not reach the c_bsn_hi_value, because the + -- dp_bsn_sync_scheduler will be busy trying to catch up, see + -- nxt_r.update_bsn = '1' in dp_bsn_sync_scheduler.vhd. Therefore + -- expected mon_output_sync_bsn_64 hi value is still 0. + ASSERT c_bsn_hi_value = TO_UINT(mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong mon_current_input_bsn high word." SEVERITY ERROR; + ASSERT c_bsn_hi_value = TO_UINT(mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong mon_input_bsn_at_sync high word." SEVERITY ERROR; + ASSERT 0 = TO_UINT(mon_output_sync_bsn_64( 2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong mon_output_sync_bsn high word." SEVERITY ERROR; + + --------------------------------------------------------------------------- + -- Disable and verify DUT output + --------------------------------------------------------------------------- + + -- . Disable output + proc_mem_mm_bus_wr(0, 0, mm_clk, reg_miso, reg_mosi); + proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency); + proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency); + + proc_common_wait_some_cycles(dp_clk, c_block_size*10); + + -- . Read mon_output_enable + proc_mem_mm_bus_rd(8, mm_clk, reg_miso, reg_mosi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + mon_output_enable <= reg_miso.rddata(0); + + -- Verify output is on and running + proc_common_wait_some_cycles(mm_clk, 1); + ASSERT mon_output_enable = '0' REPORT "DUT mon_output_enable is not diabled." SEVERITY ERROR; + ASSERT out_enable = '0' REPORT "DUT output_enable is not enabled." SEVERITY ERROR; + + --------------------------------------------------------------------------- + -- End of test + --------------------------------------------------------------------------- + proc_common_wait_until_high(dp_clk, stimuli_end); + tb_end <= '1'; + WAIT; + END PROCESS; + + ------------------------------------------------------------------------------ + -- Streaming stimuli + ------------------------------------------------------------------------------ + + p_in_sosi : PROCESS(stimuli_sosi, verify_bsn_hi) + BEGIN + in_sosi <= stimuli_sosi; + + IF verify_bsn_hi = '1' THEN + -- Set hi word of input BSN + in_sosi.bsn(2*c_word_w-1 DOWNTO c_word_w) <= TO_UVEC(c_bsn_hi_value, c_word_w); + END IF; + END PROCESS; + + -- 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_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 + ); + + ------------------------------------------------------------------------------ + -- DUT + ------------------------------------------------------------------------------ + + u_mmp_dp_bsn_sync_scheduler : ENTITY work.mmp_dp_bsn_sync_scheduler + GENERIC MAP ( + g_bsn_w => c_bsn_w, + g_block_size => c_block_size + ) + PORT MAP ( + -- Clocks and reset + mm_rst => mm_rst, + mm_clk => mm_clk, + dp_rst => dp_rst, + dp_clk => dp_clk, + + -- MM control + reg_mosi => reg_mosi, + reg_miso => reg_miso, + + -- Streaming + in_sosi => in_sosi, + out_sosi => out_sosi, + out_start => out_start, + out_enable => out_enable + ); + +END tb; diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd index 4c1700d33404cb7a9399ba52a7ed41642e12456a..b893581245a4262daf58758174bb4fd8ba9558fd 100644 --- a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd +++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd @@ -34,22 +34,29 @@ END tb_tb_dp_bsn_source_v2; ARCHITECTURE tb OF tb_tb_dp_bsn_source_v2 IS SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + + CONSTANT c_nof_pps : NATURAL := 50; -- choose > g_block_size, because the fractional sync pattern will repeat + -- within g_block_size number of g_pps_interval's + BEGIN -- from tb_dp_bsn_source_v2.vhd -- + -- g_nof_pps : NATURAL := 20; -- g_pps_interval : NATURAL := 240 -- g_block_size : NATURAL := 32 - -- (g_pps_interval, g_block_size) -- test different clk_per_sync - u0_230 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (230, 32); - u0_240 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 32); - u0_248 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (248, 32); + u_230_32_div_7_mod_6 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 230, 32); -- 230 / 32 = 7, 230 MOD 32 = 6 + u_240_32_div_7_mod_16 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 32); -- 240 / 32 = 7, 240 MOD 32 = 16 + u_248_32_div_7_mod_24 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 248, 32); -- 248 / 32 = 7, 248 MOD 32 = 24 -- test different block_size's - u1_1 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 30); - u1_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 32); - u1_4 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 27); - + u_240_27_div_8_mod_24 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 27); -- 240 / 27 = 8, 240 MOD 27 = 24 + u_240_30_div_8_mod_0 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 30); -- 240 / 30 = 8, 240 MOD 30 = 0 + + -- test some prime values + u_101_17_div_5_mod_16 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 101, 17); -- 101 / 17 = 5, 101 MOD 17 = 16 + u_101_23_div_4_mod_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 101, 23); -- 101 / 23 = 4, 101 MOD 23 = 9 + END tb; - \ No newline at end of file + diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd new file mode 100644 index 0000000000000000000000000000000000000000..aea05a239d8776863bee18a2457fe397579c8727 --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd @@ -0,0 +1,80 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- +-- Author: Eric Kooistra +-- Purpose: Multi test bench for tb_dp_bsn_sync_scheduler.vhd + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; +USE work.tb_dp_pkg.ALL; + + +-- > as 4 +-- > run -all --> OK + +ENTITY tb_tb_dp_bsn_sync_scheduler IS +END tb_tb_dp_bsn_sync_scheduler; + + +ARCHITECTURE tb OF tb_tb_dp_bsn_sync_scheduler IS + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + + -- Use g_block_size < c_nof_input_sync, because the fractional sync pattern + -- will repeat within g_block_size number of output sync intervals. The + -- repeat period of the sync pattern is visible by nxt_r.accumulate in + -- dp_bsn_scheduler.vhd. + CONSTANT c_nof_input_sync : NATURAL := 25; + +BEGIN + -- from tb_dp_bsn_scheduler.vhd + -- + -- -- 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; + -- + -- -- Output sync period + -- g_nof_samples_per_output_sync : NATURAL := 45; -- = g_block_size * 9 / 2 + + u_output_is_input : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 170); -- 170/10 = 17 block/out_sync, = in_sosi + u_output_is_input_no_gaps : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 5, 0, 85); -- 85/5 = 17 block/out_sync, = in_sosi + + u_sync_interval_0_5x : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 85); -- 85/10 = 8.5 block/out_sync, factor 85/170 = 0.5, + u_sync_interval_1_5x : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 255); -- 255/10 = 25.5 block/out_sync, factor 255/170 = 1.5, + u_sync_interval_prime_251 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 251); -- 251/10 = 25.1 block/out_sync, 251 is a prime + + u_short_block_4_3_15 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 4, 3, 3, 15); -- 15/3 = 5 block/out_sync, + u_short_block_5_3_16 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 5, 3, 3, 16); -- 16/3 = 5.33 block/out_sync, + u_short_block_6_3_17 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 6, 3, 3, 17); -- 17/3 = 5.66 block/out_sync, + + u_short_block_no_gaps_4_3_15 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 4, 3, 0, 15); -- 15/3 = 5 block/out_sync, + u_short_block_no_gaps_5_3_16 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 5, 3, 0, 16); -- 16/3 = 5.33 block/out_sync, + u_short_block_no_gaps_6_3_17 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 6, 3, 0, 17); -- 17/3 = 5.66 block/out_sync, + + u_short_block_size_2 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 7, 2, 3, 16); -- 16/2 = 8 block/out_sync, + u_short_block_size_2_no_gaps : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 7, 2, 0, 16); -- 16/2 = 8 block/out_sync, + + u_fraction_half : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 45); -- 45/10 = 4.5 block/out_sync + u_fraction_0 : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 50); -- 50/10 = 5 block/out_sync + +END tb; +