From 7fc41fe02ce58fae87920e5bc6197a8231592a6c Mon Sep 17 00:00:00 2001
From: Erik Kooistra <kooistra@astron.nl>
Date: Fri, 20 Feb 2015 11:59:12 +0000
Subject: [PATCH] SVN copied dp_mux from $UNB to $RADIOHDL. Added g_mode=3 to
 select input via MM using xon, and using xoff on all other inputs.

---
 libraries/base/dp/hdllib.cfg          |   2 +-
 libraries/base/dp/src/vhdl/dp_mux.vhd | 344 ++++++++++++++++++++++++++
 2 files changed, 345 insertions(+), 1 deletion(-)
 create mode 100644 libraries/base/dp/src/vhdl/dp_mux.vhd

diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg
index 6cac266f1c..e5e9431b70 100644
--- a/libraries/base/dp/hdllib.cfg
+++ b/libraries/base/dp/hdllib.cfg
@@ -44,7 +44,7 @@ synth_files =
     $UNB/Firmware/modules/dp/src/vhdl/dp_fifo_from_mm_reg.vhd
     $UNB/Firmware/modules/dp/src/vhdl/mms_dp_fifo_to_mm.vhd
     $UNB/Firmware/modules/dp/src/vhdl/mms_dp_fifo_from_mm.vhd
-    $UNB/Firmware/modules/dp/src/vhdl/dp_mux.vhd
+    src/vhdl/dp_mux.vhd
     $UNB/Firmware/modules/dp/src/vhdl/dp_demux.vhd
     $UNB/Firmware/modules/dp/src/vhdl/dp_loopback.vhd
     $UNB/Firmware/modules/dp/src/vhdl/dp_concat.vhd
diff --git a/libraries/base/dp/src/vhdl/dp_mux.vhd b/libraries/base/dp/src/vhdl/dp_mux.vhd
new file mode 100644
index 0000000000..ddd8ed98a2
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/dp_mux.vhd
@@ -0,0 +1,344 @@
+--------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- 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/>.
+--
+--------------------------------------------------------------------------------
+
+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;
+
+
+-- Purpose:
+--   Multiplex frames from one or more input streams into one output stream.
+-- Description:
+--   The frames are marked by sop and eop. The input selection scheme depends
+--   on g_mode:
+--   0: Framed round-robin with fair chance.
+--      Uses eop to select next input after the frame has been passed on or
+--      select the next input when there is no frame coming in on the current
+--      input, so it has had its chance.
+--   1: Framed round-robin in forced order from each input.
+--      Uses eop to select next output. Holds input selection until sop is
+--      detected on that input. Results in ordered (low to high) but blocking
+--      (on absence of sop) input selection.
+--   2: Unframed external MM control input to select the output.
+--      Three options have been considered for the flow control:
+--      a) Use src_in for all inputs, data from the not selected inputs
+--         will get lost. In case FIFOs are used they are only useful used for
+--         the selected input.
+--      b) Use c_dp_siso_rdy for unused inputs, this flushes them like with
+--         option a) but possibly even faster in case the src_in.ready may get
+--         inactive to apply backpressure.
+--      c) Use c_dp_siso_hold for unused inputs, to stop them until they get
+--         selected again.
+--      Support only option a) because assume that the sel_ctrl is rather 
+--      static and the data from the unused inputs can be ignored.
+--   3: Framed external MM control input to select the output.
+--      This scheme is identical to g_mode=0, but with xon='1' only for the 
+--      selected input. The other not selected inputs have xon='0', so they
+--      will stop getting input frames and the round-robin scheme of g_mode=0
+--      will then automatically select only remaining active input.
+--      The assumption is that the upstream input sources do stop their output
+--      after they finished the current frame when xon='0'. If necessary
+--      dp_xonoff could be used to add such frame flow control to an input
+--      stream that does not yet support xon/xoff.
+--       
+--   The low part of the src_out.channel has c_sel_w = log2(g_nof_input) nof
+--   bits and equals the input port number. The snk_in_arr().channel bits are
+--   copied into the high part of the src_out.channel. Hence the total
+--   effective output channel width becomes g_in_channel_w+c_sel_w when
+--   g_use_in_channel=TRUE else c_sel_w.
+--   If g_use_fifo=TRUE then the frames are buffered at the input, else the
+--   connecting inputs need to take care of that.
+-- Remark:
+-- . Using g_nof_input=1 is transparent.
+-- . Difference with dp_frame_scheduler is that dp_frame_scheduler does not
+--   support back pressure via the ready signals.
+-- . This dp_mux adds true_log2(nof ports) low bits to out_channel and the
+--   dp_demux removes true_log2(nof ports) low bits from in_channel.
+-- . For multiplexing time series frames or sample it can be applicable to
+--   use g_append_channel_lo=FALSE in combination with g_mode=2.
+
+ENTITY dp_mux IS
+  GENERIC (
+    -- MUX
+    g_mode              : NATURAL := 0;
+    g_nof_input         : NATURAL := 2;                   -- >= 1
+    g_append_channel_lo : BOOLEAN := TRUE;
+    g_sel_ctrl_invert   : BOOLEAN := FALSE;  -- Use default FALSE when stream array IO are indexed (0 TO g_nof_input-1), else use TRUE when indexed (g_nof_input-1 DOWNTO 0)
+    -- Input FIFO
+    g_use_fifo          : BOOLEAN := FALSE;
+    g_bsn_w             : NATURAL := 16;
+    g_data_w            : NATURAL := 16;
+    g_empty_w           : NATURAL := 1;
+    g_in_channel_w      : NATURAL := 1;
+    g_error_w           : NATURAL := 1;
+    g_use_bsn           : BOOLEAN := FALSE;
+    g_use_empty         : BOOLEAN := FALSE;
+    g_use_in_channel    : BOOLEAN := FALSE;
+    g_use_error         : BOOLEAN := FALSE;
+    g_use_sync          : BOOLEAN := FALSE;
+    g_fifo_af_margin    : NATURAL := 4;  -- Nof words below max (full) at which fifo is considered almost full
+    g_fifo_size         : t_natural_arr := array_init(1024, 2);  -- must match g_nof_input, even when g_use_fifo=FALSE
+    g_fifo_fill         : t_natural_arr := array_init(   0, 2)   -- must match g_nof_input, even when g_use_fifo=FALSE
+  ); 
+  PORT (
+    rst         : IN  STD_LOGIC;
+    clk         : IN  STD_LOGIC;
+    -- Control
+    sel_ctrl    : IN  NATURAL RANGE 0 TO g_nof_input-1 := 0;  -- used by g_mode = 2, 3
+    -- ST sinks
+    snk_out_arr : OUT t_dp_siso_arr(0 TO g_nof_input-1);
+    snk_in_arr  : IN  t_dp_sosi_arr(0 TO g_nof_input-1);
+    -- ST source
+    src_in      : IN  t_dp_siso;
+    src_out     : OUT t_dp_sosi
+  );
+END dp_mux;
+
+
+ARCHITECTURE rtl OF dp_mux IS
+
+  -- Convert unconstrained range (that starts at INTEGER'LEFT) to 0 TO g_nof_input-1 range
+  CONSTANT c_fifo_fill  : t_natural_arr(0 TO g_nof_input-1) := g_fifo_fill;
+  CONSTANT c_fifo_size  : t_natural_arr(0 TO g_nof_input-1) := g_fifo_size;
+  
+  -- The low part of src_out.channel is used to represent the input port and the high part of src_out.channel is copied from snk_in_arr().channel
+  CONSTANT c_sel_w      : NATURAL := true_log2(g_nof_input);
+
+  CONSTANT c_rl         : NATURAL := 1;
+  SIGNAL tb_ready_reg   : STD_LOGIC_VECTOR(0 TO g_nof_input*(1+c_rl)-1);
+  
+  TYPE state_type IS (s_idle, s_output);
+  
+  SIGNAL state            : state_type;
+  SIGNAL nxt_state        : state_type;
+
+  SIGNAL i_snk_out_arr    : t_dp_siso_arr(0 TO g_nof_input-1);
+  
+  SIGNAL sel_ctrl_reg     : NATURAL RANGE 0 TO g_nof_input-1;
+  SIGNAL nxt_sel_ctrl_reg : NATURAL;
+  
+  SIGNAL in_sel           : NATURAL RANGE 0 TO g_nof_input-1;  -- input port low part of src_out.channel
+  SIGNAL nxt_in_sel       : NATURAL;
+  SIGNAL next_sel         : NATURAL;
+  
+  SIGNAL rd_siso_arr      : t_dp_siso_arr(0 TO g_nof_input-1);
+  SIGNAL rd_sosi_arr      : t_dp_sosi_arr(0 TO g_nof_input-1);
+  SIGNAL hold_src_in_arr  : t_dp_siso_arr(0 TO g_nof_input-1);
+  SIGNAL next_src_out_arr : t_dp_sosi_arr(0 TO g_nof_input-1);
+  SIGNAL pend_src_out_arr : t_dp_sosi_arr(0 TO g_nof_input-1);  -- SOSI control
+  
+  SIGNAL prev_src_in      : t_dp_siso;
+  SIGNAL src_out_hi       : t_dp_sosi;  -- snk_in_arr().channel as high part of src_out.channel
+  SIGNAL nxt_src_out_hi   : t_dp_sosi;
+  SIGNAL channel_lo       : STD_LOGIC_VECTOR(c_sel_w-1 DOWNTO 0);
+  SIGNAL nxt_channel_lo   : STD_LOGIC_VECTOR(c_sel_w-1 DOWNTO 0);
+  
+BEGIN
+
+  snk_out_arr <= i_snk_out_arr;
+
+  -- Monitor sink valid input and sink ready output
+  proc_dp_siso_alert(clk, snk_in_arr, i_snk_out_arr, tb_ready_reg);
+
+  p_src_out_wires : PROCESS(src_out_hi, channel_lo)
+  BEGIN
+    -- SOSI
+    src_out <= src_out_hi;
+    
+    IF g_append_channel_lo=TRUE THEN
+      -- The high part of src_out.channel copies the snk_in_arr().channel, the low part of src_out.channel is used to indicate the input port
+      src_out.channel                     <= SHIFT_UVEC(src_out_hi.channel, -c_sel_w);
+      src_out.channel(c_sel_w-1 DOWNTO 0) <= channel_lo;
+    END IF;
+  END PROCESS;
+  
+  p_clk: PROCESS(clk, rst)
+  BEGIN
+    IF rst='1' THEN
+      sel_ctrl_reg <= 0;
+      in_sel       <= 0;
+      prev_src_in  <= c_dp_siso_rst;
+      state        <= s_idle;
+      src_out_hi   <= c_dp_sosi_rst;
+      channel_lo   <= (OTHERS=>'0');
+    ELSIF rising_edge(clk) THEN
+      sel_ctrl_reg <= nxt_sel_ctrl_reg;
+      in_sel       <= nxt_in_sel;
+      prev_src_in  <= src_in;
+      state        <= nxt_state;
+      src_out_hi   <= nxt_src_out_hi;
+      channel_lo   <= nxt_channel_lo;
+    END IF;
+  END PROCESS;
+  
+  gen_input : FOR I IN 0 TO g_nof_input-1 GENERATE
+    gen_fifo : IF g_use_fifo=TRUE GENERATE
+      u_fill : ENTITY work.dp_fifo_fill
+      GENERIC MAP (
+        g_bsn_w          => g_bsn_w,
+        g_data_w         => g_data_w,
+        g_empty_w        => g_empty_w,
+        g_channel_w      => g_in_channel_w,
+        g_error_w        => g_error_w,
+        g_use_bsn        => g_use_bsn,
+        g_use_empty      => g_use_empty,
+        g_use_channel    => g_use_in_channel,
+        g_use_error      => g_use_error,
+        g_use_sync       => g_use_sync,
+        g_fifo_fill      => c_fifo_fill(I),
+        g_fifo_size      => c_fifo_size(I),
+        g_fifo_af_margin => g_fifo_af_margin,
+        g_fifo_rl        => 1
+      )
+      PORT MAP (
+        rst      => rst,
+        clk      => clk,
+        -- ST sink
+        snk_out  => i_snk_out_arr(I),
+        snk_in   => snk_in_arr(I),
+        -- ST source
+        src_in   => rd_siso_arr(I),
+        src_out  => rd_sosi_arr(I)
+      );
+    END GENERATE;
+    no_fifo : IF g_use_fifo=FALSE GENERATE
+      i_snk_out_arr <= rd_siso_arr;
+      rd_sosi_arr   <= snk_in_arr;
+    END GENERATE;
+    
+    -- Hold the sink input to be able to register the source output
+    u_hold : ENTITY work.dp_hold_input
+    PORT MAP (
+      rst          => rst,
+      clk          => clk,
+      -- ST sink
+      snk_out      => OPEN,                 -- SISO ready
+      snk_in       => rd_sosi_arr(I),       -- SOSI
+      -- ST source
+      src_in       => hold_src_in_arr(I),   -- SISO ready
+      next_src_out => next_src_out_arr(I),  -- SOSI
+      pend_src_out => pend_src_out_arr(I),
+      src_out_reg  => src_out_hi
+    );
+  END GENERATE;
+  
+  -- Register and adjust external MM sel_ctrl for g_sel_ctrl_invert
+  nxt_sel_ctrl_reg <= sel_ctrl WHEN g_sel_ctrl_invert=FALSE ELSE g_nof_input-1-sel_ctrl;
+    
+  -- The output register stage matches RL = 1 for src_in.ready
+  nxt_src_out_hi <= next_src_out_arr(in_sel);  -- default output selected next_src_out_arr 
+  nxt_channel_lo <= TO_UVEC(in_sel, c_sel_w);  -- pass on input index via channel low
+  
+  ------------------------------------------------------------------------------
+  -- Unframed MM controlled input selection scheme
+  ------------------------------------------------------------------------------
+  
+  gen_ctrl : IF g_mode=2 GENERATE
+    hold_src_in_arr <= (OTHERS=>src_in);  -- pass src_in on to all inputs, only the selected input sosi gets used and the sosi from the other inputs will get lost
+    rd_siso_arr     <= (OTHERS=>src_in);
+    
+    nxt_in_sel <= sel_ctrl_reg;  -- external MM control selects the input
+  END GENERATE;
+  
+  ------------------------------------------------------------------------------
+  -- Framed input selection scheme
+  ------------------------------------------------------------------------------
+  
+  gen_framed : IF g_mode=0 OR g_mode=1 OR g_mode=3 GENERATE
+    p_hold_src_in_arr : PROCESS(rd_siso_arr, pend_src_out_arr, in_sel, src_in)
+    BEGIN
+      hold_src_in_arr <= rd_siso_arr;       -- default ready for hold input when ready for sink input
+      IF pend_src_out_arr(in_sel).eop='1' THEN
+        hold_src_in_arr(in_sel) <= src_in;  -- also ready for hold input when the eop is there
+      END IF;
+    END PROCESS;
+  
+    next_sel <= in_sel+1 WHEN in_sel<g_nof_input-1 ELSE 0;
+  
+    p_state : PROCESS(state, in_sel, next_sel, pend_src_out_arr, src_in, prev_src_in, sel_ctrl_reg)
+    BEGIN
+      rd_siso_arr <= (OTHERS=>c_dp_siso_hold);  -- default not ready for input, but xon='1'
+      
+      nxt_in_sel <= in_sel;
+      
+      nxt_state <= state;
+      
+      CASE state IS
+        WHEN s_idle =>
+          -- Need to check pend_src_out_arr(in_sel).sop, which can be active if prev_src_in.ready was '1',
+          -- because src_in.ready may be '0' and then next_src_out_arr(in_sel).sop is '0'
+          IF pend_src_out_arr(in_sel).sop='1' THEN
+            IF pend_src_out_arr(in_sel).eop='1' THEN
+              rd_siso_arr <= (OTHERS=>c_dp_siso_hold);  -- the sop and the eop are there, it is a frame with only one data word, stop reading this input
+              IF src_in.ready='1' THEN
+                nxt_in_sel            <= next_sel;      -- the pend_src_out_arr(in_sel).eop will be output, so continue to next input.
+                rd_siso_arr(next_sel) <= src_in;
+              END IF;
+            ELSE
+              rd_siso_arr(in_sel) <= src_in;            -- the sop is there, so start outputting the frame from this input
+              nxt_state <= s_output;
+            END IF;
+          ELSE
+            CASE g_mode IS
+              WHEN 0 | 3 =>
+                -- Framed round-robin with fair chance per input
+                IF prev_src_in.ready='0' THEN
+                  rd_siso_arr(in_sel) <= src_in;        -- no sop, remain at current input to give it a chance
+                ELSE
+                  nxt_in_sel            <= next_sel;    -- no sop, select next input, because the current input has had a chance
+                  rd_siso_arr(next_sel) <= src_in;
+                END IF;
+              WHEN OTHERS =>  -- = 1
+                -- Framed round-robin in forced order from each input
+                rd_siso_arr(in_sel) <= src_in;          -- no sop, remain at current input to wait for a frame     
+            END CASE;
+          END IF;
+        WHEN OTHERS => -- s_output
+          rd_siso_arr(in_sel) <= src_in;                -- output the rest of the selected input frame
+          IF pend_src_out_arr(in_sel).eop='1' THEN
+            rd_siso_arr <= (OTHERS=>c_dp_siso_hold);    -- the eop is there, stop reading this input
+            IF src_in.ready='1' THEN
+              nxt_in_sel            <= next_sel;        -- the pend_src_out_arr(in_sel).eop will be output, so continue to next input. 
+              rd_siso_arr(next_sel) <= src_in;
+              nxt_state <= s_idle;
+            END IF;
+          END IF;
+      END CASE;
+      
+      -- Pass on frame level flow control
+      FOR I IN 0 TO g_nof_input-1 LOOP
+        rd_siso_arr(I).xon <= src_in.xon;
+        
+        IF g_mode=3 THEN
+          -- Framed MM control select input via XON
+          rd_siso_arr(I).xon <= '0';            -- force xon='0' for not selected inputs
+          IF sel_ctrl_reg=I THEN
+            rd_siso_arr(I).xon <= src_in.xon;   -- pass on frame level flow control for selected input
+          END IF;
+        END IF;
+      END LOOP;
+    END PROCESS;
+    
+  END GENERATE;
+      
+END rtl;
-- 
GitLab