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