diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg index 3385ef085294734990746f48cb90267228449553..6e9f06dc39d1fc5fd18a50b36888de444246b87c 100644 --- a/libraries/base/dp/hdllib.cfg +++ b/libraries/base/dp/hdllib.cfg @@ -124,6 +124,9 @@ synth_files = src/vhdl/dp_src_out_timer.vhd src/vhdl/dp_sync_checker.vhd src/vhdl/mms_dp_sync_checker.vhd + src/vhdl/dp_folder.vhd + src/vhdl/dp_unfolder.vhd + src/vhdl/dp_switch.vhd tb/vhdl/dp_stream_player.vhd tb/vhdl/dp_sosi_recorder.vhd tb/vhdl/dp_stream_rec_play.vhd @@ -187,7 +190,10 @@ test_bench_files = tb/vhdl/tb_dp_xonoff.vhd tb/vhdl/tb_mms_dp_xonoff.vhd tb/vhdl/tb_dp_sync_insert.vhd - + tb/vhdl/tb_dp_folder.vhd + tb/vhdl/tb_dp_switch.vhd + + tb/vhdl/tb_tb_dp_block_gen.vhd tb/vhdl/tb_tb_dp_bsn_align.vhd tb/vhdl/tb_tb_dp_concat.vhd diff --git a/libraries/base/dp/src/vhdl/dp_folder.vhd b/libraries/base/dp/src/vhdl/dp_folder.vhd new file mode 100644 index 0000000000000000000000000000000000000000..98e6895f1bb42fba81f3537ee318502686c47278 --- /dev/null +++ b/libraries/base/dp/src/vhdl/dp_folder.vhd @@ -0,0 +1,238 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 +-- 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, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; + +-- Author: +-- . Daniel van der Schuur +-- Purpose: +-- . Fold n input streams into n/2, n/2/2, n/2/2/2, .. output streams +-- Description: +-- . This component assumes the user has scheduled the input streams properly. +-- . Input data that is to be folded onto one stream must not be valid at the same clock cycle +-- . nof_outputs = ceil_div(g_nof_inputs, 2^(g_nof_folds)) for g_nof_folds>=0 +-- . Examples: +-- . g_nof_inputs=10, g_nof_folds=0 -> nof_outputs=10 +-- . g_nof_inputs=10, g_nof_folds=1 -> nof_outputs= 5 +-- . g_nof_inputs=10, g_nof_folds=2 -> nof_outputs= 3 +-- . g_nof_inputs=10, g_nof_folds=3 -> nof_outputs= 2 +-- . g_nof_inputs=10, g_nof_folds=4 -> nof_outputs= 1 +-- . g_nof_inputs=10, g_nof_folds<0 -> nof_outputs= 1 +-- . This entity recursively instantiates (registered) stages of itself when folding streams multiple times. + +ENTITY dp_folder IS + GENERIC ( + g_nof_inputs : NATURAL; -- Number of inputs to fold >0 + g_nof_folds : INTEGER := -1; -- >0: Number of folds; 0: Wire out to in; <0: Fold until one output remains + g_output_block_size : NATURAL := 0; -- >0: Create SOP/EOP tagged output blocks of this size. 0: Forward incoming SOP,EOP. + g_fwd_sync_bsn : BOOLEAN := FALSE -- TRUE: forwards (stored) input Sync+BSN (from snk_in_arr(0)) to all output streams + ); + PORT ( + clk : IN STD_LOGIC; + rst : IN STD_LOGIC; + + snk_in_arr : IN t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); + src_out_arr : OUT t_dp_sosi_arr(sel_a_b(g_nof_folds>=0, ceil_div(g_nof_inputs, ceil_pow2(g_nof_folds)), 1)-1 DOWNTO 0) + ); +END dp_folder; + +ARCHITECTURE str OF dp_folder IS + + COMPONENT dp_folder IS + GENERIC ( + g_nof_inputs : NATURAL; + g_nof_folds : INTEGER := -1; + g_output_block_size : NATURAL := 0; + g_fwd_sync_bsn : BOOLEAN := FALSE + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_in_arr : IN t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); + + src_out_arr : OUT t_dp_sosi_arr(sel_a_b(g_nof_folds>=0, ceil_div(g_nof_inputs, ceil_pow2(g_nof_folds)), 1)-1 DOWNTO 0) + ); + END COMPONENT; + + CONSTANT c_nof_muxes : NATURAL := ceil_div(g_nof_inputs, 2); -- Using ceil_div always yields an even number of mux inputs + + SIGNAL mux_snk_in_arr : t_dp_sosi_arr(2*c_nof_muxes-1 DOWNTO 0); + SIGNAL mux_snk_in_2arr_2 : t_dp_sosi_2arr_2(c_nof_muxes-1 DOWNTO 0); + + SIGNAL mux_src_out_arr : t_dp_sosi_arr(c_nof_muxes-1 DOWNTO 0); + SIGNAL nxt_mux_src_out_arr : t_dp_sosi_arr(c_nof_muxes-1 DOWNTO 0); + + SIGNAL dp_folder_src_out_arr : t_dp_sosi_arr(sel_a_b(g_nof_folds>=0, ceil_div(g_nof_inputs, ceil_pow2(g_nof_folds)), 1)-1 DOWNTO 0); + + SIGNAL dp_block_gen_snk_in_arr : t_dp_sosi_arr(sel_a_b(g_nof_folds>=0, ceil_div(g_nof_inputs, ceil_pow2(g_nof_folds)), 1)-1 DOWNTO 0); + SIGNAL dp_block_gen_src_out_arr : t_dp_sosi_arr(sel_a_b(g_nof_folds>=0, ceil_div(g_nof_inputs, ceil_pow2(g_nof_folds)), 1)-1 DOWNTO 0); + +BEGIN + + gen_arch: IF g_nof_folds/=0 GENERATE + ----------------------------------------------------------------------------- + -- Wire input array to mux_snk_in_arr to make sure we have an even number + -- of buses to work with in case of an odd number of inputs + -- . We want an even number of buses because we will wire up 2-input muxes + ----------------------------------------------------------------------------- + gen_even_nof_buses: FOR i IN 0 TO g_nof_inputs-1 GENERATE + mux_snk_in_arr(i) <= snk_in_arr(i); + END GENERATE; + + ----------------------------------------------------------------------------- + -- Wire inputs to the 2-input muxes + ----------------------------------------------------------------------------- + gen_mux_inputs_0: FOR i IN 0 TO c_nof_muxes-1 GENERATE + mux_snk_in_2arr_2(i)(0) <= mux_snk_in_arr(2*i); + mux_snk_in_2arr_2(i)(1) <= mux_snk_in_arr(2*i+1); + END GENERATE; + + ----------------------------------------------------------------------------- + -- Simple 2-input mux logic + ----------------------------------------------------------------------------- + p_mux : PROCESS(mux_snk_in_2arr_2) + BEGIN + FOR i IN 0 TO c_nof_muxes-1 LOOP + nxt_mux_src_out_arr(i) <= c_dp_sosi_rst; + IF mux_snk_in_2arr_2(i)(0).valid='1' THEN + nxt_mux_src_out_arr(i).data <= mux_snk_in_2arr_2(i)(0).data; + nxt_mux_src_out_arr(i).re <= mux_snk_in_2arr_2(i)(0).re; + nxt_mux_src_out_arr(i).im <= mux_snk_in_2arr_2(i)(0).im; + nxt_mux_src_out_arr(i).valid <= '1'; + ELSIF mux_snk_in_2arr_2(i)(1).valid='1' THEN + nxt_mux_src_out_arr(i).data <= mux_snk_in_2arr_2(i)(1).data; + nxt_mux_src_out_arr(i).re <= mux_snk_in_2arr_2(i)(1).re; + nxt_mux_src_out_arr(i).im <= mux_snk_in_2arr_2(i)(1).im; + nxt_mux_src_out_arr(i).valid <= '1'; + END IF; + END LOOP; + END PROCESS; + + -- Registers + p_clk: PROCESS(clk, rst) + BEGIN + IF rst='1' THEN + mux_src_out_arr <= (OTHERS=>c_dp_sosi_rst); + ELSIF rising_edge(clk) THEN + mux_src_out_arr <= nxt_mux_src_out_arr; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- Not done folding; add a stage. + -- . g_nof_folds <0 : user wants to fold all the way to one output + -- . g_nof_folds >0 : user wants to fold n times + ----------------------------------------------------------------------------- + gen_dp_folder: IF (g_nof_folds<0 AND c_nof_muxes>1) OR g_nof_folds>1 GENERATE + u_dp_folder : dp_folder + GENERIC MAP ( + g_nof_inputs => c_nof_muxes, + g_nof_folds => g_nof_folds-1, + g_output_block_size => g_output_block_size, + g_fwd_sync_bsn => g_fwd_sync_bsn + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_in_arr => mux_src_out_arr, + src_out_arr => dp_folder_src_out_arr + ); + + src_out_arr <= dp_folder_src_out_arr; + + END GENERATE; + + ----------------------------------------------------------------------------- + -- c_nof_muxes=1 or g_nof_folds=1, so this is the last stage. + ----------------------------------------------------------------------------- + gen_src_out_arr: IF (g_nof_folds<0 AND c_nof_muxes=1) OR g_nof_folds=1 GENERATE + dp_block_gen_snk_in_arr <= mux_src_out_arr; + + ----------------------------------------------------------------------------- + -- Add SOP and EOP to the outputs + ----------------------------------------------------------------------------- + gen_ctrl : IF g_output_block_size>0 GENERATE + gen_dp_block_gen : FOR i IN 0 TO c_nof_muxes-1 GENERATE + u_dp_block_gen : ENTITY work.dp_block_gen + GENERIC MAP ( + g_use_src_in => FALSE, + g_nof_data => g_output_block_size, + g_preserve_sync => TRUE, + g_preserve_bsn => TRUE + ) + PORT MAP( + rst => rst, + clk => clk, + + snk_in => dp_block_gen_snk_in_arr(i), + src_out => dp_block_gen_src_out_arr(i) + ); + END GENERATE; + END GENERATE; + + no_ctrl : IF g_output_block_size=0 GENERATE + dp_block_gen_src_out_arr <= dp_block_gen_snk_in_arr; + END GENERATE; + + ----------------------------------------------------------------------------- + -- Re-add input sync + BSN to all output streams + ----------------------------------------------------------------------------- + gen_sync_bsn : IF g_fwd_sync_bsn = TRUE GENERATE + gen_dp_fifo_info: FOR i IN 0 TO c_nof_muxes-1 GENERATE + u_dp_fifo_info : ENTITY work.dp_fifo_info + GENERIC MAP ( + g_use_sync => TRUE, + g_use_bsn => TRUE + ) + PORT MAP ( + rst => rst, + clk => clk, + + data_snk_in => dp_block_gen_src_out_arr(i), -- delayed snk_in data + info_snk_in => snk_in_arr(0), -- original snk_in info + + src_in => c_dp_siso_rdy, + src_out => src_out_arr(i) + ); + END GENERATE; + END GENERATE; + + no_sync_bsn : IF g_fwd_sync_bsn = FALSE GENERATE + src_out_arr <= dp_block_gen_src_out_arr; + END GENERATE; + END GENERATE; + END GENERATE; + + ----------------------------------------------------------------------------- + -- Wire output to input if g_nof_folds=0 + ----------------------------------------------------------------------------- + gen_wire_out_to_in: IF g_nof_folds=0 GENERATE + dp_block_gen_snk_in_arr <= snk_in_arr; + END GENERATE; + +END str; + diff --git a/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd b/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd index 26945f2feace344b9a69c86d1d18d58413575e5e..c002b9efd71ad0faf2d61463a242cde38cdd87b8 100644 --- a/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd +++ b/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd @@ -75,7 +75,7 @@ PACKAGE dp_stream_pkg Is channel : STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0); err : STD_LOGIC_VECTOR(c_dp_stream_error_w-1 DOWNTO 0); -- name field 'err' to avoid the 'error' keyword END RECORD; - + -- Initialise signal declarations with c_dp_stream_rst/rdy to ease the interpretation of slv fields with unused bits CONSTANT c_dp_siso_rst : t_dp_siso := ('0', '0'); CONSTANT c_dp_siso_x : t_dp_siso := ('X', 'X'); @@ -329,7 +329,13 @@ PACKAGE dp_stream_pkg Is FUNCTION func_dp_stream_set_data(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING ) RETURN t_dp_sosi_arr; FUNCTION func_dp_stream_set_data(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING; mask : STD_LOGIC_VECTOR) RETURN t_dp_sosi_arr; - + -- Concatenate the data from a SOSI array into a single SOSI stream (assumes streams are in sync) + FUNCTION func_dp_stream_concat(snk_in_arr : t_dp_sosi_arr; data_w : NATURAL) RETURN t_dp_sosi; -- Concat SOSI_ARR data into single SOSI + FUNCTION func_dp_stream_concat(src_in : t_dp_siso; nof_streams : NATURAL) RETURN t_dp_siso_arr; -- Wire single SISO to SISO_ARR + -- Deconcatenate data from SOSI into SOSI array + FUNCTION func_dp_stream_deconcat(snk_in : t_dp_sosi; nof_streams, data_w : NATURAL) RETURN t_dp_sosi_arr; -- Deconcat SOSI data + FUNCTION func_dp_stream_deconcat(src_out_arr : t_dp_siso_arr) RETURN t_dp_siso; -- Wire SISO_ARR(0) to single SISO + END dp_stream_pkg; @@ -1150,5 +1156,46 @@ PACKAGE BODY dp_stream_pkg IS RETURN v_dp; END; + -- Concatenate the data (and complex fields) from a SOSI array into a single SOSI stream (assumes streams are in sync) + FUNCTION func_dp_stream_concat(snk_in_arr : t_dp_sosi_arr; data_w : NATURAL) RETURN t_dp_sosi IS + VARIABLE v_src_out : t_dp_sosi := snk_in_arr(0); + VARIABLE v_compl_data_w : NATURAL := data_w/2; + BEGIN + FOR i IN snk_in_arr'RANGE LOOP + v_src_out.data((i+1)* data_w-1 DOWNTO i* data_w) := snk_in_arr(i).data( data_w-1 DOWNTO 0); + v_src_out.re( (i+1)*v_compl_data_w-1 DOWNTO i*v_compl_data_w) := snk_in_arr(i).re(v_compl_data_w-1 DOWNTO 0); + v_src_out.im( (i+1)*v_compl_data_w-1 DOWNTO i*v_compl_data_w) := snk_in_arr(i).im(v_compl_data_w-1 DOWNTO 0); + END LOOP; + RETURN v_src_out; + END; + + FUNCTION func_dp_stream_concat(src_in : t_dp_siso; nof_streams : NATURAL) RETURN t_dp_siso_arr IS -- Wire single SISO to SISO_ARR + VARIABLE v_snk_out_arr : t_dp_siso_arr(nof_streams-1 DOWNTO 0); + BEGIN + FOR i IN v_snk_out_arr'RANGE LOOP + v_snk_out_arr(i) := src_in; + END LOOP; + RETURN v_snk_out_arr; + END; + + -- Deconcatenate data from SOSI into SOSI array + FUNCTION func_dp_stream_deconcat(snk_in : t_dp_sosi; nof_streams, data_w : NATURAL) RETURN t_dp_sosi_arr IS + VARIABLE v_src_out_arr : t_dp_sosi_arr(nof_streams-1 DOWNTO 0); + VARIABLE v_compl_data_w : NATURAL := data_w/2; + BEGIN + FOR i IN v_src_out_arr'RANGE LOOP + v_src_out_arr(i) := snk_in; + v_src_out_arr(i).data( data_w-1 DOWNTO 0) := snk_in.data((i+1)* data_w-1 DOWNTO i* data_w); + v_src_out_arr(i).re( v_compl_data_w-1 DOWNTO 0) := snk_in.re ((i+1)*v_compl_data_w-1 DOWNTO i*v_compl_data_w); + v_src_out_arr(i).im( v_compl_data_w-1 DOWNTO 0) := snk_in.im ((i+1)*v_compl_data_w-1 DOWNTO i*v_compl_data_w); + END LOOP; + RETURN v_src_out_arr; + END; + + FUNCTION func_dp_stream_deconcat(src_out_arr : t_dp_siso_arr) RETURN t_dp_siso IS -- Wire SISO_ARR(0) to single SISO + BEGIN + RETURN src_out_arr(0); + END; + END dp_stream_pkg; diff --git a/libraries/base/dp/src/vhdl/dp_switch.vhd b/libraries/base/dp/src/vhdl/dp_switch.vhd new file mode 100644 index 0000000000000000000000000000000000000000..0f51fcfe7186816db8848d09b8b6b6fc29d0610c --- /dev/null +++ b/libraries/base/dp/src/vhdl/dp_switch.vhd @@ -0,0 +1,149 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 +-- 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: +-- . Daniel van der Schuur +-- Purpose: +-- . Switch one of g_nof_inputs input streams to the output. +-- Description: +-- . Respects frame boundaries (SOP..EOP). +-- . This is basically an MM-controlled dp_mux (with g_mode=4). +-- . There is always one input enabled. If undesired, add your own input and +-- assign c_dp_sosi_rst to it. Switching to that input will disable the output. +-- Remark: +-- . dp_mux requires a minimum invaled gap of 1 cycle to switch to a new input! +-- . So this does not work for continuous streams! + +LIBRARY IEEE, common_lib, mm_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.common_field_pkg.ALL; +USE work.dp_stream_pkg.ALL; + +ENTITY dp_switch IS + GENERIC ( + g_nof_inputs : NATURAL; -- Number of inputs + g_default_enabled : NATURAL := 0; -- This input number 0..g_nof_inputs-1 will be enabled by default + g_use_fifo : BOOLEAN := FALSE; -- This and the generics below are forwarded to dp_mux only + 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 : NATURAL := 1024; + g_fifo_fill : NATURAL := 0 + ); + PORT ( + dp_clk : IN STD_LOGIC; + dp_rst : IN STD_LOGIC; + + mm_clk : IN STD_LOGIC; + mm_rst : IN STD_LOGIC; + + snk_in_arr : IN t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); + src_out : OUT t_dp_sosi; + + reg_mosi : IN t_mem_mosi; + reg_miso : OUT t_mem_miso + ); +END dp_switch; + + +ARCHITECTURE str OF dp_switch IS + + CONSTANT c_field_arr : t_common_field_arr(0 DOWNTO 0) := (0=> ( field_name_pad("input_select"), "RW", ceil_log2(g_nof_inputs), field_default(g_default_enabled) )); + + SIGNAL mm_fields_out : STD_LOGIC_VECTOR(field_slv_out_len(c_field_arr)-1 DOWNTO 0); + SIGNAL dp_mux_sel_ctrl_req : NATURAL; + SIGNAL dp_mux_sel_ctrl : NATURAL; + +BEGIN + + ------------------------------------------------------------------------------ + -- A single MM register contains input to select + ------------------------------------------------------------------------------ + u_mm_fields: ENTITY mm_lib.mm_fields + GENERIC MAP( + g_field_arr => c_field_arr + ) + PORT MAP ( + mm_clk => mm_clk, + mm_rst => mm_rst, + + mm_mosi => reg_mosi, + mm_miso => reg_miso, + + slv_clk => dp_clk, + slv_rst => dp_rst, + + slv_out => mm_fields_out + ); + + ------------------------------------------------------------------------------ + -- Don't allow user to select a non-existent input + -- . If user requests non-existent input, the default input is forwarded instead. + ------------------------------------------------------------------------------ + dp_mux_sel_ctrl_req <= TO_UINT(mm_fields_out(field_hi(c_field_arr, "input_select") DOWNTO field_lo(c_field_arr, "input_select"))); + dp_mux_sel_ctrl <= dp_mux_sel_ctrl_req WHEN dp_mux_sel_ctrl_req<g_nof_inputs ELSE g_default_enabled; + + ------------------------------------------------------------------------------ + -- DP mux forwards input based on dp_mux_sel_ctrl + ------------------------------------------------------------------------------ + u_dp_mux : ENTITY work.dp_mux + GENERIC MAP ( + g_mode => 4, -- Use sel_ctrl + g_sel_ctrl_invert => TRUE, -- Invert the control as we're using DOWNTO ranged sosi_arrays. + g_nof_input => g_nof_inputs, + g_use_fifo => g_use_fifo, + g_bsn_w => g_bsn_w, + g_data_w => g_data_w, + g_empty_w => g_empty_w, + g_in_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_in_channel => g_use_in_channel, + g_use_error => g_use_error, + g_use_sync => g_use_sync, + g_fifo_af_margin => g_fifo_af_margin, + g_fifo_size => array_init(g_fifo_size, g_nof_inputs), + g_fifo_fill => array_init(g_fifo_fill, g_nof_inputs) + ) + PORT MAP ( + clk => dp_clk, + rst => dp_rst, + + sel_ctrl => dp_mux_sel_ctrl, + + snk_in_arr => snk_in_arr, + + src_out => src_out, + src_in => c_dp_siso_rdy + ); + +END str; diff --git a/libraries/base/dp/src/vhdl/dp_unfolder.vhd b/libraries/base/dp/src/vhdl/dp_unfolder.vhd new file mode 100644 index 0000000000000000000000000000000000000000..e2d5343d0b0c1dbff8fed361acee9ea8814cbee8 --- /dev/null +++ b/libraries/base/dp/src/vhdl/dp_unfolder.vhd @@ -0,0 +1,251 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 +-- 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, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; + +-- Author: +-- . Daniel van der Schuur +-- Purpose: +-- . Unfold n input streams into n*2, n*2*2, n*2*2*2, .. output streams +-- Description: +-- . Reversed operation of dp_folder. + +ENTITY dp_unfolder IS + GENERIC ( + g_nof_inputs : NATURAL; -- Number of inputs + g_nof_unfolds : NATURAL := 0; -- Number of times to unfold + g_output_block_size : NATURAL := 0; -- >0: Create SOP/EOP tagged output blocks of this size. + g_fwd_sync_bsn : BOOLEAN := FALSE; -- TRUE: forwards (stored) input Sync+BSN (from snk_in_arr(0)) to all output streams + g_output_align : BOOLEAN := TRUE -- TRUE: Use pipeline stages to align the outputs + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_in_arr : IN t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); + + src_out_arr : OUT t_dp_sosi_arr(g_nof_inputs*pow2(g_nof_unfolds)-1 DOWNTO 0) + ); +END dp_unfolder; + +ARCHITECTURE str OF dp_unfolder IS + + COMPONENT dp_unfolder IS + GENERIC ( + g_nof_inputs : NATURAL; + g_nof_unfolds : NATURAL := 0; + g_output_align : BOOLEAN := TRUE + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_in_arr : IN t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); + + src_out_arr : OUT t_dp_sosi_arr(g_nof_inputs*pow2(g_nof_unfolds)-1 DOWNTO 0) + ); + END COMPONENT; + + CONSTANT c_nof_demuxes : NATURAL := g_nof_inputs; + CONSTANT c_nof_outputs : NATURAL := g_nof_inputs*pow2(g_nof_unfolds); + + SIGNAL output_sel_arr : STD_LOGIC_VECTOR(c_nof_demuxes-1 DOWNTO 0); + SIGNAL nxt_output_sel_arr : STD_LOGIC_VECTOR(c_nof_demuxes-1 DOWNTO 0); + + SIGNAL demux_src_out_2arr_2 : t_dp_sosi_2arr_2(c_nof_demuxes-1 DOWNTO 0); + SIGNAL nxt_demux_src_out_2arr_2 : t_dp_sosi_2arr_2(c_nof_demuxes-1 DOWNTO 0); + SIGNAL demux_src_out_arr : t_dp_sosi_arr(2*c_nof_demuxes-1 DOWNTO 0); + + SIGNAL dp_pipeline_snk_in_arr : t_dp_sosi_arr(c_nof_outputs-1 DOWNTO 0); + + SIGNAL dp_block_gen_snk_in_arr : t_dp_sosi_arr(c_nof_outputs-1 DOWNTO 0); + SIGNAL dp_block_gen_src_out_arr : t_dp_sosi_arr(c_nof_outputs-1 DOWNTO 0); + +BEGIN + + gen_arch: IF g_nof_unfolds/=0 GENERATE + ----------------------------------------------------------------------------- + -- Simple 2-output demux logic + ----------------------------------------------------------------------------- + gen_demux_comb: FOR i IN 0 TO c_nof_demuxes-1 GENERATE + nxt_output_sel_arr(i) <= NOT output_sel_arr(i) WHEN snk_in_arr(i).valid='1' ELSE output_sel_arr(i); + END GENERATE; + + p_demux : PROCESS(snk_in_arr, output_sel_arr) + BEGIN + FOR i IN 0 TO c_nof_demuxes-1 LOOP + nxt_demux_src_out_2arr_2(i)(0) <= c_dp_sosi_rst; + nxt_demux_src_out_2arr_2(i)(1) <= c_dp_sosi_rst; + IF snk_in_arr(i).valid='1' THEN + IF output_sel_arr(i)='0' THEN + nxt_demux_src_out_2arr_2(i)(0).data <= snk_in_arr(i).data; + nxt_demux_src_out_2arr_2(i)(0).re <= snk_in_arr(i).re; + nxt_demux_src_out_2arr_2(i)(0).im <= snk_in_arr(i).im; + nxt_demux_src_out_2arr_2(i)(0).valid <= '1'; + ELSE + nxt_demux_src_out_2arr_2(i)(1).data <= snk_in_arr(i).data; + nxt_demux_src_out_2arr_2(i)(1).re <= snk_in_arr(i).re; + nxt_demux_src_out_2arr_2(i)(1).im <= snk_in_arr(i).im; + nxt_demux_src_out_2arr_2(i)(1).valid <= '1'; + END IF; + END IF; + END LOOP; + END PROCESS; + + -- Registers + p_clk: PROCESS(clk, rst) + BEGIN + IF rst='1' THEN + demux_src_out_2arr_2 <= (OTHERS=>(OTHERS=>c_dp_sosi_rst)); + output_sel_arr <= (OTHERS=>'0'); + ELSIF rising_edge(clk) THEN + demux_src_out_2arr_2 <= nxt_demux_src_out_2arr_2; + output_sel_arr <= nxt_output_sel_arr; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- Wire the 2D demux output array to 1D array to match entity I/O type + ----------------------------------------------------------------------------- + gen_demux_inputs_0: FOR i IN 0 TO c_nof_demuxes-1 GENERATE + demux_src_out_arr(2*i) <= demux_src_out_2arr_2(i)(0); + demux_src_out_arr(2*i+1) <= demux_src_out_2arr_2(i)(1); + END GENERATE; + + ----------------------------------------------------------------------------- + -- g_nof_unfolds>1, so add an unfolder stage. + ----------------------------------------------------------------------------- + gen_dp_unfolder: IF g_nof_unfolds>1 GENERATE + u_dp_unfolder : dp_unfolder + GENERIC MAP ( + g_nof_inputs => c_nof_demuxes*2, -- Next stage has all our demux outputs as inputs + g_nof_unfolds => g_nof_unfolds-1, + g_output_align => g_output_align + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_in_arr => demux_src_out_arr, + src_out_arr => src_out_arr + ); + END GENERATE; + + ----------------------------------------------------------------------------- + -- g_nof_unfolds=1, so this is the last stage. Wire up the outputs. + ----------------------------------------------------------------------------- + gen_src_out_arr: IF g_nof_unfolds=1 GENERATE + + gen_output_align : IF g_output_align=TRUE GENERATE + dp_pipeline_snk_in_arr <= demux_src_out_arr; + + ----------------------------------------------------------------------------- + -- Pipeline to re-align the unfolder output + ----------------------------------------------------------------------------- + gen_dp_pipeline : FOR i IN 0 TO c_nof_outputs-1 GENERATE + u_dp_pipeline : ENTITY dp_lib.dp_pipeline + GENERIC MAP ( + g_pipeline => 0 + (pow2(g_nof_unfolds) - i REM pow2(g_nof_unfolds)-1) + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_in => dp_pipeline_snk_in_arr(i), + src_out => dp_block_gen_snk_in_arr(i) + ); + END GENERATE; + END GENERATE; + + no_output_align : IF g_output_align=FALSE GENERATE + dp_block_gen_snk_in_arr <= demux_src_out_arr; + END GENERATE; + + ----------------------------------------------------------------------------- + -- Add SOP and EOP to the outputs + ----------------------------------------------------------------------------- + gen_ctrl : IF g_output_block_size>0 GENERATE + gen_dp_block_gen : FOR i IN 0 TO c_nof_outputs-1 GENERATE + u_dp_block_gen : ENTITY work.dp_block_gen + GENERIC MAP ( + g_use_src_in => FALSE, + g_nof_data => g_output_block_size, + g_preserve_sync => TRUE, + g_preserve_bsn => TRUE + ) + PORT MAP( + rst => rst, + clk => clk, + + snk_in => dp_block_gen_snk_in_arr(i), + src_out => dp_block_gen_src_out_arr(i) + ); + END GENERATE; + END GENERATE; + + no_ctrl : IF g_output_block_size=0 GENERATE + dp_block_gen_src_out_arr <= dp_block_gen_snk_in_arr; + END GENERATE; + + ----------------------------------------------------------------------------- + -- Re-add input sync + BSN to all output streams + ----------------------------------------------------------------------------- + gen_sync_bsn : IF g_fwd_sync_bsn = TRUE GENERATE + gen_dp_fifo_info: FOR i IN 0 TO c_nof_outputs-1 GENERATE + u_dp_fifo_info : ENTITY work.dp_fifo_info + GENERIC MAP ( + g_use_sync => TRUE, + g_use_bsn => TRUE + ) + PORT MAP ( + rst => rst, + clk => clk, + + data_snk_in => dp_block_gen_src_out_arr(i), -- delayed snk_in data + info_snk_in => snk_in_arr(0), -- original snk_in info + + src_in => c_dp_siso_rdy, + src_out => src_out_arr(i) + ); + END GENERATE; + END GENERATE; + + no_sync_bsn : IF g_fwd_sync_bsn = FALSE GENERATE + src_out_arr <= dp_block_gen_src_out_arr; + END GENERATE; + + END GENERATE; + + END GENERATE; + + ----------------------------------------------------------------------------- + -- Wire output to input if g_nof_unfolds=0 + ----------------------------------------------------------------------------- + gen_wire_out_to_in: IF g_nof_unfolds=0 GENERATE + dp_block_gen_snk_in_arr <= snk_in_arr; + END GENERATE; + +END str; + diff --git a/libraries/base/dp/tb/vhdl/tb_dp_folder.vhd b/libraries/base/dp/tb/vhdl/tb_dp_folder.vhd new file mode 100644 index 0000000000000000000000000000000000000000..7963cb48e04f42d9116770836df5b880c044c1ab --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_dp_folder.vhd @@ -0,0 +1,129 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.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 +-- . Daniel van der Schuur +-- Purpose +-- . Verify stream through dp_unfolder->dp_folder stages + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE work.dp_stream_pkg.ALL; +USE work.tb_dp_pkg.ALL; + +ENTITY tb_dp_folder IS +END tb_dp_folder; + + +ARCHITECTURE tb OF tb_dp_folder IS + + CONSTANT c_nof_inputs : NATURAL := 1; + CONSTANT c_nof_unfolds : NATURAL := 1; -- 5 folds = 1->2->4->8->16->32 unfolded streams + CONSTANT c_nof_unfolded_streams : NATURAL := c_nof_inputs*pow2(c_nof_unfolds); + + CONSTANT c_data_w : NATURAL := 32; + CONSTANT c_nof_packets : NATURAL := 10; + CONSTANT c_packet_len : NATURAL := 20; + CONSTANT c_packet_gap : NATURAL := 0; + + CONSTANT c_clk_period : TIME := 5 ns; -- 200 MHz + CONSTANT c_mm_clk_period : TIME := 20 ns; -- 50 MHz + + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC; + + SIGNAL proc_dp_gen_block_data_in_en : STD_LOGIC := '1'; + SIGNAL proc_dp_gen_block_data_src_in : t_dp_siso := c_dp_siso_rdy; + + SIGNAL proc_dp_gen_block_data_src_out : t_dp_sosi; + + SIGNAL dp_unfolder_snk_in_arr : t_dp_sosi_arr(c_nof_inputs-1 DOWNTO 0); + SIGNAL dp_folder_snk_in_arr : t_dp_sosi_arr(c_nof_unfolded_streams-1 DOWNTO 0); + SIGNAL dp_folder_src_out_arr : t_dp_sosi_arr(c_nof_inputs-1 DOWNTO 0); + +BEGIN + + ----------------------------------------------------------------------------- + -- Clock,reset generation + ----------------------------------------------------------------------------- + clk <= NOT clk AFTER c_clk_period/2; + rst <= '1', '0' AFTER c_clk_period*7; + + ----------------------------------------------------------------------------- + -- Generate packet stream + ----------------------------------------------------------------------------- + p_generate_packets : PROCESS + BEGIN + proc_dp_gen_block_data_src_out <= c_dp_sosi_rst; + + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 5); + + FOR i IN 0 TO c_nof_packets-1 LOOP + -- Generate single packet + proc_dp_gen_block_data(c_data_w, 0, c_packet_len, 0, 0, '0', "0", clk, proc_dp_gen_block_data_in_en, proc_dp_gen_block_data_src_in, proc_dp_gen_block_data_src_out); + + -- Insert optional gap between the packets + proc_common_wait_some_cycles(clk, c_packet_gap); + END LOOP; + WAIT; + END PROCESS; + + ----------------------------------------------------------------------------- + -- Unfold the single stream into multiple streams, the fold it back into one again + ----------------------------------------------------------------------------- + dp_unfolder_snk_in_arr(0) <= proc_dp_gen_block_data_src_out; + + u_dp_unfolder : ENTITY work.dp_unfolder + GENERIC MAP ( + g_nof_inputs => c_nof_inputs, + g_nof_unfolds => c_nof_unfolds, + g_output_align => FALSE -- We're going to fold these outputs again, so don't align them! + ) + PORT MAP ( + clk => clk, + rst => rst, + + snk_in_arr => dp_unfolder_snk_in_arr, + src_out_arr => dp_folder_snk_in_arr + ); + + u_dp_folder : ENTITY work.dp_folder + GENERIC MAP ( + g_nof_inputs => c_nof_unfolded_streams, + g_nof_folds => -1 -- Fold until 1 output remains, +-- g_output_block_size => c_packet_len + ) + PORT MAP ( + clk => clk, + rst => rst, + + snk_in_arr => dp_folder_snk_in_arr, + src_out_arr => dp_folder_src_out_arr + ); + +END tb; + diff --git a/libraries/base/dp/tb/vhdl/tb_dp_switch.vhd b/libraries/base/dp/tb/vhdl/tb_dp_switch.vhd new file mode 100644 index 0000000000000000000000000000000000000000..0c772e3d0fd54f40511a0d349c866feb77595d0c --- /dev/null +++ b/libraries/base/dp/tb/vhdl/tb_dp_switch.vhd @@ -0,0 +1,143 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.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 +-- . Daniel van der Schuur +-- Purpose +-- . Generate input streams for dp_switch, verify by eye in wave window. + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; +USE work.dp_stream_pkg.ALL; +USE work.tb_dp_pkg.ALL; + +ENTITY tb_dp_switch IS +END tb_dp_switch; + + +ARCHITECTURE tb OF tb_dp_switch IS + + CONSTANT c_nof_inputs : NATURAL := 3; + CONSTANT c_data_w : NATURAL := 32; + CONSTANT c_nof_packets : NATURAL := 10; + CONSTANT c_packet_len : NATURAL := 20; + CONSTANT c_packet_gap : NATURAL := 1; --NOTE: dp_mux requires a minimum gap of 1 to select a new input! + + CONSTANT c_dp_clk_period : TIME := 5 ns; -- 200 MHz + CONSTANT c_mm_clk_period : TIME := 20 ns; -- 50 MHz + + SIGNAL dp_clk : STD_LOGIC := '1'; + SIGNAL dp_rst : STD_LOGIC; + + SIGNAL mm_clk : STD_LOGIC := '1'; + SIGNAL mm_rst : STD_LOGIC; + + SIGNAL proc_dp_gen_block_data_in_en : STD_LOGIC := '1'; + SIGNAL proc_dp_gen_block_data_src_in : t_dp_siso := c_dp_siso_rdy; + + SIGNAL proc_dp_gen_block_data_src_out_arr : t_dp_sosi_arr(c_nof_inputs-1 DOWNTO 0); + + SIGNAL dp_switch_snk_in_arr : t_dp_sosi_arr(c_nof_inputs-1 DOWNTO 0); + SIGNAL dp_switch_src_out : t_dp_sosi; + + SIGNAL reg_dp_switch_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL reg_dp_switch_miso : t_mem_miso; + +BEGIN + + ----------------------------------------------------------------------------- + -- Clock,reset generation + ----------------------------------------------------------------------------- + dp_clk <= NOT dp_clk AFTER c_dp_clk_period/2; + dp_rst <= '1', '0' AFTER c_dp_clk_period*7; + + mm_clk <= NOT mm_clk AFTER c_mm_clk_period/2; + mm_rst <= '1', '0' AFTER c_mm_clk_period*7; + + ----------------------------------------------------------------------------- + -- Generate packet streams + ----------------------------------------------------------------------------- + gen_generate_packets : FOR i IN 0 TO c_nof_inputs-1 GENERATE + p_generate_packets : PROCESS + BEGIN + proc_dp_gen_block_data_src_out_arr(i) <= c_dp_sosi_rst; + + proc_common_wait_until_low(dp_clk, dp_rst); + proc_common_wait_some_cycles(dp_clk, 5); + + FOR j IN 0 TO c_nof_packets-1 LOOP + -- Generate single packet + proc_dp_gen_block_data(c_data_w, i*1000, c_packet_len, 0, 0, '0', "0", dp_clk, proc_dp_gen_block_data_in_en, proc_dp_gen_block_data_src_in, proc_dp_gen_block_data_src_out_arr(i)); + + -- Insert optional gap between the packets + proc_common_wait_some_cycles(dp_clk, c_packet_gap); + END LOOP; + WAIT; + END PROCESS; + END GENERATE; + + ----------------------------------------------------------------------------- + -- MM write different input selections + ----------------------------------------------------------------------------- + p_generate_packets : PROCESS + BEGIN + reg_dp_switch_mosi <= c_mem_mosi_rst; + proc_common_wait_until_low(mm_clk, mm_rst); + proc_common_wait_some_cycles(mm_clk, 15); + proc_mem_mm_bus_wr(0, 1, mm_clk, reg_dp_switch_mosi); + WAIT; + END PROCESS; + + ----------------------------------------------------------------------------- + -- dp_switch forwards either stream 0 or stream 1 + -- . Both inputs carry the same generated stream + ----------------------------------------------------------------------------- + gen_dp_switch_snk_in_arr : FOR i IN 0 TO c_nof_inputs-1 GENERATE + dp_switch_snk_in_arr(i) <= proc_dp_gen_block_data_src_out_arr(i); + END GENERATE; + + u_dp_switch : ENTITY work.dp_switch + GENERIC MAP ( + g_nof_inputs => c_nof_inputs, + g_default_enabled => 2 + ) + PORT MAP ( + dp_clk => dp_clk, + dp_rst => dp_rst, + + mm_clk => mm_clk, + mm_rst => mm_rst, + + snk_in_arr => dp_switch_snk_in_arr, + src_out => dp_switch_src_out, + + reg_mosi => reg_dp_switch_mosi, + reg_miso => reg_dp_switch_miso + ); + +END tb; +