From 6b431af8a9613991b3dea4749b07b672feca82d8 Mon Sep 17 00:00:00 2001 From: Eric Kooistra <kooistra@astron.nl> Date: Thu, 23 Mar 2023 14:01:56 +0100 Subject: [PATCH] Add first version of dp_rsn_source.vhd, compiles ok but not tested yet. --- libraries/base/dp/hdllib.cfg | 1 + libraries/base/dp/src/vhdl/dp_rsn_source.vhd | 232 +++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 libraries/base/dp/src/vhdl/dp_rsn_source.vhd diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg index bd4c013312..945734723b 100644 --- a/libraries/base/dp/hdllib.cfg +++ b/libraries/base/dp/hdllib.cfg @@ -90,6 +90,7 @@ synth_files = src/vhdl/dp_bsn_monitor_v2.vhd src/vhdl/dp_bsn_monitor_reg_v2.vhd src/vhdl/mms_dp_bsn_monitor_v2.vhd + src/vhdl/dp_rsn_source.vhd src/vhdl/dp_bsn_source.vhd src/vhdl/dp_bsn_source_v2.vhd src/vhdl/dp_bsn_source_reg.vhd diff --git a/libraries/base/dp/src/vhdl/dp_rsn_source.vhd b/libraries/base/dp/src/vhdl/dp_rsn_source.vhd new file mode 100644 index 0000000000..2fcf6f8bf2 --- /dev/null +++ b/libraries/base/dp/src/vhdl/dp_rsn_source.vhd @@ -0,0 +1,232 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2022 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- Author: E. Kooistra +-- Purpose: +-- Derive a new block and sync interval from an input bs_sosi stream, and +-- use the Raw sample Sequence Number (RSN) as rs_sosi.bsn according to the +-- PPS and RSN grid defined in [1]. +-- Description: +-- The rs_sosi timing is derived from the input bs_sosi, but the rs_sosi +-- uses RSN as rs_sosi.bsn and has its own block and sync intervals. +-- Assumption is that every clk cycle carries valid data when the +-- bs_sosi.valid is '1', so no gaps in bs_sosi.valid. +-- The bs_sosi can stop and restart. It (re)starts with bs_sosi.sync and +-- bs_sosi.sop when bs_sosi.valid becomes '1' and it stops when +-- bs_sosi.valid becomes '0' immediately after a bs_sosi.eop. +-- The rs_sosi block size is g_block_size. +-- The rs_sosi sync interval has g_nof_clk_per_sync. If g_nof_clk_per_sync +-- is not a multiple of g_block_size, then the rs_sosi.sync will occur at +-- the next start of block. +-- The initial RSN in rs_sosi.bsn is bs_sosi.bsn * g_block_size. +-- The rs_sosi starts when the bs_sosi starts, so when bs_sosi.sync = '1' +-- and bs_sosi.valid becomes '1'. +-- The rs_sosi ends after the rs_sosi.eop, when the bs_sosi ends. There +-- may be undefined filler data in the last rs_sosi block to finish it +-- after the rs_sosi.eop. +-- Remark: +-- * The bs_sosi typically comes from dp_bsn_source_v2.vhd +-- +-- References: +-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station + +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_rsn_source IS + GENERIC ( + g_block_size : NATURAL := 256; -- >= 3, see state machine + g_nof_clk_per_sync : NATURAL := 200 * 10**6; + g_bsn_w : NATURAL := 64 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Input stream sosi control using BSN + bs_sosi : IN t_dp_sosi; -- input reference stream using BSN + + -- Output stream sosi control using RSN + nof_clk_per_sync : IN STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := TO_UVEC(g_nof_clk_per_sync, c_word_w); + rs_sosi : OUT t_dp_sosi; -- output stream using RSN and g_block_size, g_nof_clk_per_sync + rs_restart : OUT STD_LOGIC; -- = rs_sosi.sync for first sync after bs_sosi.valid went high + rs_new_interval : OUT STD_LOGIC -- = active during first rs_sosi.sync interval + ); +END dp_rsn_source; + + +ARCHITECTURE rtl OF dp_rsn_source IS + + CONSTANT c_block_size_cnt_w : NATURAL := ceil_log2(g_block_size); + + TYPE t_state_enum IS (s_off, s_on_sop, s_on, s_on_eop); + + SIGNAL state : t_state_enum; + SIGNAL nxt_state : t_state_enum; + SIGNAL prev_state : t_state_enum; + + SIGNAL rsn : STD_LOGIC_VECTOR(g_bsn_w + c_block_size_cnt_w - 1 DOWNTO 0); + + SIGNAL nxt_sync : STD_LOGIC; + SIGNAL sync : STD_LOGIC; + SIGNAL nxt_sync_size_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL sync_size_cnt : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + + SIGNAL block_size_cnt : STD_LOGIC_VECTOR(c_block_size_cnt_w-1 DOWNTO 0); + SIGNAL nxt_block_size_cnt : STD_LOGIC_VECTOR(c_block_size_cnt_w-1 DOWNTO 0); + + SIGNAL i_rs_sosi : t_dp_sosi := c_dp_sosi_init; + SIGNAL nxt_rs_sosi : t_dp_sosi; + + SIGNAL i_rs_restart : STD_LOGIC; + SIGNAL nxt_rs_restart : STD_LOGIC; + SIGNAL i_rs_new_interval : STD_LOGIC; + SIGNAL reg_rs_new_interval : STD_LOGIC; + +BEGIN + + rs_sosi <= i_rs_sosi; + rs_restart <= i_rs_restart; + rs_new_interval <= i_rs_new_interval; + + -- Use sum of inputs nof bits for product, but actual RSN fits in g_bsn_w. + rsn <= MULT_UVEC(bs_sosi.bsn, TO_UVEC(g_block_size, c_block_size_cnt_w)); + + p_state : PROCESS(sync, sync_size_cnt, nof_clk_per_sync, + state, prev_state, i_rs_sosi, block_size_cnt) + BEGIN + -- Maintain sync_size_cnt for nof_clk_per_sync + -- . nof_clk_per_sync is the number of clk per sync interval and the + -- average number of clk per rs_sosi.sync interval, due to that the + -- rs_sosi.sync has to occur at the rs_sosi.sop of a block. + -- . The sync_size_cnt is started in s_off at the bs_sosi.sync. + -- . The sync interval is nof_clk_per_sync, so when the sync_size_cnt + -- wraps, then the sync is used to ensure that rs_sosi.sync will + -- pulse at the rs_sosi.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 rs_sosi.sync on next rs_sosi.sop + nxt_sync_size_cnt <= (OTHERS=>'0'); + END IF; + + -- State machine for rs_sosi + nxt_state <= state; + nxt_rs_sosi <= i_rs_sosi; -- hold rs_sosi.bsn + nxt_rs_sosi.sync <= '0'; + nxt_rs_sosi.valid <= '0'; + nxt_rs_sosi.sop <= '0'; + nxt_rs_sosi.eop <= '0'; + nxt_block_size_cnt <= block_size_cnt; + + CASE state IS + WHEN s_off => + nxt_rs_sosi <= c_dp_sosi_rst; + nxt_rs_sosi.bsn <= rsn(g_bsn_w-1 DOWNTO 0); -- RSN + nxt_sync <= '0'; + nxt_sync_size_cnt <= (OTHERS=>'0'); + IF bs_sosi.sync = '1' THEN + nxt_rs_sosi.sync <= '1'; + nxt_rs_sosi.sop <= '1'; + nxt_rs_sosi.valid <= '1'; + nxt_state <= s_on; + END IF; + + -- using separate states s_on_sop and s_on_eop instead of only + -- s_on state and block_size_cnt, cause that g_block_size must be + -- >= 3, but that is fine. + WHEN s_on_sop => + -- Start of block + nxt_rs_sosi.sop <= '1'; + nxt_rs_sosi.valid <= '1'; + nxt_state <= s_on; + -- block_size_cnt = 0 at rs_sosi.sop + nxt_block_size_cnt <= (OTHERS=>'0'); + -- after first block, increment bsn per block + IF prev_state = s_on_eop THEN + nxt_rs_sosi.bsn <= INCR_DP_BSN(i_rs_sosi.bsn, g_block_size, g_bsn_w); -- RSN + END IF; + -- check for pending sync + IF sync = '1' THEN + nxt_rs_sosi.sync <= '1'; + nxt_sync <= '0'; + END IF; + + WHEN s_on => + -- During block + nxt_rs_sosi.valid <= '1'; + -- block_size_cnt increments to determine end of block + nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1); + IF UNSIGNED(block_size_cnt) >= g_block_size - 3 THEN + nxt_state <= s_on_eop; + END IF; + + WHEN s_on_eop => + -- End of block + nxt_rs_sosi.eop <= '1'; + nxt_rs_sosi.valid <= '1'; + nxt_state <= s_on_sop; + -- block_size_cnt is dont care at at rs_sosi.eop + -- accept dp_off after eop, to avoid fractional blocks + IF bs_sosi.valid = '0' THEN + nxt_state <= s_off; + END IF; + + WHEN OTHERS => -- reover from undefined state + nxt_state <= s_off; + END CASE; + END PROCESS; + + -- rs_sosi.valid transition from 0 to 1 is a rs_restart, use nxt_rs_restart + -- to have rs_restart at first rs_sosi.sync and rs_sosi.sop. + nxt_rs_restart <= nxt_rs_sosi.valid AND NOT i_rs_sosi.valid; + + i_rs_new_interval <= '1' WHEN i_rs_restart = '1' ELSE + '0' WHEN i_rs_sosi.sync = '1' ELSE + reg_rs_new_interval; + + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + prev_state <= s_off; + state <= s_off; + i_rs_sosi <= c_dp_sosi_rst; + sync_size_cnt <= (OTHERS=>'0'); + sync <= '0'; + block_size_cnt <= (OTHERS=>'0'); + i_rs_restart <= '0'; + reg_rs_new_interval <= '0'; + ELSIF rising_edge(clk) THEN + prev_state <= state; + state <= nxt_state; + i_rs_sosi <= nxt_rs_sosi; + sync_size_cnt <= nxt_sync_size_cnt; + sync <= nxt_sync; + block_size_cnt <= nxt_block_size_cnt; + i_rs_restart <= nxt_rs_restart; + reg_rs_new_interval <= i_rs_new_interval; + END IF; + END PROCESS; + +END rtl; -- GitLab