Skip to content
Snippets Groups Projects
Commit 68b08de5 authored by Reinier van der Walle's avatar Reinier van der Walle
Browse files

Merge branch 'L2SDP-801' into 'master'

L2SDP-801 Verify bsn time offset in dp_bsn_source_v2.vhd

Closes L2SDP-801

See merge request desp/hdl!272
parents fbb063c3 c1a19510
No related branches found
No related tags found
1 merge request!272L2SDP-801 Verify bsn time offset in dp_bsn_source_v2.vhd
Pipeline #34729 passed
...@@ -191,6 +191,7 @@ test_bench_files = ...@@ -191,6 +191,7 @@ test_bench_files =
tb/vhdl/tb_common_transpose_symbol.vhd tb/vhdl/tb_common_transpose_symbol.vhd
tb/vhdl/tb_common_zip.vhd tb/vhdl/tb_common_zip.vhd
tb/vhdl/tb_common_variable_delay.vhd tb/vhdl/tb_common_variable_delay.vhd
tb/vhdl/tb_common_gcd.vhd
tb/vhdl/tb_requantize.vhd tb/vhdl/tb_requantize.vhd
tb/vhdl/tb_resize.vhd tb/vhdl/tb_resize.vhd
tb/vhdl/tb_round.vhd tb/vhdl/tb_round.vhd
...@@ -224,6 +225,7 @@ regression_test_vhdl = ...@@ -224,6 +225,7 @@ regression_test_vhdl =
tb/vhdl/tb_common_shiftreg.vhd tb/vhdl/tb_common_shiftreg.vhd
tb/vhdl/tb_common_transpose_symbol.vhd tb/vhdl/tb_common_transpose_symbol.vhd
tb/vhdl/tb_common_variable_delay.vhd tb/vhdl/tb_common_variable_delay.vhd
tb/vhdl/tb_common_gcd.vhd
tb/vhdl/tb_tb_resize.vhd tb/vhdl/tb_tb_resize.vhd
tb/vhdl/tb_tb_round.vhd tb/vhdl/tb_tb_round.vhd
tb/vhdl/tb_requantize.vhd tb/vhdl/tb_requantize.vhd
......
...@@ -205,6 +205,7 @@ PACKAGE common_pkg IS ...@@ -205,6 +205,7 @@ PACKAGE common_pkg IS
FUNCTION ceil_div( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; FUNCTION ceil_div( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED;
FUNCTION ceil_value( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; FUNCTION ceil_value( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED;
FUNCTION floor_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; FUNCTION floor_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED;
FUNCTION gcd(a, b : NATURAL) RETURN NATURAL; -- greatest common divider
FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR; -- standard logic to 1 element standard logic vector 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 sl( n: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- 1 element standard logic vector to standard logic
...@@ -719,6 +720,15 @@ PACKAGE BODY common_pkg IS ...@@ -719,6 +720,15 @@ PACKAGE BODY common_pkg IS
RETURN p(w-1 DOWNTO 0); -- return same width as n RETURN p(w-1 DOWNTO 0); -- return same width as n
END; END;
FUNCTION gcd(a, b : NATURAL) RETURN NATURAL IS -- greatest common divider
BEGIN
IF b = 0 THEN
RETURN a;
ELSE
RETURN gcd(b, a MOD b);
END IF;
END;
FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR IS FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR IS
VARIABLE r : STD_LOGIC_VECTOR(0 DOWNTO 0); VARIABLE r : STD_LOGIC_VECTOR(0 DOWNTO 0);
BEGIN BEGIN
......
-------------------------------------------------------------------------------
--
-- Copyright (C) 2009
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.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: E. Kooistra 2022
-- Purpose: Test bench for gcd() in common_pkg.vhd
-- Usage:
-- > run -a
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
USE work.common_pkg.ALL;
ENTITY tb_common_gcd IS
END tb_common_gcd;
ARCHITECTURE tb OF tb_common_gcd IS
BEGIN
ASSERT gcd( 0, 10) = 10 REPORT "Wrong gcd( 0, 10)" SEVERITY ERROR;
ASSERT gcd( 1, 1) = 1 REPORT "Wrong gcd( 1, 1)" SEVERITY ERROR;
ASSERT gcd(10, 1) = 1 REPORT "Wrong gcd(10, 1)" SEVERITY ERROR;
ASSERT gcd(10, 3) = 1 REPORT "Wrong gcd(10, 3)" SEVERITY ERROR;
ASSERT gcd(10, 4) = 2 REPORT "Wrong gcd(10, 4)" SEVERITY ERROR;
ASSERT gcd(10, 5) = 5 REPORT "Wrong gcd(10, 5)" SEVERITY ERROR;
ASSERT gcd(16, 4) = 4 REPORT "Wrong gcd(16, 4)" SEVERITY ERROR;
ASSERT gcd(15, 5) = 5 REPORT "Wrong gcd(15, 5)" SEVERITY ERROR;
ASSERT gcd(17, 17) = 17 REPORT "Wrong gcd(17, 17)" SEVERITY ERROR;
ASSERT gcd(17, 4) = 1 REPORT "Wrong gcd(17, 4)" SEVERITY ERROR;
END tb;
...@@ -67,8 +67,7 @@ ENTITY dp_bsn_source_reg_v2 IS ...@@ -67,8 +67,7 @@ ENTITY dp_bsn_source_reg_v2 IS
st_nof_clk_per_sync : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); -- nof block per sync st_nof_clk_per_sync : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); -- nof block per sync
st_bsn_init : OUT STD_LOGIC_VECTOR; -- wr init BSN st_bsn_init : OUT STD_LOGIC_VECTOR; -- wr init BSN
st_current_bsn : IN STD_LOGIC_VECTOR; -- rd current BSN st_current_bsn : IN STD_LOGIC_VECTOR; -- rd current BSN
st_bsn_time_offset : OUT STD_LOGIC_VECTOR; st_bsn_time_offset : OUT STD_LOGIC_VECTOR
st_current_bsn_time_offset : IN STD_LOGIC_VECTOR
); );
END dp_bsn_source_reg_v2; END dp_bsn_source_reg_v2;
...@@ -101,7 +100,6 @@ ARCHITECTURE rtl OF dp_bsn_source_reg_v2 IS ...@@ -101,7 +100,6 @@ ARCHITECTURE rtl OF dp_bsn_source_reg_v2 IS
SIGNAL mm_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); SIGNAL mm_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL mm_bsn_time_offset_wr : STD_LOGIC; SIGNAL mm_bsn_time_offset_wr : STD_LOGIC;
SIGNAL mm_current_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
-- Registers in st_clk domain -- Registers in st_clk domain
...@@ -156,7 +154,7 @@ BEGIN ...@@ -156,7 +154,7 @@ BEGIN
-- write bsn_time_offset -- write bsn_time_offset
WHEN 4 => WHEN 4 =>
mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0); mm_bsn_time_offset <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0);
mm_bsn_time_offset_wr <= '1'; mm_bsn_time_offset_wr <= '1';
WHEN OTHERS => NULL; -- not used MM addresses WHEN OTHERS => NULL; -- not used MM addresses
...@@ -179,11 +177,11 @@ BEGIN ...@@ -179,11 +177,11 @@ BEGIN
sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn(31 DOWNTO 0); sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn(31 DOWNTO 0);
mm_current_bsn_hi <= mm_current_bsn(63 DOWNTO 32); -- first read low part and preserve high part mm_current_bsn_hi <= mm_current_bsn(63 DOWNTO 32); -- first read low part and preserve high part
WHEN 3 => WHEN 3 =>
sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi; sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi; -- then read preserved high part
-- Read current bsn_time_offset -- Read current bsn_time_offset
WHEN 4 => WHEN 4 =>
sla_out.rddata(c_bsn_time_offset_w-1 DOWNTO 0) <= mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); sla_out.rddata <= RESIZE_UVEC(mm_bsn_time_offset, c_word_w);
WHEN OTHERS => NULL; -- not used MM addresses WHEN OTHERS => NULL; -- not used MM addresses
END CASE; END CASE;
...@@ -213,7 +211,7 @@ BEGIN ...@@ -213,7 +211,7 @@ BEGIN
st_on_pps <= mm_on_pps; st_on_pps <= mm_on_pps;
st_nof_clk_per_sync <= mm_nof_clk_per_sync; st_nof_clk_per_sync <= mm_nof_clk_per_sync;
st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0); st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0);
st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); st_bsn_time_offset <= mm_bsn_time_offset;
p_st_clk : PROCESS(st_rst, st_clk) p_st_clk : PROCESS(st_rst, st_clk)
BEGIN BEGIN
...@@ -222,16 +220,16 @@ BEGIN ...@@ -222,16 +220,16 @@ BEGIN
st_bsn_time_offset <= TO_UVEC(0, c_bsn_time_offset_w); st_bsn_time_offset <= TO_UVEC(0, c_bsn_time_offset_w);
ELSIF rising_edge(st_clk) THEN ELSIF rising_edge(st_clk) THEN
IF mm_bsn_init_wr='1' THEN IF mm_bsn_init_wr='1' THEN
st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0); -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0);
END IF; END IF;
IF mm_bsn_time_offset_wr='1' THEN IF mm_bsn_time_offset_wr='1' THEN
st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word st_bsn_time_offset <= mm_bsn_time_offset;
END IF; END IF;
END IF; END IF;
END PROCESS; END PROCESS;
mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn; -- MM user may read current_bsn twice to avoid small chance that the high part of the double word changed (i.e. incremented) mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn;
mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= st_current_bsn_time_offset;
END GENERATE; -- no_cross END GENERATE; -- no_cross
gen_cross : IF g_cross_clock_domain = TRUE GENERATE gen_cross : IF g_cross_clock_domain = TRUE GENERATE
...@@ -311,8 +309,8 @@ BEGIN ...@@ -311,8 +309,8 @@ BEGIN
PORT MAP ( PORT MAP (
in_rst => mm_rst, in_rst => mm_rst,
in_clk => mm_clk, in_clk => mm_clk,
in_new => mm_bsn_time_offset_wr, -- use wr of mm_bsn_time_offset high part for in_new to ensure proper transfer of double word in_new => mm_bsn_time_offset_wr,
in_dat => mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0), in_dat => mm_bsn_time_offset,
in_done => OPEN, -- pulses when no more pending in_new in_done => OPEN, -- pulses when no more pending in_new
out_rst => st_rst, out_rst => st_rst,
out_clk => st_clk, out_clk => st_clk,
...@@ -320,20 +318,6 @@ BEGIN ...@@ -320,20 +318,6 @@ BEGIN
out_new => OPEN out_new => OPEN
); );
-- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless
u_current_bsn_offset : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => st_rst,
in_clk => st_clk,
in_new => '1',
in_dat => st_current_bsn_time_offset,
in_done => OPEN, -- pulses when no more pending in_new
out_rst => mm_rst,
out_clk => mm_clk,
out_dat => mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0),
out_new => OPEN
);
END GENERATE; -- gen_cross END GENERATE; -- gen_cross
END rtl; END rtl;
...@@ -19,29 +19,40 @@ ...@@ -19,29 +19,40 @@
-- --
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Author : P.Donker okt. 2020, added bsn_time_offset
-- E. Kooistra aug 2022, corected s_bsn_time_offset, clarified
-- sync_size_cnt, removed redundant current_bsn_time_offset
-- and s_init, simplified implementation of dp_on_status.
--
-- Purpose : -- Purpose :
-- Start a periodic block sync interval and maintain a block sequence -- Start a periodic block sync interval and maintain a block sequence
-- number -- number according to the PPS and BSN grid defined in [1].
-- Description: -- Description:
-- When dp_on is low then all outputs are low. When dp_on is high, the -- When dp_on is low then all outputs are low. When dp_on is high, the
-- output sync starts pulsing after bsn_time_offset (in clk cycles) with -- output sync starts pulsing after bsn_time_offset (in clk cycles) with
-- a period of g_block_size number of clk cycles and the output valid, -- a period of g_block_size number of clk cycles and the output valid,
-- sop and eop will be active. -- sop and eop will be active.
-- Alternatively, one can assert dp_on while dp_on_pps is high to -- The dp_on is asynchronous. Alternatively, one can assert dp_on while
-- start the data path on the next PPS. -- dp_on_pps is high to start the data path on the next PPS.
-- The dp_on is asynchronous. The dp_bsn_source_v2 takes care that -- The src_out.sync always happens at the src_out.sop.
-- src_out.valid starts with a src_out.sop and that src_out.valid can -- If nof_clk_per_sync / g_block_size is an integer than all src_out.sync
-- only go low after a src_out.eop, to ensure that src_out only produces -- intervals will have nof_clk_per_sync clk cycles, else nof_clk_per_sync
-- complete sop-eop blocks that enter the subsequent processing. -- is the average number of clock cycles between src_out.sync and then the
-- The bs_start is active at the first src_out.sop after dp_on went high. -- number of blocks per sync intervals will vary between c_nof_block_hi
-- and c_nof_block_lo.
-- The dp_bsn_source_v2 takes care that src_out.valid starts with a
-- src_out.sync and src_out.sop and that src_out.valid can only go low
-- after a src_out.eop, to ensure that src_out only produces complete
-- sop-eop blocks that enter the subsequent processing.
-- The bs_restart is active at the first src_out.sop after dp_on went high.
-- Remarks: -- Remarks:
-- . Starting the data path is only possible from the dp_off state, so one -- . Starting the data path is only possible from the dp_off state, so one
-- has to disable (dp_on='0') the data path before restarting it. -- has to disable (dp_on='0') the data path before restarting it.
-- . Effectively dp_on_status = src_out.valid, because when the BSN source -- . Effectively dp_on_status = src_out.valid, because when the BSN source
-- is on, then src_out.valid = '1' at every clk cycle. -- is on, then src_out.valid = '1' at every clk cycle.
-- --
-- author : P.Donker okt. 2020, added bsn_time_offset -- References:
-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station
LIBRARY IEEE, common_lib; LIBRARY IEEE, common_lib;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
...@@ -51,7 +62,7 @@ USE work.dp_stream_pkg.ALL; ...@@ -51,7 +62,7 @@ USE work.dp_stream_pkg.ALL;
ENTITY dp_bsn_source_v2 IS ENTITY dp_bsn_source_v2 IS
GENERIC ( GENERIC (
g_block_size : NATURAL := 256; g_block_size : NATURAL := 256; -- >= 3, see state machine
g_nof_clk_per_sync : NATURAL := 200 * 10**6; g_nof_clk_per_sync : NATURAL := 200 * 10**6;
g_bsn_w : NATURAL := 48; g_bsn_w : NATURAL := 48;
g_bsn_time_offset_w : NATURAL := 10 g_bsn_time_offset_w : NATURAL := 10
...@@ -70,7 +81,6 @@ ENTITY dp_bsn_source_v2 IS ...@@ -70,7 +81,6 @@ ENTITY dp_bsn_source_v2 IS
nof_clk_per_sync : IN STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := TO_UVEC(g_nof_clk_per_sync, c_word_w); nof_clk_per_sync : IN STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := TO_UVEC(g_nof_clk_per_sync, c_word_w);
bsn_init : IN STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); bsn_init : IN STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
bsn_time_offset : IN STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0'); bsn_time_offset : IN STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
current_bsn_time_offset : OUT STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); -- output for monitoring purpose in test bench.
src_out : OUT t_dp_sosi -- only uses sync, bsn[], valid, sop and eop src_out : OUT t_dp_sosi -- only uses sync, bsn[], valid, sop and eop
); );
...@@ -82,21 +92,10 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS ...@@ -82,21 +92,10 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS
CONSTANT c_block_size_cnt_w : NATURAL := ceil_log2(g_block_size); CONSTANT c_block_size_cnt_w : NATURAL := ceil_log2(g_block_size);
CONSTANT c_block_cnt_zero : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS => '0'); CONSTANT c_block_cnt_zero : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS => '0');
-- The state machine starts synchronously via s_bsn_time_offset, s_dp_on_sop to s_dp_on, or it starts CONSTANT c_nof_block_lo : NATURAL := g_nof_clk_per_sync / g_block_size;
-- directly to s_dp_on. When in dp_on it loops between s_dp_on and s_dp_on_eop for every block. CONSTANT c_nof_block_hi : NATURAL := ceil_div(g_nof_clk_per_sync, g_block_size);
-- If the BSN source is switched off, then after completing the block s_dp_on_eop goes back to
-- s_dp_off.
--
-- Sketch of the state machine:
--
-- s_init --> s_dp_off --> s_bsn_time_offset -- ------------ (loop) <---
-- \ \ \ / \
-- \ ---------------------------> s_dp_on_sop --> s_dp_on --> s_dp_on_eop --
-- \ /
-- ----------------------------------------------------------------- (off) <---
TYPE t_state_enum IS (s_dp_off, s_bsn_time_offset, s_dp_on_sop, s_dp_on, s_dp_on_eop);
TYPE t_state_enum IS (s_init, s_dp_off, s_bsn_time_offset, s_dp_on_sop, s_dp_on, s_dp_on_eop);
SIGNAL state : t_state_enum; SIGNAL state : t_state_enum;
SIGNAL nxt_state : t_state_enum; SIGNAL nxt_state : t_state_enum;
...@@ -108,144 +107,152 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS ...@@ -108,144 +107,152 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS
SIGNAL i_src_out : t_dp_sosi := c_dp_sosi_init; SIGNAL i_src_out : t_dp_sosi := c_dp_sosi_init;
SIGNAL nxt_src_out : t_dp_sosi; SIGNAL nxt_src_out : t_dp_sosi;
SIGNAL i_dp_on_status : STD_LOGIC;
SIGNAL nxt_dp_on_status : STD_LOGIC;
SIGNAL nxt_bs_restart : STD_LOGIC; SIGNAL nxt_bs_restart : STD_LOGIC;
SIGNAL nxt_bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); SIGNAL nxt_bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
SIGNAL bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); SIGNAL bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
SIGNAL i_current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); SIGNAL nxt_sync_size_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
SIGNAL nxt_current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0); SIGNAL sync_size_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
SIGNAL nxt_clk_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
SIGNAL clk_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
SIGNAL nxt_sync : STD_LOGIC; SIGNAL nxt_sync : STD_LOGIC;
SIGNAL sync : STD_LOGIC; SIGNAL sync : STD_LOGIC;
BEGIN BEGIN
src_out <= i_src_out; src_out <= i_src_out;
dp_on_status <= i_dp_on_status; dp_on_status <= i_src_out.valid;
current_bsn_time_offset <= i_current_bsn_time_offset;
p_state : PROCESS(state, i_src_out, block_size_cnt, clk_cnt, sync, i_dp_on_status, bsn_time_offset_cnt, bsn_time_offset, nof_clk_per_sync, bsn_init, dp_on, dp_on_pps, pps, prev_state) p_state : PROCESS(sync, sync_size_cnt, nof_clk_per_sync,
state, i_src_out, block_size_cnt, bsn_time_offset_cnt,
bsn_init, dp_on, dp_on_pps, pps, bsn_time_offset, prev_state)
BEGIN BEGIN
-- Maintain sync_size_cnt for nof_clk_per_sync
-- . nof_clk_per_sync is the number of clk per pps interval and the
-- average number of clk per src_out.sync interval, due to that the
-- src_out.sync has to occur at the src_out.sop of a block.
-- . The sync_size_cnt is started in s_dp_off at the pps
-- . The pps interval is nof_clk_per_sync, so when the sync_size_cnt
-- wraps, then the sync is used to ensure that src_out.sync will
-- pulse at the src_out.sop of the next block.
nxt_sync <= sync;
nxt_sync_size_cnt <= INCR_UVEC(sync_size_cnt, 1);
IF UNSIGNED(sync_size_cnt) = UNSIGNED(nof_clk_per_sync) - 1 THEN
nxt_sync <= '1'; -- will set src_out.sync on next src_out.sop
nxt_sync_size_cnt <= (OTHERS=>'0');
END IF;
-- State machine for src_out
nxt_state <= state; nxt_state <= state;
nxt_src_out <= i_src_out; nxt_src_out <= i_src_out; -- hold src_out.bsn
nxt_src_out.sync <= '0'; nxt_src_out.sync <= '0';
nxt_src_out.valid <= '0'; nxt_src_out.valid <= '0';
nxt_src_out.sop <= '0'; nxt_src_out.sop <= '0';
nxt_src_out.eop <= '0'; nxt_src_out.eop <= '0';
nxt_block_size_cnt <= block_size_cnt; nxt_block_size_cnt <= block_size_cnt;
nxt_clk_cnt <= INCR_UVEC(clk_cnt, 1);
nxt_sync <= sync;
nxt_dp_on_status <= i_dp_on_status;
nxt_bs_restart <= '0';
nxt_bsn_time_offset_cnt <= bsn_time_offset_cnt; nxt_bsn_time_offset_cnt <= bsn_time_offset_cnt;
nxt_current_bsn_time_offset <= bsn_time_offset;
IF UNSIGNED(clk_cnt) = UNSIGNED(nof_clk_per_sync) - 1 THEN
nxt_clk_cnt <= (OTHERS=>'0');
nxt_sync <= '1'; -- will set src_out.sync on next src_out.sop
END IF;
CASE state IS CASE state IS
WHEN s_init =>
nxt_state <= s_dp_off;
WHEN s_dp_off => WHEN s_dp_off =>
nxt_dp_on_status <= '0';
nxt_src_out.bsn <= RESIZE_DP_BSN(bsn_init); nxt_src_out.bsn <= RESIZE_DP_BSN(bsn_init);
nxt_bsn_time_offset_cnt <= (OTHERS=>'0');
nxt_sync <= '0'; nxt_sync <= '0';
nxt_clk_cnt <= (OTHERS=>'0'); nxt_sync_size_cnt <= (OTHERS=>'0');
nxt_block_size_cnt <= (OTHERS=>'0');
IF dp_on = '1' THEN IF dp_on = '1' THEN
nxt_sync <= '1'; -- ensure issue sync at first sync interval
IF dp_on_pps = '1' THEN IF dp_on_pps = '1' THEN
nxt_sync <= '1'; -- ensure issue sync at first sync interval for start at PPS. -- start at pps
IF pps = '1' THEN IF pps = '1' THEN
nxt_bsn_time_offset_cnt <= (OTHERS=>'0');
nxt_state <= s_bsn_time_offset; nxt_state <= s_bsn_time_offset;
END IF; END IF;
ELSE ELSE
-- start immediately
nxt_state <= s_dp_on_sop; nxt_state <= s_dp_on_sop;
END IF; END IF;
END IF; END IF;
WHEN s_bsn_time_offset => WHEN s_bsn_time_offset =>
IF UNSIGNED(bsn_time_offset_cnt) = UNSIGNED(bsn_time_offset) THEN IF UNSIGNED(bsn_time_offset_cnt) = UNSIGNED(bsn_time_offset) THEN
-- The bsn_time_offset can be 0, so IF UNSIGNED(bsn_time_offset)-1
-- can not be used to account for on clk latency of state
-- s_bsn_time_offset. Therefore do not count sync_size_cnt during
-- latency of state s_bsn_time_offset.
nxt_sync_size_cnt <= sync_size_cnt;
nxt_state <= s_dp_on_sop; nxt_state <= s_dp_on_sop;
ELSE ELSE
nxt_bsn_time_offset_cnt <= INCR_UVEC(bsn_time_offset_cnt, 1); nxt_bsn_time_offset_cnt <= INCR_UVEC(bsn_time_offset_cnt, 1);
END IF; END IF;
-- using separate states s_dp_on_sop and s_dp_on_eop instead of only
-- s_dp_on state and block_size_cnt, cause that g_block_size must be
-- >= 3, but that is fine.
WHEN s_dp_on_sop => WHEN s_dp_on_sop =>
nxt_dp_on_status <= '1'; -- Start of block
nxt_src_out.sop <= '1'; nxt_src_out.sop <= '1';
nxt_src_out.valid <= '1'; nxt_src_out.valid <= '1';
nxt_state <= s_dp_on; nxt_state <= s_dp_on;
-- block_size_cnt = 0 at src_out.sop
nxt_block_size_cnt <= (OTHERS=>'0'); nxt_block_size_cnt <= (OTHERS=>'0');
-- after first block, increment bsn per block
IF prev_state = s_dp_on_eop THEN IF prev_state = s_dp_on_eop THEN
nxt_src_out.bsn <= INCR_DP_BSN(i_src_out.bsn, 1, g_bsn_w); nxt_src_out.bsn <= INCR_DP_BSN(i_src_out.bsn, 1, g_bsn_w);
END IF; END IF;
-- check for pending sync
IF sync = '1' THEN IF sync = '1' THEN
nxt_src_out.sync <= '1'; nxt_src_out.sync <= '1';
nxt_sync <= '0'; nxt_sync <= '0';
END IF; END IF;
IF i_dp_on_status = '0' THEN -- transition from 0 to 1 is a (re)start
nxt_bs_restart <= '1'; -- bs_restart indicates a restart as a pulse on the sop (and sync if dp_on_pps is used).
END IF;
WHEN s_dp_on => WHEN s_dp_on =>
-- During block
nxt_src_out.valid <= '1'; nxt_src_out.valid <= '1';
-- block_size_cnt increments to determine end of block
nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1); nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1);
IF UNSIGNED(block_size_cnt) = g_block_size -3 THEN IF UNSIGNED(block_size_cnt) >= g_block_size - 3 THEN
nxt_state <= s_dp_on_eop; nxt_state <= s_dp_on_eop;
END IF; END IF;
WHEN s_dp_on_eop => WHEN s_dp_on_eop =>
-- End of block
nxt_src_out.eop <= '1'; nxt_src_out.eop <= '1';
nxt_src_out.valid <= '1'; nxt_src_out.valid <= '1';
nxt_state <= s_dp_on_sop; nxt_state <= s_dp_on_sop;
nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1); -- block_size_cnt is dont care at at src_out.eop
-- accept dp_off after eop, to avoid fractional blocks
IF dp_on = '0' THEN IF dp_on = '0' THEN
nxt_state <= s_dp_off; nxt_state <= s_dp_off;
END IF; END IF;
WHEN OTHERS => -- s_init WHEN OTHERS => -- reover from undefined state
nxt_state <= s_dp_off; nxt_state <= s_dp_off;
END CASE; END CASE;
END PROCESS; END PROCESS;
-- src_out.valid transition from 0 to 1 is a bs_restart, use nxt_bs_restart
-- to have bs_restart at first src_out.sync and src_out.sop.
nxt_bs_restart <= nxt_src_out.valid AND NOT i_src_out.valid;
p_clk : PROCESS(rst, clk) p_clk : PROCESS(rst, clk)
BEGIN BEGIN
IF rst='1' THEN IF rst='1' THEN
prev_state <= s_init; prev_state <= s_dp_off;
state <= s_init; state <= s_dp_off;
i_src_out <= c_dp_sosi_rst; i_src_out <= c_dp_sosi_rst;
clk_cnt <= (OTHERS=>'0'); sync_size_cnt <= (OTHERS=>'0');
sync <= '0'; sync <= '0';
block_size_cnt <= (OTHERS=>'0'); block_size_cnt <= (OTHERS=>'0');
i_dp_on_status <= '0';
bs_restart <= '0'; bs_restart <= '0';
bsn_time_offset_cnt <= (OTHERS=>'0'); bsn_time_offset_cnt <= (OTHERS=>'0');
ELSIF rising_edge(clk) THEN ELSIF rising_edge(clk) THEN
prev_state <= state; prev_state <= state;
state <= nxt_state; state <= nxt_state;
i_src_out <= nxt_src_out; i_src_out <= nxt_src_out;
clk_cnt <= nxt_clk_cnt; sync_size_cnt <= nxt_sync_size_cnt;
sync <= nxt_sync; sync <= nxt_sync;
block_size_cnt <= nxt_block_size_cnt; block_size_cnt <= nxt_block_size_cnt;
i_dp_on_status <= nxt_dp_on_status;
bs_restart <= nxt_bs_restart; bs_restart <= nxt_bs_restart;
bsn_time_offset_cnt <= nxt_bsn_time_offset_cnt; bsn_time_offset_cnt <= nxt_bsn_time_offset_cnt;
i_current_bsn_time_offset <= nxt_current_bsn_time_offset;
END IF; END IF;
END PROCESS; END PROCESS;
END rtl; END rtl;
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
-- --
-- Author: Eric Kooistra, 30 July 2021 -- Author: Eric Kooistra, 30 July 2021
-- Purpose : -- Purpose :
-- Create programmable sync interval for input stream. -- Create programmable sync interval for input stream, according to the
-- PPS and BSN grid defined in [1].
-- Description: -- Description:
-- * ctrl_start_bsn: -- * ctrl_start_bsn:
-- The output sync interval starts at an in_sosi.bsn that is programmable via -- The output sync interval starts at an in_sosi.bsn that is programmable via
...@@ -105,6 +106,9 @@ ...@@ -105,6 +106,9 @@
-- The differences are that: -- The differences are that:
-- . dp_bsn_sync_scheduler requires in_sosi.sync and copies the other in_sosi -- . dp_bsn_sync_scheduler requires in_sosi.sync and copies the other in_sosi
-- ctrl, info and data, whereas generates bs_sosi.ctrl. -- ctrl, info and data, whereas generates bs_sosi.ctrl.
--
-- References:
-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station
LIBRARY IEEE, common_lib; LIBRARY IEEE, common_lib;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
......
...@@ -70,7 +70,6 @@ ARCHITECTURE str OF mms_dp_bsn_source_v2 IS ...@@ -70,7 +70,6 @@ ARCHITECTURE str OF mms_dp_bsn_source_v2 IS
SIGNAL dp_on_status : STD_LOGIC; SIGNAL dp_on_status : STD_LOGIC;
SIGNAL capture_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); SIGNAL capture_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
BEGIN BEGIN
...@@ -99,8 +98,7 @@ BEGIN ...@@ -99,8 +98,7 @@ BEGIN
st_nof_clk_per_sync => nof_clk_per_sync, st_nof_clk_per_sync => nof_clk_per_sync,
st_bsn_init => bsn_init, st_bsn_init => bsn_init,
st_current_bsn => capture_bsn, st_current_bsn => capture_bsn,
st_bsn_time_offset => bsn_time_offset, st_bsn_time_offset => bsn_time_offset
st_current_bsn_time_offset => current_bsn_time_offset
); );
u_bsn_source : ENTITY work.dp_bsn_source_v2 u_bsn_source : ENTITY work.dp_bsn_source_v2
...@@ -121,13 +119,10 @@ BEGIN ...@@ -121,13 +119,10 @@ BEGIN
bsn_init => bsn_init, bsn_init => bsn_init,
nof_clk_per_sync => nof_clk_per_sync, nof_clk_per_sync => nof_clk_per_sync,
bsn_time_offset => bsn_time_offset, bsn_time_offset => bsn_time_offset,
current_bsn_time_offset => current_bsn_time_offset,
-- Streaming -- Streaming
src_out => i_bs_sosi src_out => i_bs_sosi
); );
--capture_bsn <= i_bs_sosi.bsn; -- capture current BSN
--capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND dp_pps='1'; -- capture BSN at external PPS
capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND i_bs_sosi.sync='1'; -- capture BSN at internal sync capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND i_bs_sosi.sync='1'; -- capture BSN at internal sync
END str; END str;
......
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- --
-- Copyright (C) 2011 -- Copyright (C) 2020
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
-- --
...@@ -19,22 +19,28 @@ ...@@ -19,22 +19,28 @@
-- --
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Author: P. Donker -- Author: P. Donker, E. Kooistra
-- Verify if eop and sop come in pairs and if sync is at sop and at expected_sync puls. -- Purpose: Tb to verify that the BS source can be started in any PPS interval
-- The tb is using a SSN (second sample number) and BSN (block sample number) generator as reference -- to create the BSN grid and sync interval as defined in Fig. 3.1 in
-- for the test, it uses g_pps_interval and g_block_size for generator timing settings. -- [1].
-- Start/Stop BSN source tests: -- Decsription:
-- 1) test 1x asynchronously (dp_on_pps='0') without automatic check, check visualy in wave window. -- * Start/Stop BSN source tests:
-- 2) test 3x synchronously (dp_on_pps='1') with automatic check. -- 1) test once asynchronously (dp_on_pps='0') without automatic check, check
-- visualy in wave window.
-- 2) test c_nof_repeat synchronously (dp_on_pps='1') with automatic check.
-- . Verify if bs_sosi.eop and bs_sosi.sop come in pairs
-- . Verify that bs_sosi.sync is at bs_sosi.sop
-- . Verify that bs_sosi has fixed latency with respect to ref_grid
--
-- References:
-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station
-- [2] https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset
-- --
-- [doc] = https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset
-- Usage: -- Usage:
-- > as 10 -- > as 8, e.g. view bs_sosi, exp_grid and unexpected_bs_sync
-- > run -all -- > run -all
-- . sop, eop are verified automatically -- . sop, eop are verified automatically
-- . sync and bsn are verified automatically -- . sync and bsn are verified automatically using the ref_grid
-- and then manually verify on/off in Wave window
LIBRARY IEEE, common_lib, dp_lib; LIBRARY IEEE, common_lib, dp_lib;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
...@@ -46,171 +52,196 @@ USE dp_lib.tb_dp_pkg.ALL; ...@@ -46,171 +52,196 @@ USE dp_lib.tb_dp_pkg.ALL;
ENTITY tb_dp_bsn_source_v2 IS ENTITY tb_dp_bsn_source_v2 IS
GENERIC ( GENERIC (
g_nof_pps : NATURAL := 20; g_pps_interval : NATURAL := 16; --101;
g_pps_interval : NATURAL := 230; g_block_size : NATURAL := 5 --23
g_block_size : NATURAL := 32
); );
END tb_dp_bsn_source_v2; END tb_dp_bsn_source_v2;
ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS
-- The nof block per sync interval will be the same after every
-- c_min_nof_pps_interval. The c_gcd is the greatest common divider of
-- g_pps_interval and g_block_size, so g_block_size / c_gcd yields an
-- integer. When g_pps_interval and g_block_size are relative prime,
-- then c_gcd = 1, and then it takes g_block_size nof g_pps_interval
-- for the pattern of c_nof_block_per_sync_lo and c_nof_block_per_sync_hi
-- to repeat. If c_gcd = g_block_size, then c_nof_block_per_sync_lo =
-- c_nof_block_per_sync_hi, so then the nof block per sync interval is
-- the same in every pps interval.
CONSTANT c_gcd : NATURAL := gcd(g_pps_interval, g_block_size);
CONSTANT c_min_nof_pps_interval : NATURAL := g_block_size / c_gcd;
CONSTANT c_nof_block_per_sync_lo : NATURAL := g_pps_interval / g_block_size;
CONSTANT c_nof_block_per_sync_hi : NATURAL := ceil_div(g_pps_interval, g_block_size);
-- choose c_nof_pps and c_nof_repeat > c_min_nof_pps_interval, because the
-- fractional sync pattern will repeat every c_min_nof_pps_interval number
-- of g_pps_intervals.
CONSTANT c_factor : NATURAL := 3;
CONSTANT c_nof_pps : NATURAL := c_min_nof_pps_interval * c_factor;
CONSTANT c_nof_repeat : NATURAL := c_min_nof_pps_interval * c_factor;
CONSTANT c_clk_period : TIME := 10 ns; CONSTANT c_clk_period : TIME := 10 ns;
CONSTANT c_bsn_w : NATURAL := 31; CONSTANT c_bsn_w : NATURAL := 31;
CONSTANT c_dut_latency : NATURAL := 2; CONSTANT c_bsn_time_offset_w : NATURAL := ceil_log2(g_block_size);
-- Minimum latency between sync and PPS, due to logic in DUT
CONSTANT c_dut_latency : NATURAL := 3;
-- The state name tells what kind of test is being done -- The state name tells what kind of test is being done
TYPE t_state_enum IS ( TYPE t_state_enum IS (
s_disable, s_disable,
s_start, s_dp_on,
s_pps_start s_dp_on_pps
); );
SIGNAL tb_state : t_state_enum; -- Define the PPS (SSN) and BSN grid that both start at 0 according to Figure 3.1 in [1]:
TYPE t_time_grid IS RECORD
pps : STD_LOGIC; -- pulse per second, g_pps_interval clk per pps interval
ssn : NATURAL; -- seconds sequence number
bsn : NATURAL; -- block sequence number, g_block_size clk per block
sync : STD_LOGIC; -- active at sop when pps is active or was active
sop : STD_LOGIC; -- start of block
eop : STD_LOGIC; -- end of block
END RECORD;
CONSTANT c_time_grid_rst : t_time_grid := ('0', 0, 0, '0', '0', '0');
-- Reference grid
SIGNAL ref_grid : t_time_grid := c_time_grid_rst;
SIGNAL ssn_eop : STD_LOGIC := '0';
SIGNAL hold_pps : STD_LOGIC := '0';
SIGNAL nxt_hold_pps : STD_LOGIC := '0';
-- Tb
SIGNAL tb_end : STD_LOGIC := '0'; SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL rst : STD_LOGIC := '1'; SIGNAL rst : STD_LOGIC := '1';
SIGNAL clk : STD_LOGIC := '1'; SIGNAL clk : STD_LOGIC := '1';
SIGNAL tb_state : t_state_enum := s_disable;
-- DUT -- DUT
SIGNAL dp_on : STD_LOGIC := '0'; SIGNAL dp_on : STD_LOGIC := '0';
SIGNAL dp_on_pps : STD_LOGIC := '0'; SIGNAL dp_on_pps : STD_LOGIC := '0';
SIGNAL dp_on_status : STD_LOGIC;
SIGNAL bs_restart : STD_LOGIC;
SIGNAL bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); SIGNAL bsn_init : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL bs_sosi : t_dp_sosi; SIGNAL bs_sosi : t_dp_sosi;
-- Verify -- Verify
SIGNAL exp_grid : t_time_grid;
SIGNAL unexpected_bs_sync : STD_LOGIC;
SIGNAL sl0 : STD_LOGIC := '0';
SIGNAL verify_en : STD_LOGIC := '0';
SIGNAL verify_sync : STD_LOGIC := '0'; SIGNAL verify_sync : STD_LOGIC := '0';
SIGNAL hold_bs_sop : STD_LOGIC; SIGNAL hold_bs_sop : STD_LOGIC;
SIGNAL prev_bs_valid : STD_LOGIC;
SIGNAL bs_starts_cnt : NATURAL := 0;
SIGNAL tb_bsn_cnt : INTEGER := 0; SIGNAL dbg_c_nof_pps : NATURAL := c_nof_pps;
SIGNAL dbg_c_nof_repeat : NATURAL := c_nof_repeat;
-- Define the PPS grid and the BSN grid that both start at 0 according to Figure 3.1 in [doc]:
SIGNAL SSN : NATURAL := 0; SIGNAL dbg_nof_blk : NATURAL;
SIGNAL BSN : NATURAL := 0; SIGNAL dbg_accumulate : NATURAL;
SIGNAL dbg_expected_bsn : NATURAL;
SIGNAL pps_sop : STD_LOGIC := '0';
SIGNAL nxt_pps_sop : STD_LOGIC := '0';
SIGNAL pps_eop : STD_LOGIC := '0';
SIGNAL bsn_sop : STD_LOGIC := '0';
SIGNAL nxt_bsn_sop : STD_LOGIC := '0';
SIGNAL bsn_eop : STD_LOGIC := '0';
SIGNAL expected_sync : STD_LOGIC := '0';
SIGNAL expected_sync_dly : STD_LOGIC := '0';
SIGNAL expected_bsn : NATURAL := 0;
SIGNAL expected_offset_bsn : NATURAL := 0;
SIGNAL dbg_nof_blk : NATURAL := 0;
SIGNAL dbg_accumulate : NATURAL := 0;
SIGNAL dbg_expected_bsn : NATURAL := 0;
BEGIN BEGIN
-----------------------------------------------------------------------------
-- Stimuli
-----------------------------------------------------------------------------
rst <= '1', '0' AFTER c_clk_period*7; rst <= '1', '0' AFTER c_clk_period*7;
clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; clk <= (NOT clk) OR tb_end AFTER c_clk_period/2;
-----------------------------------------------------------------------------
-- SSN/BSN generator -- Generate reference time grid
SSN <= SSN + 1 WHEN rising_edge(clk) AND pps_eop='1'; -- Seconds Sequence Number -----------------------------------------------------------------------------
BSN <= BSN + 1 WHEN rising_edge(clk) AND bsn_eop='1'; -- Block Sequence Number proc_common_gen_pulse(1, g_pps_interval, '1', sl0, clk, ref_grid.pps);
proc_common_gen_pulse(1, g_block_size, '1', sl0, clk, ref_grid.sop);
proc_common_gen_pulse(1, g_pps_interval, '1', rst, clk, nxt_pps_sop); -- make PPS grid with pps_sop at start of interval ref_grid.eop <= ref_grid.sop'DELAYED((g_block_size - 1) * c_clk_period);
pps_sop <= nxt_pps_sop AFTER c_clk_period; ssn_eop <= ref_grid.pps'DELAYED((g_pps_interval - 1) * c_clk_period);
pps_eop <= pps_sop'DELAYED((g_pps_interval-1)*c_clk_period); -- make PPS grid with pps_eop at end of intervals ref_grid.ssn <= ref_grid.ssn + 1 WHEN rising_edge(clk) AND ssn_eop = '1';
ref_grid.bsn <= ref_grid.bsn + 1 WHEN rising_edge(clk) AND ref_grid.eop = '1';
proc_common_gen_pulse(1, g_block_size, '1', rst, clk, nxt_bsn_sop); -- make BSN grid with bsn_sop at start of interval
bsn_sop <= nxt_bsn_sop AFTER c_clk_period; -- Issue sync at start of block
bsn_eop <= bsn_sop'DELAYED((g_block_size-1)*c_clk_period); -- make BSN grid with bsn_eop at end of interval p_ref_grid_sync : PROCESS(ref_grid, hold_pps)
-- Define the expected sync that occurs when pps_sop = bsn sop, or else at the first bsn_sop after the pps_sop, see Figure 3.1 in [doc].
p_expected_sync : PROCESS
BEGIN BEGIN
WAIT UNTIL rising_edge(clk); ref_grid.sync <= '0';
expected_sync <= '0'; nxt_hold_pps <= hold_pps;
proc_common_wait_until_high(clk, pps_sop);
IF bsn_sop = '1' THEN IF ref_grid.pps = '1' THEN
expected_sync <= '1'; IF ref_grid.sop = '1' THEN
expected_bsn <= BSN+1; ref_grid.sync <= '1'; -- immediately issue sync
expected_offset_bsn <= 0;
ELSE ELSE
proc_common_wait_until_high(clk, bsn_sop); nxt_hold_pps <= '1'; -- wait until next block
expected_sync <= '1'; END IF;
expected_bsn <= BSN+1; END IF;
expected_offset_bsn <= (BSN+1) * g_block_size - SSN * g_pps_interval;
IF hold_pps = '1' THEN
IF ref_grid.sop = '1' THEN
ref_grid.sync <= '1'; -- issue pending sync
nxt_hold_pps <= '0';
END IF;
END IF; END IF;
END PROCESS; END PROCESS;
expected_sync_dly <= expected_sync'DELAYED(c_dut_latency*c_clk_period); hold_pps <= nxt_hold_pps WHEN rising_edge(clk);
-- MM control exp_grid <= ref_grid'DELAYED(c_dut_latency * c_clk_period);
-----------------------------------------------------------------------------
-- Stimuli
-----------------------------------------------------------------------------
p_mm : PROCESS p_mm : PROCESS
VARIABLE v_bsn_time_offset : NATURAL; VARIABLE v_ssn : NATURAL;
VARIABLE v_bsn_init : NATURAL; VARIABLE v_bsn_init : NATURAL;
VARIABLE v_bsn_time_offset : NATURAL;
BEGIN BEGIN
tb_end <= '0';
tb_state <= s_disable;
--pps <= '0';
dp_on <= '0';
dp_on_pps <= '0';
-- Get synchronous to clk -- Get synchronous to clk
proc_common_wait_until_low(clk, rst); proc_common_wait_until_low(clk, rst);
proc_common_wait_some_cycles(clk, 500); proc_common_wait_some_cycles(clk, 10);
-- Start asynchronously by making dp_on high -- Start asynchronously by making dp_on high
proc_common_wait_until_high(clk, expected_sync_dly); verify_en <= '0'; -- only verify visualy in wave window
tb_state <= s_pps_start; tb_state <= s_dp_on;
dp_on_pps <= '0'; dp_on_pps <= '0';
dp_on <= '1'; dp_on <= '1';
verify_sync <= '0'; -- only verify visualy in wave window proc_common_wait_some_cycles(clk, c_nof_pps*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; tb_state <= s_disable;
dp_on <= '0'; dp_on <= '0';
dp_on_pps <= '0'; dp_on_pps <= '0';
proc_common_wait_some_cycles(clk, 10);
-- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop)
proc_common_wait_until_high(clk, pps_sop); -- Start synchronously by making dp_on and dp_on_pps high
v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; verify_en <= '1'; -- verify automatically in test bench
WHILE v_bsn_time_offset > 0 LOOP
proc_common_wait_some_cycles(clk, g_pps_interval); FOR I IN 0 TO c_nof_repeat-1 LOOP
v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size; -- Wait some variable time between tests, to enforce testing different
END LOOP; -- bsn_time_offset values
proc_common_wait_some_cycles(clk, 20);
-- Start synchronously by making dp_on high at pps proc_common_wait_some_cycles(clk, I*g_pps_interval);
FOR i IN 0 TO 2 LOOP
-- Now start on PPS -- Wait until in the beginning of PPS interval
proc_common_wait_until_high(clk, expected_sync); proc_common_wait_until_hi_lo(clk, ref_grid.pps);
v_bsn_time_offset := ((SSN + 1) * g_pps_interval) MOD g_block_size; proc_common_wait_some_cycles(clk, c_dut_latency);
v_bsn_init := ((SSN + 1) * g_pps_interval) / g_block_size;
IF v_bsn_time_offset = 0 THEN -- Determine bsn_init and bsn_time_offset for BSN source start
bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); -- . bsn_init = BSN at sync
ELSE -- . bsn_time_offset = number of clk that sync occurs after PPS
bsn_init <= TO_UVEC(v_bsn_init+1, c_bsn_w); v_ssn := ref_grid.ssn + 1; -- +1 to prepare start in next PPS interval
END IF; v_bsn_init := ceil_div(v_SSN * g_pps_interval, g_block_size); -- Equation 3.6 in [1]
tb_state <= s_pps_start; v_bsn_time_offset := v_bsn_init * g_block_size - v_SSN * g_pps_interval; -- Equation 3.7 in [1]
bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); --
bsn_time_offset <= TO_UVEC(v_bsn_time_offset, c_bsn_time_offset_w);
-- Start synchronously by making dp_on and dp_on_pps high
tb_state <= s_dp_on_pps;
dp_on_pps <= '1'; dp_on_pps <= '1';
dp_on <= '1'; dp_on <= '1';
verify_sync <= '1'; -- verify automatically in test bench proc_common_wait_some_cycles(clk, c_nof_pps*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; tb_state <= s_disable;
dp_on <= '0'; dp_on <= '0';
dp_on_pps <= '0'; dp_on_pps <= '0';
-- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop)
proc_common_wait_until_high(clk, pps_sop);
v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size;
WHILE v_bsn_time_offset > 0 LOOP
proc_common_wait_some_cycles(clk, g_pps_interval);
v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size;
END LOOP; END LOOP;
END LOOP; proc_common_wait_some_cycles(clk, 10);
ASSERT bs_starts_cnt = 1 + c_nof_repeat REPORT "Wrong number of BSN source starts." SEVERITY ERROR;
tb_end <= '1'; tb_end <= '1';
WAIT; WAIT;
...@@ -219,20 +250,21 @@ BEGIN ...@@ -219,20 +250,21 @@ BEGIN
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Verification -- Verification
-- . Some aspects of bs_sosi are verified multiple times in different ways,
-- this overlap is fine, because the tb and DUT are rather complicated, so
-- using different approaches also helpt to verify the tb itself.
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
verify_sync <= verify_en AND bs_sosi.valid;
proc_dp_verify_sop_and_eop(clk, bs_sosi.valid, bs_sosi.sop, bs_sosi.eop, hold_bs_sop); -- Verify that sop and eop come in pairs proc_dp_verify_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 --proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, exp_grid.sop, exp_grid.sync); -- Verify sync at sop and at expected_sync
-- Verify sync at sop and at expected_sync again: -- Verify sync at sop and at expected_sync
-- . now using the proc_dp_verify_sync() variant for dp_bsn_source_v2 that proc_dp_verify_sync(0, -- start bsn of PPS grid and BSN grid is 0, see [1]
-- 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_pps_interval,
g_block_size, g_block_size,
clk, clk,
verify_sync, verify_en,
bs_sosi.sync, bs_sosi.sync,
bs_sosi.sop, bs_sosi.sop,
bs_sosi.bsn, bs_sosi.bsn,
...@@ -240,6 +272,38 @@ BEGIN ...@@ -240,6 +272,38 @@ BEGIN
dbg_accumulate, dbg_accumulate,
dbg_expected_bsn); dbg_expected_bsn);
-- Verify bs_sosi by comparing with exp_grid, this again verifies bs_sosi.sync, sop and bsn
p_verify_bs_sosi_grid : PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
unexpected_bs_sync <= '0';
IF verify_en = '1' AND bs_sosi.valid = '1' THEN
ASSERT TO_UINT(bs_sosi.bsn) = exp_grid.bsn REPORT "Wrong bs_sosi.bsn /= exp_grid.bsn" SEVERITY ERROR;
ASSERT bs_sosi.sync = exp_grid.sync REPORT "Wrong bs_sosi.sync /= exp_grid.sync" SEVERITY ERROR;
ASSERT bs_sosi.sop = exp_grid.sop REPORT "Wrong bs_sosi.sop /= exp_grid.sop" SEVERITY ERROR;
ASSERT bs_sosi.eop = exp_grid.eop REPORT "Wrong bs_sosi.eop /= exp_grid.eop" SEVERITY ERROR;
-- Mark error in Wave window
IF bs_sosi.sync = '1' AND bs_sosi.sync /= exp_grid.sync THEN
unexpected_bs_sync <= '1';
END IF;
END IF;
END IF;
END PROCESS;
-- Verify that bs_sosi.valid = '1' did happen after dp_on by verifying bs_start
prev_bs_valid <= bs_sosi.valid WHEN rising_edge(clk);
bs_starts_cnt <= bs_starts_cnt + 1 WHEN rising_edge(clk) AND bs_sosi.valid = '1' AND prev_bs_valid = '0';
p_verify_bs_restart : PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
IF bs_restart = '1' THEN
ASSERT bs_sosi.sync = '1' REPORT "Unexpected bs_start while bs_sosi.sync /= 1" SEVERITY ERROR;
ASSERT prev_bs_valid = '0' REPORT "Unexpected bs_start while prev_bs_valid /= 0" SEVERITY ERROR;
END IF;
END IF;
END PROCESS;
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- DUT: dp_bsn_source_v2 -- DUT: dp_bsn_source_v2
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
...@@ -248,16 +312,23 @@ BEGIN ...@@ -248,16 +312,23 @@ BEGIN
GENERIC MAP ( GENERIC MAP (
g_block_size => g_block_size, g_block_size => g_block_size,
g_nof_clk_per_sync => g_pps_interval, g_nof_clk_per_sync => g_pps_interval,
g_bsn_w => c_bsn_w g_bsn_w => c_bsn_w,
g_bsn_time_offset_w => c_bsn_time_offset_w
) )
PORT MAP ( PORT MAP (
rst => rst, rst => rst,
clk => clk, clk => clk,
pps => pps_sop, pps => ref_grid.pps,
-- MM control -- MM control
dp_on => dp_on, dp_on => dp_on,
dp_on_pps => dp_on_pps, dp_on_pps => dp_on_pps,
dp_on_status => dp_on_status, -- = src_out.valid
bs_restart => bs_restart, -- = src_out.sop for first sop after dp_on went high
bsn_init => bsn_init, bsn_init => bsn_init,
bsn_time_offset => bsn_time_offset,
-- Streaming -- Streaming
src_out => bs_sosi src_out => bs_sosi
); );
......
...@@ -63,6 +63,10 @@ ...@@ -63,6 +63,10 @@
-- temporarily changing the ASSERT condition -- temporarily changing the ASSERT condition
-- b Initialy used LOOP in p_stimuli to repeat test. Later used list of -- 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. -- c_nof_test_intervals and tb_state to try different stimuli.
--
-- References:
-- [1] https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=L2M&title=L2+STAT+Decision%3A+Timing+in+Station
--
LIBRARY IEEE, common_lib, dp_lib; LIBRARY IEEE, common_lib, dp_lib;
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.ALL;
...@@ -516,6 +520,7 @@ BEGIN ...@@ -516,6 +520,7 @@ BEGIN
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
verify_sync <= NOT recover_from_in_lost; verify_sync <= NOT recover_from_in_lost;
-- Verify that sync is on PPS and BSN grid as defined in [1].
proc_dp_verify_sync(TO_UINT(ctrl_start_bsn), proc_dp_verify_sync(TO_UINT(ctrl_start_bsn),
ctrl_interval_size, ctrl_interval_size,
g_block_size, g_block_size,
......
...@@ -25,7 +25,7 @@ USE IEEE.std_logic_1164.ALL; ...@@ -25,7 +25,7 @@ USE IEEE.std_logic_1164.ALL;
USE work.tb_dp_pkg.ALL; USE work.tb_dp_pkg.ALL;
-- > as 2 -- > as 4
-- > run -all --> OK -- > run -all --> OK
ENTITY tb_tb_dp_bsn_source_v2 IS ENTITY tb_tb_dp_bsn_source_v2 IS
...@@ -33,30 +33,44 @@ END tb_tb_dp_bsn_source_v2; ...@@ -33,30 +33,44 @@ END tb_tb_dp_bsn_source_v2;
ARCHITECTURE tb OF tb_tb_dp_bsn_source_v2 IS 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 SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
-- within g_block_size number of g_pps_interval's
BEGIN BEGIN
-- from tb_dp_bsn_source_v2.vhd -- from tb_dp_bsn_source_v2.vhd
-- --
-- g_nof_pps : NATURAL := 20;
-- g_pps_interval : NATURAL := 240 -- g_pps_interval : NATURAL := 240
-- g_block_size : NATURAL := 32 -- g_block_size : NATURAL := 32
-- test different clk_per_sync -- test integer case
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_20_10 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 10); -- 20 // 10 = 2, 20 MOD 10 = 0, 20/10 = 2 block/sync
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_22_11 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (22, 11); -- 22 // 11 = 2, 22 MOD 11 = 0, 22/11 = 2 block/sync
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 u_39_13 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (39, 13); -- 39 // 13 = 3, 39 MOD 13 = 0, 39/13 = 3 block/sync
-- test smallest nof block per sync
u_10_10 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (10, 10); -- 1 block/sync
u_5_5 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (5, 5); -- 1 block/sync
-- test smallest g_block_size case
u_3_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (3, 3); -- 3 // 3 = 1, 3 MOD 3 = 0, 3/3 = 1 block/sync
u_6_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (6, 3); -- 6 // 3 = 2, 6 MOD 3 = 0, 6/3 = 2 block/sync
u_7_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (7, 3); -- 7 // 3 = 2, 7 MOD 3 = 1, 7/3 = 2.33 block/sync
-- test lofar case with 0.5 fraction in average nof block/sync
u_20_8 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 8); -- 20 // 8 = 2, 20 MOD 8 = 4, 20/8 = 2.5 block/sync
-- test different block_size's -- test fractional (corner) cases
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_18_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (18, 9); -- 18 MOD 9 = 0
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 u_17_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (17, 9); -- 17 MOD 9 = 8 = g_block_size - 1
u_19_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (19, 9); -- 19 MOD 9 = 1
u_20_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 9); -- 20 MOD 9 = 2
u_25_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (25, 9); -- 25 MOD 9 = 7
u_26_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (26, 9); -- 26 MOD 9 = 8 = g_block_size - 1
u_27_9 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (27, 9); -- 27 MOD 9 = 0
-- test some prime values -- 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_17_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (17, 3); -- 17 // 3 = 5, 17 MOD 3 = 2, 17/3 = 5.66 block/sync
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 u_101_17 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (101, 17); -- 101 // 17 = 5, 101 MOD 17 = 16, 101/17 = 5.9411 block/sync
END tb; END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment