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