diff --git a/cores/base/common/common_components/common_switch.vhd b/cores/base/common/common_components/common_switch.vhd new file mode 100644 index 0000000000000000000000000000000000000000..553c2353278f4ad9f98e8f53b2b3f45b4ce27388 --- /dev/null +++ b/cores/base/common/common_components/common_switch.vhd @@ -0,0 +1,103 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 +-- 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/>. +-- +------------------------------------------------------------------------------- + +-- Purpose : Switch output high or low +-- Description: +-- . The output goes high when switch_high='1' and low when switch_low='1'. +-- . If g_or_high is true then the output follows the switch_high immediately, +-- else it goes high in the next clk cycle. +-- . If g_and_low is true then the output follows the switch_low immediately, +-- else it goes low in the next clk cycle. +-- The g_priority_lo defines which input has priority when switch_high and +-- switch_low are active simultaneously. + +LIBRARY ieee; +USE ieee.std_logic_1164.ALL; + +ENTITY common_switch IS + GENERIC ( + g_rst_level : STD_LOGIC := '0'; -- Defines the output level at reset. + g_priority_lo : BOOLEAN := TRUE; -- When TRUE then input switch_low has priority, else switch_high. Don't care when switch_high and switch_low are pulses that do not occur simultaneously. + g_or_high : BOOLEAN := FALSE; -- When TRUE and priority hi then the registered switch_level is OR-ed with the input switch_high to get out_level, else out_level is the registered switch_level + g_and_low : BOOLEAN := FALSE -- When TRUE and priority lo then the registered switch_level is AND-ed with the input switch_low to get out_level, else out_level is the registered switch_level + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + clken : IN STD_LOGIC := '1'; + switch_high : IN STD_LOGIC; -- A pulse on switch_high makes the out_level go high + switch_low : IN STD_LOGIC; -- A pulse on switch_low makes the out_level go low + out_level : OUT STD_LOGIC + ); +END; + +ARCHITECTURE rtl OF common_switch IS + + SIGNAL switch_level : STD_LOGIC := g_rst_level; + SIGNAL nxt_switch_level : STD_LOGIC; + +BEGIN + + gen_wire : IF g_or_high=FALSE AND g_and_low=FALSE GENERATE + out_level <= switch_level; + END GENERATE; + + gen_or : IF g_or_high=TRUE AND g_and_low=FALSE GENERATE + out_level <= switch_level OR switch_high; + END GENERATE; + + gen_and : IF g_or_high=FALSE AND g_and_low=TRUE GENERATE + out_level <= switch_level AND (NOT switch_low); + END GENERATE; + + gen_or_and : IF g_or_high=TRUE AND g_and_low=TRUE GENERATE + out_level <= (switch_level OR switch_high) AND (NOT switch_low); + END GENERATE; + + p_reg : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + switch_level <= g_rst_level; + ELSIF rising_edge(clk) THEN + IF clken='1' THEN + switch_level <= nxt_switch_level; + END IF; + END IF; + END PROCESS; + + p_switch_level : PROCESS(switch_level, switch_low, switch_high) + BEGIN + nxt_switch_level <= switch_level; + IF g_priority_lo=TRUE THEN + IF switch_low='1' THEN + nxt_switch_level <= '0'; + ELSIF switch_high='1' THEN + nxt_switch_level <= '1'; + END IF; + ELSE + IF switch_high='1' THEN + nxt_switch_level <= '1'; + ELSIF switch_low='1' THEN + nxt_switch_level <= '0'; + END IF; + END IF; + END PROCESS; +END rtl; diff --git a/cores/base/common/common_components/hdllib.cfg b/cores/base/common/common_components/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9b7bc1bb2357ab86146944d31ce18045f5161faa --- /dev/null +++ b/cores/base/common/common_components/hdllib.cfg @@ -0,0 +1,18 @@ +hdl_lib_name = common_components +hdl_library_clause_name = common_components_lib +hdl_lib_uses_synth = +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + common_switch.vhd + +test_bench_files = + +regression_test_vhdl = + +[modelsim_project_file] +modelsim_copy_files = + +[quartus_project_file] + diff --git a/cores/base/common/common_pkg/common_lfsr_sequences_pkg.vhd b/cores/base/common/common_pkg/common_lfsr_sequences_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..eb45149411f8e8148d523ef2b7a78f1159d64ce6 --- /dev/null +++ b/cores/base/common/common_pkg/common_lfsr_sequences_pkg.vhd @@ -0,0 +1,193 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 +-- 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/>. +-- +-------------------------------------------------------------------------------- + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE work.common_pkg.ALL; + +PACKAGE common_lfsr_sequences_pkg IS + + CONSTANT c_common_lfsr_max_nof_feedbacks : NATURAL := 6; + CONSTANT c_common_lfsr_first : NATURAL := 1; -- also support n = 1 and 2 in addition to n >= 3 + + TYPE t_FEEDBACKS IS ARRAY (c_common_lfsr_max_nof_feedbacks-1 DOWNTO 0) OF NATURAL; + TYPE t_SEQUENCES IS ARRAY (NATURAL RANGE <>) OF t_FEEDBACKS; + + -- XNOR feedbacks for n = 1: + -- (0,0,0,0,0, 0) yields repeat <1> + -- (0,0,0,0,0, 1) yields repeat <0, 1> + + -- XNOR feedbacks for n = 2: + -- (0,0,0,0, 0, 1) yields repeat <1, 2> + -- (0,0,0,0, 0, 2) yields repeat <0, 1, 3, 2> + -- (0,0,0,0, 2, 1) yields repeat <0, 1, 2> + + -- XNOR feedbacks from outputs for n = 3 .. 72 from Xilinx xapp052.pdf (that lists feedbacks for in total 168 sequences) + CONSTANT c_common_lfsr_sequences : t_SEQUENCES := ((0,0,0,0,0, 1), -- 1 : <0, 1> + (0,0,0,0, 0, 2), -- 2 : <0, 1, 3, 2> + (0,0,0,0, 3, 2), -- 3 + (0,0,0,0, 4, 3), -- 4 + (0,0,0,0, 5, 3), -- 5 + (0,0,0,0, 6, 5), -- 6 + (0,0,0,0, 7, 6), -- 7 + (0,0, 8, 6, 5, 4), -- 8 + (0,0,0,0, 9, 5), -- 9 + (0,0,0,0, 10, 7), -- 10 + (0,0,0,0, 11, 9), -- 11 + (0,0, 12, 6, 4, 1), -- 12 + (0,0, 13, 4, 3, 1), -- 13 + (0,0, 14, 5, 3, 1), -- 14 + (0,0,0,0, 15,14 ), -- 15 + (0,0, 16,15,13, 4), -- 16 + (0,0,0,0, 17,14 ), -- 17 + (0,0,0,0, 18,11 ), -- 18 + (0,0, 19, 6, 2, 1), -- 19 + (0,0,0,0, 20,17 ), -- 20 + (0,0,0,0, 21,19 ), -- 21 + (0,0,0,0, 22,21 ), -- 22 + (0,0,0,0, 23,18 ), -- 23 + (0,0, 24,23,22,17), -- 24 + (0,0,0,0, 25,22 ), -- 25 + (0,0, 26, 6, 2, 1), -- 26 + (0,0, 27, 5, 2, 1), -- 27 + (0,0,0,0, 28,25 ), -- 28 + (0,0,0,0, 29,27 ), -- 29 + (0,0, 30, 6, 4, 1), -- 30 + (0,0,0,0, 31,28 ), -- 31 + (0,0, 32,22, 2, 1), -- 32 + (0,0,0,0, 33,20 ), -- 33 + (0,0, 34,27, 2, 1), -- 34 + (0,0,0,0, 35,33 ), -- 35 + (0,0,0,0, 36,25 ), -- 36 + ( 37, 5, 4, 3, 2, 1), -- 37 + (0,0, 38, 6, 5, 1), -- 38 + (0,0,0,0, 39,35 ), -- 39 + (0,0, 40,38,21,19), -- 40 + (0,0,0,0, 41,38 ), -- 41 + (0,0, 42,41,20,19), -- 42 + (0,0, 43,42,38,37), -- 43 + (0,0, 44,43,18,17), -- 44 + (0,0, 45,44,42,41), -- 45 + (0,0, 46,45,26,25), -- 46 + (0,0,0,0, 47,42 ), -- 47 + (0,0, 48,47,21,20), -- 48 + (0,0,0,0, 49,40 ), -- 49 + (0,0, 50,49,24,23), -- 50 + (0,0, 51,50,36,35), -- 51 + (0,0,0,0, 52,49 ), -- 52 + (0,0, 53,52,38,37), -- 53 + (0,0, 54,53,18,17), -- 54 + (0,0,0,0, 55,31 ), -- 55 + (0,0, 56,55,35,34), -- 56 + (0,0,0,0, 57,50 ), -- 57 + (0,0,0,0, 58,39 ), -- 58 + (0,0, 59,58,38,37), -- 59 + (0,0,0,0, 60,59 ), -- 60 + (0,0, 61,60,46,45), -- 61 + (0,0, 62,61, 6, 5), -- 62 + (0,0,0,0, 63,62 ), -- 63 + (0,0, 64,63,61,60), -- 64 + (0,0,0,0, 65,47 ), -- 65 + (0,0, 66,65,57,56), -- 66 + (0,0, 67,66,58,57), -- 67 + (0,0,0,0, 68,59 ), -- 68 + (0,0, 69,67,42,40), -- 69 + (0,0, 70,69,55,54), -- 70 + (0,0,0,0, 71,65 ), -- 71 + (0,0, 72,66,25,19)); -- 72 + + + -- Procedure for calculating the next PSRG and COUNTER sequence value + PROCEDURE common_lfsr_nxt_seq(CONSTANT c_lfsr_nr : IN NATURAL; + CONSTANT g_incr : IN INTEGER; + in_en : IN STD_LOGIC; + in_req : IN STD_LOGIC; + in_dat : IN STD_LOGIC_VECTOR; + prsg : IN STD_LOGIC_VECTOR; + cntr : IN STD_LOGIC_VECTOR; + SIGNAL nxt_prsg : OUT STD_LOGIC_VECTOR; + SIGNAL nxt_cntr : OUT STD_LOGIC_VECTOR); + + -- Use lfsr part of common_lfsr_nxt_seq to make a random bit generator function + -- . width of lfsr selects the LFSR sequence + -- . initialized lfsr with (OTHERS=>'0') + -- . use lfsr(lfsr'HIGH) as random bit + FUNCTION func_common_random(lfsr : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + +END common_lfsr_sequences_pkg; + + +PACKAGE BODY common_lfsr_sequences_pkg IS + + PROCEDURE common_lfsr_nxt_seq(CONSTANT c_lfsr_nr : IN NATURAL; + CONSTANT g_incr : IN INTEGER; + in_en : IN STD_LOGIC; + in_req : IN STD_LOGIC; + in_dat : IN STD_LOGIC_VECTOR; + prsg : IN STD_LOGIC_VECTOR; + cntr : IN STD_LOGIC_VECTOR; + SIGNAL nxt_prsg : OUT STD_LOGIC_VECTOR; + SIGNAL nxt_cntr : OUT STD_LOGIC_VECTOR) IS + VARIABLE v_feedback : STD_LOGIC; + BEGIN + nxt_prsg <= prsg; + nxt_cntr <= cntr; + IF in_en='0' THEN -- init reference value + nxt_prsg <= in_dat; + nxt_cntr <= in_dat; + ELSIF in_req='1' THEN -- next reference value + -- PRSG shift + nxt_prsg <= prsg(prsg'HIGH-1 DOWNTO 0) & '0'; + -- PRSG feedback + v_feedback := '0'; + FOR I IN c_common_lfsr_max_nof_feedbacks-1 DOWNTO 0 LOOP + IF c_common_lfsr_sequences(c_lfsr_nr)(I) /= 0 THEN + v_feedback := v_feedback XOR prsg(c_common_lfsr_sequences(c_lfsr_nr)(I)-1); + END IF; + END LOOP; + nxt_prsg(0) <= NOT v_feedback; + + -- COUNTER + nxt_cntr <= INCR_UVEC(cntr, g_incr); + END IF; + END common_lfsr_nxt_seq; + + FUNCTION func_common_random(lfsr : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_lfsr_nr : NATURAL := lfsr'LENGTH - c_common_lfsr_first; + VARIABLE v_nxt_lfsr : STD_LOGIC_VECTOR(lfsr'RANGE); + VARIABLE v_feedback : STD_LOGIC; + BEGIN + -- shift + v_nxt_lfsr := lfsr(lfsr'HIGH-1 DOWNTO 0) & '0'; + -- feedback + v_feedback := '0'; + FOR I IN c_common_lfsr_max_nof_feedbacks-1 DOWNTO 0 LOOP + IF c_common_lfsr_sequences(c_lfsr_nr)(I) /= 0 THEN + v_feedback := v_feedback XOR lfsr(c_common_lfsr_sequences(c_lfsr_nr)(I)-1); + END IF; + END LOOP; + v_nxt_lfsr(0) := NOT v_feedback; + RETURN v_nxt_lfsr; + END func_common_random; + +END common_lfsr_sequences_pkg; diff --git a/cores/base/common/common_pkg/common_pkg.vhd b/cores/base/common/common_pkg/common_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..9782ee21f5885d5fa7ae412122d754e1661be518 --- /dev/null +++ b/cores/base/common/common_pkg/common_pkg.vhd @@ -0,0 +1,2294 @@ + +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 +-- 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; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE IEEE.MATH_REAL.ALL; + +PACKAGE common_pkg IS + + -- CONSTANT DECLARATIONS ---------------------------------------------------- + + -- some integers + CONSTANT c_0 : NATURAL := 0; + CONSTANT c_zero : NATURAL := 0; + CONSTANT c_1 : NATURAL := 1; + CONSTANT c_one : NATURAL := 1; + CONSTANT c_2 : NATURAL := 2; + CONSTANT c_4 : NATURAL := 4; + CONSTANT c_quad : NATURAL := 4; + CONSTANT c_8 : NATURAL := 8; + CONSTANT c_16 : NATURAL := 16; + CONSTANT c_32 : NATURAL := 32; + CONSTANT c_64 : NATURAL := 64; + CONSTANT c_128 : NATURAL := 128; + CONSTANT c_256 : NATURAL := 256; + + -- widths and sizes + CONSTANT c_halfword_sz : NATURAL := 2; + CONSTANT c_word_sz : NATURAL := 4; + CONSTANT c_longword_sz : NATURAL := 8; + CONSTANT c_nibble_w : NATURAL := 4; + CONSTANT c_byte_w : NATURAL := 8; + CONSTANT c_octet_w : NATURAL := 8; + CONSTANT c_halfword_w : NATURAL := c_byte_w*c_halfword_sz; + CONSTANT c_word_w : NATURAL := c_byte_w*c_word_sz; + CONSTANT c_integer_w : NATURAL := 32; -- unfortunately VHDL integer type is limited to 32 bit values + CONSTANT c_natural_w : NATURAL := c_integer_w-1; -- unfortunately VHDL natural type is limited to 31 bit values (0 and the positive subset of the VHDL integer type0 + CONSTANT c_longword_w : NATURAL := c_byte_w*c_longword_sz; + + -- logic + CONSTANT c_sl0 : STD_LOGIC := '0'; + CONSTANT c_sl1 : STD_LOGIC := '1'; + CONSTANT c_unsigned_0 : UNSIGNED(0 DOWNTO 0) := TO_UNSIGNED(0,1); + CONSTANT c_unsigned_1 : UNSIGNED(0 DOWNTO 0) := TO_UNSIGNED(1,1); + CONSTANT c_signed_0 : SIGNED(1 DOWNTO 0) := TO_SIGNED(0,2); + CONSTANT c_signed_1 : SIGNED(1 DOWNTO 0) := TO_SIGNED(1,2); + CONSTANT c_slv0 : STD_LOGIC_VECTOR(255 DOWNTO 0) := (OTHERS=>'0'); + CONSTANT c_slv1 : STD_LOGIC_VECTOR(255 DOWNTO 0) := (OTHERS=>'1'); + CONSTANT c_word_01 : STD_LOGIC_VECTOR(31 DOWNTO 0) := "01010101010101010101010101010101"; + CONSTANT c_word_10 : STD_LOGIC_VECTOR(31 DOWNTO 0) := "10101010101010101010101010101010"; + CONSTANT c_slv01 : STD_LOGIC_VECTOR(255 DOWNTO 0) := c_word_01 & c_word_01 & c_word_01 & c_word_01 & c_word_01 & c_word_01 & c_word_01 & c_word_01; + CONSTANT c_slv10 : STD_LOGIC_VECTOR(255 DOWNTO 0) := c_word_10 & c_word_10 & c_word_10 & c_word_10 & c_word_10 & c_word_10 & c_word_10 & c_word_10; + + -- math + CONSTANT c_nof_complex : NATURAL := 2; -- Real and imaginary part of complex number + CONSTANT c_sign_w : NATURAL := 1; -- Sign bit, can be used to skip one of the double sign bits of a product + CONSTANT c_sum_of_prod_w : NATURAL := 1; -- Bit growth for sum of 2 products, can be used in case complex multiply has normalized real and imag inputs instead of normalized amplitude inputs + + -- FF, block RAM, FIFO + CONSTANT c_meta_delay_len : NATURAL := 3; -- default nof flipflops (FF) in meta stability recovery delay line (e.g. for clock domain crossing) + CONSTANT c_meta_fifo_depth : NATURAL := 16; -- default use 16 word deep FIFO to cross clock domain, typically > 2*c_meta_delay_len or >~ 8 is enough + + CONSTANT c_bram_m9k_nof_bits : NATURAL := 1024*9; -- size of 1 Altera M9K block RAM in bits + CONSTANT c_bram_m9k_max_w : NATURAL := 36; -- maximum width of 1 Altera M9K block RAM, so the size is then 256 words of 36 bits + CONSTANT c_bram_m9k_fifo_depth : NATURAL := c_bram_m9k_nof_bits/c_bram_m9k_max_w; -- using a smaller FIFO depth than this leaves part of the RAM unused + + CONSTANT c_fifo_afull_margin : NATURAL := 4; -- default or minimal FIFO almost full margin + + -- DSP + CONSTANT c_dsp_mult_w : NATURAL := 18; -- Width of the embedded multipliers in Stratix IV + + -- TYPE DECLARATIONS -------------------------------------------------------- + TYPE t_boolean_arr IS ARRAY (INTEGER RANGE <>) OF BOOLEAN; -- INTEGER left index starts default at -2**31 + TYPE t_integer_arr IS ARRAY (INTEGER RANGE <>) OF INTEGER; -- INTEGER left index starts default at -2**31 + TYPE t_natural_arr IS ARRAY (INTEGER RANGE <>) OF NATURAL; -- INTEGER left index starts default at -2**31 + TYPE t_nat_boolean_arr IS ARRAY (NATURAL RANGE <>) OF BOOLEAN; -- NATURAL left index starts default at 0 + TYPE t_nat_integer_arr IS ARRAY (NATURAL RANGE <>) OF INTEGER; -- NATURAL left index starts default at 0 + TYPE t_nat_natural_arr IS ARRAY (NATURAL RANGE <>) OF NATURAL; -- NATURAL left index starts default at 0 + TYPE t_sl_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC; + TYPE t_slv_1_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(0 DOWNTO 0); + TYPE t_slv_2_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(1 DOWNTO 0); + TYPE t_slv_4_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(3 DOWNTO 0); + TYPE t_slv_8_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(7 DOWNTO 0); + TYPE t_slv_12_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(11 DOWNTO 0); + TYPE t_slv_16_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(15 DOWNTO 0); + TYPE t_slv_18_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(17 DOWNTO 0); + TYPE t_slv_24_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(23 DOWNTO 0); + TYPE t_slv_32_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(31 DOWNTO 0); + TYPE t_slv_44_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(43 DOWNTO 0); + TYPE t_slv_48_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(47 DOWNTO 0); + TYPE t_slv_64_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(63 DOWNTO 0); + TYPE t_slv_128_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(127 DOWNTO 0); + TYPE t_slv_256_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(255 DOWNTO 0); + TYPE t_slv_512_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(511 DOWNTO 0); + TYPE t_slv_1024_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(1023 DOWNTO 0); + + CONSTANT c_boolean_arr : t_boolean_arr := (TRUE, FALSE); -- array all possible values that can be iterated over + CONSTANT c_nat_boolean_arr : t_nat_boolean_arr := (TRUE, FALSE); -- array all possible values that can be iterated over + + TYPE t_integer_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF INTEGER; + TYPE t_boolean_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF BOOLEAN; + TYPE t_sl_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF STD_LOGIC; + TYPE t_slv_8_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF STD_LOGIC_VECTOR(7 DOWNTO 0); + TYPE t_slv_16_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF STD_LOGIC_VECTOR(15 DOWNTO 0); + TYPE t_slv_32_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF STD_LOGIC_VECTOR(31 DOWNTO 0); + TYPE t_slv_64_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF STD_LOGIC_VECTOR(63 DOWNTO 0); + + TYPE t_natural_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_natural_arr(1 DOWNTO 0); + + -- STRUCTURE DECLARATIONS --------------------------------------------------- + + -- Clock and Reset + -- + -- . rst = Reset. Can be used asynchronously to take effect immediately + -- when used before the clk'EVENT section. May also be used as + -- synchronous reset using it as first condition in the clk'EVENT + -- section. As synchronous reset it requires clock activity to take + -- effect. A synchronous rst may or may not depend on clken, + -- however typically rst should take priority over clken. + -- . clk = Clock. Used in clk'EVENT line via rising_edge(clk) or sometimes + -- as falling_edge(clk). + -- . clken = Clock Enable. Used for the whole clk'EVENT section. + TYPE t_sys_rce IS RECORD + rst : STD_LOGIC; + clk : STD_LOGIC; + clken : STD_LOGIC; -- := '1'; + END RECORD; + + TYPE t_sys_ce IS RECORD + clk : STD_LOGIC; + clken : STD_LOGIC; -- := '1'; + END RECORD; + + + -- FUNCTION DECLARATIONS ---------------------------------------------------- + + -- All functions assume [high downto low] input ranges + + FUNCTION pow2(n : NATURAL) RETURN NATURAL; -- = 2**n + FUNCTION ceil_pow2(n : INTEGER) RETURN NATURAL; -- = 2**n, returns 1 for n<0 + + FUNCTION true_log2(n : NATURAL) RETURN NATURAL; -- true_log2(n) = log2(n) + FUNCTION ceil_log2(n : NATURAL) RETURN NATURAL; -- ceil_log2(n) = log2(n), but force ceil_log2(1) = 1 + + FUNCTION floor_log10(n : NATURAL) RETURN NATURAL; + + FUNCTION is_pow2(n : NATURAL) RETURN BOOLEAN; -- return TRUE when n is a power of 2, so 0, 1, 2, 4, 8, 16, ... + FUNCTION true_log_pow2(n : NATURAL) RETURN NATURAL; -- 2**true_log2(n), return power of 2 that is >= n + + FUNCTION ratio( n, d : NATURAL) RETURN NATURAL; -- return n/d when n MOD d = 0 else return 0, so ratio * d = n only when integer ratio > 0 + FUNCTION ratio2(n, m : NATURAL) RETURN NATURAL; -- return integer ratio of n/m or m/n, whichever is the largest + + FUNCTION ceil_div( n, d : NATURAL) RETURN NATURAL; -- ceil_div = n/d + (n MOD d)/=0 + FUNCTION ceil_value( n, d : NATURAL) RETURN NATURAL; -- ceil_value = ceil_div(n, d) * d + FUNCTION floor_value(n, d : NATURAL) RETURN NATURAL; -- floor_value = (n/d) * d + FUNCTION ceil_div( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; + FUNCTION ceil_value( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; + FUNCTION floor_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED; + + FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR; -- standard logic to 1 element standard logic vector + FUNCTION sl( n: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- 1 element standard logic vector to standard logic + + FUNCTION to_natural_arr(n : t_integer_arr; to_zero : BOOLEAN) RETURN t_natural_arr; -- if to_zero=TRUE then negative numbers are forced to zero, otherwise they will give a compile range error + FUNCTION to_natural_arr(n : t_nat_natural_arr) RETURN t_natural_arr; + FUNCTION to_integer_arr(n : t_natural_arr) RETURN t_integer_arr; + FUNCTION to_integer_arr(n : t_nat_natural_arr) RETURN t_integer_arr; + FUNCTION to_slv_32_arr( n : t_integer_arr) RETURN t_slv_32_arr; + FUNCTION to_slv_32_arr( n : t_natural_arr) RETURN t_slv_32_arr; + + FUNCTION vector_tree(slv : STD_LOGIC_VECTOR; operation : STRING) RETURN STD_LOGIC; -- Core operation tree function for vector "AND", "OR", "XOR" + FUNCTION vector_and(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- '1' when all slv bits are '1' else '0' + FUNCTION vector_or( slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- '0' when all slv bits are '0' else '1' + FUNCTION vector_xor(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- '1' when the slv has an odd number of '1' bits else '0' + FUNCTION vector_one_hot(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- Returns slv when it contains one hot bit, else returns 0. + + FUNCTION andv(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- alias of vector_and + FUNCTION orv( slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- alias of vector_or + FUNCTION xorv(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- alias of vector_xor + + FUNCTION matrix_and(mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC; -- '1' when all matrix bits are '1' else '0' + FUNCTION matrix_or( mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC; -- '0' when all matrix bits are '0' else '1' + + FUNCTION smallest(n, m : INTEGER) RETURN INTEGER; + FUNCTION smallest(n, m, l : INTEGER) RETURN INTEGER; + FUNCTION smallest(n : t_natural_arr) RETURN NATURAL; + + FUNCTION largest(n, m : INTEGER) RETURN INTEGER; + FUNCTION largest(n : t_natural_arr) RETURN NATURAL; + + FUNCTION func_sum( n : t_natural_arr) RETURN NATURAL; -- sum of all elements in array + FUNCTION func_sum( n : t_nat_natural_arr) RETURN NATURAL; + FUNCTION func_product(n : t_natural_arr) RETURN NATURAL; -- product of all elements in array + FUNCTION func_product(n : t_nat_natural_arr) RETURN NATURAL; + + FUNCTION "+" (L, R: t_natural_arr) RETURN t_natural_arr; -- element wise sum + FUNCTION "+" (L : t_natural_arr; R : INTEGER) RETURN t_natural_arr; -- element wise sum + FUNCTION "+" (L : INTEGER; R : t_natural_arr) RETURN t_natural_arr; -- element wise sum + + FUNCTION "-" (L, R: t_natural_arr) RETURN t_natural_arr; -- element wise subtract + FUNCTION "-" (L, R: t_natural_arr) RETURN t_integer_arr; -- element wise subtract, support negative result + FUNCTION "-" (L : t_natural_arr; R : INTEGER) RETURN t_natural_arr; -- element wise subtract + FUNCTION "-" (L : INTEGER; R : t_natural_arr) RETURN t_natural_arr; -- element wise subtract + + FUNCTION "*" (L, R: t_natural_arr) RETURN t_natural_arr; -- element wise product + FUNCTION "*" (L : t_natural_arr; R : NATURAL) RETURN t_natural_arr; -- element wise product + FUNCTION "*" (L : NATURAL; R : t_natural_arr) RETURN t_natural_arr; -- element wise product + + FUNCTION "/" (L, R: t_natural_arr) RETURN t_natural_arr; -- element wise division + FUNCTION "/" (L : t_natural_arr; R : POSITIVE) RETURN t_natural_arr; -- element wise division + FUNCTION "/" (L : NATURAL; R : t_natural_arr) RETURN t_natural_arr; -- element wise division + + FUNCTION is_true(a : STD_LOGIC) RETURN BOOLEAN; + FUNCTION is_true(a : STD_LOGIC) RETURN NATURAL; + FUNCTION is_true(a : BOOLEAN) RETURN STD_LOGIC; + FUNCTION is_true(a : BOOLEAN) RETURN NATURAL; + FUNCTION is_true(a : INTEGER) RETURN BOOLEAN; -- also covers NATURAL because it is a subtype of INTEGER + FUNCTION is_true(a : INTEGER) RETURN STD_LOGIC; -- also covers NATURAL because it is a subtype of INTEGER + + FUNCTION sel_a_b(sel, a, b : BOOLEAN) RETURN BOOLEAN; + FUNCTION sel_a_b(sel, a, b : INTEGER) RETURN INTEGER; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : INTEGER) RETURN INTEGER; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : REAL) RETURN REAL; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : STD_LOGIC) RETURN STD_LOGIC; + FUNCTION sel_a_b(sel : INTEGER; a, b : STD_LOGIC) RETURN STD_LOGIC; + FUNCTION sel_a_b(sel : INTEGER; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : SIGNED) RETURN SIGNED; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : UNSIGNED) RETURN UNSIGNED; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_integer_arr) RETURN t_integer_arr; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_natural_arr) RETURN t_natural_arr; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_nat_integer_arr) RETURN t_nat_integer_arr; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_nat_natural_arr) RETURN t_nat_natural_arr; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : STRING) RETURN STRING; + FUNCTION sel_a_b(sel : INTEGER; a, b : STRING) RETURN STRING; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : TIME) RETURN TIME; + FUNCTION sel_a_b(sel : BOOLEAN; a, b : SEVERITY_LEVEL) RETURN SEVERITY_LEVEL; + + -- sel_n() index sel = 0, 1, 2, ... will return a, b, c, ... + FUNCTION sel_n(sel : NATURAL; a, b, c : BOOLEAN) RETURN BOOLEAN; -- 3 + FUNCTION sel_n(sel : NATURAL; a, b, c, d : BOOLEAN) RETURN BOOLEAN; -- 4 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e : BOOLEAN) RETURN BOOLEAN; -- 5 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f : BOOLEAN) RETURN BOOLEAN; -- 6 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g : BOOLEAN) RETURN BOOLEAN; -- 7 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h : BOOLEAN) RETURN BOOLEAN; -- 8 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i : BOOLEAN) RETURN BOOLEAN; -- 9 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : BOOLEAN) RETURN BOOLEAN; -- 10 + + FUNCTION sel_n(sel : NATURAL; a, b, c : INTEGER) RETURN INTEGER; -- 3 + FUNCTION sel_n(sel : NATURAL; a, b, c, d : INTEGER) RETURN INTEGER; -- 4 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e : INTEGER) RETURN INTEGER; -- 5 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f : INTEGER) RETURN INTEGER; -- 6 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g : INTEGER) RETURN INTEGER; -- 7 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h : INTEGER) RETURN INTEGER; -- 8 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i : INTEGER) RETURN INTEGER; -- 9 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : INTEGER) RETURN INTEGER; -- 10 + + FUNCTION sel_n(sel : NATURAL; a, b : STRING) RETURN STRING; -- 2 + FUNCTION sel_n(sel : NATURAL; a, b, c : STRING) RETURN STRING; -- 3 + FUNCTION sel_n(sel : NATURAL; a, b, c, d : STRING) RETURN STRING; -- 4 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e : STRING) RETURN STRING; -- 5 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f : STRING) RETURN STRING; -- 6 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g : STRING) RETURN STRING; -- 7 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h : STRING) RETURN STRING; -- 8 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i : STRING) RETURN STRING; -- 9 + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : STRING) RETURN STRING; -- 10 + + FUNCTION array_init(init : STD_LOGIC; nof : NATURAL) RETURN STD_LOGIC_VECTOR; -- useful to init a unconstrained array of size 1 + FUNCTION array_init(init, nof : NATURAL) RETURN t_natural_arr; -- useful to init a unconstrained array of size 1 + FUNCTION array_init(init, nof : NATURAL) RETURN t_nat_natural_arr; -- useful to init a unconstrained array of size 1 + FUNCTION array_init(init, nof, incr : NATURAL) RETURN t_natural_arr; -- useful to init an array with incrementing numbers + FUNCTION array_init(init, nof, incr : NATURAL) RETURN t_nat_natural_arr; + FUNCTION array_init(init, nof, incr : INTEGER) RETURN t_slv_16_arr; + FUNCTION array_init(init, nof, incr : INTEGER) RETURN t_slv_32_arr; + FUNCTION array_init(init, nof, width : NATURAL) RETURN STD_LOGIC_VECTOR; -- useful to init an unconstrained std_logic_vector with repetitive content + FUNCTION array_init(init, nof, width, incr : NATURAL) RETURN STD_LOGIC_VECTOR; -- useful to init an unconstrained std_logic_vector with incrementing content + FUNCTION array_sinit(init : INTEGER; nof, width : NATURAL) RETURN STD_LOGIC_VECTOR; -- useful to init an unconstrained std_logic_vector with repetitive content + + FUNCTION init_slv_64_matrix(nof_a, nof_b, k : INTEGER) RETURN t_slv_64_matrix; -- initialize all elements in t_slv_64_matrix to value k + + -- Concatenate two or more STD_LOGIC_VECTORs into a single STD_LOGIC_VECTOR or extract one of them from a concatenated STD_LOGIC_VECTOR + FUNCTION func_slv_concat( use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a, b, c, d, e, f, g : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a, b, c, d, e, f : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( use_a, use_b, use_c, use_d, use_e : BOOLEAN; a, b, c, d, e : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( use_a, use_b, use_c, use_d : BOOLEAN; a, b, c, d : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( use_a, use_b, use_c : BOOLEAN; a, b, c : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( use_a, use_b : BOOLEAN; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL) RETURN NATURAL; + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w : NATURAL) RETURN NATURAL; + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e : BOOLEAN; a_w, b_w, c_w, d_w, e_w : NATURAL) RETURN NATURAL; + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d : BOOLEAN; a_w, b_w, c_w, d_w : NATURAL) RETURN NATURAL; + FUNCTION func_slv_concat_w(use_a, use_b, use_c : BOOLEAN; a_w, b_w, c_w : NATURAL) RETURN NATURAL; + FUNCTION func_slv_concat_w(use_a, use_b : BOOLEAN; a_w, b_w : NATURAL) RETURN NATURAL; + FUNCTION func_slv_extract( use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( use_a, use_b, use_c, use_d, use_e : BOOLEAN; a_w, b_w, c_w, d_w, e_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( use_a, use_b, use_c, use_d : BOOLEAN; a_w, b_w, c_w, d_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( use_a, use_b, use_c : BOOLEAN; a_w, b_w, c_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( use_a, use_b : BOOLEAN; a_w, b_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + + FUNCTION TO_UINT(vec : STD_LOGIC_VECTOR) RETURN NATURAL; -- beware: NATURAL'HIGH = 2**31-1, not 2*32-1, use TO_SINT to avoid warning + FUNCTION TO_SINT(vec : STD_LOGIC_VECTOR) RETURN INTEGER; + + FUNCTION TO_UVEC(dec, w : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION TO_SVEC(dec, w : INTEGER) RETURN STD_LOGIC_VECTOR; + + FUNCTION TO_SVEC_32(dec : INTEGER) RETURN STD_LOGIC_VECTOR; -- = TO_SVEC() with w=32 for t_slv_32_arr slv elements + +-- The RESIZE for SIGNED in IEEE.NUMERIC_STD extends the sign bit or it keeps the sign bit and LS part. This + -- behaviour of preserving the sign bit is less suitable for DSP and not necessary in general. A more + -- appropriate approach is to ignore the MSbit sign and just keep the LS part. For too large values this + -- means that the result gets wrapped, but that is fine for default behaviour, because that is also what + -- happens for RESIZE of UNSIGNED. Therefor this is what the RESIZE_NUM for SIGNED and the RESIZE_SVEC do + -- and better not use RESIZE for SIGNED anymore. + FUNCTION RESIZE_NUM( u : UNSIGNED; w : NATURAL) RETURN UNSIGNED; -- left extend with '0' or keep LS part (same as RESIZE for UNSIGNED) + FUNCTION RESIZE_NUM( s : SIGNED; w : NATURAL) RETURN SIGNED; -- extend sign bit or keep LS part + FUNCTION RESIZE_UVEC(sl : STD_LOGIC; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- left extend with '0' into slv + FUNCTION RESIZE_UVEC(vec : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- left extend with '0' or keep LS part + FUNCTION RESIZE_SVEC(vec : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- extend sign bit or keep LS part + FUNCTION RESIZE_UINT(u : INTEGER; w : NATURAL) RETURN INTEGER; -- left extend with '0' or keep LS part + FUNCTION RESIZE_SINT(s : INTEGER; w : NATURAL) RETURN INTEGER; -- extend sign bit or keep LS part + + FUNCTION RESIZE_UVEC_32(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- = RESIZE_UVEC() with w=32 for t_slv_32_arr slv elements + FUNCTION RESIZE_SVEC_32(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- = RESIZE_SVEC() with w=32 for t_slv_32_arr slv elements + + FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR; + FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : UNSIGNED) RETURN STD_LOGIC_VECTOR; + FUNCTION INCR_SVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR; + FUNCTION INCR_SVEC(vec : STD_LOGIC_VECTOR; dec : SIGNED) RETURN STD_LOGIC_VECTOR; + -- Used in common_add_sub.vhd + FUNCTION ADD_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR; -- l_vec + r_vec, treat slv operands as signed, slv output width is res_w + FUNCTION SUB_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR; -- l_vec - r_vec, treat slv operands as signed, slv output width is res_w + FUNCTION ADD_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR; -- l_vec + r_vec, treat slv operands as unsigned, slv output width is res_w + FUNCTION SUB_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR; -- l_vec - r_vec, treat slv operands as unsigned, slv output width is res_w + + FUNCTION ADD_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- l_vec + r_vec, treat slv operands as signed, slv output width is l_vec'LENGTH + FUNCTION SUB_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- l_vec - r_vec, treat slv operands as signed, slv output width is l_vec'LENGTH + FUNCTION ADD_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- l_vec + r_vec, treat slv operands as unsigned, slv output width is l_vec'LENGTH + FUNCTION SUB_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- l_vec - r_vec, treat slv operands as unsigned, slv output width is l_vec'LENGTH + + FUNCTION COMPLEX_MULT_REAL(a_re, a_im, b_re, b_im : INTEGER) RETURN INTEGER; -- Calculate real part of complex multiplication: a_re*b_re - a_im*b_im + FUNCTION COMPLEX_MULT_IMAG(a_re, a_im, b_re, b_im : INTEGER) RETURN INTEGER; -- Calculate imag part of complex multiplication: a_im*b_re + a_re*b_im + + FUNCTION SHIFT_UVEC(vec : STD_LOGIC_VECTOR; shift : INTEGER) RETURN STD_LOGIC_VECTOR; -- < 0 shift left, > 0 shift right + FUNCTION SHIFT_SVEC(vec : STD_LOGIC_VECTOR; shift : INTEGER) RETURN STD_LOGIC_VECTOR; -- < 0 shift left, > 0 shift right + + FUNCTION offset_binary(a : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + + FUNCTION truncate( vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- remove n LSBits from vec, so result has width vec'LENGTH-n + FUNCTION truncate_and_resize_uvec(vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR; -- remove n LSBits from vec and then resize to width w + FUNCTION truncate_and_resize_svec(vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem for signed values + FUNCTION scale( vec : STD_LOGIC_VECTOR; n: NATURAL) RETURN STD_LOGIC_VECTOR; -- add n '0' LSBits to vec + FUNCTION scale_and_resize_uvec( vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR; -- add n '0' LSBits to vec and then resize to width w + FUNCTION scale_and_resize_svec( vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem for signed values + FUNCTION truncate_or_resize_uvec( vec : STD_LOGIC_VECTOR; b : BOOLEAN; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- when b=TRUE then truncate to width w, else resize to width w + FUNCTION truncate_or_resize_svec( vec : STD_LOGIC_VECTOR; b : BOOLEAN; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem for signed values + + FUNCTION s_round( vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- remove n LSBits from vec by rounding away from 0, so result has width vec'LENGTH-n, and clip to avoid wrap + FUNCTION s_round( vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- remove n LSBits from vec by rounding away from 0, so result has width vec'LENGTH-n + FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- idem but round up to +infinity (s_round_up = u_round) + FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem but round up to +infinity (s_round_up = u_round) + FUNCTION u_round( vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- idem round up for unsigned values + FUNCTION u_round( vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem round up for unsigned values + + FUNCTION hton(a : STD_LOGIC_VECTOR; w, sz : NATURAL) RETURN STD_LOGIC_VECTOR; -- convert endianity from host to network, sz in symbols of width w + FUNCTION hton(a : STD_LOGIC_VECTOR; sz : NATURAL) RETURN STD_LOGIC_VECTOR; -- convert endianity from host to network, sz in bytes + FUNCTION hton(a : STD_LOGIC_VECTOR ) RETURN STD_LOGIC_VECTOR; -- convert endianity from host to network, for all bytes in a + FUNCTION ntoh(a : STD_LOGIC_VECTOR; sz : NATURAL) RETURN STD_LOGIC_VECTOR; -- convert endianity from network to host, sz in bytes, ntoh() = hton() + FUNCTION ntoh(a : STD_LOGIC_VECTOR ) RETURN STD_LOGIC_VECTOR; -- convert endianity from network to host, for all bytes in a, ntoh() = hton() + + FUNCTION flip(a : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- bit flip a vector, map a[h:0] to [0:h] + FUNCTION flip(a, w : NATURAL) RETURN NATURAL; -- bit flip a vector, map a[h:0] to [0:h], h = w-1 + FUNCTION flip(a : t_slv_32_arr) RETURN t_slv_32_arr; + FUNCTION flip(a : t_integer_arr) RETURN t_integer_arr; + FUNCTION flip(a : t_natural_arr) RETURN t_natural_arr; + FUNCTION flip(a : t_nat_natural_arr) RETURN t_nat_natural_arr; + + FUNCTION transpose(a : STD_LOGIC_VECTOR; row, col : NATURAL) RETURN STD_LOGIC_VECTOR; -- transpose a vector, map a[i*row+j] to output index [j*col+i] + FUNCTION transpose(a, row, col : NATURAL) RETURN NATURAL; -- transpose index a = [i*row+j] to output index [j*col+i] + + FUNCTION split_w(input_w: NATURAL; min_out_w: NATURAL; max_out_w: NATURAL) RETURN NATURAL; + + FUNCTION pad(str: STRING; width: NATURAL; pad_char: CHARACTER) RETURN STRING; + + FUNCTION slice_up(str: STRING; width: NATURAL; i: NATURAL) RETURN STRING; + FUNCTION slice_up(str: STRING; width: NATURAL; i: NATURAL; pad_char: CHARACTER) RETURN STRING; + FUNCTION slice_dn(str: STRING; width: NATURAL; i: NATURAL) RETURN STRING; + + FUNCTION nat_arr_to_concat_slv(nat_arr: t_natural_arr; nof_elements: NATURAL) RETURN STD_LOGIC_VECTOR; + + ------------------------------------------------------------------------------ + -- Component specific functions + ------------------------------------------------------------------------------ + + -- common_fifo_* + PROCEDURE proc_common_fifo_asserts (CONSTANT c_fifo_name : IN STRING; + CONSTANT c_note_is_ful : IN BOOLEAN; + CONSTANT c_fail_rd_emp : IN BOOLEAN; + SIGNAL wr_rst : IN STD_LOGIC; + SIGNAL wr_clk : IN STD_LOGIC; + SIGNAL wr_full : IN STD_LOGIC; + SIGNAL wr_en : IN STD_LOGIC; + SIGNAL rd_clk : IN STD_LOGIC; + SIGNAL rd_empty : IN STD_LOGIC; + SIGNAL rd_en : IN STD_LOGIC); + + -- common_fanout_tree + FUNCTION func_common_fanout_tree_pipelining(c_nof_stages, c_nof_output_per_cell, c_nof_output : NATURAL; + c_cell_pipeline_factor_arr, c_cell_pipeline_arr : t_natural_arr) RETURN t_natural_arr; + + -- common_reorder_symbol + FUNCTION func_common_reorder2_is_there(I, J : NATURAL) RETURN BOOLEAN; + FUNCTION func_common_reorder2_is_active(I, J, N : NATURAL) RETURN BOOLEAN; + FUNCTION func_common_reorder2_get_select_index(I, J, N : NATURAL) RETURN INTEGER; + FUNCTION func_common_reorder2_get_select(I, J, N : NATURAL; select_arr : t_natural_arr) RETURN NATURAL; + FUNCTION func_common_reorder2_inverse_select(N : NATURAL; select_arr : t_natural_arr) RETURN t_natural_arr; + + -- Generate faster sample SCLK from digital DCLK for sim only + PROCEDURE proc_common_dclk_generate_sclk(CONSTANT Pfactor : IN POSITIVE; + SIGNAL dclk : IN STD_LOGIC; + SIGNAL sclk : INOUT STD_LOGIC); + +END common_pkg; + +PACKAGE BODY common_pkg IS + + FUNCTION pow2(n : NATURAL) RETURN NATURAL IS + BEGIN + RETURN 2**n; + END; + + FUNCTION ceil_pow2(n : INTEGER) RETURN NATURAL IS + -- Also allows negative exponents and rounds up before returning the value + BEGIN + RETURN natural(integer(ceil(2**real(n)))); + END; + + FUNCTION true_log2(n : NATURAL) RETURN NATURAL IS + -- Purpose: For calculating extra vector width of existing vector + -- Description: Return mathematical ceil(log2(n)) + -- n log2() + -- 0 -> -oo --> FAILURE + -- 1 -> 0 + -- 2 -> 1 + -- 3 -> 2 + -- 4 -> 2 + -- 5 -> 3 + -- 6 -> 3 + -- 7 -> 3 + -- 8 -> 3 + -- 9 -> 4 + -- etc, up to n = NATURAL'HIGH = 2**31-1 + BEGIN + RETURN natural(integer(ceil(log2(real(n))))); + END; + + FUNCTION ceil_log2(n : NATURAL) RETURN NATURAL IS + -- Purpose: For calculating vector width of new vector + -- Description: + -- Same as true_log2() except ceil_log2(1) = 1, which is needed to support + -- the vector width width for 1 address, to avoid NULL array for single + -- word register address. + -- If n = 0, return 0 so we get a NULL array when using + -- STD_LOGIC_VECTOR(ceil_log2(g_addr_w)-1 DOWNTO 0), instead of an error. + BEGIN + IF n = 0 THEN + RETURN 0; -- Get NULL array + ELSIF n = 1 THEN + RETURN 1; -- avoid NULL array + ELSE + RETURN true_log2(n); + END IF; + END; + + FUNCTION floor_log10(n : NATURAL) RETURN NATURAL IS + BEGIN + RETURN natural(integer(floor(log10(real(n))))); + END; + + FUNCTION is_pow2(n : NATURAL) RETURN BOOLEAN IS + BEGIN + RETURN n=2**true_log2(n); + END; + + FUNCTION true_log_pow2(n : NATURAL) RETURN NATURAL IS + BEGIN + RETURN 2**true_log2(n); + END; + + FUNCTION ratio(n, d : NATURAL) RETURN NATURAL IS + BEGIN + IF n MOD d = 0 THEN + RETURN n/d; + ELSE + RETURN 0; + END IF; + END; + + FUNCTION ratio2(n, m : NATURAL) RETURN NATURAL IS + BEGIN + RETURN largest(ratio(n,m), ratio(m,n)); + END; + + FUNCTION ceil_div(n, d : NATURAL) RETURN NATURAL IS + BEGIN + RETURN n/d + sel_a_b(n MOD d = 0, 0, 1); + END; + + FUNCTION ceil_value(n, d : NATURAL) RETURN NATURAL IS + BEGIN + RETURN ceil_div(n, d) * d; + END; + + FUNCTION floor_value(n, d : NATURAL) RETURN NATURAL IS + BEGIN + RETURN (n / d) * d; + END; + + FUNCTION ceil_div(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED IS + BEGIN + RETURN n/d + sel_a_b(n MOD d = 0, 0, 1); -- "/" returns same width as n + END; + + FUNCTION ceil_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED IS + CONSTANT w : NATURAL := n'LENGTH; + VARIABLE p : UNSIGNED(2*w-1 DOWNTO 0); + BEGIN + p := ceil_div(n, d) * d; + RETURN p(w-1 DOWNTO 0); -- return same width as n + END; + + FUNCTION floor_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED IS + CONSTANT w : NATURAL := n'LENGTH; + VARIABLE p : UNSIGNED(2*w-1 DOWNTO 0); + BEGIN + p := (n / d) * d; + RETURN p(w-1 DOWNTO 0); -- return same width as n + END; + + FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR IS + VARIABLE r : STD_LOGIC_VECTOR(0 DOWNTO 0); + BEGIN + r(0) := n; + RETURN r; + END; + + FUNCTION sl(n: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + VARIABLE r : STD_LOGIC; + BEGIN + r := n(n'LOW); + RETURN r; + END; + + FUNCTION to_natural_arr(n : t_integer_arr; to_zero : BOOLEAN) RETURN t_natural_arr IS + VARIABLE vN : t_integer_arr(n'LENGTH-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := n; + FOR I IN vN'RANGE LOOP + IF to_zero=FALSE THEN + vR(I) := vN(I); + ELSE + vR(I) := 0; + IF vN(I)>0 THEN + vR(I) := vN(I); + END IF; + END IF; + END LOOP; + RETURN vR; + END; + + FUNCTION to_natural_arr(n : t_nat_natural_arr) RETURN t_natural_arr IS + VARIABLE vN : t_nat_natural_arr(n'LENGTH-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := n; + FOR I IN vN'RANGE LOOP + vR(I) := vN(I); + END LOOP; + RETURN vR; + END; + + FUNCTION to_integer_arr(n : t_natural_arr) RETURN t_integer_arr IS + VARIABLE vN : t_natural_arr(n'LENGTH-1 DOWNTO 0); + VARIABLE vR : t_integer_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := n; + FOR I IN vN'RANGE LOOP + vR(I) := vN(I); + END LOOP; + RETURN vR; + END; + + FUNCTION to_integer_arr(n : t_nat_natural_arr) RETURN t_integer_arr IS + VARIABLE vN : t_natural_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := to_natural_arr(n); + RETURN to_integer_arr(vN); + END; + + FUNCTION to_slv_32_arr(n : t_integer_arr) RETURN t_slv_32_arr IS + VARIABLE vN : t_integer_arr(n'LENGTH-1 DOWNTO 0); + VARIABLE vR : t_slv_32_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := n; + FOR I IN vN'RANGE LOOP + vR(I) := TO_SVEC(vN(I), 32); + END LOOP; + RETURN vR; + END; + + FUNCTION to_slv_32_arr(n : t_natural_arr) RETURN t_slv_32_arr IS + VARIABLE vN : t_natural_arr(n'LENGTH-1 DOWNTO 0); + VARIABLE vR : t_slv_32_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := n; + FOR I IN vN'RANGE LOOP + vR(I) := TO_UVEC(vN(I), 32); + END LOOP; + RETURN vR; + END; + + FUNCTION vector_tree(slv : STD_LOGIC_VECTOR; operation : STRING) RETURN STD_LOGIC IS + -- Linear loop to determine result takes combinatorial delay that is proportional to slv'LENGTH: + -- FOR I IN slv'RANGE LOOP + -- v_result := v_result OPERATION slv(I); + -- END LOOP; + -- RETURN v_result; + -- Instead use binary tree to determine result with smallest combinatorial delay that depends on log2(slv'LENGTH) + CONSTANT c_slv_w : NATURAL := slv'LENGTH; + CONSTANT c_nof_stages : NATURAL := ceil_log2(c_slv_w); + CONSTANT c_w : NATURAL := 2**c_nof_stages; -- extend the input slv to a vector with length power of 2 to ease using binary tree + TYPE t_stage_arr IS ARRAY (-1 TO c_nof_stages-1) OF STD_LOGIC_VECTOR(c_w-1 DOWNTO 0); + VARIABLE v_stage_arr : t_stage_arr; + VARIABLE v_result : STD_LOGIC := '0'; + BEGIN + -- default any unused, the stage results will be kept in the LSBits and the last result in bit 0 + IF operation="AND" THEN v_stage_arr := (OTHERS=>(OTHERS=>'1')); + ELSIF operation="OR" THEN v_stage_arr := (OTHERS=>(OTHERS=>'0')); + ELSIF operation="XOR" THEN v_stage_arr := (OTHERS=>(OTHERS=>'0')); + ELSE + ASSERT TRUE REPORT "common_pkg: Unsupported vector_tree operation" SEVERITY FAILURE; + END IF; + v_stage_arr(-1)(c_slv_w-1 DOWNTO 0) := slv; -- any unused input c_w : c_slv_w bits have void default value + FOR J IN 0 TO c_nof_stages-1 LOOP + FOR I IN 0 TO c_w/(2**(J+1))-1 LOOP + IF operation="AND" THEN v_stage_arr(J)(I) := v_stage_arr(J-1)(2*I) AND v_stage_arr(J-1)(2*I+1); + ELSIF operation="OR" THEN v_stage_arr(J)(I) := v_stage_arr(J-1)(2*I) OR v_stage_arr(J-1)(2*I+1); + ELSIF operation="XOR" THEN v_stage_arr(J)(I) := v_stage_arr(J-1)(2*I) XOR v_stage_arr(J-1)(2*I+1); + END IF; + END LOOP; + END LOOP; + RETURN v_stage_arr(c_nof_stages-1)(0); + END; + + FUNCTION vector_and(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + BEGIN + RETURN vector_tree(slv, "AND"); + END; + + FUNCTION vector_or(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + BEGIN + RETURN vector_tree(slv, "OR"); + END; + + FUNCTION vector_xor(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + BEGIN + RETURN vector_tree(slv, "XOR"); + END; + + FUNCTION vector_one_hot(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_one_hot : BOOLEAN := FALSE; + VARIABLE v_zeros : STD_LOGIC_VECTOR(slv'RANGE) := (OTHERS=>'0'); + BEGIN + FOR i IN slv'RANGE LOOP + IF slv(i) = '1' THEN + IF NOT(v_one_hot) THEN + -- No hot bits found so far + v_one_hot := TRUE; + ELSE + -- This is the second hot bit found; return zeros. + RETURN v_zeros; + END IF; + END IF; + END LOOP; + -- No or a single hot bit found in slv; return slv. + RETURN slv; + END; + + FUNCTION andv(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + BEGIN + RETURN vector_tree(slv, "AND"); + END; + + FUNCTION orv(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + BEGIN + RETURN vector_tree(slv, "OR"); + END; + + FUNCTION xorv(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC IS + BEGIN + RETURN vector_tree(slv, "XOR"); + END; + + FUNCTION matrix_and(mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC IS + VARIABLE v_mat : t_sl_matrix(0 TO wi-1, 0 TO wj-1) := mat; -- map to fixed range + VARIABLE v_result : STD_LOGIC := '1'; + BEGIN + FOR I IN 0 TO wi-1 LOOP + FOR J IN 0 TO wj-1 LOOP + v_result := v_result AND v_mat(I,J); + END LOOP; + END LOOP; + RETURN v_result; + END; + + FUNCTION matrix_or(mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC IS + VARIABLE v_mat : t_sl_matrix(0 TO wi-1, 0 TO wj-1) := mat; -- map to fixed range + VARIABLE v_result : STD_LOGIC := '0'; + BEGIN + FOR I IN 0 TO wi-1 LOOP + FOR J IN 0 TO wj-1 LOOP + v_result := v_result OR v_mat(I,J); + END LOOP; + END LOOP; + RETURN v_result; + END; + + FUNCTION smallest(n, m : INTEGER) RETURN INTEGER IS + BEGIN + IF n < m THEN + RETURN n; + ELSE + RETURN m; + END IF; + END; + + FUNCTION smallest(n, m, l : INTEGER) RETURN INTEGER IS + VARIABLE v : NATURAL; + BEGIN + v := n; + IF v > m THEN v := m; END IF; + IF v > l THEN v := l; END IF; + RETURN v; + END; + + FUNCTION smallest(n : t_natural_arr) RETURN NATURAL IS + VARIABLE m : NATURAL := 0; + BEGIN + FOR I IN n'RANGE LOOP + IF n(I) < m THEN + m := n(I); + END IF; + END LOOP; + RETURN m; + END; + + FUNCTION largest(n, m : INTEGER) RETURN INTEGER IS + BEGIN + IF n > m THEN + RETURN n; + ELSE + RETURN m; + END IF; + END; + + FUNCTION largest(n : t_natural_arr) RETURN NATURAL IS + VARIABLE m : NATURAL := 0; + BEGIN + FOR I IN n'RANGE LOOP + IF n(I) > m THEN + m := n(I); + END IF; + END LOOP; + RETURN m; + END; + + FUNCTION func_sum(n : t_natural_arr) RETURN NATURAL IS + VARIABLE vS : NATURAL; + BEGIN + vS := 0; + FOR I IN n'RANGE LOOP + vS := vS + n(I); + END LOOP; + RETURN vS; + END; + + FUNCTION func_sum(n : t_nat_natural_arr) RETURN NATURAL IS + VARIABLE vN : t_natural_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := to_natural_arr(n); + RETURN func_sum(vN); + END; + + FUNCTION func_product(n : t_natural_arr) RETURN NATURAL IS + VARIABLE vP : NATURAL; + BEGIN + vP := 1; + FOR I IN n'RANGE LOOP + vP := vP * n(I); + END LOOP; + RETURN vP; + END; + + FUNCTION func_product(n : t_nat_natural_arr) RETURN NATURAL IS + VARIABLE vN : t_natural_arr(n'LENGTH-1 DOWNTO 0); + BEGIN + vN := to_natural_arr(n); + RETURN func_product(vN); + END; + + FUNCTION "+" (L, R: t_natural_arr) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + vR := R; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) + vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION "+" (L: t_natural_arr; R : INTEGER) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) + R; + END LOOP; + RETURN vP; + END; + + FUNCTION "+" (L: INTEGER; R : t_natural_arr) RETURN t_natural_arr IS + BEGIN + RETURN R + L; + END; + + FUNCTION "-" (L, R: t_natural_arr) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + vR := R; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) - vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION "-" (L, R: t_natural_arr) RETURN t_integer_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_integer_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + vR := R; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) - vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION "-" (L: t_natural_arr; R : INTEGER) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) - R; + END LOOP; + RETURN vP; + END; + + FUNCTION "-" (L: INTEGER; R : t_natural_arr) RETURN t_natural_arr IS + CONSTANT w : NATURAL := R'LENGTH; + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vR := R; + FOR I IN vR'RANGE LOOP + vP(I) := L - vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION "*" (L, R: t_natural_arr) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + vR := R; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) * vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION "*" (L: t_natural_arr; R : NATURAL) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) * R; + END LOOP; + RETURN vP; + END; + + FUNCTION "*" (L: NATURAL; R : t_natural_arr) RETURN t_natural_arr IS + BEGIN + RETURN R * L; + END; + + FUNCTION "/" (L, R: t_natural_arr) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + vR := R; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) / vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION "/" (L: t_natural_arr; R : POSITIVE) RETURN t_natural_arr IS + CONSTANT w : NATURAL := L'LENGTH; + VARIABLE vL : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vL := L; + FOR I IN vL'RANGE LOOP + vP(I) := vL(I) / R; + END LOOP; + RETURN vP; + END; + + FUNCTION "/" (L: NATURAL; R : t_natural_arr) RETURN t_natural_arr IS + CONSTANT w : NATURAL := R'LENGTH; + VARIABLE vR : t_natural_arr(w-1 DOWNTO 0); + VARIABLE vP : t_natural_arr(w-1 DOWNTO 0); + BEGIN + vR := R; + FOR I IN vR'RANGE LOOP + vP(I) := L / vR(I); + END LOOP; + RETURN vP; + END; + + FUNCTION is_true(a : STD_LOGIC) RETURN BOOLEAN IS BEGIN IF a='1' THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END; + FUNCTION is_true(a : STD_LOGIC) RETURN NATURAL IS BEGIN IF a='1' THEN RETURN 1; ELSE RETURN 0; END IF; END; + FUNCTION is_true(a : BOOLEAN) RETURN STD_LOGIC IS BEGIN IF a=TRUE THEN RETURN '1'; ELSE RETURN '0'; END IF; END; + FUNCTION is_true(a : BOOLEAN) RETURN NATURAL IS BEGIN IF a=TRUE THEN RETURN 1; ELSE RETURN 0; END IF; END; + FUNCTION is_true(a : INTEGER) RETURN BOOLEAN IS BEGIN IF a/=0 THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END; + FUNCTION is_true(a : INTEGER) RETURN STD_LOGIC IS BEGIN IF a/=0 THEN RETURN '1'; ELSE RETURN '0'; END IF; END; + + FUNCTION sel_a_b(sel, a, b : INTEGER) RETURN INTEGER IS + BEGIN + IF sel /= 0 THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel, a, b : BOOLEAN) RETURN BOOLEAN IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : INTEGER) RETURN INTEGER IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : REAL) RETURN REAL IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : STD_LOGIC) RETURN STD_LOGIC IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : INTEGER; a, b : STD_LOGIC) RETURN STD_LOGIC IS + BEGIN + IF sel /= 0 THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : INTEGER; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + IF sel /= 0 THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : SIGNED) RETURN SIGNED IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : UNSIGNED) RETURN UNSIGNED IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_integer_arr) RETURN t_integer_arr IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_natural_arr) RETURN t_natural_arr IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_nat_integer_arr) RETURN t_nat_integer_arr IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : t_nat_natural_arr) RETURN t_nat_natural_arr IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : STRING) RETURN STRING IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : INTEGER; a, b : STRING) RETURN STRING IS + BEGIN + IF sel /= 0 THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : TIME) RETURN TIME IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + FUNCTION sel_a_b(sel : BOOLEAN; a, b : SEVERITY_LEVEL) RETURN SEVERITY_LEVEL IS + BEGIN + IF sel = TRUE THEN + RETURN a; + ELSE + RETURN b; + END IF; + END; + + -- sel_n : boolean + FUNCTION sel_n(sel : NATURAL; a, b, c : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d, e); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d, e, f); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d, e, f, g); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d, e, f, g, h); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d, e, f, g, h, i); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : BOOLEAN) RETURN BOOLEAN IS + CONSTANT c_arr : t_nat_boolean_arr := (a, b, c, d, e, f, g, h, i, j); + BEGIN + RETURN c_arr(sel); + END; + + -- sel_n : integer + FUNCTION sel_n(sel : NATURAL; a, b, c : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d, e); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d, e, f); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d, e, f, g); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d, e, f, g, h); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d, e, f, g, h, i); + BEGIN + RETURN c_arr(sel); + END; + + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : INTEGER) RETURN INTEGER IS + CONSTANT c_arr : t_nat_integer_arr := (a, b, c, d, e, f, g, h, i, j); + BEGIN + RETURN c_arr(sel); + END; + + -- sel_n : string + FUNCTION sel_n(sel : NATURAL; a, b : STRING) RETURN STRING IS BEGIN IF sel=0 THEN RETURN a ; ELSE RETURN b; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c : STRING) RETURN STRING IS BEGIN IF sel<2 THEN RETURN sel_n(sel, a, b ); ELSE RETURN c; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d : STRING) RETURN STRING IS BEGIN IF sel<3 THEN RETURN sel_n(sel, a, b, c ); ELSE RETURN d; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e : STRING) RETURN STRING IS BEGIN IF sel<4 THEN RETURN sel_n(sel, a, b, c, d ); ELSE RETURN e; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f : STRING) RETURN STRING IS BEGIN IF sel<5 THEN RETURN sel_n(sel, a, b, c, d, e ); ELSE RETURN f; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g : STRING) RETURN STRING IS BEGIN IF sel<6 THEN RETURN sel_n(sel, a, b, c, d, e, f ); ELSE RETURN g; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h : STRING) RETURN STRING IS BEGIN IF sel<7 THEN RETURN sel_n(sel, a, b, c, d, e, f, g ); ELSE RETURN h; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i : STRING) RETURN STRING IS BEGIN IF sel<8 THEN RETURN sel_n(sel, a, b, c, d, e, f, g, h ); ELSE RETURN i; END IF; END; + FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : STRING) RETURN STRING IS BEGIN IF sel<9 THEN RETURN sel_n(sel, a, b, c, d, e, f, g, h, i); ELSE RETURN j; END IF; END; + + FUNCTION array_init(init : STD_LOGIC; nof : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_arr : STD_LOGIC_VECTOR(0 TO nof-1); + BEGIN + FOR I IN v_arr'RANGE LOOP + v_arr(I) := init; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof : NATURAL) RETURN t_natural_arr IS + VARIABLE v_arr : t_natural_arr(0 TO nof-1); + BEGIN + FOR I IN v_arr'RANGE LOOP + v_arr(I) := init; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof : NATURAL) RETURN t_nat_natural_arr IS + VARIABLE v_arr : t_nat_natural_arr(0 TO nof-1); + BEGIN + FOR I IN v_arr'RANGE LOOP + v_arr(I) := init; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof, incr : NATURAL) RETURN t_natural_arr IS + VARIABLE v_arr : t_natural_arr(0 TO nof-1); + VARIABLE v_i : NATURAL; + BEGIN + v_i := 0; + FOR I IN v_arr'RANGE LOOP + v_arr(I) := init + v_i * incr; + v_i := v_i + 1; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof, incr : NATURAL) RETURN t_nat_natural_arr IS + VARIABLE v_arr : t_nat_natural_arr(0 TO nof-1); + VARIABLE v_i : NATURAL; + BEGIN + v_i := 0; + FOR I IN v_arr'RANGE LOOP + v_arr(I) := init + v_i * incr; + v_i := v_i + 1; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof, incr : INTEGER) RETURN t_slv_16_arr IS + VARIABLE v_arr : t_slv_16_arr(0 TO nof-1); + VARIABLE v_i : NATURAL; + BEGIN + v_i := 0; + FOR I IN v_arr'RANGE LOOP + v_arr(I) := TO_SVEC(init + v_i * incr, 16); + v_i := v_i + 1; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof, incr : INTEGER) RETURN t_slv_32_arr IS + VARIABLE v_arr : t_slv_32_arr(0 TO nof-1); + VARIABLE v_i : NATURAL; + BEGIN + v_i := 0; + FOR I IN v_arr'RANGE LOOP + v_arr(I) := TO_SVEC(init + v_i * incr, 32); + v_i := v_i + 1; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof, width : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_arr : STD_LOGIC_VECTOR(nof*width-1 DOWNTO 0); + BEGIN + FOR I IN 0 TO nof-1 LOOP + v_arr(width*(I+1)-1 DOWNTO width*I) := TO_UVEC(init, width); + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_init(init, nof, width, incr : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_arr : STD_LOGIC_VECTOR(nof*width-1 DOWNTO 0); + VARIABLE v_i : NATURAL; + BEGIN + v_i := 0; + FOR I IN 0 TO nof-1 LOOP + v_arr(width*(I+1)-1 DOWNTO width*I) := TO_UVEC(init + v_i * incr, width); + v_i := v_i + 1; + END LOOP; + RETURN v_arr; + END; + + FUNCTION array_sinit(init :INTEGER; nof, width : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_arr : STD_LOGIC_VECTOR(nof*width-1 DOWNTO 0); + BEGIN + FOR I IN 0 TO nof-1 LOOP + v_arr(width*(I+1)-1 DOWNTO width*I) := TO_SVEC(init, width); + END LOOP; + RETURN v_arr; + END; + + FUNCTION init_slv_64_matrix(nof_a, nof_b, k : INTEGER) RETURN t_slv_64_matrix IS + VARIABLE v_mat : t_slv_64_matrix(nof_a-1 DOWNTO 0, nof_b-1 DOWNTO 0); + BEGIN + FOR I IN 0 TO nof_a-1 LOOP + FOR J IN 0 TO nof_b-1 LOOP + v_mat(I,J) := TO_SVEC(k, 64); + END LOOP; + END LOOP; + RETURN v_mat; + END; + + + -- Support concatenation of up to 7 slv into 1 slv + FUNCTION func_slv_concat(use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a, b, c, d, e, f, g : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_max_w : NATURAL := a'LENGTH + b'LENGTH + c'LENGTH + d'LENGTH + e'LENGTH + f'LENGTH + g'LENGTH; + VARIABLE v_res : STD_LOGIC_VECTOR(c_max_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_len : NATURAL := 0; + BEGIN + IF use_a = TRUE THEN v_res(a'LENGTH-1 + v_len DOWNTO v_len) := a; v_len := v_len + a'LENGTH; END IF; + IF use_b = TRUE THEN v_res(b'LENGTH-1 + v_len DOWNTO v_len) := b; v_len := v_len + b'LENGTH; END IF; + IF use_c = TRUE THEN v_res(c'LENGTH-1 + v_len DOWNTO v_len) := c; v_len := v_len + c'LENGTH; END IF; + IF use_d = TRUE THEN v_res(d'LENGTH-1 + v_len DOWNTO v_len) := d; v_len := v_len + d'LENGTH; END IF; + IF use_e = TRUE THEN v_res(e'LENGTH-1 + v_len DOWNTO v_len) := e; v_len := v_len + e'LENGTH; END IF; + IF use_f = TRUE THEN v_res(f'LENGTH-1 + v_len DOWNTO v_len) := f; v_len := v_len + f'LENGTH; END IF; + IF use_g = TRUE THEN v_res(g'LENGTH-1 + v_len DOWNTO v_len) := g; v_len := v_len + g'LENGTH; END IF; + RETURN v_res(v_len-1 DOWNTO 0); + END func_slv_concat; + + FUNCTION func_slv_concat(use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a, b, c, d, e, f : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(use_a, use_b, use_c, use_d, use_e, use_f, FALSE, a, b, c, d, e, f, "0"); + END func_slv_concat; + + FUNCTION func_slv_concat(use_a, use_b, use_c, use_d, use_e : BOOLEAN; a, b, c, d, e : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(use_a, use_b, use_c, use_d, use_e, FALSE, FALSE, a, b, c, d, e, "0", "0"); + END func_slv_concat; + + FUNCTION func_slv_concat(use_a, use_b, use_c, use_d : BOOLEAN; a, b, c, d : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(use_a, use_b, use_c, use_d, FALSE, FALSE, FALSE, a, b, c, d, "0", "0", "0"); + END func_slv_concat; + + FUNCTION func_slv_concat(use_a, use_b, use_c : BOOLEAN; a, b, c : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(use_a, use_b, use_c, FALSE, FALSE, FALSE, FALSE, a, b, c, "0", "0", "0", "0"); + END func_slv_concat; + + FUNCTION func_slv_concat(use_a, use_b : BOOLEAN; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(use_a, use_b, FALSE, FALSE, FALSE, FALSE, FALSE, a, b, "0", "0", "0", "0", "0"); + END func_slv_concat; + + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL) RETURN NATURAL IS + VARIABLE v_len : NATURAL := 0; + BEGIN + IF use_a = TRUE THEN v_len := v_len + a_w; END IF; + IF use_b = TRUE THEN v_len := v_len + b_w; END IF; + IF use_c = TRUE THEN v_len := v_len + c_w; END IF; + IF use_d = TRUE THEN v_len := v_len + d_w; END IF; + IF use_e = TRUE THEN v_len := v_len + e_w; END IF; + IF use_f = TRUE THEN v_len := v_len + f_w; END IF; + IF use_g = TRUE THEN v_len := v_len + g_w; END IF; + RETURN v_len; + END func_slv_concat_w; + + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w : NATURAL) RETURN NATURAL IS + BEGIN + RETURN func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f, FALSE, a_w, b_w, c_w, d_w, e_w, f_w, 0); + END func_slv_concat_w; + + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e : BOOLEAN; a_w, b_w, c_w, d_w, e_w : NATURAL) RETURN NATURAL IS + BEGIN + RETURN func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, FALSE, FALSE, a_w, b_w, c_w, d_w, e_w, 0, 0); + END func_slv_concat_w; + + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d : BOOLEAN; a_w, b_w, c_w, d_w : NATURAL) RETURN NATURAL IS + BEGIN + RETURN func_slv_concat_w(use_a, use_b, use_c, use_d, FALSE, FALSE, FALSE, a_w, b_w, c_w, d_w, 0, 0, 0); + END func_slv_concat_w; + + FUNCTION func_slv_concat_w(use_a, use_b, use_c : BOOLEAN; a_w, b_w, c_w : NATURAL) RETURN NATURAL IS + BEGIN + RETURN func_slv_concat_w(use_a, use_b, use_c, FALSE, FALSE, FALSE, FALSE, a_w, b_w, c_w, 0, 0, 0, 0); + END func_slv_concat_w; + + FUNCTION func_slv_concat_w(use_a, use_b : BOOLEAN; a_w, b_w : NATURAL) RETURN NATURAL IS + BEGIN + RETURN func_slv_concat_w(use_a, use_b, FALSE, FALSE, FALSE, FALSE, FALSE, a_w, b_w, 0, 0, 0, 0, 0); + END func_slv_concat_w; + + -- extract slv + FUNCTION func_slv_extract(use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_w : NATURAL := 0; + VARIABLE v_lo : NATURAL := 0; + BEGIN + -- if the selected slv is not used in vec, then return dummy, else return the selected slv from vec + CASE sel IS + WHEN 0 => + IF use_a = TRUE THEN v_w := a_w; ELSE RETURN c_slv0(a_w-1 DOWNTO 0); END IF; + WHEN 1 => + IF use_b = TRUE THEN v_w := b_w; ELSE RETURN c_slv0(b_w-1 DOWNTO 0); END IF; + IF use_a = TRUE THEN v_lo := v_lo + a_w; END IF; + WHEN 2 => + IF use_c = TRUE THEN v_w := c_w; ELSE RETURN c_slv0(c_w-1 DOWNTO 0); END IF; + IF use_a = TRUE THEN v_lo := v_lo + a_w; END IF; + IF use_b = TRUE THEN v_lo := v_lo + b_w; END IF; + WHEN 3 => + IF use_d = TRUE THEN v_w := d_w; ELSE RETURN c_slv0(d_w-1 DOWNTO 0); END IF; + IF use_a = TRUE THEN v_lo := v_lo + a_w; END IF; + IF use_b = TRUE THEN v_lo := v_lo + b_w; END IF; + IF use_c = TRUE THEN v_lo := v_lo + c_w; END IF; + WHEN 4 => + IF use_e = TRUE THEN v_w := e_w; ELSE RETURN c_slv0(e_w-1 DOWNTO 0); END IF; + IF use_a = TRUE THEN v_lo := v_lo + a_w; END IF; + IF use_b = TRUE THEN v_lo := v_lo + b_w; END IF; + IF use_c = TRUE THEN v_lo := v_lo + c_w; END IF; + IF use_d = TRUE THEN v_lo := v_lo + d_w; END IF; + WHEN 5 => + IF use_f = TRUE THEN v_w := f_w; ELSE RETURN c_slv0(f_w-1 DOWNTO 0); END IF; + IF use_a = TRUE THEN v_lo := v_lo + a_w; END IF; + IF use_b = TRUE THEN v_lo := v_lo + b_w; END IF; + IF use_c = TRUE THEN v_lo := v_lo + c_w; END IF; + IF use_d = TRUE THEN v_lo := v_lo + d_w; END IF; + IF use_e = TRUE THEN v_lo := v_lo + e_w; END IF; + WHEN 6 => + IF use_g = TRUE THEN v_w := g_w; ELSE RETURN c_slv0(g_w-1 DOWNTO 0); END IF; + IF use_a = TRUE THEN v_lo := v_lo + a_w; END IF; + IF use_b = TRUE THEN v_lo := v_lo + b_w; END IF; + IF use_c = TRUE THEN v_lo := v_lo + c_w; END IF; + IF use_d = TRUE THEN v_lo := v_lo + d_w; END IF; + IF use_e = TRUE THEN v_lo := v_lo + e_w; END IF; + IF use_f = TRUE THEN v_lo := v_lo + f_w; END IF; + WHEN OTHERS => REPORT "Unknown common_pkg func_slv_extract argument" SEVERITY FAILURE; + END CASE; + RETURN vec(v_w-1 + v_lo DOWNTO v_lo); -- extracted slv + END func_slv_extract; + + FUNCTION func_slv_extract(use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(use_a, use_b, use_c, use_d, use_e, use_f, FALSE, a_w, b_w, c_w, d_w, e_w, f_w, 0, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(use_a, use_b, use_c, use_d, use_e : BOOLEAN; a_w, b_w, c_w, d_w, e_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(use_a, use_b, use_c, use_d, use_e, FALSE, FALSE, a_w, b_w, c_w, d_w, e_w, 0, 0, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(use_a, use_b, use_c, use_d : BOOLEAN; a_w, b_w, c_w, d_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(use_a, use_b, use_c, use_d, FALSE, FALSE, FALSE, a_w, b_w, c_w, d_w, 0, 0, 0, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(use_a, use_b, use_c : BOOLEAN; a_w, b_w, c_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(use_a, use_b, use_c, FALSE, FALSE, FALSE, FALSE, a_w, b_w, c_w, 0, 0, 0, 0, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(use_a, use_b : BOOLEAN; a_w, b_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(use_a, use_b, FALSE, FALSE, FALSE, FALSE, FALSE, a_w, b_w, 0, 0, 0, 0, 0, vec, sel); + END func_slv_extract; + + + FUNCTION TO_UINT(vec : STD_LOGIC_VECTOR) RETURN NATURAL IS + BEGIN + RETURN TO_INTEGER(UNSIGNED(vec)); + END; + + FUNCTION TO_SINT(vec : STD_LOGIC_VECTOR) RETURN INTEGER IS + BEGIN + RETURN TO_INTEGER(SIGNED(vec)); + END; + + FUNCTION TO_UVEC(dec, w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(TO_UNSIGNED(dec, w)); + END; + + FUNCTION TO_SVEC(dec, w : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(TO_SIGNED(dec, w)); + END; + + FUNCTION TO_SVEC_32(dec : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN TO_SVEC(dec, 32); + END; + + FUNCTION RESIZE_NUM(u : UNSIGNED; w : NATURAL) RETURN UNSIGNED IS + BEGIN + -- left extend with '0' or keep LS part (same as RESIZE for UNSIGNED) + RETURN RESIZE(u, w); + END; + + FUNCTION RESIZE_NUM(s : SIGNED; w : NATURAL) RETURN SIGNED IS + BEGIN + -- extend sign bit or keep LS part + IF w>s'LENGTH THEN + RETURN RESIZE(s, w); -- extend sign bit + ELSE + RETURN SIGNED(RESIZE(UNSIGNED(s), w)); -- keep LSbits (= vec[w-1:0]) + END IF; + END; + + FUNCTION RESIZE_UVEC(sl : STD_LOGIC; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_slv0 : STD_LOGIC_VECTOR(w-1 DOWNTO 1) := (OTHERS=>'0'); + BEGIN + RETURN v_slv0 & sl; + END; + + FUNCTION RESIZE_UVEC(vec : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(UNSIGNED(vec), w)); + END; + + FUNCTION RESIZE_SVEC(vec : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(SIGNED(vec), w)); + END; + + FUNCTION RESIZE_UINT(u : INTEGER; w : NATURAL) RETURN INTEGER IS + VARIABLE v : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + BEGIN + v := TO_UVEC(u, c_word_w); + RETURN TO_UINT(v(w-1 DOWNTO 0)); + END; + + FUNCTION RESIZE_SINT(s : INTEGER; w : NATURAL) RETURN INTEGER IS + VARIABLE v : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + BEGIN + v := TO_SVEC(s, c_word_w); + RETURN TO_SINT(v(w-1 DOWNTO 0)); + END; + + FUNCTION RESIZE_UVEC_32(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(vec, 32); + END; + + FUNCTION RESIZE_SVEC_32(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_SVEC(vec, 32); + END; + + FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_dec : INTEGER; + BEGIN + IF dec < 0 THEN + v_dec := -dec; + RETURN STD_LOGIC_VECTOR(UNSIGNED(vec) - v_dec); -- uses function "-" (L : UNSIGNED, R : NATURAL), there is no function + with R : INTEGER argument + ELSE + v_dec := dec; + RETURN STD_LOGIC_VECTOR(UNSIGNED(vec) + v_dec); -- uses function "+" (L : UNSIGNED, R : NATURAL) + END IF; + END; + + FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : UNSIGNED) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(UNSIGNED(vec) + dec); + END; + + FUNCTION INCR_SVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_dec : INTEGER; + BEGIN + RETURN STD_LOGIC_VECTOR(SIGNED(vec) + v_dec); -- uses function "+" (L : SIGNED, R : INTEGER) + END; + + FUNCTION INCR_SVEC(vec : STD_LOGIC_VECTOR; dec : SIGNED) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(SIGNED(vec) + dec); + END; + + FUNCTION ADD_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(SIGNED(l_vec), res_w) + SIGNED(r_vec)); + END; + + FUNCTION SUB_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(SIGNED(l_vec), res_w) - SIGNED(r_vec)); + END; + + FUNCTION ADD_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(UNSIGNED(l_vec), res_w) + UNSIGNED(r_vec)); + END; + + FUNCTION SUB_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR; res_w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(UNSIGNED(l_vec), res_w) - UNSIGNED(r_vec)); + END; + + + FUNCTION ADD_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN ADD_SVEC(l_vec, r_vec, l_vec'LENGTH); + END; + + FUNCTION SUB_SVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN SUB_SVEC(l_vec, r_vec, l_vec'LENGTH); + END; + + FUNCTION ADD_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN ADD_UVEC(l_vec, r_vec, l_vec'LENGTH); + END; + + FUNCTION SUB_UVEC(l_vec : STD_LOGIC_VECTOR; r_vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN SUB_UVEC(l_vec, r_vec, l_vec'LENGTH); + END; + + FUNCTION COMPLEX_MULT_REAL(a_re, a_im, b_re, b_im : INTEGER) RETURN INTEGER IS + BEGIN + RETURN (a_re*b_re - a_im*b_im); + END; + + FUNCTION COMPLEX_MULT_IMAG(a_re, a_im, b_re, b_im : INTEGER) RETURN INTEGER IS + BEGIN + RETURN (a_im*b_re + a_re*b_im); + END; + + FUNCTION SHIFT_UVEC(vec : STD_LOGIC_VECTOR; shift : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + IF shift < 0 THEN + RETURN STD_LOGIC_VECTOR(SHIFT_LEFT(UNSIGNED(vec), -shift)); -- fill zeros from right + ELSE + RETURN STD_LOGIC_VECTOR(SHIFT_RIGHT(UNSIGNED(vec), shift)); -- fill zeros from left + END IF; + END; + + FUNCTION SHIFT_SVEC(vec : STD_LOGIC_VECTOR; shift : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + IF shift < 0 THEN + RETURN STD_LOGIC_VECTOR(SHIFT_LEFT(SIGNED(vec), -shift)); -- same as SHIFT_LEFT for UNSIGNED + ELSE + RETURN STD_LOGIC_VECTOR(SHIFT_RIGHT(SIGNED(vec), shift)); -- extend sign + END IF; + END; + + -- + -- offset_binary() : maps offset binary to or from two-complement binary. + -- + -- National ADC08DC1020 offset binary two-complement binary + -- + full scale = 127.5 : 11111111 = 255 127 = 01111111 + -- ... + -- + = +0.5 : 10000000 = 128 0 = 00000000 + -- 0 + -- - = -0.5 : 01111111 = 127 -1 = 11111111 + -- ... + -- - full scale = -127.5 : 00000000 = 0 -128 = 10000000 + -- + -- To map between the offset binary and two complement binary involves + -- adding 128 to the binary value or equivalently inverting the sign bit. + -- The offset_binary() mapping can be done and undone both ways. + -- The offset_binary() mapping to two-complement binary yields a DC offset + -- of -0.5 Lsb. + FUNCTION offset_binary(a : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_res : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0) := a; + BEGIN + v_res(v_res'HIGH) := NOT v_res(v_res'HIGH); -- invert MSbit to get to from offset binary to two's complement, or vice versa + RETURN v_res; + END; + + FUNCTION truncate(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + CONSTANT c_trunc_w : NATURAL := c_vec_w-n; + VARIABLE v_vec : STD_LOGIC_VECTOR(c_vec_w-1 DOWNTO 0) := vec; + VARIABLE v_res : STD_LOGIC_VECTOR(c_trunc_w-1 DOWNTO 0); + BEGIN + v_res := v_vec(c_vec_w-1 DOWNTO n); -- keep MS part + RETURN v_res; + END; + + FUNCTION truncate_and_resize_uvec(vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + CONSTANT c_trunc_w : NATURAL := c_vec_w-n; + VARIABLE v_trunc : STD_LOGIC_VECTOR(c_trunc_w-1 DOWNTO 0); + VARIABLE v_res : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + v_trunc := truncate(vec, n); -- first keep MS part + v_res := RESIZE_UVEC(v_trunc, w); -- then keep LS part or left extend with '0' + RETURN v_res; + END; + + FUNCTION truncate_and_resize_svec(vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + CONSTANT c_trunc_w : NATURAL := c_vec_w-n; + VARIABLE v_trunc : STD_LOGIC_VECTOR(c_trunc_w-1 DOWNTO 0); + VARIABLE v_res : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + v_trunc := truncate(vec, n); -- first keep MS part + v_res := RESIZE_SVEC(v_trunc, w); -- then keep sign bit and LS part or left extend sign bit + RETURN v_res; + END; + + FUNCTION scale(vec : STD_LOGIC_VECTOR; n: NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + CONSTANT c_scale_w : NATURAL := c_vec_w+n; + VARIABLE v_res : STD_LOGIC_VECTOR(c_scale_w-1 DOWNTO 0) := (OTHERS=>'0'); + BEGIN + v_res(c_scale_w-1 DOWNTO n) := vec; -- scale by adding n zero bits at the right + RETURN v_res; + END; + + FUNCTION scale_and_resize_uvec(vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + CONSTANT c_scale_w : NATURAL := c_vec_w+n; + VARIABLE v_scale : STD_LOGIC_VECTOR(c_scale_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_res : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + v_scale(c_scale_w-1 DOWNTO n) := vec; -- first scale by adding n zero bits at the right + v_res := RESIZE_UVEC(v_scale, w); -- then keep LS part or left extend with '0' + RETURN v_res; + END; + + FUNCTION scale_and_resize_svec(vec : STD_LOGIC_VECTOR; n, w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + CONSTANT c_scale_w : NATURAL := c_vec_w+n; + VARIABLE v_scale : STD_LOGIC_VECTOR(c_scale_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_res : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + v_scale(c_scale_w-1 DOWNTO n) := vec; -- first scale by adding n zero bits at the right + v_res := RESIZE_SVEC(v_scale, w); -- then keep LS part or left extend sign bit + RETURN v_res; + END; + + FUNCTION truncate_or_resize_uvec(vec : STD_LOGIC_VECTOR; b : BOOLEAN; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + VARIABLE c_n : INTEGER := c_vec_w-w; + VARIABLE v_res : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + IF b=TRUE AND c_n>0 THEN + v_res := truncate_and_resize_uvec(vec, c_n, w); + ELSE + v_res := RESIZE_UVEC(vec, w); + END IF; + RETURN v_res; + END; + + FUNCTION truncate_or_resize_svec(vec : STD_LOGIC_VECTOR; b : BOOLEAN; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + VARIABLE c_n : INTEGER := c_vec_w-w; + VARIABLE v_res : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + IF b=TRUE AND c_n>0 THEN + v_res := truncate_and_resize_svec(vec, c_n, w); + ELSE + v_res := RESIZE_SVEC(vec, w); + END IF; + RETURN v_res; + END; + + + -- Functions s_round, s_round_up and u_round: + -- + -- . The returned output width is input width - n. + -- . If n=0 then the return value is the same as the input value so only + -- wires (NOP, no operation). + -- . Both have the same implementation but different c_max and c_clip values. + -- . Round up for unsigned so +2.5 becomes 3 + -- . Round away from zero for signed so round up for positive and round down for negative, so +2.5 becomes 3 and -2.5 becomes -3. + -- . Round away from zero is also used by round() in Matlab, Python, TCL + -- . Rounding up implies adding 0.5 and then truncation, use clip = TRUE to + -- clip the potential overflow due to adding 0.5 to +max. + -- . For negative values overflow due to rounding can not occur, because c_half-1 >= 0 for n>0 + -- . If the input comes from a product and is rounded to the input width then + -- clip can safely be FALSE, because e.g. for unsigned 4b*4b=8b->4b the + -- maximum product is 15*15=225 <= 255-8, and for signed 4b*4b=8b->4b the + -- maximum product is -8*-8=+64 <= 127-8, so wrapping due to rounding + -- overflow will never occur. + + FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR IS + -- Use SIGNED to avoid NATURAL (32 bit range) overflow error + CONSTANT c_in_w : NATURAL := vec'LENGTH; + CONSTANT c_out_w : NATURAL := vec'LENGTH - n; + CONSTANT c_one : SIGNED(c_in_w-1 DOWNTO 0) := TO_SIGNED(1, c_in_w); + CONSTANT c_half : SIGNED(c_in_w-1 DOWNTO 0) := SHIFT_LEFT(c_one, n-1); -- = 2**(n-1) + CONSTANT c_max : SIGNED(c_in_w-1 DOWNTO 0) := SIGNED('0' & c_slv1(c_in_w-2 DOWNTO 0)) - c_half; -- = 2**(c_in_w-1)-1 - c_half + CONSTANT c_clip : SIGNED(c_out_w-1 DOWNTO 0) := SIGNED('0' & c_slv1(c_out_w-2 DOWNTO 0)); -- = 2**(c_out_w-1)-1 + VARIABLE v_in : SIGNED(c_in_w-1 DOWNTO 0); + VARIABLE v_out : SIGNED(c_out_w-1 DOWNTO 0); + BEGIN + v_in := SIGNED(vec); + IF n > 0 THEN + IF clip = TRUE AND v_in > c_max THEN + v_out := c_clip; -- Round clip to maximum positive to avoid wrap to negative + ELSE + IF vec(vec'HIGH)='0' THEN + v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half + 0, n), c_out_w); -- Round up for positive + ELSE + v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half - 1, n), c_out_w); -- Round down for negative + END IF; + END IF; + ELSE + v_out := RESIZE_NUM(v_in, c_out_w); -- NOP + END IF; + RETURN STD_LOGIC_VECTOR(v_out); + END; + + FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN s_round(vec, n, FALSE); -- no round clip + END; + + -- An alternative is to always round up, also for negative numbers (i.e. s_round_up = u_round). + FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN u_round(vec, n, clip); + END; + + FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN u_round(vec, n, FALSE); -- no round clip + END; + + -- Unsigned numbers are round up (almost same as s_round, but without the else on negative vec) + FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN ) RETURN STD_LOGIC_VECTOR IS + -- Use UNSIGNED to avoid NATURAL (32 bit range) overflow error + CONSTANT c_in_w : NATURAL := vec'LENGTH; + CONSTANT c_out_w : NATURAL := vec'LENGTH - n; + CONSTANT c_one : UNSIGNED(c_in_w-1 DOWNTO 0) := TO_UNSIGNED(1, c_in_w); + CONSTANT c_half : UNSIGNED(c_in_w-1 DOWNTO 0) := SHIFT_LEFT(c_one, n-1); -- = 2**(n-1) + CONSTANT c_max : UNSIGNED(c_in_w-1 DOWNTO 0) := UNSIGNED(c_slv1(c_in_w-1 DOWNTO 0)) - c_half; -- = 2**c_in_w-1 - c_half + CONSTANT c_clip : UNSIGNED(c_out_w-1 DOWNTO 0) := UNSIGNED(c_slv1(c_out_w-1 DOWNTO 0)); -- = 2**c_out_w-1 + VARIABLE v_in : UNSIGNED(c_in_w-1 DOWNTO 0); + VARIABLE v_out : UNSIGNED(c_out_w-1 DOWNTO 0); + BEGIN + v_in := UNSIGNED(vec); + IF n > 0 THEN + IF clip = TRUE AND v_in > c_max THEN + v_out := c_clip; -- Round clip to +max to avoid wrap to 0 + ELSE + v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half, n), c_out_w); -- Round up + END IF; + ELSE + v_out := RESIZE_NUM(v_in, c_out_w); -- NOP + END IF; + RETURN STD_LOGIC_VECTOR(v_out); + END; + + FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN u_round(vec, n, FALSE); -- no round clip + END; + + + FUNCTION hton(a : STD_LOGIC_VECTOR; w, sz : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_a : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0) := a; -- map a to range [h:0] + VARIABLE v_b : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0) := a; -- default b = a + VARIABLE vL : NATURAL; + VARIABLE vK : NATURAL; + BEGIN + -- Note: + -- . if sz = 1 then v_b = v_a + -- . if a'LENGTH > sz*w then v_b(a'LENGTH:sz*w) = v_a(a'LENGTH:sz*w) + FOR vL IN 0 TO sz-1 LOOP + vK := sz-1 - vL; + v_b((vL+1)*w-1 DOWNTO vL*w) := v_a((vK+1)*w-1 DOWNTO vK*w); + END LOOP; + RETURN v_b; + END FUNCTION; + + FUNCTION hton(a : STD_LOGIC_VECTOR; sz : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN hton(a, c_byte_w, sz); -- symbol width w = c_byte_w = 8 + END FUNCTION; + + FUNCTION hton(a : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_sz : NATURAL := a'LENGTH/ c_byte_w; + BEGIN + RETURN hton(a, c_byte_w, c_sz); -- symbol width w = c_byte_w = 8 + END FUNCTION; + + FUNCTION ntoh(a : STD_LOGIC_VECTOR; sz : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN hton(a, sz); -- i.e. ntoh() = hton() + END FUNCTION; + + FUNCTION ntoh(a : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN hton(a); -- i.e. ntoh() = hton() + END FUNCTION; + + FUNCTION flip(a : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_a : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0) := a; + VARIABLE v_b : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0); + BEGIN + FOR I IN v_a'RANGE LOOP + v_b(a'LENGTH-1-I) := v_a(I); + END LOOP; + RETURN v_b; + END; + + FUNCTION flip(a, w : NATURAL) RETURN NATURAL IS + BEGIN + RETURN TO_UINT(flip(TO_UVEC(a, w))); + END; + + FUNCTION flip(a : t_slv_32_arr) RETURN t_slv_32_arr IS + VARIABLE v_a : t_slv_32_arr(a'LENGTH-1 DOWNTO 0) := a; + VARIABLE v_b : t_slv_32_arr(a'LENGTH-1 DOWNTO 0); + BEGIN + FOR I IN v_a'RANGE LOOP + v_b(a'LENGTH-1-I) := v_a(I); + END LOOP; + RETURN v_b; + END; + + FUNCTION flip(a : t_integer_arr) RETURN t_integer_arr IS + VARIABLE v_a : t_integer_arr(a'LENGTH-1 DOWNTO 0) := a; + VARIABLE v_b : t_integer_arr(a'LENGTH-1 DOWNTO 0); + BEGIN + FOR I IN v_a'RANGE LOOP + v_b(a'LENGTH-1-I) := v_a(I); + END LOOP; + RETURN v_b; + END; + + FUNCTION flip(a : t_natural_arr) RETURN t_natural_arr IS + VARIABLE v_a : t_natural_arr(a'LENGTH-1 DOWNTO 0) := a; + VARIABLE v_b : t_natural_arr(a'LENGTH-1 DOWNTO 0); + BEGIN + FOR I IN v_a'RANGE LOOP + v_b(a'LENGTH-1-I) := v_a(I); + END LOOP; + RETURN v_b; + END; + + FUNCTION flip(a : t_nat_natural_arr) RETURN t_nat_natural_arr IS + VARIABLE v_a : t_nat_natural_arr(a'LENGTH-1 DOWNTO 0) := a; + VARIABLE v_b : t_nat_natural_arr(a'LENGTH-1 DOWNTO 0); + BEGIN + FOR I IN v_a'RANGE LOOP + v_b(a'LENGTH-1-I) := v_a(I); + END LOOP; + RETURN v_b; + END; + + FUNCTION transpose(a : STD_LOGIC_VECTOR; row, col : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE vIn : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0); + VARIABLE vOut : STD_LOGIC_VECTOR(a'LENGTH-1 DOWNTO 0); + BEGIN + vIn := a; -- map input vector to h:0 range + vOut := vIn; -- default leave any unused MSbits the same + FOR J IN 0 TO row-1 LOOP + FOR I IN 0 TO col-1 LOOP + vOut(J*col + I) := vIn(I*row + J); -- transpose vector, map input index [i*row+j] to output index [j*col+i] + END LOOP; + END LOOP; + RETURN vOut; + END FUNCTION; + + FUNCTION transpose(a, row, col : NATURAL) RETURN NATURAL IS -- transpose index a = [i*row+j] to output index [j*col+i] + VARIABLE vI : NATURAL; + VARIABLE vJ : NATURAL; + BEGIN + vI := a / row; + vJ := a MOD row; + RETURN vJ * col + vI; + END; + + FUNCTION split_w(input_w: NATURAL; min_out_w: NATURAL; max_out_w: NATURAL) RETURN NATURAL IS -- Calculate input_w in multiples as close as possible to max_out_w + -- Examples: split_w(256, 8, 32) = 32; split_w(16, 8, 32) = 16; split_w(72, 8, 32) = 18; -- Input_w must be multiple of 2. + VARIABLE r: NATURAL; + BEGIN + r := input_w; + FOR i IN 1 TO ceil_log2(input_w) LOOP -- Useless to divide the number beyond this + IF r <= max_out_w AND r >= min_out_w THEN + RETURN r; + ELSIF i = ceil_log2(input_w) THEN -- last iteration + RETURN 0; -- Indicates wrong values were used + END IF; + r := r / 2; + END LOOP; + END; + + FUNCTION pad(str: STRING; width: NATURAL; pad_char: CHARACTER) RETURN STRING IS + VARIABLE v_str : STRING(1 TO width) := (OTHERS => pad_char); + BEGIN + v_str(width-str'LENGTH+1 TO width) := str; + RETURN v_str; + END; + + FUNCTION slice_up(str: STRING; width: NATURAL; i: NATURAL) RETURN STRING IS + BEGIN + RETURN str(i*width+1 TO (i+1)*width); + END; + + -- If the input value is not a multiple of the desired width, the return value is padded with + -- the passed pad value. E.g. if input='10' and desired width is 4, return value is '0010'. + FUNCTION slice_up(str: STRING; width: NATURAL; i: NATURAL; pad_char: CHARACTER) RETURN STRING IS + VARIABLE padded_str : STRING(1 TO width) := (OTHERS=>'0'); + BEGIN + padded_str := pad(str(i*width+1 TO (i+1)*width), width, '0'); + RETURN padded_str; + END; + + FUNCTION slice_dn(str: STRING; width: NATURAL; i: NATURAL) RETURN STRING IS + BEGIN + RETURN str((i+1)*width-1 DOWNTO i*width); + END; + + + FUNCTION nat_arr_to_concat_slv(nat_arr: t_natural_arr; nof_elements: NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_concat_slv : STD_LOGIC_VECTOR(nof_elements*32-1 DOWNTO 0) := (OTHERS=>'0'); + BEGIN + FOR i IN 0 TO nof_elements-1 LOOP + v_concat_slv(i*32+32-1 DOWNTO i*32) := TO_UVEC(nat_arr(i), 32); + END LOOP; + RETURN v_concat_slv; + END; + + + ------------------------------------------------------------------------------ + -- common_fifo_* + ------------------------------------------------------------------------------ + + PROCEDURE proc_common_fifo_asserts (CONSTANT c_fifo_name : IN STRING; + CONSTANT c_note_is_ful : IN BOOLEAN; + CONSTANT c_fail_rd_emp : IN BOOLEAN; + SIGNAL wr_rst : IN STD_LOGIC; + SIGNAL wr_clk : IN STD_LOGIC; + SIGNAL wr_full : IN STD_LOGIC; + SIGNAL wr_en : IN STD_LOGIC; + SIGNAL rd_clk : IN STD_LOGIC; + SIGNAL rd_empty : IN STD_LOGIC; + SIGNAL rd_en : IN STD_LOGIC) IS + BEGIN + -- c_fail_rd_emp : when TRUE report FAILURE when read from an empty FIFO, important when FIFO rd_val is not used + -- c_note_is_ful : when TRUE report NOTE when FIFO goes full, to note that operation is on the limit + -- FIFO overflow is always reported as FAILURE + + -- The FIFO wr_full goes high at reset to indicate that it can not be written and it goes low a few cycles after reset. + -- Therefore only check on wr_full going high when wr_rst='0'. + + --synthesis translate_off + ASSERT NOT(c_fail_rd_emp=TRUE AND rising_edge(rd_clk) AND rd_empty='1' AND rd_en='1') REPORT c_fifo_name & " : read from empty fifo occurred!" SEVERITY FAILURE; + ASSERT NOT(c_note_is_ful=TRUE AND rising_edge(wr_full) AND wr_rst='0') REPORT c_fifo_name & " : fifo is full now" SEVERITY NOTE; + ASSERT NOT( rising_edge(wr_clk) AND wr_full='1' AND wr_en='1') REPORT c_fifo_name & " : fifo overflow occurred!" SEVERITY FAILURE; + --synthesis translate_on + END PROCEDURE proc_common_fifo_asserts; + + + ------------------------------------------------------------------------------ + -- common_fanout_tree + ------------------------------------------------------------------------------ + + FUNCTION func_common_fanout_tree_pipelining(c_nof_stages, c_nof_output_per_cell, c_nof_output : NATURAL; + c_cell_pipeline_factor_arr, c_cell_pipeline_arr : t_natural_arr) RETURN t_natural_arr IS + CONSTANT k_cell_pipeline_factor_arr : t_natural_arr(c_nof_stages-1 DOWNTO 0) := c_cell_pipeline_factor_arr; + CONSTANT k_cell_pipeline_arr : t_natural_arr(c_nof_output_per_cell-1 DOWNTO 0) := c_cell_pipeline_arr; + VARIABLE v_stage_pipeline_arr : t_natural_arr(c_nof_output-1 DOWNTO 0) := (OTHERS=>0); + VARIABLE v_prev_stage_pipeline_arr : t_natural_arr(c_nof_output-1 DOWNTO 0) := (OTHERS=>0); + BEGIN + loop_stage : FOR j IN 0 TO c_nof_stages-1 LOOP + v_prev_stage_pipeline_arr := v_stage_pipeline_arr; + loop_cell : FOR i IN 0 TO c_nof_output_per_cell**j-1 LOOP + v_stage_pipeline_arr((i+1)*c_nof_output_per_cell-1 DOWNTO i*c_nof_output_per_cell) := v_prev_stage_pipeline_arr(i) + (k_cell_pipeline_factor_arr(j) * k_cell_pipeline_arr); + END LOOP; + END LOOP; + RETURN v_stage_pipeline_arr; + END FUNCTION func_common_fanout_tree_pipelining; + + + ------------------------------------------------------------------------------ + -- common_reorder_symbol + ------------------------------------------------------------------------------ + + -- Determine whether the stage I and row J index refer to any (active or redundant) 2-input reorder cell instantiation + FUNCTION func_common_reorder2_is_there(I, J : NATURAL) RETURN BOOLEAN IS + VARIABLE v_odd : BOOLEAN; + VARIABLE v_even : BOOLEAN; + BEGIN + v_odd := (I MOD 2 = 1) AND (J MOD 2 = 1); -- for odd stage at each odd row + v_even := (I MOD 2 = 0) AND (J MOD 2 = 0); -- for even stage at each even row + RETURN v_odd OR v_even; + END func_common_reorder2_is_there; + + -- Determine whether the stage I and row J index refer to an active 2-input reorder cell instantiation in a reorder network with N stages + FUNCTION func_common_reorder2_is_active(I, J, N : NATURAL) RETURN BOOLEAN IS + VARIABLE v_inst : BOOLEAN; + VARIABLE v_act : BOOLEAN; + BEGIN + v_inst := func_common_reorder2_is_there(I, J); + v_act := (I > 0) AND (I <= N) AND (J > 0) AND (J < N); + RETURN v_inst AND v_act; + END func_common_reorder2_is_active; + + -- Get the index K in the select setting array for the reorder2 cell on stage I and row J in a reorder network with N stages + FUNCTION func_common_reorder2_get_select_index(I, J, N : NATURAL) RETURN INTEGER IS + CONSTANT c_nof_reorder2_per_odd_stage : NATURAL := N/2; + CONSTANT c_nof_reorder2_per_even_stage : NATURAL := (N-1)/2; + VARIABLE v_nof_odd_stages : NATURAL; + VARIABLE v_nof_even_stages : NATURAL; + VARIABLE v_offset : NATURAL; + VARIABLE v_K : INTEGER; + BEGIN + -- for I, J that do not refer to an reorder cell instance for -1 as dummy return value. + -- for the redundant two port reorder cells at the border rows for -1 to indicate that the cell should pass on the input. + v_K := -1; + IF func_common_reorder2_is_active(I, J, N) THEN + -- for the active two port reorder cells use the setting at index v_K from the select setting array + v_nof_odd_stages := I/2; + v_nof_even_stages := (I-1)/2; + v_offset := (J-1)/2; -- suits both odd stage and even stage + v_K := v_nof_odd_stages * c_nof_reorder2_per_odd_stage + v_nof_even_stages * c_nof_reorder2_per_even_stage + v_offset; + END IF; + RETURN v_K; + END func_common_reorder2_get_select_index; + + -- Get the select setting for the reorder2 cell on stage I and row J in a reorder network with N stages + FUNCTION func_common_reorder2_get_select(I, J, N : NATURAL; select_arr : t_natural_arr) RETURN NATURAL IS + CONSTANT c_nof_select : NATURAL := select_arr'LENGTH; + CONSTANT c_select_arr : t_natural_arr(c_nof_select-1 DOWNTO 0) := select_arr; -- force range downto 0 + VARIABLE v_sel : NATURAL; + VARIABLE v_K : INTEGER; + BEGIN + v_sel := 0; + v_K := func_common_reorder2_get_select_index(I, J, N); + IF v_K>=0 THEN + v_sel := c_select_arr(v_K); + END IF; + RETURN v_sel; + END func_common_reorder2_get_select; + + -- Determine the inverse of a reorder network by using two reorder networks in series + FUNCTION func_common_reorder2_inverse_select(N : NATURAL; select_arr : t_natural_arr) RETURN t_natural_arr IS + CONSTANT c_nof_select : NATURAL := select_arr'LENGTH; + CONSTANT c_select_arr : t_natural_arr(c_nof_select-1 DOWNTO 0) := select_arr; -- force range downto 0 + VARIABLE v_sel : NATURAL; + VARIABLE v_Ki : INTEGER; + VARIABLE v_Ii : NATURAL; + VARIABLE v_inverse_arr : t_natural_arr(2*c_nof_select-1 DOWNTO 0) := (OTHERS=>0); -- default set identity for the reorder2 cells in both reorder instances + BEGIN + -- the inverse select consists of inverse_in reorder and inverse_out reorder in series + IF N MOD 2 = 1 THEN + -- N is odd so only need to fill in the inverse_in reorder, the inverse_out reorder remains at default pass on + FOR I IN 1 TO N LOOP + FOR J IN 0 TO N-1 LOOP + -- get the DUT setting + v_sel := func_common_reorder2_get_select(I, J, N, c_select_arr); + -- map DUT I to inverse v_Ii stage index and determine the index for the inverse setting + v_Ii := 1+N-I; + v_Ki := func_common_reorder2_get_select_index(v_Ii, J, N); + IF v_Ki>=0 THEN + v_inverse_arr(v_Ki) := v_sel; + END IF; + END LOOP; + END LOOP; + ELSE + -- N is even so only use stage 1 of the inverse_out reorder, the other stages remain at default pass on + FOR K IN 0 TO N/2-1 LOOP + v_Ki := c_nof_select + K; -- stage 1 of the inverse_out reorder + v_inverse_arr(v_Ki) := c_select_arr(K); + END LOOP; + -- N is even so leave stage 1 of the inverse_in reorder at default pass on, and do inverse the other stages + FOR I IN 2 TO N LOOP + FOR J IN 0 TO N-1 LOOP + -- get the DUT setting + v_sel := func_common_reorder2_get_select(I, J, N, c_select_arr); + -- map DUT I to inverse v_Ii stage index and determine the index for the inverse setting + v_Ii := 2+N-I; + v_Ki := func_common_reorder2_get_select_index(v_Ii, J, N); + IF v_Ki>=0 THEN + v_inverse_arr(v_Ki) := v_sel; + END IF; + END LOOP; + END LOOP; + END IF; + RETURN v_inverse_arr; + END func_common_reorder2_inverse_select; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate faster sample SCLK from digital DCLK for sim only + -- Description: + -- The SCLK kan be used to serialize Pfactor >= 1 symbols per word and then + -- view them in a scope component that is use internally in the design. + -- The scope component is only instantiated for simulation, to view the + -- serialized symbols, typically with decimal radix and analogue format. + -- The scope component will not be synthesized, because the SCLK can not + -- be synthesized. + -- + -- Pfactor = 4 + -- _______ _______ _______ _______ + -- DCLK ___| |_______| |_______| |_______| |_______ + -- ___________________ _ _ _ _ _ _ _ _ _ _ _ _ + -- SCLK |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| + -- + -- The rising edges of SCLK occur after the rising edge of DCLK, to ensure + -- that they all apply to the same wide data word that was clocked by the + -- rising edge of the DCLK. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_dclk_generate_sclk(CONSTANT Pfactor : IN POSITIVE; + SIGNAL dclk : IN STD_LOGIC; + SIGNAL sclk : INOUT STD_LOGIC) IS + VARIABLE v_dperiod : TIME; + VARIABLE v_speriod : TIME; + BEGIN + SCLK <= '1'; + -- Measure DCLK period + WAIT UNTIL rising_edge(DCLK); + v_dperiod := NOW; + WAIT UNTIL rising_edge(DCLK); + v_dperiod := NOW - v_dperiod; + v_speriod := v_dperiod / Pfactor; + -- Generate Pfactor SCLK periods per DCLK period + WHILE TRUE LOOP + -- Realign at every DCLK + WAIT UNTIL rising_edge(DCLK); + -- Create Pfactor SCLK periods within this DCLK period + SCLK <= '0'; + IF Pfactor>1 THEN + FOR I IN 0 TO 2*Pfactor-1-2 LOOP + WAIT FOR v_speriod/2; + SCLK <= NOT SCLK; + END LOOP; + END IF; + WAIT FOR v_speriod/2; + SCLK <= '1'; + -- Wait for next DCLK + END LOOP; + WAIT; + END proc_common_dclk_generate_sclk; + +END common_pkg; + diff --git a/cores/base/common/common_pkg/common_str_pkg.vhd b/cores/base/common/common_pkg/common_str_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..54e51b4d03a8384238a94bb0490ad9675fd6d807 --- /dev/null +++ b/cores/base/common/common_pkg/common_str_pkg.vhd @@ -0,0 +1,282 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 +-- 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; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE STD.TEXTIO.ALL; +USE IEEE.STD_LOGIC_TEXTIO.ALL; +USE work.common_pkg.ALL; + +PACKAGE common_str_pkg IS + + TYPE t_str_4_arr IS ARRAY (INTEGER RANGE <>) OF STRING(1 TO 4); + + FUNCTION nof_digits(number: NATURAL) RETURN NATURAL; + FUNCTION nof_digits_int(number: INTEGER) RETURN NATURAL; + + FUNCTION time_to_str(in_time : TIME) RETURN STRING; + FUNCTION str_to_time(in_str : STRING) RETURN TIME; + FUNCTION slv_to_str(slv : STD_LOGIC_VECTOR) RETURN STRING; + FUNCTION str_to_hex(str : STRING) RETURN STRING; + FUNCTION slv_to_hex(slv : STD_LOGIC_VECTOR) RETURN STRING; + FUNCTION hex_to_slv(str : STRING) RETURN STD_LOGIC_VECTOR; + + Function hex_nibble_to_slv(c: character) return std_logic_vector; + + FUNCTION int_to_str(int: INTEGER) RETURN STRING; + FUNCTION real_to_str(re: REAL; width : INTEGER; digits : INTEGER) RETURN STRING; + + PROCEDURE print_str(str : STRING); + + FUNCTION str_to_ascii_integer_arr(s: STRING) RETURN t_integer_arr; + FUNCTION str_to_ascii_slv_8_arr( s: STRING) RETURN t_slv_8_arr; + FUNCTION str_to_ascii_slv_32_arr( s: STRING) RETURN t_slv_32_arr; + FUNCTION str_to_ascii_slv_32_arr( s: STRING; arr_size : NATURAL) RETURN t_slv_32_arr; + +END common_str_pkg; + +PACKAGE BODY common_str_pkg IS + + FUNCTION nof_digits(number: NATURAL) RETURN NATURAL IS + -- Returns number of digits in a natural number. Only used in string processing, so defined here. + -- log10(0) is not allowed so: + -- . nof_digits(0) = 1 + -- We're adding 1 so: + -- . nof_digits(1) = 1 + -- . nof_digits(9) = 1 + -- . nof_digits(10) = 2 + BEGIN + IF number>0 THEN + RETURN floor_log10(number)+1; + ELSE + RETURN 1; + END IF; + END; + + FUNCTION nof_digits_int(number: INTEGER) RETURN NATURAL IS + -- Returns number of digits in a natural number. Only used in string processing, so defined here. + -- log10(0) is not allowed so: + -- . nof_digits(0) = 1 + -- We're adding 1 so: + -- . nof_digits(1) = 1 + -- . nof_digits(9) = 1 + -- . nof_digits(10) = 2 + -- . nof_digits(1) = 2 + BEGIN + IF number=0 THEN + RETURN 1; + ELSE + IF number > 0 THEN + RETURN floor_log10(number)+1; + ELSE + RETURN floor_log10(-1*number)+2; + END IF; + END IF; + END; + + FUNCTION time_to_str(in_time : TIME) RETURN STRING IS + CONSTANT c_max_len_time : NATURAL := 20; + VARIABLE v_line : LINE; + VARIABLE v_str : STRING(1 TO c_max_len_time):= (OTHERS => ' '); + BEGIN + write(v_line, in_time); + v_str(v_line.ALL'RANGE) := v_line.ALL; + deallocate(v_line); + RETURN v_str; + END; + + FUNCTION str_to_time(in_str : STRING) RETURN TIME IS + BEGIN + RETURN TIME'VALUE(in_str); + END; + + FUNCTION slv_to_str(slv : STD_LOGIC_VECTOR) RETURN STRING IS + VARIABLE v_line : LINE; + VARIABLE v_str : STRING(1 TO slv'LENGTH) := (OTHERS => ' '); + BEGIN + write(v_line, slv); + v_str(v_line.ALL'RANGE) := v_line.ALL; + deallocate(v_line); + RETURN v_str; + END; + + FUNCTION str_to_hex(str : STRING) RETURN STRING IS + CONSTANT c_nof_nibbles : NATURAL := ceil_div(str'LENGTH, c_nibble_w); + VARIABLE v_nibble_arr : t_str_4_arr(0 TO c_nof_nibbles-1) := (OTHERS=>(OTHERS=>'0')); + VARIABLE v_hex : STRING(1 TO c_nof_nibbles) := (OTHERS => '0'); + BEGIN + FOR i IN 0 TO v_hex'RIGHT-1 LOOP + v_nibble_arr(i) := slice_up(str, c_nibble_w, i, '0'); + + CASE v_nibble_arr(i) IS + WHEN "0000" => v_hex(i+1) := '0'; + WHEN "0001" => v_hex(i+1) := '1'; + WHEN "0010" => v_hex(i+1) := '2'; + WHEN "0011" => v_hex(i+1) := '3'; + WHEN "0100" => v_hex(i+1) := '4'; + WHEN "0101" => v_hex(i+1) := '5'; + WHEN "0110" => v_hex(i+1) := '6'; + WHEN "0111" => v_hex(i+1) := '7'; + WHEN "1000" => v_hex(i+1) := '8'; + WHEN "1001" => v_hex(i+1) := '9'; + WHEN "1010" => v_hex(i+1) := 'A'; + WHEN "1011" => v_hex(i+1) := 'B'; + WHEN "1100" => v_hex(i+1) := 'C'; + WHEN "1101" => v_hex(i+1) := 'D'; + WHEN "1110" => v_hex(i+1) := 'E'; + WHEN "1111" => v_hex(i+1) := 'F'; + WHEN OTHERS => v_hex(i+1) := 'X'; + END CASE; + END LOOP; + RETURN v_hex; + END; + + FUNCTION slv_to_hex(slv :STD_LOGIC_VECTOR) RETURN STRING IS + BEGIN + RETURN str_to_hex(slv_to_str(slv)); + END; + + FUNCTION hex_to_slv(str: STRING) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_length : NATURAL := str'LENGTH; + VARIABLE v_str : STRING(1 TO str'LENGTH) := str; -- Keep local copy of str to prevent range mismatch + VARIABLE v_result : STD_LOGIC_VECTOR(c_length * 4 - 1 DOWNTO 0); + BEGIN + FOR i IN c_length DOWNTO 1 LOOP + v_result(3 +(c_length - i)*4 DOWNTO (c_length-i)*4) := hex_nibble_to_slv(v_str(i)); + END LOOP; + RETURN v_result; + END; + + FUNCTION hex_nibble_to_slv(c: CHARACTER) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_result : STD_LOGIC_VECTOR(3 DOWNTO 0); + BEGIN + CASE c IS + WHEN '0' => v_result := "0000"; + WHEN '1' => v_result := "0001"; + WHEN '2' => v_result := "0010"; + WHEN '3' => v_result := "0011"; + WHEN '4' => v_result := "0100"; + WHEN '5' => v_result := "0101"; + WHEN '6' => v_result := "0110"; + WHEN '7' => v_result := "0111"; + WHEN '8' => v_result := "1000"; + WHEN '9' => v_result := "1001"; + WHEN 'A' => v_result := "1010"; + WHEN 'B' => v_result := "1011"; + WHEN 'C' => v_result := "1100"; + WHEN 'D' => v_result := "1101"; + WHEN 'E' => v_result := "1110"; + WHEN 'F' => v_result := "1111"; + WHEN 'a' => v_result := "1010"; + WHEN 'b' => v_result := "1011"; + WHEN 'c' => v_result := "1100"; + WHEN 'd' => v_result := "1101"; + WHEN 'e' => v_result := "1110"; + WHEN 'f' => v_result := "1111"; + WHEN 'x' => v_result := "XXXX"; + WHEN 'X' => v_result := "XXXX"; + WHEN 'z' => v_result := "ZZZZ"; + WHEN 'Z' => v_result := "ZZZZ"; + + WHEN OTHERS => v_result := "0000"; + END CASE; + RETURN v_result; + END hex_nibble_to_slv; + + FUNCTION int_to_str(int: INTEGER) RETURN STRING IS +-- CONSTANT c_max_len_int : NATURAL := 20; + VARIABLE v_line: LINE; + VARIABLE v_str: STRING(1 TO nof_digits_int(int)):= (OTHERS => ' '); + BEGIN + STD.TEXTIO.WRITE(v_line, int); + v_str(v_line.ALL'RANGE) := v_line.ALL; + deallocate(v_line); + RETURN v_str; + END; + + FUNCTION real_to_str(re: REAL; width : INTEGER; digits : INTEGER) RETURN STRING IS + VARIABLE v_line: LINE; + VARIABLE v_str: STRING(1 TO width):= (OTHERS => ' '); + BEGIN + STD.TEXTIO.WRITE(v_line, re, right, width, digits); + v_str(v_line.ALL'RANGE) := v_line.ALL; + deallocate(v_line); + RETURN v_str; + END; + + PROCEDURE print_str(str: STRING) IS + VARIABLE v_line: LINE; + BEGIN + write(v_line, str); + writeline(output, v_line); + deallocate(v_line); + END; + + FUNCTION str_to_ascii_integer_arr(s: STRING) RETURN t_integer_arr IS + VARIABLE r: t_integer_arr(0 TO s'RIGHT-1); + BEGIN + FOR i IN s'RANGE LOOP + r(i-1) := CHARACTER'POS(s(i)); + END LOOP; + RETURN r; + END; + + FUNCTION str_to_ascii_slv_8_arr(s: STRING) RETURN t_slv_8_arr IS + VARIABLE r: t_slv_8_arr(0 TO s'RIGHT-1); + BEGIN + FOR i IN s'RANGE LOOP + r(i-1) := TO_UVEC(str_to_ascii_integer_arr(s)(i-1), 8); + END LOOP; + RETURN r; + END; + + -- Returns minimum array size required to fit the string + FUNCTION str_to_ascii_slv_32_arr(s: STRING) RETURN t_slv_32_arr IS + CONSTANT c_slv_8: t_slv_8_arr(0 TO s'RIGHT-1) := str_to_ascii_slv_8_arr(s); + CONSTANT c_bytes_per_word : NATURAL := 4; + -- Initialize all elements to (OTHERS=>'0') so any unused bytes become a NULL character + VARIABLE r: t_slv_32_arr(0 TO ceil_div(s'RIGHT * c_byte_w, c_word_w)-1) := (OTHERS=>(OTHERS=>'0')); + BEGIN + FOR word IN r'RANGE LOOP --0, 1 + FOR byte IN 0 TO c_bytes_per_word-1 LOOP -- 0,1,2,3 + IF byte+c_bytes_per_word*word<=c_slv_8'RIGHT THEN + r(word)(byte*c_byte_w+c_byte_w-1 DOWNTO byte*c_byte_w) := c_slv_8(byte+c_bytes_per_word*word); + END IF; + END LOOP; + END LOOP; + + RETURN r; + END; + + -- Overloaded version to match array size to arr_size + FUNCTION str_to_ascii_slv_32_arr(s: STRING; arr_size: NATURAL) RETURN t_slv_32_arr IS + CONSTANT slv_32: t_slv_32_arr(0 TO ceil_div(s'RIGHT * c_byte_w, c_word_w)-1) := str_to_ascii_slv_32_arr(s); + VARIABLE r: t_slv_32_arr(0 TO arr_size-1) := (OTHERS=>(OTHERS=>'0')); + BEGIN + FOR word IN slv_32'RANGE LOOP + r(word) := slv_32(word); + END LOOP; + RETURN r; + END; + +END common_str_pkg; + diff --git a/cores/base/common/common_pkg/hdllib.cfg b/cores/base/common/common_pkg/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f6768a9929395c39a4348540ac0a859cafb71f78 --- /dev/null +++ b/cores/base/common/common_pkg/hdllib.cfg @@ -0,0 +1,21 @@ +hdl_lib_name = common_pkg +hdl_library_clause_name = common_pkg_lib +hdl_lib_uses_synth = +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + common_pkg.vhd + common_str_pkg.vhd + common_lfsr_sequences_pkg.vhd + tb_common_pkg.vhd + +test_bench_files = + +regression_test_vhdl = + +[modelsim_project_file] +modelsim_copy_files = + +[quartus_project_file] + diff --git a/cores/base/common/common_pkg/tb_common_pkg.vhd b/cores/base/common/common_pkg/tb_common_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..7e87a3ffb399f71d02e7349b757b0ba4ef972091 --- /dev/null +++ b/cores/base/common/common_pkg/tb_common_pkg.vhd @@ -0,0 +1,1358 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2011 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE std.textio.ALL; -- for boolean, integer file IO +USE IEEE.std_logic_textio.ALL; -- for std_logic, std_logic_vector file IO +USE work.common_pkg.ALL; + + +PACKAGE tb_common_pkg IS + + PROCEDURE proc_common_wait_some_cycles(SIGNAL clk : IN STD_LOGIC; + c_nof_cycles : IN NATURAL); + + PROCEDURE proc_common_wait_some_cycles(SIGNAL clk : IN STD_LOGIC; + c_nof_cycles : IN REAL); + + PROCEDURE proc_common_wait_some_cycles(SIGNAL clk_in : IN STD_LOGIC; + SIGNAL clk_out : IN STD_LOGIC; + c_nof_cycles : IN NATURAL); + + PROCEDURE proc_common_wait_some_pulses(SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : IN STD_LOGIC; + c_nof_pulses : IN NATURAL); + + PROCEDURE proc_common_wait_until_evt(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_evt(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN INTEGER); + + PROCEDURE proc_common_wait_until_evt(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_high(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_high(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_low(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_low(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_hi_lo(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_hi_lo(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_lo_hi(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_lo_hi(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC); + + PROCEDURE proc_common_wait_until_value(CONSTANT c_value : IN INTEGER; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN INTEGER); + + PROCEDURE proc_common_wait_until_value(CONSTANT c_value : IN INTEGER; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC_VECTOR); + + PROCEDURE proc_common_wait_until_value(CONSTANT c_timeout : IN NATURAL; + CONSTANT c_value : IN INTEGER; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC_VECTOR); + + -- Wait until absolute simulation time NOW = c_time + PROCEDURE proc_common_wait_until_time(SIGNAL clk : IN STD_LOGIC; + CONSTANT c_time : IN TIME); + + -- Exit simulation on timeout failure + PROCEDURE proc_common_timeout_failure(CONSTANT c_timeout : IN TIME; + SIGNAL tb_end : IN STD_LOGIC); + + + -- Stop simulation using severity FAILURE when g_tb_end=TRUE, else for use in multi tb report as severity NOTE + PROCEDURE proc_common_stop_simulation(SIGNAL tb_end : IN STD_LOGIC); + + PROCEDURE proc_common_stop_simulation(CONSTANT g_tb_end : IN BOOLEAN; + CONSTANT g_latency : IN NATURAL; -- latency between tb_done and tb_)end + SIGNAL clk : IN STD_LOGIC; + SIGNAL tb_done : IN STD_LOGIC; + SIGNAL tb_end : OUT STD_LOGIC); + + PROCEDURE proc_common_stop_simulation(CONSTANT g_tb_end : IN BOOLEAN; + SIGNAL clk : IN STD_LOGIC; + SIGNAL tb_done : IN STD_LOGIC; + SIGNAL tb_end : OUT STD_LOGIC); + + -- Handle stream ready signal, only support ready latency c_rl = 0 or 1. + PROCEDURE proc_common_ready_latency(CONSTANT c_rl : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- when '1' then active output when ready + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_valid : OUT STD_LOGIC); + + + -- Generate a single active, inactive pulse + PROCEDURE proc_common_gen_pulse(CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC); + + -- Pulse forever after rst was released + PROCEDURE proc_common_gen_pulse(CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC); + + -- Generate a single '1', '0' pulse + PROCEDURE proc_common_gen_pulse(SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC); + + -- Generate a periodic pulse with arbitrary duty cycle + PROCEDURE proc_common_gen_duty_pulse(CONSTANT c_delay : IN NATURAL; -- delay pulse for nof_clk after enable + CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- once enabled, the pulse remains enabled + SIGNAL pulse : OUT STD_LOGIC); + + PROCEDURE proc_common_gen_duty_pulse(CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- once enabled, the pulse remains enabled + SIGNAL pulse : OUT STD_LOGIC); + + -- Generate counter data with valid and arbitrary increment or fixed increment=1 + PROCEDURE proc_common_gen_data(CONSTANT c_rl : IN NATURAL; -- 0, 1 are supported by proc_common_ready_latency() + CONSTANT c_init : IN INTEGER; + CONSTANT c_incr : IN INTEGER; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- when '0' then no valid output even when ready='1' + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_data : OUT STD_LOGIC_VECTOR; + SIGNAL out_valid : OUT STD_LOGIC); + + PROCEDURE proc_common_gen_data(CONSTANT c_rl : IN NATURAL; -- 0, 1 are supported by proc_common_ready_latency() + CONSTANT c_init : IN INTEGER; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- when '0' then no valid output even when ready='1' + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_data : OUT STD_LOGIC_VECTOR; + SIGNAL out_valid : OUT STD_LOGIC); + + -- Generate frame control + PROCEDURE proc_common_sop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC; + SIGNAL in_sop : OUT STD_LOGIC); + + PROCEDURE proc_common_eop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC; + SIGNAL in_eop : OUT STD_LOGIC); + + PROCEDURE proc_common_val(CONSTANT c_val_len : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC); + + PROCEDURE proc_common_val_duty(CONSTANT c_hi_len : IN NATURAL; + CONSTANT c_lo_len : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC); + + PROCEDURE proc_common_eop_flush(CONSTANT c_flush_len : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC; + SIGNAL in_eop : OUT STD_LOGIC); + + -- Verify the DUT output incrementing data, only support ready latency c_rl = 0 or 1. + PROCEDURE proc_common_verify_data(CONSTANT c_rl : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_valid : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify the DUT output valid for ready latency, only support ready latency c_rl = 0 or 1. + PROCEDURE proc_common_verify_valid(CONSTANT c_rl : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL prev_ready : INOUT STD_LOGIC; + SIGNAL out_valid : IN STD_LOGIC); + + -- Verify the DUT input to output latency for SL ctrl signals + PROCEDURE proc_common_verify_latency(CONSTANT c_str : IN STRING; -- e.g. "valid", "sop", "eop" + CONSTANT c_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL in_ctrl : IN STD_LOGIC; + SIGNAL pipe_ctrl_vec : INOUT STD_LOGIC_VECTOR; -- range [0:c_latency] + SIGNAL out_ctrl : IN STD_LOGIC); + + -- Verify the DUT input to output latency for SLV data signals + PROCEDURE proc_common_verify_latency(CONSTANT c_str : IN STRING; -- e.g. "data" + CONSTANT c_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL in_data : IN STD_LOGIC_VECTOR; + SIGNAL pipe_data_vec : INOUT STD_LOGIC_VECTOR; -- range [0:(1 + c_latency)*c_data_w-1] + SIGNAL out_data : IN STD_LOGIC_VECTOR); + + -- Verify the expected value, e.g. to check that a test has ran at all + PROCEDURE proc_common_verify_value(CONSTANT mode : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC_VECTOR; + SIGNAL res : IN STD_LOGIC_VECTOR); + -- open, read line, close file + PROCEDURE proc_common_open_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + file_name : IN STRING; + file_mode : IN FILE_OPEN_KIND); + + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + read_value_0 : OUT INTEGER); + + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + read_value_0 : OUT INTEGER; + read_value_1 : OUT INTEGER); + + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + value_array : OUT t_integer_arr; + nof_reads : IN INTEGER); + + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + read_slv : OUT STD_LOGIC_VECTOR); + + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + res_string : OUT STRING); + + PROCEDURE proc_common_close_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT); + + -- read entire file + PROCEDURE proc_common_read_integer_file(file_name : IN STRING; + nof_header_lines : NATURAL; + nof_row : NATURAL; + nof_col : NATURAL; + SIGNAL return_array : OUT t_integer_arr); + + PROCEDURE proc_common_read_mif_file(file_name : IN STRING; + SIGNAL return_array : OUT t_integer_arr); + + -- Complex multiply function with conjugate option for input b + FUNCTION func_complex_multiply(in_ar, in_ai, in_br, in_bi : STD_LOGIC_VECTOR; conjugate_b : BOOLEAN; str : STRING; g_out_dat_w : NATURAL) RETURN STD_LOGIC_VECTOR; + + FUNCTION func_decstring_to_integer(in_string: STRING) RETURN INTEGER; + + FUNCTION func_hexstring_to_integer(in_string: STRING) RETURN INTEGER; + + FUNCTION func_find_char_in_string(in_string: STRING; find_char: CHARACTER) RETURN INTEGER; + + FUNCTION func_find_string_in_string(in_string: STRING; find_string: STRING) RETURN BOOLEAN; + +END tb_common_pkg; + + +PACKAGE BODY tb_common_pkg IS + + ------------------------------------------------------------------------------ + -- PROCEDURE: Wait some clock cycles + ------------------------------------------------------------------------------ + PROCEDURE proc_common_wait_some_cycles(SIGNAL clk : IN STD_LOGIC; + c_nof_cycles : IN NATURAL) IS + BEGIN + FOR I IN 0 TO c_nof_cycles-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + END proc_common_wait_some_cycles; + + PROCEDURE proc_common_wait_some_cycles(SIGNAL clk : IN STD_LOGIC; + c_nof_cycles : IN REAL) IS + BEGIN + proc_common_wait_some_cycles(clk, NATURAL(c_nof_cycles)); + END proc_common_wait_some_cycles; + + PROCEDURE proc_common_wait_some_cycles(SIGNAL clk_in : IN STD_LOGIC; + SIGNAL clk_out : IN STD_LOGIC; + c_nof_cycles : IN NATURAL) IS + BEGIN + proc_common_wait_some_cycles(clk_in, c_nof_cycles); + proc_common_wait_some_cycles(clk_out, c_nof_cycles); + END proc_common_wait_some_cycles; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Wait some pulses + ------------------------------------------------------------------------------ + PROCEDURE proc_common_wait_some_pulses(SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : IN STD_LOGIC; + c_nof_pulses : IN NATURAL) IS + BEGIN + FOR I IN 0 TO c_nof_pulses-1 LOOP + proc_common_wait_until_hi_lo(clk, pulse); + END LOOP; + END proc_common_wait_some_pulses; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Wait until the level input event + -- PROCEDURE: Wait until the level input is high + -- PROCEDURE: Wait until the level input is low + -- PROCEDURE: Wait until the input is equal to c_value + ------------------------------------------------------------------------------ + PROCEDURE proc_common_wait_until_evt(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + VARIABLE v_level : STD_LOGIC := level; + BEGIN + WAIT UNTIL rising_edge(clk); + WHILE v_level=level LOOP + v_level := level; + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_common_wait_until_evt; + + PROCEDURE proc_common_wait_until_evt(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN INTEGER) IS + VARIABLE v_level : INTEGER := level; + BEGIN + WAIT UNTIL rising_edge(clk); + WHILE v_level=level LOOP + v_level := level; + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_common_wait_until_evt; + + PROCEDURE proc_common_wait_until_evt(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + VARIABLE v_level : STD_LOGIC := level; + VARIABLE v_I : NATURAL := 0; + BEGIN + WAIT UNTIL rising_edge(clk); + WHILE v_level=level LOOP + v_level := level; + WAIT UNTIL rising_edge(clk); + v_I := v_I + 1; + IF v_I>=c_timeout-1 THEN + REPORT "COMMON : level evt timeout" SEVERITY ERROR; + EXIT; + END IF; + END LOOP; + END proc_common_wait_until_evt; + + PROCEDURE proc_common_wait_until_high(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + IF level/='1' THEN + WAIT UNTIL rising_edge(clk) AND level='1'; + END IF; + END proc_common_wait_until_high; + + PROCEDURE proc_common_wait_until_high(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + FOR I IN 0 TO c_timeout-1 LOOP + IF level='1' THEN + EXIT; + ELSE + IF I=c_timeout-1 THEN + REPORT "COMMON : level high timeout" SEVERITY ERROR; + END IF; + WAIT UNTIL rising_edge(clk); + END IF; + END LOOP; + END proc_common_wait_until_high; + + PROCEDURE proc_common_wait_until_low(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + IF level/='0' THEN + WAIT UNTIL rising_edge(clk) AND level='0'; + END IF; + END proc_common_wait_until_low; + + PROCEDURE proc_common_wait_until_low(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + FOR I IN 0 TO c_timeout-1 LOOP + IF level='0' THEN + EXIT; + ELSE + IF I=c_timeout-1 THEN + REPORT "COMMON : level low timeout" SEVERITY ERROR; + END IF; + WAIT UNTIL rising_edge(clk); + END IF; + END LOOP; + END proc_common_wait_until_low; + + PROCEDURE proc_common_wait_until_hi_lo(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + IF level/='1' THEN + proc_common_wait_until_high(clk, level); + END IF; + proc_common_wait_until_low(clk, level); + END proc_common_wait_until_hi_lo; + + PROCEDURE proc_common_wait_until_hi_lo(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + IF level/='1' THEN + proc_common_wait_until_high(c_timeout, clk, level); + END IF; + proc_common_wait_until_low(c_timeout, clk, level); + END proc_common_wait_until_hi_lo; + + PROCEDURE proc_common_wait_until_lo_hi(SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + IF level/='0' THEN + proc_common_wait_until_low(clk, level); + END IF; + proc_common_wait_until_high(clk, level); + END proc_common_wait_until_lo_hi; + + PROCEDURE proc_common_wait_until_lo_hi(CONSTANT c_timeout : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC) IS + BEGIN + IF level/='0' THEN + proc_common_wait_until_low(c_timeout, clk, level); + END IF; + proc_common_wait_until_high(c_timeout, clk, level); + END proc_common_wait_until_lo_hi; + + PROCEDURE proc_common_wait_until_value(CONSTANT c_value : IN INTEGER; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN INTEGER) IS + BEGIN + WHILE level/=c_value LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_common_wait_until_value; + + PROCEDURE proc_common_wait_until_value(CONSTANT c_value : IN INTEGER; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC_VECTOR) IS + BEGIN + WHILE SIGNED(level)/=c_value LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_common_wait_until_value; + + PROCEDURE proc_common_wait_until_value(CONSTANT c_timeout : IN NATURAL; + CONSTANT c_value : IN INTEGER; + SIGNAL clk : IN STD_LOGIC; + SIGNAL level : IN STD_LOGIC_VECTOR) IS + BEGIN + FOR I IN 0 TO c_timeout-1 LOOP + IF SIGNED(level)=c_value THEN + EXIT; + ELSE + IF I=c_timeout-1 THEN + REPORT "COMMON : level value timeout" SEVERITY ERROR; + END IF; + WAIT UNTIL rising_edge(clk); + END IF; + END LOOP; + END proc_common_wait_until_value; + + PROCEDURE proc_common_wait_until_time(SIGNAL clk : IN STD_LOGIC; + CONSTANT c_time : IN TIME) IS + BEGIN + WHILE NOW < c_time LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END PROCEDURE; + + PROCEDURE proc_common_timeout_failure(CONSTANT c_timeout : IN TIME; + SIGNAL tb_end : IN STD_LOGIC) IS + BEGIN + WHILE tb_end='0' LOOP + ASSERT NOW < c_timeout REPORT "Test bench timeout." SEVERITY FAILURE; + WAIT FOR 1 us; + END LOOP; + END PROCEDURE; + + PROCEDURE proc_common_stop_simulation(SIGNAL tb_end : IN STD_LOGIC) IS + BEGIN + WAIT UNTIL tb_end='1'; + -- For modelsim_regression_test_vhdl.py: + -- The tb_end will stop the test verification bases on error or failure. The wait is necessary to + -- stop the simulation using failure, without causing the test to fail. + WAIT FOR 1 ns; + REPORT "Tb simulation finished." SEVERITY FAILURE; + WAIT; + END PROCEDURE; + + PROCEDURE proc_common_stop_simulation(CONSTANT g_tb_end : IN BOOLEAN; + CONSTANT g_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL tb_done : IN STD_LOGIC; + SIGNAL tb_end : OUT STD_LOGIC) IS + BEGIN + -- Wait until simulation indicates done + proc_common_wait_until_high(clk, tb_done); + + -- Wait some more cycles + proc_common_wait_some_cycles(clk, g_latency); + + -- Stop the simulation or only report NOTE + tb_end <= '1'; + -- For modelsim_regression_test_vhdl.py: + -- The tb_end will stop the test verification bases on error or failure. The wait is necessary to + -- stop the simulation using failure, without causing the test to fail. + WAIT FOR 1 ns; + IF g_tb_end=FALSE THEN + REPORT "Tb Simulation finished." SEVERITY NOTE; + ELSE + REPORT "Tb Simulation finished." SEVERITY FAILURE; + END IF; + WAIT; + END PROCEDURE; + + PROCEDURE proc_common_stop_simulation(CONSTANT g_tb_end : IN BOOLEAN; + SIGNAL clk : IN STD_LOGIC; + SIGNAL tb_done : IN STD_LOGIC; + SIGNAL tb_end : OUT STD_LOGIC) IS + BEGIN + proc_common_stop_simulation(g_tb_end, 0, clk, tb_done, tb_end); + END PROCEDURE; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Handle stream ready signal for data valid + -- . output active when ready='1' and enable='1' + -- . only support ready latency c_rl = 0 or 1 + ------------------------------------------------------------------------------ + PROCEDURE proc_common_ready_latency(CONSTANT c_rl : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_valid : OUT STD_LOGIC) IS + BEGIN + -- skip ready cycles until enable='1' + out_valid <= '0'; + WHILE enable='0' LOOP + IF c_rl=0 THEN + WAIT UNTIL rising_edge(clk); + WHILE ready /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END IF; + IF c_rl=1 THEN + WHILE ready /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + WAIT UNTIL rising_edge(clk); + END IF; + END LOOP; + -- active output when ready + IF c_rl=0 THEN + out_valid <= '1'; + WAIT UNTIL rising_edge(clk); + WHILE ready /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END IF; + IF c_rl=1 THEN + WHILE ready /= '1' LOOP + out_valid <= '0'; + WAIT UNTIL rising_edge(clk); + END LOOP; + out_valid <= '1'; + WAIT UNTIL rising_edge(clk); + END IF; + END proc_common_ready_latency; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate a single active, inactive pulse + ------------------------------------------------------------------------------ + PROCEDURE proc_common_gen_pulse(CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC) IS + VARIABLE v_cnt : NATURAL RANGE 0 TO c_period := 0; + BEGIN + WHILE v_cnt<c_period LOOP + IF v_cnt<c_active THEN + pulse <= c_level; + ELSE + pulse <= NOT c_level; + END IF; + v_cnt := v_cnt+1; + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_common_gen_pulse; + + -- Pulse forever after rst was released + PROCEDURE proc_common_gen_pulse(CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC) IS + VARIABLE v_cnt : NATURAL RANGE 0 TO c_period := 0; + BEGIN + pulse <= NOT c_level; + IF rst='0' THEN + WAIT UNTIL rising_edge(clk); + WHILE TRUE LOOP + proc_common_gen_pulse(c_active, c_period, c_level, clk, pulse); + END LOOP; + END IF; + END proc_common_gen_pulse; + + -- pulse '1', '0' + PROCEDURE proc_common_gen_pulse(SIGNAL clk : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC) IS + BEGIN + proc_common_gen_pulse(1, 2, '1', clk, pulse); + END proc_common_gen_pulse; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate a periodic pulse with arbitrary duty cycle + ------------------------------------------------------------------------------ + PROCEDURE proc_common_gen_duty_pulse(CONSTANT c_delay : IN NATURAL; -- delay pulse for nof_clk after enable + CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC) IS + VARIABLE v_cnt : NATURAL RANGE 0 TO c_period-1 := 0; + BEGIN + pulse <= NOT c_level; + IF rst='0' THEN + proc_common_wait_until_high(clk, enable); -- if enabled then continue immediately else wait here + proc_common_wait_some_cycles(clk, c_delay); -- apply initial c_delay. Once enabled, the pulse remains enabled + WHILE TRUE LOOP + WAIT UNTIL rising_edge(clk); + IF v_cnt<c_active THEN + pulse <= c_level; + ELSE + pulse <= NOT c_level; + END IF; + IF v_cnt<c_period-1 THEN + v_cnt := v_cnt+1; + ELSE + v_cnt := 0; + END IF; + END LOOP; + END IF; + END proc_common_gen_duty_pulse; + + PROCEDURE proc_common_gen_duty_pulse(CONSTANT c_active : IN NATURAL; -- pulse active for nof clk + CONSTANT c_period : IN NATURAL; -- pulse period for nof clk + CONSTANT c_level : IN STD_LOGIC; -- pulse level when active + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; + SIGNAL pulse : OUT STD_LOGIC) IS + BEGIN + proc_common_gen_duty_pulse(0, c_active, c_period, c_level, rst, clk, enable, pulse); + END proc_common_gen_duty_pulse; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate counter data with valid + -- . Output counter data dependent on enable and ready + ------------------------------------------------------------------------------ + -- arbitrary c_incr + PROCEDURE proc_common_gen_data(CONSTANT c_rl : IN NATURAL; -- 0, 1 are supported by proc_common_ready_latency() + CONSTANT c_init : IN INTEGER; + CONSTANT c_incr : IN INTEGER; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- when '0' then no valid output even when ready='1' + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_data : OUT STD_LOGIC_VECTOR; + SIGNAL out_valid : OUT STD_LOGIC) IS + CONSTANT c_data_w : NATURAL := out_data'LENGTH; + VARIABLE v_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= TO_SVEC(c_init, c_data_w); + BEGIN + out_valid <= '0'; + out_data <= v_data; + IF rst='0' THEN + WAIT UNTIL rising_edge(clk); + WHILE TRUE LOOP + out_data <= v_data; + proc_common_ready_latency(c_rl, clk, enable, ready, out_valid); + v_data := INCR_UVEC(v_data, c_incr); + END LOOP; + END IF; + END proc_common_gen_data; + + -- c_incr = 1 + PROCEDURE proc_common_gen_data(CONSTANT c_rl : IN NATURAL; -- 0, 1 are supported by proc_common_ready_latency() + CONSTANT c_init : IN INTEGER; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL enable : IN STD_LOGIC; -- when '0' then no valid output even when ready='1' + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_data : OUT STD_LOGIC_VECTOR; + SIGNAL out_valid : OUT STD_LOGIC) IS + BEGIN + proc_common_gen_data(c_rl, c_init, 1, rst, clk, enable, ready, out_data, out_valid); + END proc_common_gen_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate frame control + ------------------------------------------------------------------------------ + PROCEDURE proc_common_sop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC; + SIGNAL in_sop : OUT STD_LOGIC) IS + BEGIN + in_val <= '1'; + in_sop <= '1'; + proc_common_wait_some_cycles(clk, 1); + in_sop <= '0'; + END proc_common_sop; + + PROCEDURE proc_common_eop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC; + SIGNAL in_eop : OUT STD_LOGIC) IS + BEGIN + in_val <= '1'; + in_eop <= '1'; + proc_common_wait_some_cycles(clk, 1); + in_val <= '0'; + in_eop <= '0'; + END proc_common_eop; + + PROCEDURE proc_common_val(CONSTANT c_val_len : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC) IS + BEGIN + in_val <= '1'; + proc_common_wait_some_cycles(clk, c_val_len); + in_val <= '0'; + END proc_common_val; + + PROCEDURE proc_common_val_duty(CONSTANT c_hi_len : IN NATURAL; + CONSTANT c_lo_len : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC) IS + BEGIN + in_val <= '1'; + proc_common_wait_some_cycles(clk, c_hi_len); + in_val <= '0'; + proc_common_wait_some_cycles(clk, c_lo_len); + END proc_common_val_duty; + + PROCEDURE proc_common_eop_flush(CONSTANT c_flush_len : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : OUT STD_LOGIC; + SIGNAL in_eop : OUT STD_LOGIC) IS + BEGIN + -- . eop + proc_common_eop(clk, in_val, in_eop); + -- . flush after in_eop to empty the shift register + proc_common_wait_some_cycles(clk, c_flush_len); + END proc_common_eop_flush; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify incrementing data + ------------------------------------------------------------------------------ + PROCEDURE proc_common_verify_data(CONSTANT c_rl : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL out_valid : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + VARIABLE v_exp_data : STD_LOGIC_VECTOR(out_data'RANGE); + BEGIN + IF rising_edge(clk) THEN + -- out_valid must be active, because only the out_data will it differ from the previous out_data + IF out_valid='1' THEN + -- for ready_latency = 1 out_valid indicates new data + -- for ready_latency = 0 out_valid only indicates new data when it is confirmed by ready + IF c_rl=1 OR (c_rl=0 AND ready='1') THEN + prev_out_data <= out_data; + v_exp_data := INCR_UVEC(prev_out_data, 1); -- increment first then compare to also support increment wrap around + IF verify_en='1' AND UNSIGNED(out_data) /= UNSIGNED(v_exp_data) THEN + REPORT "COMMON : Wrong out_data count" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_common_verify_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output valid + -- . only support ready latency c_rl = 0 or 1 + ------------------------------------------------------------------------------ + PROCEDURE proc_common_verify_valid(CONSTANT c_rl : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL prev_ready : INOUT STD_LOGIC; + SIGNAL out_valid : IN STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + -- for ready latency c_rl = 1 out_valid may only be asserted after ready + -- for ready latency c_rl = 0 out_valid may always be asserted + prev_ready <= '0'; + IF c_rl=1 THEN + prev_ready <= ready; + IF verify_en='1' AND out_valid='1' THEN + IF prev_ready/='1' THEN + REPORT "COMMON : Wrong ready latency between ready and out_valid" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_common_verify_valid; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT input to output latency + ------------------------------------------------------------------------------ + -- for SL ctrl + PROCEDURE proc_common_verify_latency(CONSTANT c_str : IN STRING; -- e.g. "valid", "sop", "eop" + CONSTANT c_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL in_ctrl : IN STD_LOGIC; + SIGNAL pipe_ctrl_vec : INOUT STD_LOGIC_VECTOR; -- range [0:c_latency] + SIGNAL out_ctrl : IN STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + pipe_ctrl_vec <= in_ctrl & pipe_ctrl_vec(0 TO c_latency-1); -- note: pipe_ctrl_vec(c_latency) is a dummy place holder to avoid [0:-1] range + IF verify_en='1' THEN + IF c_latency=0 THEN + IF in_ctrl/=out_ctrl THEN + REPORT "COMMON : Wrong zero latency between input " & c_str & " and output " & c_str SEVERITY ERROR; + END IF; + ELSE + IF pipe_ctrl_vec(c_latency-1)/=out_ctrl THEN + REPORT "COMMON : Wrong latency between input " & c_str & " and output " & c_str SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_common_verify_latency; + + + -- for SLV data + PROCEDURE proc_common_verify_latency(CONSTANT c_str : IN STRING; -- e.g. "data" + CONSTANT c_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL in_data : IN STD_LOGIC_VECTOR; + SIGNAL pipe_data_vec : INOUT STD_LOGIC_VECTOR; -- range [0:(1 + c_latency)*c_data_w-1] + SIGNAL out_data : IN STD_LOGIC_VECTOR) IS + CONSTANT c_data_w : NATURAL := in_data'LENGTH; + CONSTANT c_data_vec_w : NATURAL := pipe_data_vec'LENGTH; -- = (1 + c_latency) * c_data_w + BEGIN + IF rising_edge(clk) THEN + pipe_data_vec <= in_data & pipe_data_vec(0 TO c_data_vec_w-c_data_w-1); -- note: pipe_data_vec(c_latency) is a dummy place holder to avoid [0:-1] range + IF verify_en='1' THEN + IF c_latency=0 THEN + IF UNSIGNED(in_data)/=UNSIGNED(out_data) THEN + REPORT "COMMON : Wrong zero latency between input " & c_str & " and output " & c_str SEVERITY ERROR; + END IF; + ELSE + IF UNSIGNED(pipe_data_vec(c_data_vec_w-c_data_w-c_data_w TO c_data_vec_w-c_data_w-1))/=UNSIGNED(out_data) THEN + REPORT "COMMON : Wrong latency between input " & c_str & " and output " & c_str SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_common_verify_latency; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the expected value + -- . e.g. to check that a test has ran at all + ------------------------------------------------------------------------------ + PROCEDURE proc_common_verify_value(CONSTANT mode : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC_VECTOR; + SIGNAL res : IN STD_LOGIC_VECTOR) IS + BEGIN + IF rising_edge(clk) THEN + IF en='1' THEN + IF mode = 0 AND UNSIGNED(res) /= UNSIGNED(exp) THEN + REPORT "COMMON : Wrong result value" SEVERITY ERROR; -- == (equal) + END IF; + IF mode = 1 AND UNSIGNED(res) < UNSIGNED(exp) THEN + REPORT "COMMON : Wrong result value too small" SEVERITY ERROR; -- >= (at least) + END IF; + END IF; + END IF; + END proc_common_verify_value; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Opens a file for access and reports fail or success of opening. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_open_file( file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + file_name : IN STRING; + file_mode : IN FILE_OPEN_KIND) IS + BEGIN + IF file_status=OPEN_OK THEN + file_close(in_file); + END IF; + file_open (file_status, in_file, file_name, file_mode); + IF file_status=OPEN_OK THEN + REPORT "COMMON : File opened " SEVERITY NOTE; + ELSE + REPORT "COMMON : Unable to open file " SEVERITY FAILURE; + END IF; + END proc_common_open_file; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Reads an integer from a file. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + read_value_0 : OUT INTEGER) IS + VARIABLE v_line : LINE; + VARIABLE v_good : BOOLEAN; + BEGIN + IF file_status/=OPEN_OK THEN + REPORT "COMMON : File is not opened " SEVERITY FAILURE; + ELSE + IF ENDFILE(in_file) THEN + REPORT "COMMON : end of file " SEVERITY NOTE; + ELSE + READLINE(in_file, v_line); + READ(v_line, read_value_0, v_good); + IF v_good = FALSE THEN + REPORT "COMMON : Read from line unsuccessful " SEVERITY FAILURE; + END IF; + END IF; + END IF; + END proc_common_readline_file; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Reads two integers from two columns in a file. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + read_value_0 : OUT INTEGER; + read_value_1 : OUT INTEGER) IS + VARIABLE v_line : LINE; + VARIABLE v_good : BOOLEAN; + BEGIN + IF file_status/=OPEN_OK THEN + REPORT "COMMON : File is not opened " SEVERITY FAILURE; + ELSE + IF ENDFILE(in_file) THEN + REPORT "COMMON : end of file " SEVERITY NOTE; + ELSE + READLINE(in_file, v_line); + READ(v_line, read_value_0, v_good); + IF v_good = FALSE THEN + REPORT "COMMON : Read from line unsuccessful " SEVERITY FAILURE; + END IF; + READ(v_line, read_value_1, v_good); + IF v_good = FALSE THEN + REPORT "COMMON : Read from line unsuccessful " SEVERITY FAILURE; + END IF; + END IF; + END IF; + END proc_common_readline_file; + +------------------------------------------------------------------------------ + -- PROCEDURE: Reads an array of integer from a file. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + value_array : OUT t_integer_arr; + nof_reads : IN INTEGER) IS + VARIABLE v_line : LINE; + VARIABLE v_good : BOOLEAN; + BEGIN + IF file_status/=OPEN_OK THEN + REPORT "COMMON : File is not opened " SEVERITY FAILURE; + ELSE + IF ENDFILE(in_file) THEN + REPORT "COMMON : end of file " SEVERITY NOTE; + ELSE + READLINE(in_file, v_line); + FOR I IN 0 TO nof_reads - 1 LOOP + READ(v_line, value_array(I), v_good); + IF v_good = FALSE THEN + REPORT "COMMON : Read from line unsuccessful " SEVERITY FAILURE; + END IF; + END LOOP; + END IF; + END IF; + END proc_common_readline_file; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Reads an std_logic_vector from a file + ------------------------------------------------------------------------------ + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + read_slv : OUT STD_LOGIC_VECTOR) IS + VARIABLE v_line : LINE; + VARIABLE v_good : BOOLEAN; + BEGIN + IF file_status/=OPEN_OK THEN + REPORT "COMMON : File is not opened " SEVERITY FAILURE; + ELSE + IF ENDFILE(in_file) THEN + REPORT "COMMON : end of file " SEVERITY NOTE; + ELSE + READLINE(in_file, v_line); + READ(v_line, read_slv, v_good); + IF v_good = FALSE THEN + REPORT "COMMON : Read from line unsuccessful " SEVERITY FAILURE; + END IF; + END IF; + END IF; + END proc_common_readline_file; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Reads a string of any length from a file pointer. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_readline_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT; + res_string : OUT STRING) IS + VARIABLE v_line : LINE; + VARIABLE v_char : CHARACTER; + VARIABLE is_string : BOOLEAN; + BEGIN + IF file_status/=OPEN_OK THEN + REPORT "COMMON : File is not opened " SEVERITY FAILURE; + ELSE + IF ENDFILE(in_file) THEN + REPORT "COMMON : end of file " SEVERITY NOTE; + ELSE + readline(in_file, v_line); + -- clear the contents of the result string + FOR I IN res_string'RANGE LOOP + res_string(I) := ' '; + END LOOP; + -- read all characters of the line, up to the length + -- of the results string + FOR I IN res_string'RANGE LOOP + read(v_line, v_char, is_string); + IF NOT is_string THEN -- found end of line + EXIT; + END IF; + res_string(I) := v_char; + END LOOP; + END IF; + END IF; + END proc_common_readline_file; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Closes a file. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_close_file(file_status : INOUT FILE_OPEN_STATUS; + FILE in_file : TEXT) IS + BEGIN + IF file_status/=OPEN_OK THEN + REPORT "COMMON : File was not opened " SEVERITY WARNING; + END IF; + FILE_CLOSE(in_file); + REPORT "COMMON : File closed " SEVERITY NOTE; + END proc_common_close_file; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Reads the integer data from nof_rows with nof_col values per + -- row from a file and returns it row by row in an array of + -- integers. + ------------------------------------------------------------------------------ + PROCEDURE proc_common_read_integer_file(file_name : IN STRING; + nof_header_lines : NATURAL; + nof_row : NATURAL; + nof_col : NATURAL; + SIGNAL return_array : OUT t_integer_arr) IS + VARIABLE v_file_status : FILE_OPEN_STATUS; + FILE v_in_file : TEXT; + VARIABLE v_input_line : LINE; + VARIABLE v_string : STRING(1 TO 80); + VARIABLE v_row_arr : t_integer_arr(0 TO nof_col-1); + BEGIN + IF file_name/="UNUSED" AND file_name/="unused" THEN + -- Open the file for reading + proc_common_open_file(v_file_status, v_in_file, file_name, READ_MODE); + -- Read and skip the header + FOR J IN 0 TO nof_header_lines-1 LOOP + proc_common_readline_file(v_file_status, v_in_file, v_string); + END LOOP; + FOR J IN 0 TO nof_row-1 LOOP + proc_common_readline_file(v_file_status, v_in_file, v_row_arr, nof_col); + FOR I IN 0 TO nof_col-1 LOOP + return_array(J*nof_col + I) <= v_row_arr(I); -- use loop to be independent of t_integer_arr downto or to range + END LOOP; + IF ENDFILE(v_in_file) THEN + IF J/=nof_row-1 THEN + REPORT "COMMON : Unexpected end of file" SEVERITY FAILURE; + END IF; + EXIT; + END IF; + END LOOP; + -- Close the file + proc_common_close_file(v_file_status, v_in_file); + ELSE + return_array <= (return_array'RANGE=>0); + END IF; + END proc_common_read_integer_file; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Reads the data column from a .mif file and returns it in an + -- array of integers + ------------------------------------------------------------------------------ + PROCEDURE proc_common_read_mif_file( file_name : IN STRING; + SIGNAL return_array : OUT t_integer_arr) IS + VARIABLE v_file_status : FILE_OPEN_STATUS; + FILE v_in_file : TEXT; + VARIABLE v_input_line : LINE; + VARIABLE v_string : STRING(1 TO 80); + VARIABLE v_mem_width : NATURAL := 0; + VARIABLE v_mem_depth : NATURAL := 0; + VARIABLE v_up_bound : NATURAL := 0; + VARIABLE v_low_bound : NATURAL := 0; + VARIABLE v_end_header : BOOLEAN := FALSE; + VARIABLE v_char : CHARACTER; + BEGIN + -- Open the .mif file for reading + proc_common_open_file(v_file_status, v_in_file, file_name, READ_MODE); + -- Read the header. + WHILE NOT v_end_header LOOP + proc_common_readline_file(v_file_status, v_in_file, v_string); + IF(func_find_string_in_string(v_string, "WIDTH=")) THEN -- check for "WIDTH=" + v_up_bound := func_find_char_in_string(v_string, ';'); + v_low_bound := func_find_char_in_string(v_string, '='); + v_mem_width := func_decstring_to_integer(v_string(v_low_bound+1 TO v_up_bound-1)); + ELSIF(func_find_string_in_string(v_string, "DEPTH=")) THEN -- check for "DEPTH=" + v_up_bound := func_find_char_in_string(v_string, ';'); + v_low_bound := func_find_char_in_string(v_string, '='); + v_mem_depth := func_decstring_to_integer(v_string(v_low_bound+1 TO v_up_bound-1)); + ELSIF(func_find_string_in_string(v_string, "CONTENT BEGIN")) THEN + v_end_header := TRUE; + END IF; + END LOOP; + -- Read the data + FOR I IN 0 TO v_mem_depth-1 LOOP + proc_common_readline_file(v_file_status, v_in_file, v_string); -- Read the next line from the file. + v_low_bound := func_find_char_in_string(v_string, ':'); -- Find the left position of the string that contains the data field + v_up_bound := func_find_char_in_string(v_string, ';'); -- Find the right position of the string that contains the data field + return_array(I) <= func_hexstring_to_integer(v_string(v_low_bound+1 TO v_up_bound-1)); + END LOOP; + -- Close the file + proc_common_close_file(v_file_status, v_in_file); + END proc_common_read_mif_file; + + ------------------------------------------------------------------------------ + -- FUNCTION: Complex multiply with conjugate option for input b + ------------------------------------------------------------------------------ + FUNCTION func_complex_multiply(in_ar, in_ai, in_br, in_bi : STD_LOGIC_VECTOR; conjugate_b : BOOLEAN; str : STRING; g_out_dat_w : NATURAL) RETURN STD_LOGIC_VECTOR IS + -- Function: Signed complex multiply + -- p = a * b when g_conjugate_b = FALSE + -- = (ar + j ai) * (br + j bi) + -- = ar*br - ai*bi + j ( ar*bi + ai*br) + -- + -- p = a * conj(b) when g_conjugate_b = TRUE + -- = (ar + j ai) * (br - j bi) + -- = ar*br + ai*bi + j (-ar*bi + ai*br) + -- From mti_numeric_std.vhd follows: + -- . SIGNED * --> output width = 2 * input width + -- . SIGNED + --> output width = largest(input width) + CONSTANT c_in_w : NATURAL := in_ar'LENGTH; -- all input have same width + CONSTANT c_res_w : NATURAL := 2*c_in_w+1; -- *2 for multiply, +1 for sum of two products + VARIABLE v_ar : SIGNED(c_in_w-1 DOWNTO 0); + VARIABLE v_ai : SIGNED(c_in_w-1 DOWNTO 0); + VARIABLE v_br : SIGNED(c_in_w-1 DOWNTO 0); + VARIABLE v_bi : SIGNED(c_in_w-1 DOWNTO 0); + VARIABLE v_result_re : SIGNED(c_res_w-1 DOWNTO 0); + VARIABLE v_result_im : SIGNED(c_res_w-1 DOWNTO 0); + BEGIN + -- Calculate expected result + v_ar := RESIZE_NUM(SIGNED(in_ar), c_in_w); + v_ai := RESIZE_NUM(SIGNED(in_ai), c_in_w); + v_br := RESIZE_NUM(SIGNED(in_br), c_in_w); + v_bi := RESIZE_NUM(SIGNED(in_bi), c_in_w); + IF conjugate_b=FALSE THEN + v_result_re := RESIZE_NUM(v_ar*v_br, c_res_w) - v_ai*v_bi; + v_result_im := RESIZE_NUM(v_ar*v_bi, c_res_w) + v_ai*v_br; + ELSE + v_result_re := RESIZE_NUM(v_ar*v_br, c_res_w) + v_ai*v_bi; + v_result_im := RESIZE_NUM(v_ai*v_br, c_res_w) - v_ar*v_bi; + END IF; + -- Note that for the product needs as many bits as the sum of the input widths. However the + -- sign bit is then only needed for the case that both inputs have the largest negative + -- values, only then the MSBits will be "01". For all other inputs the MSbits will always + -- be "00" for positive numbers or "11" for negative numbers. MSbits "10" can not occur. + -- For largest negative inputs the complex multiply result becomes: + -- + -- 3b inputs --> 6b products --> c_res_w = 7b + -- -4 * -4 + -4 * -4 = +16 + +16 = +64 -- most negative valued inputs + -- b100 * b100 + b100 * b100 = b010000 + b010000 = b0100000 + -- + -- --> if g_out_dat_w = 6b then + -- a) IEEE unsigned resizing skips the MSbits so b0100000 = +64 becomes b_100000 = -64 + -- b) IEEE signed resizing preserves the MSbit so b0100000 = +64 becomes b0_00000 = 0 + -- c) detect MSbits = "01" to clip max positive to get _b011111 = +63 + -- Option a) seems to map best on the FPGA hardware multiplier IP. + IF str="RE" THEN + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(v_result_re, g_out_dat_w)); -- conform option a) + ELSE + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(v_result_im, g_out_dat_w)); -- conform option a) + END IF; + END; + + ------------------------------------------------------------------------------ + -- FUNCTION: Converts the decimal value represented in a string to an integer value. + ------------------------------------------------------------------------------ + FUNCTION func_decstring_to_integer(in_string: STRING) RETURN INTEGER IS + CONSTANT c_nof_digits : NATURAL := in_string'LENGTH; -- Define the length of the string + VARIABLE v_char : CHARACTER; + VARIABLE v_weight : INTEGER := 1; + VARIABLE v_return_int : INTEGER := 0; + BEGIN + -- Walk through the string character by character. + FOR I IN c_nof_digits-1 DOWNTO 0 LOOP + v_char := in_string(I+in_string'LOW); + CASE v_char IS + WHEN '0' => v_return_int := v_return_int + 0*v_weight; + WHEN '1' => v_return_int := v_return_int + 1*v_weight; + WHEN '2' => v_return_int := v_return_int + 2*v_weight; + WHEN '3' => v_return_int := v_return_int + 3*v_weight; + WHEN '4' => v_return_int := v_return_int + 4*v_weight; + WHEN '5' => v_return_int := v_return_int + 5*v_weight; + WHEN '6' => v_return_int := v_return_int + 6*v_weight; + WHEN '7' => v_return_int := v_return_int + 7*v_weight; + WHEN '8' => v_return_int := v_return_int + 8*v_weight; + WHEN '9' => v_return_int := v_return_int + 9*v_weight; + WHEN OTHERS => NULL; + END CASE; + IF (v_char /= ' ') THEN -- Only increment the weight when the character is NOT a spacebar. + v_weight := v_weight*10; -- Addapt the weight for the next decimal digit. + END IF; + END LOOP; + RETURN(v_return_int); + END FUNCTION func_decstring_to_integer; + + ------------------------------------------------------------------------------ + -- FUNCTION: Converts the hexadecimal value represented in a string to an integer value. + ------------------------------------------------------------------------------ + FUNCTION func_hexstring_to_integer(in_string: STRING) RETURN INTEGER IS + CONSTANT c_nof_digits : NATURAL := in_string'LENGTH; -- Define the length of the string + VARIABLE v_char : CHARACTER; + VARIABLE v_weight : INTEGER := 1; + VARIABLE v_return_int : INTEGER := 0; + BEGIN + -- Walk through the string character by character. + FOR I IN c_nof_digits-1 DOWNTO 0 LOOP + v_char := in_string(I+in_string'LOW); + CASE v_char IS + WHEN '0' => v_return_int := v_return_int + 0*v_weight; + WHEN '1' => v_return_int := v_return_int + 1*v_weight; + WHEN '2' => v_return_int := v_return_int + 2*v_weight; + WHEN '3' => v_return_int := v_return_int + 3*v_weight; + WHEN '4' => v_return_int := v_return_int + 4*v_weight; + WHEN '5' => v_return_int := v_return_int + 5*v_weight; + WHEN '6' => v_return_int := v_return_int + 6*v_weight; + WHEN '7' => v_return_int := v_return_int + 7*v_weight; + WHEN '8' => v_return_int := v_return_int + 8*v_weight; + WHEN '9' => v_return_int := v_return_int + 9*v_weight; + WHEN 'A' | 'a' => v_return_int := v_return_int + 10*v_weight; + WHEN 'B' | 'b' => v_return_int := v_return_int + 11*v_weight; + WHEN 'C' | 'c' => v_return_int := v_return_int + 12*v_weight; + WHEN 'D' | 'd' => v_return_int := v_return_int + 13*v_weight; + WHEN 'E' | 'e' => v_return_int := v_return_int + 14*v_weight; + WHEN 'F' | 'f' => v_return_int := v_return_int + 15*v_weight; + WHEN OTHERS => NULL; + END CASE; + IF (v_char /= ' ') THEN -- Only increment the weight when the character is NOT a spacebar. + v_weight := v_weight*16; -- Addapt the weight for the next hexadecimal digit. + END IF; + END LOOP; + RETURN(v_return_int); + END FUNCTION func_hexstring_to_integer; + + ------------------------------------------------------------------------------ + -- FUNCTION: Finds the first instance of a given character in a string + -- and returns its position. + ------------------------------------------------------------------------------ + FUNCTION func_find_char_in_string(in_string: STRING; find_char: CHARACTER) RETURN INTEGER IS + VARIABLE v_char_position : INTEGER := 0; + BEGIN + FOR I IN 1 TO in_string'LENGTH LOOP + IF(in_string(I) = find_char) THEN + v_char_position := I; + END IF; + END LOOP; + RETURN(v_char_position); + END FUNCTION func_find_char_in_string; + + ------------------------------------------------------------------------------ + -- FUNCTION: Checks if a string(find_string) is part of a larger string(in_string). + -- The result is returned as a BOOLEAN. + ------------------------------------------------------------------------------ + FUNCTION func_find_string_in_string(in_string: STRING; find_string: STRING) RETURN BOOLEAN IS + CONSTANT c_in_length : NATURAL := in_string'LENGTH; -- Define the length of the string to search in + CONSTANT c_find_length : NATURAL := find_string'LENGTH; -- Define the length of the string to be find + VARIABLE v_found_it : BOOLEAN := FALSE; + BEGIN + FOR I IN 1 TO c_in_length-c_find_length LOOP + IF(in_string(I TO (I+c_find_length-1)) = find_string) THEN + v_found_it := TRUE; + END IF; + END LOOP; + RETURN(v_found_it); + END FUNCTION func_find_string_in_string; + +END tb_common_pkg; + diff --git a/cores/base/dp/dp_components/dp_hold_ctrl.vhd b/cores/base/dp/dp_components/dp_hold_ctrl.vhd new file mode 100644 index 0000000000000000000000000000000000000000..fa36f9310c02a57584afb31b854c896430b33a9e --- /dev/null +++ b/cores/base/dp/dp_components/dp_hold_ctrl.vhd @@ -0,0 +1,70 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_components_lib; +USE IEEE.std_logic_1164.all; + +-- Purpose: +-- Hold hld_ctrl active until next ready high when in_ctrl is active while +-- ready went low +-- Description: +-- When ready goes low there may still arrive one new valid data. The control +-- information for this data can then be held with this component. When ready +-- goes high again the held data can then be output and the hld_ctrl is +-- released. After that the subsequent data output can come directly from the +-- up stream source, until ready goes low again. +-- Remarks: +-- . Ready latency RL = 1 +-- . The in_ctrl is typically in_valid, in_sop or in_eop +-- . Typically used together with dp_hold_data + +ENTITY dp_hold_ctrl IS + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + ready : IN STD_LOGIC; + in_ctrl : IN STD_LOGIC; + hld_ctrl : OUT STD_LOGIC + ); +END dp_hold_ctrl; + + +ARCHITECTURE rtl OF dp_hold_ctrl IS + + SIGNAL hi_ctrl : STD_LOGIC; + SIGNAL lo_ctrl : STD_LOGIC; + +BEGIN + + hi_ctrl <= in_ctrl AND NOT ready; -- capture + lo_ctrl <= NOT in_ctrl AND ready; -- release + + u_hld_ctrl : ENTITY common_components_lib.common_switch + PORT MAP ( + rst => rst, + clk => clk, + switch_high => hi_ctrl, + switch_low => lo_ctrl, + out_level => hld_ctrl + ); + +END rtl; diff --git a/cores/base/dp/dp_components/dp_hold_input.vhd b/cores/base/dp/dp_components/dp_hold_input.vhd new file mode 100644 index 0000000000000000000000000000000000000000..81ab370cfc135e4089c9fe9f7c74759d13285801 --- /dev/null +++ b/cores/base/dp/dp_components/dp_hold_input.vhd @@ -0,0 +1,157 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, dp_pkg_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +-- Purpose: +-- Hold the sink input +-- Description: +-- This dp_hold_input provides the necessary input logic to hold the input +-- data and control to easily register the source output. Compared to +-- dp_pipeling the dp_hold_input is the same except for the output register +-- stage. In this way dp_hold_input can be used in a more complicated stream +-- component where the output is not always the same as the input. +-- The snk_in.valid and hold_in.valid are never high at the same time. +-- If src_in.ready goes low while snk_in.valid is high then this snk_in.valid +-- is held in hold_in.valid and the corresponding snk_in.data will get held +-- in the external src_out_reg.data. When src_in.ready goes high again then +-- the held data becomes valid via src_out_reg.valid and hold_in.valid goes +-- low. Due to the RL=1 the next cycle the snk_in.valid from the sink may go +-- high. The next_src_out control signals are equal to pend_src_out AND +-- src_in.ready, so they can directly be assigned to src_out_reg.data if the +-- snk_in.data needs to be passed on. +-- The internal pend_src_out control signals are available outside, in +-- addition to the next_src_out control signals, to support external control +-- independent of src_in.ready. Use pend_scr_out instead of next_src_out +-- to avoid combinatorial loop when src_in.ready depends on next_src_out. +-- The pend_src_out signals are used to implement show ahead behaviour like +-- with RL=0, but for RL=1. The input can then be stopped based on the snk_in +-- data and later on continued again without losing this snk_in data, because +-- it was held as described above. +-- Remarks: +-- . Ready latency = 1 +-- . Without flow control so when src_in.ready = '1' fixed, then dp_hold_input +-- becomes void because the dp_hold_ctrl output then remains '0'. + +ENTITY dp_hold_input IS + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- ST sink + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + -- ST source + src_in : IN t_dp_siso; + next_src_out : OUT t_dp_sosi; + pend_src_out : OUT t_dp_sosi; -- the SOSI data fields are the same as for next_src_out + src_out_reg : IN t_dp_sosi -- uses only the SOSI data fields + ); +END dp_hold_input; + + +ARCHITECTURE rtl OF dp_hold_input IS + + SIGNAL i_pend_src_out : t_dp_sosi; + SIGNAL hold_in : t_dp_sosi; -- uses only the SOSI ctrl fields + +BEGIN + + pend_src_out <= i_pend_src_out; + + -- SISO: + snk_out <= src_in; -- No change in ready latency, pass on xon frame level flow control + + -- SOSI: + -- Take care of active snk_in.valid, snk_in.sync, snk_in.sop and snk_in.eop + -- when src_in.ready went low. If hold_in.valid would not be used for + -- pend_src_out.valid and next_src_out.valid, then the pipeline would still + -- work, but the valid snk_in.data that came when src_in.ready went low, + -- will then only get pushed out on the next valid snk_in.valid. Whereas + -- hold_in.valid ensures that it will get pushed out as soon as src_in.ready + -- goes high again. This is typically necessary in case of packetized data + -- where the eop of one packet should not have to wait for the valid (sop) + -- of a next packet to get pushed out. + + u_hold_val : ENTITY work.dp_hold_ctrl + PORT MAP ( + rst => rst, + clk => clk, + ready => src_in.ready, + in_ctrl => snk_in.valid, + hld_ctrl => hold_in.valid + ); + + u_hold_sync : ENTITY work.dp_hold_ctrl + PORT MAP ( + rst => rst, + clk => clk, + ready => src_in.ready, + in_ctrl => snk_in.sync, + hld_ctrl => hold_in.sync + ); + + u_hold_sop : ENTITY work.dp_hold_ctrl + PORT MAP ( + rst => rst, + clk => clk, + ready => src_in.ready, + in_ctrl => snk_in.sop, + hld_ctrl => hold_in.sop + ); + + u_hold_eop : ENTITY work.dp_hold_ctrl + PORT MAP ( + rst => rst, + clk => clk, + ready => src_in.ready, + in_ctrl => snk_in.eop, + hld_ctrl => hold_in.eop + ); + + p_pend_src_out : PROCESS(snk_in, src_out_reg, hold_in) + BEGIN + -- Pend data + IF snk_in.valid='1' THEN + i_pend_src_out <= snk_in; -- Input data + ELSE + i_pend_src_out <= src_out_reg; -- Hold data + END IF; + i_pend_src_out.valid <= snk_in.valid OR hold_in.valid; + i_pend_src_out.sync <= snk_in.sync OR hold_in.sync; + i_pend_src_out.sop <= snk_in.sop OR hold_in.sop; + i_pend_src_out.eop <= snk_in.eop OR hold_in.eop; + END PROCESS; + + p_next_src_out : PROCESS(i_pend_src_out, src_in) + BEGIN + -- Next data + next_src_out <= i_pend_src_out; + -- Next control + next_src_out.valid <= i_pend_src_out.valid AND src_in.ready; + next_src_out.sync <= i_pend_src_out.sync AND src_in.ready; + next_src_out.sop <= i_pend_src_out.sop AND src_in.ready; + next_src_out.eop <= i_pend_src_out.eop AND src_in.ready; + END PROCESS; + +END rtl; diff --git a/cores/base/dp/dp_components/dp_latency_adapter.vhd b/cores/base/dp/dp_components/dp_latency_adapter.vhd new file mode 100644 index 0000000000000000000000000000000000000000..9fcca836dd9b256c160e26b5535a0e61b3a6b419 --- /dev/null +++ b/cores/base/dp/dp_components/dp_latency_adapter.vhd @@ -0,0 +1,260 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +-- Purpose: +-- Adapt the g_in_latency input ready to the g_out_latency output latency. +-- A typical application is to use this latency adapter to provide a read +-- ahead interface to a default FIFO with e.g. read latency 1 or 2. +-- Description: +-- If g_in_latency > g_out_latency then the input latency is first adapted +-- to zero latency by means of a latency FIFO. After that a delay line for +-- src_in.ready yields the g_out_latency output latency. +-- If g_in_latency < g_out_latency, then a delay line for src_in.ready yields +-- the g_out_latency output latency. +-- The sync input is also passed on, only if it occurs during valid. The +-- constant c_pass_sync_during_not_valid is defined to preserve the +-- corresponding section of code for passing the sync also during not valid. +-- Remark: +-- . The snk_out.ready is derived combinatorially from the src_in.ready. If for +-- timing performance it is needed to register snk_out.ready, then this can +-- be done by first increasing the ready latency using this adapter with +-- g_in_latency = g_out_latency + 1, followed by a second adapter to reach +-- the required output ready latency latency. + +ENTITY dp_latency_adapter IS + GENERIC ( + g_in_latency : NATURAL := 3; + g_out_latency : NATURAL := 1 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- Monitor internal FIFO filling + fifo_usedw : OUT STD_LOGIC_VECTOR(ceil_log2(2+g_in_latency)-1 DOWNTO 0); -- see description of c_fifo_size, c_usedw_w for explanation of why +2 + fifo_ful : OUT STD_LOGIC; + fifo_emp : OUT STD_LOGIC; + -- ST sink + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + -- ST source + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END dp_latency_adapter; + + +ARCHITECTURE rtl OF dp_latency_adapter IS + + -- The difference between the input ready latency and the output ready latency + CONSTANT c_diff_latency : INTEGER := g_out_latency - g_in_latency; + + -- Define constant to preserve the corresponding section of code, but default keep it at FALSE + CONSTANT c_pass_sync_during_not_valid : BOOLEAN := FALSE; + + -- Use g_in_latency+1 words for the FIFO data array, to go to zero latency + CONSTANT c_high : NATURAL := g_in_latency; + CONSTANT c_fifo_size : NATURAL := g_in_latency+1; -- +1 because RL=0 also requires a word + CONSTANT c_usedw_w : NATURAL := ceil_log2(c_fifo_size+1); -- +1 because to store value 2**n requires n+1 bits + + SIGNAL fifo_reg : t_dp_sosi_arr(c_high DOWNTO 0); + SIGNAL nxt_fifo_reg : t_dp_sosi_arr(c_high DOWNTO 0); + SIGNAL fifo_reg_valid : STD_LOGIC_VECTOR(c_high DOWNTO 0); -- debug signal for Wave window + + SIGNAL nxt_fifo_usedw : STD_LOGIC_VECTOR(c_usedw_w-1 DOWNTO 0); + SIGNAL nxt_fifo_ful : STD_LOGIC; + SIGNAL nxt_fifo_emp : STD_LOGIC; + + SIGNAL ff_siso : t_dp_siso; -- SISO ready + SIGNAL ff_sosi : t_dp_sosi; -- SOSI + + SIGNAL i_snk_out : t_dp_siso := c_dp_siso_rdy; + +BEGIN + + -- Use i_snk_out with defaults to force unused snk_out bits and fields to '0' + snk_out <= i_snk_out; + + gen_wires : IF c_diff_latency = 0 GENERATE -- g_out_latency = g_in_latency + i_snk_out <= src_in; -- SISO + src_out <= snk_in; -- SOSI + END GENERATE gen_wires; + + + no_fifo : IF c_diff_latency > 0 GENERATE -- g_out_latency > g_in_latency + -- Go from g_in_latency to required larger g_out_latency + u_latency : ENTITY work.dp_latency_increase + GENERIC MAP ( + g_in_latency => g_in_latency, + g_incr_latency => c_diff_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => i_snk_out, + snk_in => snk_in, + -- ST source + src_in => src_in, + src_out => src_out + ); + END GENERATE no_fifo; + + + gen_fifo : IF c_diff_latency < 0 GENERATE -- g_out_latency < g_in_latency + -- Register [0] contains the FIFO output with zero ready latency + ff_sosi <= fifo_reg(0); + + p_clk_fifo : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + fifo_reg <= (OTHERS=>c_dp_sosi_rst); + fifo_usedw <= (OTHERS=>'0'); + fifo_ful <= '0'; + fifo_emp <= '1'; + ELSIF rising_edge(clk) THEN + fifo_reg <= nxt_fifo_reg; + fifo_usedw <= nxt_fifo_usedw; + fifo_ful <= nxt_fifo_ful; + fifo_emp <= nxt_fifo_emp; + END IF; + END PROCESS; + + -- Pass on frame level flow control + i_snk_out.xon <= src_in.xon; + + p_snk_out_ready : PROCESS(fifo_reg, ff_siso, snk_in) + BEGIN + i_snk_out.ready <= '0'; + IF ff_siso.ready='1' THEN + -- Default snk_out ready when the source is ready. + i_snk_out.ready <= '1'; + ELSE + -- Extra snk_out ready to look ahead for src_in RL = 0. + -- The fifo_reg[h:0] size is g_in_latency+1 number of SOSI values. + -- . The fifo_reg[h:1] provide free space for h=g_in_latency nof data + -- when snk_out.ready is pulled low, because then there can still + -- arrive g_in_latency nof new data with snk_in.valid asserted. + -- . The [0] is the registered output SOSI value with RL=0. Therefore + -- fifo_reg[0] can still accept a new input when ff_siso.ready is + -- low. If this assignment is omitted then the functionallity is + -- still OK, but the throughtput sligthly reduces. + IF fifo_reg(0).valid='0' THEN + i_snk_out.ready <= '1'; + ELSIF fifo_reg(1).valid='0' THEN + i_snk_out.ready <= NOT(snk_in.valid); + END IF; + END IF; + END PROCESS; + + p_fifo_reg : PROCESS(fifo_reg, ff_siso, snk_in) + BEGIN + -- Keep or shift the fifo_reg dependent on ff_siso.ready, no need to explicitly check fifo_reg().valid + nxt_fifo_reg <= fifo_reg; + IF ff_siso.ready='1' THEN + nxt_fifo_reg(c_high-1 DOWNTO 0) <= fifo_reg(c_high DOWNTO 1); + nxt_fifo_reg(c_high).valid <= '0'; + nxt_fifo_reg(c_high).sync <= '0'; + nxt_fifo_reg(c_high).sop <= '0'; + nxt_fifo_reg(c_high).eop <= '0'; + -- Forcing the nxt_fifo_reg[h] control fields to '0' is robust, but not + -- strictly necessary, because the control fields in fifo_reg[h] will + -- have been set to '0' already earlier due to the snk_in when + -- ff_siso.ready was '0'. + END IF; + + -- Put input data at the first available location dependent on ff_siso.ready, no need to explicitly check snk_in.valid + IF fifo_reg(0).valid='0' THEN + nxt_fifo_reg(0) <= snk_in; -- fifo_reg is empty + ELSE + -- The fifo_reg is not empty, so filled to some extend + FOR I IN 1 TO c_high LOOP + IF fifo_reg(I).valid='0' THEN + IF ff_siso.ready='0' THEN + nxt_fifo_reg(I) <= snk_in; + ELSE + nxt_fifo_reg(I-1) <= snk_in; + END IF; + EXIT; + END IF; + END LOOP; + + -- Default the input sync during input data valid is only passed on with the valid input data. + -- When c_pass_sync_during_not_valid is enabled then the input sync during input data not valid is passed on via the head fifo_reg(0) if the fifo_reg is empty. + IF c_pass_sync_during_not_valid=TRUE AND snk_in.sync='1' AND snk_in.valid='0' THEN + -- Otherwise for input sync during input data not valid we need to insert the input sync at the last location with valid data independent of ff_siso.ready, to avoid that it gets lost. + -- For streams that do not use the sync this logic will be void and optimize away by synthesis, because then snk_in.sync = '0' fixed. + IF fifo_reg(c_high).valid='1' THEN -- fifo_reg is full + nxt_fifo_reg(c_high).sync <= '1'; -- insert input sync + ELSE + FOR I IN c_high-1 DOWNTO 0 LOOP -- fifo_reg is filled to some extend, so not full and not empty + IF fifo_reg(I).valid='1' THEN + nxt_fifo_reg(I+1).sync <= '0'; -- overrule default sync assignment + nxt_fifo_reg(I).sync <= '1'; -- insert input sync + EXIT; + END IF; + END LOOP; + END IF; + END IF; + END IF; + END PROCESS; + + p_fifo_usedw : PROCESS(nxt_fifo_reg) + BEGIN + nxt_fifo_usedw <= (OTHERS=>'0'); + FOR I IN c_high DOWNTO 0 LOOP + IF nxt_fifo_reg(I).valid='1' THEN + nxt_fifo_usedw <= TO_UVEC(I+1, c_usedw_w); + EXIT; + END IF; + END LOOP; + END PROCESS; + + fifo_reg_valid <= func_dp_stream_arr_get(fifo_reg, "VALID"); + + nxt_fifo_ful <= '1' WHEN TO_UINT(nxt_fifo_usedw)>=c_high+1 ELSE '0'; -- using >= or = is equivalent here + nxt_fifo_emp <= '1' WHEN TO_UINT(nxt_fifo_usedw) =0 ELSE '0'; + + -- Go from 0 FIFO latency to required g_out_latency (only wires when g_out_latency=0) + u_latency : ENTITY work.dp_latency_increase + GENERIC MAP ( + g_in_latency => 0, + g_incr_latency => g_out_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => ff_siso, + snk_in => ff_sosi, + -- ST source + src_in => src_in, + src_out => src_out + ); + END GENERATE gen_fifo; + +END rtl; diff --git a/cores/base/dp/dp_components/dp_latency_increase.vhd b/cores/base/dp/dp_components/dp_latency_increase.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2ddc1cfe38489449f18a9e1a4a60c0b674bd9ce6 --- /dev/null +++ b/cores/base/dp/dp_components/dp_latency_increase.vhd @@ -0,0 +1,117 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +-- Purpose: +-- Typically used in dp_latency_adapter. +-- Description: +-- Increase the output ready latency by g_incr_latency compared to the input +-- ready latency g_in_latency. Hence the output latency becomes g_in_latency +-- + g_incr_latency. +-- Remark: +-- . The SOSI data stream signals (i.e. data, empty, channel, err) are passed +-- on as wires. +-- . The out_sync, out_val, out_sop and out_eop are internally AND with the +-- delayed src_in.ready, this is only truely necessary if the input ready +-- latency is 0, but it does not harm to do it also when the input ready +-- latency > 0. However to easy achieving P&R timing it is better to not have +-- unnessary logic in the combinatorial path of out_sync, out_val, out_sop +-- and out_eop, therefore the AND with reg_val is only generated when +-- g_in_latency=0. + +ENTITY dp_latency_increase IS + GENERIC ( + g_in_latency : NATURAL := 0; -- >= 0 + g_incr_latency : NATURAL := 2 -- >= 0 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- ST sink + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + -- ST source + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END dp_latency_increase; + + +ARCHITECTURE rtl OF dp_latency_increase IS + + CONSTANT c_out_latency : NATURAL := g_in_latency + g_incr_latency; + + SIGNAL reg_ready : STD_LOGIC_VECTOR(c_out_latency DOWNTO 0); + SIGNAL reg_val : STD_LOGIC; + + SIGNAL i_snk_out : t_dp_siso := c_dp_siso_rdy; + +BEGIN + + -- Use i_snk_out with defaults to force unused snk_out bits and fields to '0' + snk_out <= i_snk_out; + + -- Support wires only for g_incr_latency=0 + no_latency : IF g_incr_latency=0 GENERATE + i_snk_out <= src_in; -- SISO + src_out <= snk_in; -- SOSI + END GENERATE no_latency; + + gen_latency : IF g_incr_latency>0 GENERATE + -- SISO + reg_ready(0) <= src_in.ready; -- use reg_ready(0) to combinatorially store src_in.ready + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + reg_ready(c_out_latency DOWNTO 1) <= (OTHERS=>'0'); + ELSIF rising_edge(clk) THEN + reg_ready(c_out_latency DOWNTO 1) <= reg_ready(c_out_latency-1 DOWNTO 0); + END IF; + END PROCESS; + + i_snk_out.xon <= src_in.xon; -- Pass on frame level flow control + i_snk_out.ready <= reg_ready(g_incr_latency); -- Adjust ready latency + + -- SOSI + gen_out : IF g_in_latency/=0 GENERATE + src_out <= snk_in; + END GENERATE; + gen_zero_out : IF g_in_latency=0 GENERATE + reg_val <= reg_ready(c_out_latency); + + p_src_out : PROCESS(snk_in, reg_val) + BEGIN + src_out <= snk_in; + src_out.sync <= snk_in.sync AND reg_val; + src_out.valid <= snk_in.valid AND reg_val; + src_out.sop <= snk_in.sop AND reg_val; + src_out.eop <= snk_in.eop AND reg_val; + END PROCESS; + END GENERATE; + END GENERATE gen_latency; + +END rtl; diff --git a/cores/base/dp/dp_components/hdllib.cfg b/cores/base/dp/dp_components/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..28b973ab8d93d763520924ea3e1a4c7c0394dbc8 --- /dev/null +++ b/cores/base/dp/dp_components/hdllib.cfg @@ -0,0 +1,22 @@ +hdl_lib_name = dp_components +hdl_library_clause_name = dp_components_lib +hdl_lib_uses_synth = common_pkg common_components dp_pkg +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + dp_latency_increase.vhd + dp_latency_adapter.vhd + dp_hold_ctrl.vhd + dp_hold_input.vhd + +test_bench_files = + tb_dp_latency_adapter.vhd + +regression_test_vhdl = + tb_dp_latency_adapter.vhd + +[modelsim_project_file] + + +[quartus_project_file] diff --git a/cores/base/dp/dp_components/tb_dp_latency_adapter.vhd b/cores/base/dp/dp_components/tb_dp_latency_adapter.vhd new file mode 100644 index 0000000000000000000000000000000000000000..b4b1633b68030f936d48417f5225bee3bb166d72 --- /dev/null +++ b/cores/base/dp/dp_components/tb_dp_latency_adapter.vhd @@ -0,0 +1,254 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + +ENTITY tb_dp_latency_adapter IS +END tb_dp_latency_adapter; + + +ARCHITECTURE tb OF tb_dp_latency_adapter IS + + -- See tb_dp_pkg.vhd for explanation and run time + + SUBTYPE t_dut_range IS INTEGER RANGE -1 to INTEGER'HIGH; + + TYPE t_dut_natural_arr IS ARRAY (t_dut_range RANGE <>) OF NATURAL; + TYPE t_dut_data_arr IS ARRAY (t_dut_range RANGE <>) OF STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + TYPE t_dut_logic_arr IS ARRAY (t_dut_range RANGE <>) OF STD_LOGIC; -- can not use STD_LOGIC_VECTOR because of integer range + + -- TX ready latency to DUT chain + CONSTANT c_tx_latency : NATURAL := 3; + CONSTANT c_tx_void : NATURAL := sel_a_b(c_tx_latency, 1, 0); -- used to avoid empty range VHDL warnings when c_tx_latency=0 + + CONSTANT c_tx_offset_sop : NATURAL := 3; + CONSTANT c_tx_period_sop : NATURAL := 7; -- sop in data valid cycle 3, 10, 17, ... + CONSTANT c_tx_offset_eop : NATURAL := 5; -- eop in data valid cycle 5, 12, 19, ... + CONSTANT c_tx_period_eop : NATURAL := c_tx_period_sop; + CONSTANT c_tx_offset_sync : NATURAL := 3; -- sync in data valid cycle 3, 20, 37, ... + CONSTANT c_tx_period_sync : NATURAL := 17; + + -- The TB supports using 1 or more dp_latency_adapter Devices Under Test in a chain. DUT 0 is the first DUT and it + -- gets the tx_data from this test bench, which has index -1. Each next DUT gets its input from the previous DUT, + -- hence the ready latency between DUTs should be the same. + -- The output latency of the previous must equal the input latency of the next DUT, hence it is sufficient to define + -- only the DUT output latencies. + --CONSTANT c_dut_latency : t_dut_natural_arr := (c_tx_latency, 3); -- verify single dp_latency_adapter with only wires + --CONSTANT c_dut_latency : t_dut_natural_arr := (c_tx_latency, 4); -- verify single dp_latency_adapter with latency increase + --CONSTANT c_dut_latency : t_dut_natural_arr := (c_tx_latency, 1); -- verify single dp_latency_adapter with latency decrease + CONSTANT c_dut_latency : t_dut_natural_arr := (c_tx_latency, 1, 2, 0, 5, 5, 2, 1, 0, 7); + + -- The nof dut latencies in the c_dut_latency array automatically also defines the nof DUTs c_nof_dut. + CONSTANT c_nof_dut : NATURAL := c_dut_latency'HIGH+1; + + -- RX ready latency from DUT chain + CONSTANT c_rx_latency : NATURAL := c_dut_latency(c_nof_dut-1); + + CONSTANT c_verify_en_wait : NATURAL := 10+c_nof_dut*2; -- wait some cycles before asserting verify enable + + CONSTANT c_empty_offset : NATURAL := 1; + CONSTANT c_channel_offset : NATURAL := 2; + + CONSTANT c_random_w : NATURAL := 19; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL clk : STD_LOGIC := '0'; + SIGNAL rst : STD_LOGIC; + SIGNAL sync : STD_LOGIC; + SIGNAL lfsr1 : STD_LOGIC_VECTOR(c_random_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL lfsr2 : STD_LOGIC_VECTOR(c_random_w DOWNTO 0) := (OTHERS=>'0'); + + SIGNAL cnt_dat : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL cnt_val : STD_LOGIC; + SIGNAL cnt_en : STD_LOGIC; + + SIGNAL tx_data : t_dp_data_arr(0 TO c_tx_latency + c_tx_void); + SIGNAL tx_val : STD_LOGIC_VECTOR(0 TO c_tx_latency + c_tx_void); + + SIGNAL in_ready : STD_LOGIC; + SIGNAL in_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL in_sync : STD_LOGIC; + SIGNAL in_val : STD_LOGIC; + SIGNAL in_sop : STD_LOGIC; + SIGNAL in_eop : STD_LOGIC; + + -- DUT index -1 = in_data + SIGNAL dut_ready : t_dut_logic_arr(-1 TO c_nof_dut-1); -- SISO + SIGNAL dut_data : t_dut_data_arr(-1 TO c_nof_dut-1); -- SOSI + SIGNAL dut_empty : t_dut_data_arr(-1 TO c_nof_dut-1) := (OTHERS=>(OTHERS=>'0')); + SIGNAL dut_channel : t_dut_data_arr(-1 TO c_nof_dut-1) := (OTHERS=>(OTHERS=>'0')); + SIGNAL dut_sync : t_dut_logic_arr(-1 TO c_nof_dut-1); + SIGNAL dut_val : t_dut_logic_arr(-1 TO c_nof_dut-1); + SIGNAL dut_sop : t_dut_logic_arr(-1 TO c_nof_dut-1); + SIGNAL dut_eop : t_dut_logic_arr(-1 TO c_nof_dut-1); + -- DUT index c_nof_dut-1 = out_data + SIGNAL dut_siso : t_dp_siso_arr(-1 TO c_nof_dut-1); + SIGNAL dut_sosi : t_dp_sosi_arr(-1 TO c_nof_dut-1) := (OTHERS=>c_dp_sosi_rst); + + SIGNAL out_ready : STD_LOGIC; + SIGNAL prev_out_ready : STD_LOGIC_VECTOR(0 TO c_rx_latency); + SIGNAL out_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL out_empty : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL out_channel : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL out_sync : STD_LOGIC; + SIGNAL out_val : STD_LOGIC; + SIGNAL out_sop : STD_LOGIC; + SIGNAL out_eop : STD_LOGIC; + SIGNAL hold_out_sop : STD_LOGIC; + SIGNAL prev_out_data : STD_LOGIC_VECTOR(out_data'RANGE); + + SIGNAL state : t_dp_state_enum; + + SIGNAL verify_en : STD_LOGIC; + SIGNAL verify_done : STD_LOGIC; + + SIGNAL exp_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0) := TO_UVEC(19555, c_dp_data_w); + +BEGIN + + -- Use intervals marked by sync to start a new test named by state. + -- + -- Under all circumstances the out_data should not mis or duplicate a count + -- while out_val is asserted as checked by p_verify. + -- The throughput must remain 100%, with only some increase in latency. This + -- can be checked manually by checking that cnt_val does not toggle when the + -- out_ready is asserted continuously. E.g. check that the out_data value + -- is sufficiently high given the number of sync intervals that have passed. + -- + -- Stimuli to verify the dp_latency_adapter DUT: + -- + -- * Use various ready latency combinations in c_dut_latency: + -- . c_in_latency > c_out_latency = 0 + -- . c_in_latency > c_out_latency > 0 + -- . c_in_latency = c_out_latency = 0 + -- . c_in_latency = c_out_latency > 0 + -- . 0 = c_in_latency < c_out_latency + -- . 0 < c_in_latency < c_out_latency + -- + -- * Manipulate the stimuli in: + -- . p_cnt_en : cnt_en not always active when in_ready is asserted + -- . p_out_ready : out_ready not always active + + clk <= NOT clk OR tb_end AFTER clk_period/2; + rst <= '1', '0' AFTER clk_period*7; + + -- Sync interval + proc_dp_sync_interval(clk, sync); + + -- Input data + cnt_val <= in_ready AND cnt_en; + + proc_dp_cnt_dat(rst, clk, cnt_val, cnt_dat); + proc_dp_tx_data(c_tx_latency, rst, clk, cnt_val, cnt_dat, tx_data, tx_val, in_data, in_val); + proc_dp_tx_ctrl(c_tx_offset_sync, c_tx_period_sync, in_data, in_val, in_sync); + proc_dp_tx_ctrl(c_tx_offset_sop, c_tx_period_sop, in_data, in_val, in_sop); + proc_dp_tx_ctrl(c_tx_offset_eop, c_tx_period_eop, in_data, in_val, in_eop); + + -- Stimuli control + proc_dp_count_en(rst, clk, sync, lfsr1, state, verify_done, tb_end, cnt_en); + proc_dp_out_ready(rst, clk, sync, lfsr2, out_ready); + + -- Output verify + proc_dp_verify_en(c_verify_en_wait, rst, clk, sync, verify_en); + proc_dp_verify_data("out_data", c_rx_latency, clk, verify_en, out_ready, out_val, out_data, prev_out_data); + proc_dp_verify_valid(c_rx_latency, clk, verify_en, out_ready, prev_out_ready, out_val); + proc_dp_verify_ctrl(c_tx_offset_sync, c_tx_period_sync, "sync", clk, verify_en, out_data, out_val, out_sync); + proc_dp_verify_ctrl(c_tx_offset_sop, c_tx_period_sop, "sop", clk, verify_en, out_data, out_val, out_sop); + proc_dp_verify_ctrl(c_tx_offset_eop, c_tx_period_eop, "eop", clk, verify_en, out_data, out_val, out_eop); + proc_dp_verify_sop_and_eop(c_rx_latency, FALSE, clk, out_val, out_val, out_sop, out_eop, hold_out_sop); -- Verify that sop and eop come in pairs, no check on valid between eop and sop + proc_dp_verify_other_sosi("empty", INCR_UVEC(out_data, c_empty_offset), clk, verify_en, out_empty); + proc_dp_verify_other_sosi("channel", INCR_UVEC(out_data, c_channel_offset), clk, verify_en, out_channel); + + -- Check that the test has ran at all + proc_dp_verify_value(e_equal, clk, verify_done, exp_data, out_data); + + ------------------------------------------------------------------------------ + -- Chain of 1 or more dp_latency_adapter DUTs + -- + -- . Note this also models a series of streaming modules in a data path + -- + ------------------------------------------------------------------------------ + + -- Map the test bench tx counter data to the input of the chain + in_ready <= dut_ready(-1); + dut_data(-1) <= in_data; + dut_empty(-1) <= INCR_UVEC(in_data, c_empty_offset); + dut_channel(-1) <= INCR_UVEC(in_data, c_channel_offset); + dut_sync(-1) <= in_sync; + dut_val(-1) <= in_val; + dut_sop(-1) <= in_sop; + dut_eop(-1) <= in_eop; + + -- map sl, slv to record + dut_ready(-1) <= dut_siso(-1).ready; -- SISO + dut_sosi(-1).data(c_dp_data_w-1 DOWNTO 0) <= dut_data(-1); -- SOSI + dut_sosi(-1).empty <= dut_empty(-1)(c_dp_empty_w-1 DOWNTO 0); + dut_sosi(-1).channel <= dut_channel(-1)(c_dp_channel_w-1 DOWNTO 0); + dut_sosi(-1).sync <= dut_sync(-1); + dut_sosi(-1).valid <= dut_val(-1); + dut_sosi(-1).sop <= dut_sop(-1); + dut_sosi(-1).eop <= dut_eop(-1); + + gen_chain : FOR I IN 0 TO c_nof_dut-1 GENERATE + dut : ENTITY work.dp_latency_adapter + GENERIC MAP ( + g_in_latency => c_dut_latency(I-1), + g_out_latency => c_dut_latency(I) + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => dut_siso(I-1), + snk_in => dut_sosi(I-1), + -- ST source + src_in => dut_siso(I), + src_out => dut_sosi(I) + ); + END GENERATE; + + -- map record to sl, slv + dut_siso(c_nof_dut-1).ready <= dut_ready(c_nof_dut-1); -- SISO + dut_data(c_nof_dut-1) <= dut_sosi(c_nof_dut-1).data(c_dp_data_w-1 DOWNTO 0); -- SOSI + dut_empty(c_nof_dut-1)(c_dp_empty_w-1 DOWNTO 0) <= dut_sosi(c_nof_dut-1).empty; + dut_channel(c_nof_dut-1)(c_dp_channel_w-1 DOWNTO 0) <= dut_sosi(c_nof_dut-1).channel; + dut_sync(c_nof_dut-1) <= dut_sosi(c_nof_dut-1).sync; + dut_val(c_nof_dut-1) <= dut_sosi(c_nof_dut-1).valid; + dut_sop(c_nof_dut-1) <= dut_sosi(c_nof_dut-1).sop; + dut_eop(c_nof_dut-1) <= dut_sosi(c_nof_dut-1).eop; + + -- Map the output of the DUT chain to the test bench output data + dut_ready(c_nof_dut-1) <= out_ready; + out_data <= dut_data(c_nof_dut-1); + out_empty <= dut_empty(c_nof_dut-1); + out_channel <= dut_channel(c_nof_dut-1); + out_sync <= dut_sync(c_nof_dut-1); + out_val <= dut_val(c_nof_dut-1); + out_sop <= dut_sop(c_nof_dut-1); + out_eop <= dut_eop(c_nof_dut-1); + +END tb; diff --git a/cores/base/dp/dp_counter/dp_counter.vhd b/cores/base/dp/dp_counter/dp_counter.vhd new file mode 100644 index 0000000000000000000000000000000000000000..6e92fe1af263e3fd51c38223b00c3a44318db97a --- /dev/null +++ b/cores/base/dp/dp_counter/dp_counter.vhd @@ -0,0 +1,213 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2017 +-- 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: +-- . Pipeline wrapper around dp_counter_func +-- Description: +-- . See dp_counter_func. +-- . dp_counter_func contains only functional logic (no pipelining). +-- . This wrapper adds pipelining for the source side outputs (g_pipeline_src_out) +-- and source side inputs (g_pipeline_src_in). +-- Usage: +-- . When g_nof_counters = 1 then dp_counter can reshape the block sizes in a +-- sync intervals, as is done in dp_block_reshape.vhd +-- . When g_nof_counters = 2 then dp_counter can reshape the block sizes and the +-- sync interval, as is done in dp_block_reshape_sync.vhd +-- . When g_nof_counters > 1 then the dp_counter count_src_out_arr provides the +-- block indices for an array of data. The g_nof_counters then defines the +-- dimension of the array within a sync interval. For example: +-- +-- a[N][M][P] with N=3, M=5, p=10 will have N*M*P = 150 valid per sync interval. +-- +-- The dp_counter can then provide for each valid in the sync interval the +-- corresponding array index (n, m, p), using: +-- +-- g_nof_counters = 3 +-- g_range_start = (0, 0, 0) +-- g_range_stop = (3, 5, 10) +-- g_range_step = (1, 1, 1) +-- +-- Remarks: +-- . Unfortunately it is not possible to use a generic in the definition of another generic. Therefore the +-- g_range_* generics are defined as unconstraint. +-- . When dp_counter is instanciated the length of the g_range_* generics must at least fit g_nof_counters. +-- In theory count_src_out_arr could be declared as unconstraint and then g_nof_counters could be derived +-- from the length of count_src_out_arr, but it is more clear to explitely to declare g_nof_counters. +-- +-- 31-05-2018 J Hargreaves added count_offset_in_arr input +-- This signal can be used to apply an offset to the start count +-- USAGE +-- Do not connect the signal if it is not needed: It will consume extra resources +-- It only works when g_range_step(i) is 1 +-- It only works when g_range_start(i) + count_offset_in_arr(i) < g_range_stop(i) +-- Any other useage will break counters >= stage i + +LIBRARY IEEE, dp_pipeline_lib, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_counter IS + GENERIC ( + g_nof_counters : NATURAL := 1; + g_range_start : t_nat_natural_arr; -- range must fit (g_nof_counters-1 DOWNTO 0) + g_range_stop : t_nat_natural_arr; -- range must fit (g_nof_counters-1 DOWNTO 0) + g_range_step : t_nat_natural_arr; -- range must fit (g_nof_counters-1 DOWNTO 0) + g_pipeline_src_out : NATURAL := 1; -- Pipeline source outputs (data,valid,sop,eop etc) + g_pipeline_src_in : NATURAL := 0 -- Pipeline source inputs (ready,xon). This will also pipeline src_out. + ); + PORT ( + clk : IN STD_LOGIC; + rst : IN STD_LOGIC; + + snk_in : IN t_dp_sosi; + snk_out : OUT t_dp_siso; + + src_out : OUT t_dp_sosi; + src_in : IN t_dp_siso := c_dp_siso_rdy; + + count_offset_in_arr : IN t_nat_natural_arr(g_nof_counters-1 DOWNTO 0) := (OTHERS=>0); + count_src_out_arr : OUT t_dp_sosi_arr(g_nof_counters-1 DOWNTO 0) + ); +END dp_counter; + + +ARCHITECTURE wrap OF dp_counter IS + + -- force downto range for unconstraint g_range generics + CONSTANT c_range_len : NATURAL := g_range_start'LENGTH; -- g_nof_counters must be <= c_range_len + CONSTANT c_range_start : t_nat_natural_arr(c_range_len-1 DOWNTO 0) := g_range_start; + CONSTANT c_range_stop : t_nat_natural_arr(c_range_len-1 DOWNTO 0) := g_range_stop; + CONSTANT c_range_step : t_nat_natural_arr(c_range_len-1 DOWNTO 0) := g_range_step; + + CONSTANT c_use_dp_pipeline : BOOLEAN := (g_pipeline_src_out>0 AND g_pipeline_src_in=0); + CONSTANT c_use_dp_pipeline_ready : BOOLEAN := (g_pipeline_src_in>0); + + SIGNAL dp_counter_func_src_out_arr : t_dp_sosi_arr(g_nof_counters-1 DOWNTO 0); + +BEGIN + + ------------------------------------------------------------------------------ + -- dp_counter_func + ------------------------------------------------------------------------------ + u_dp_counter_func : ENTITY work.dp_counter_func + GENERIC MAP ( + g_nof_counters => g_nof_counters, + g_range_start => c_range_start, + g_range_stop => c_range_stop, + g_range_step => c_range_step + ) + PORT MAP ( + rst => rst, + clk => clk, + + count_en => snk_in.valid, + + count_offset_in_arr => count_offset_in_arr, + count_src_out_arr => dp_counter_func_src_out_arr + ); + + ------------------------------------------------------------------------------ + -- dp_pipeline + ------------------------------------------------------------------------------ + gen_dp_pipeline : IF c_use_dp_pipeline = TRUE GENERATE + u_dp_pipeline_snk_in : ENTITY dp_pipeline_lib.dp_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline_src_out + ) + PORT MAP ( + clk => clk, + rst => rst, + + snk_in => snk_in, + snk_out => snk_out, + + src_out => src_out, + src_in => src_in + ); + + gen_dp_pipeline_count_src_out_arr : FOR i IN 0 TO g_nof_counters-1 GENERATE + u_dp_pipeline_count_src_out_arr : ENTITY dp_pipeline_lib.dp_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline_src_out + ) + PORT MAP ( + clk => clk, + rst => rst, + + snk_in => dp_counter_func_src_out_arr(i), + + src_out => count_src_out_arr(i), + src_in => src_in + ); + END GENERATE; + END GENERATE; + + ------------------------------------------------------------------------------ + -- dp_pipeline_ready + ------------------------------------------------------------------------------ + gen_dp_pipeline_ready : IF c_use_dp_pipeline_ready = TRUE GENERATE + u_dp_pipeline_ready : ENTITY dp_pipeline_lib.dp_pipeline_ready + GENERIC MAP ( + g_in_latency => 1 + ) + PORT MAP ( + clk => clk, + rst => rst, + + snk_in => snk_in, + snk_out => snk_out, + + src_out => src_out, + src_in => src_in + ); + + gen_dp_pipeline_ready_count_src_out_arr : FOR i IN 0 TO g_nof_counters-1 GENERATE + u_dp_pipeline_ready_count_src_out_arr : ENTITY dp_pipeline_lib.dp_pipeline_ready + GENERIC MAP ( + g_in_latency => 1 + ) + PORT MAP ( + clk => clk, + rst => rst, + + snk_in => dp_counter_func_src_out_arr(i), + + src_out => count_src_out_arr(i), + src_in => src_in + ); + END GENERATE; + END GENERATE; + + ------------------------------------------------------------------------------ + -- No pipelining + ------------------------------------------------------------------------------ + no_dp_pipeline : IF c_use_dp_pipeline=FALSE AND c_use_dp_pipeline_ready=FALSE GENERATE + src_out <= snk_in; + snk_out <= src_in; + + count_src_out_arr <= dp_counter_func_src_out_arr; + END GENERATE; + +END wrap; diff --git a/cores/base/dp/dp_counter/dp_counter_func.vhd b/cores/base/dp/dp_counter/dp_counter_func.vhd new file mode 100644 index 0000000000000000000000000000000000000000..188250c85c45a2fcdb8da3c40caae92b244b4316 --- /dev/null +++ b/cores/base/dp/dp_counter/dp_counter_func.vhd @@ -0,0 +1,139 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2017 +-- 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: +-- . Provide an array of counters with dimension carryover +-- Description: +-- . Provides g_nof_counters counters,[g_nof_counters-1]..[c0] +-- . Every counter is specified like Python's range(start,stop,step). +-- . c0 changes the fastest, c4 changes the slowest +-- . Faster changing dimensions carry over in slower changing dimensions +-- when dimension maximum is reached +-- . The outputs are in sync with / apply to src_out. +-- . range(0,1,1) = [0] is the smallest count range +-- Usage: +-- . The count values themselves (c0..c4) are very useful to tag streaming +-- data with corresponding ID indices. +-- . The extra outputs (e.g. c0_min, c0_max) can be used to trigger other +-- logic when minimum/maximum values per dimension are reached. + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_counter_func IS + GENERIC ( + g_nof_counters : NATURAL := 1; + g_range_start : t_nat_natural_arr; -- range must fit (g_nof_counters-1 DOWNTO 0) + g_range_stop : t_nat_natural_arr; -- range must fit (g_nof_counters-1 DOWNTO 0) + g_range_step : t_nat_natural_arr -- range must fit (g_nof_counters-1 DOWNTO 0) + ); + PORT ( + clk : IN STD_LOGIC; + rst : IN STD_LOGIC; + + count_en : IN STD_LOGIC; + + count_offset_in_arr : IN t_nat_natural_arr(g_nof_counters-1 DOWNTO 0) := (OTHERS=>0); + count_src_out_arr : OUT t_dp_sosi_arr(g_nof_counters-1 DOWNTO 0) + ); +END dp_counter_func; + + +ARCHITECTURE str OF dp_counter_func IS + + -- force downto range for unconstraint g_range generics + CONSTANT c_range_len : NATURAL := g_range_start'LENGTH; -- g_nof_counters must be <= c_range_len + CONSTANT c_range_start : t_nat_natural_arr(c_range_len-1 DOWNTO 0) := g_range_start; + CONSTANT c_range_stop : t_nat_natural_arr(c_range_len-1 DOWNTO 0) := g_range_stop; + CONSTANT c_range_step : t_nat_natural_arr(c_range_len-1 DOWNTO 0) := g_range_step; + + CONSTANT c_max_count_w : NATURAL := 32; + + SIGNAL count_en_arr : STD_LOGIC_VECTOR(g_nof_counters-1 DOWNTO 0); + SIGNAL check_max_arr : STD_LOGIC_VECTOR(g_nof_counters-1 DOWNTO 0); + SIGNAL count_init_arr : STD_LOGIC_VECTOR(g_nof_counters-1 DOWNTO 0); + SIGNAL count_min_arr : STD_LOGIC_VECTOR(g_nof_counters-1 DOWNTO 0); + SIGNAL count_max_arr : STD_LOGIC_VECTOR(g_nof_counters-1 DOWNTO 0); + TYPE t_count_arr IS ARRAY(g_nof_counters-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_max_count_w-1 DOWNTO 0); + SIGNAL count_arr : t_count_arr; + +BEGIN + + -------------------------------------------------------------------------------- + -- Counter control inputs + ------------------------------------------------------------------------------- + gen_dp_counter_func_single_input : FOR i IN 0 TO g_nof_counters-1 GENERATE + + gen_c0 : IF i=0 GENERATE + count_en_arr(i) <= count_en; + check_max_arr(i) <= count_en; + END GENERATE; + + gen_c1_upwards : IF i>0 GENERATE + count_en_arr(i) <= count_init_arr(i-1) OR count_min_arr(i-1); + check_max_arr(i) <= count_max_arr(i-1); + END GENERATE; + + END GENERATE; + + -------------------------------------------------------------------------------- + -- Array of dp_counter_func_single instances + ------------------------------------------------------------------------------- + gen_dp_counter_func_single : FOR i IN 0 TO g_nof_counters-1 GENERATE + u_dp_counter_func_single : ENTITY work.dp_counter_func_single + GENERIC MAP ( + g_range_start => c_range_start(i), + g_range_stop => c_range_stop(i), + g_range_step => c_range_step(i) + ) + PORT MAP ( + rst => rst, + clk => clk, + + count_en => count_en_arr(i), + check_max => check_max_arr(i), + count_offset => count_offset_in_arr(i), + + count => count_arr(i), + count_init => count_init_arr(i), + count_min => count_min_arr(i), + count_max => count_max_arr(i) + ); + END GENERATE; + + -------------------------------------------------------------------------------- + -- Counter outputs + ------------------------------------------------------------------------------- + gen_dp_counter_func_single_output : FOR i IN 0 TO g_nof_counters-1 GENERATE + count_src_out_arr(i).sync <= '0'; -- not used, force to '0' to avoid toggling between '0' and 'X' in Wave window + -- when sync is passed on through other components + count_src_out_arr(i).sop <= count_min_arr(i); + count_src_out_arr(i).eop <= count_max_arr(i); + count_src_out_arr(i).valid <= count_en; + count_src_out_arr(i).data <= RESIZE_DP_DATA(count_arr(i)); + END GENERATE; + +END str; diff --git a/cores/base/dp/dp_counter/dp_counter_func_single.vhd b/cores/base/dp/dp_counter/dp_counter_func_single.vhd new file mode 100644 index 0000000000000000000000000000000000000000..7281e35970e3b8a343554998e78dde688e9015ba --- /dev/null +++ b/cores/base/dp/dp_counter/dp_counter_func_single.vhd @@ -0,0 +1,152 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2017 +-- 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: +-- . Simple counter with start, stop, step; used in dp_counter_func. +-- Description: +-- . range(0,2,1) = [0, 1] is the smallest count range allowed +-- Usage: +-- . Not for standalone use; part of dp_counter_func. + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_counter_func_single IS --FIXME move this to common + GENERIC ( + g_range_start : NATURAL; -- (start,stop,step) like python range(start, stop, step) + g_range_stop : NATURAL; + g_range_step : NATURAL + ); + PORT ( + clk : IN STD_LOGIC; + rst : IN STD_LOGIC; + + count_en : IN STD_LOGIC; + check_max : IN STD_LOGIC := '1'; + count_offset : NATURAL := 0; + + count : OUT STD_LOGIC_VECTOR(31 DOWNTO 0); + count_init : OUT STD_LOGIC; -- Pulses at first init + count_min : OUT STD_LOGIC; --Pulses when count=start + count_max : OUT STD_LOGIC --Pulses when count=max + ); +END dp_counter_func_single; + + +ARCHITECTURE rtl OF dp_counter_func_single IS + + -- The user defines the counters like a Python range(start,stop,step) in which the stop value + -- is never actually reached. Calculate the actual maximum values here. + -- . Example: + -- . range(0,4,2) = [0, 2] + -- . range(0,5,2) = [0, 2, 4] + -- . range(0,6,2) = [0, 2, 4] + -- . range(0,7,2) = [0, 2, 4, 6] + -- . range(1,7,2) = [1, 3, 5] + -- . The maximum value is: start+((stop-1-start)/step)*step + CONSTANT c_nof_count : NATURAL := (g_range_stop-1-g_range_start)/g_range_step + 1; + CONSTANT c_count_max : NATURAL := g_range_start+(c_nof_count-1)*g_range_step; + CONSTANT c_count_w : NATURAL := ceil_log2(c_count_max+1); + + TYPE t_reg IS RECORD + count_en : STD_LOGIC; + count : STD_LOGIC_VECTOR(c_count_w-1 DOWNTO 0); + count_min : STD_LOGIC; + count_max : STD_LOGIC; + count_init : STD_LOGIC; + END RECORD; + + SIGNAL r, nxt_r : t_reg; + +BEGIN + + -------------------------------------------------------------------------------- + -- Combinational logic + -------------------------------------------------------------------------------- + p_comb : PROCESS(rst, r, count_en, check_max, count_offset) + VARIABLE v : t_reg; + BEGIN + v := r; + v.count_min := '0'; + v.count_max := '0'; + v.count_init := '0'; + + IF count_en='1' THEN + + -- Start counting / init + IF r.count_en='0' THEN + v.count_en := '1'; + v.count := TO_UVEC(g_range_start+count_offset, c_count_w); + v.count_min := '1'; + v.count_init := '1'; + -- keep counting + ELSE + v.count := INCR_UVEC(r.count, g_range_step); + IF c_count_max>0 AND check_max='1' AND r.count = TO_UVEC(c_count_max-g_range_step, c_count_w) THEN -- count max almost reached + v.count_max := '1'; + ELSIF r.count = TO_UVEC(c_count_max, c_count_w) THEN -- count max reached + -- Reset count to start value + v.count := TO_UVEC(g_range_start, c_count_w); + v.count_min := '1'; + END IF; + END IF; + + -- If the maximum count is 0, count_max is always high. + IF c_count_max=0 THEN + v.count_max := '1'; + END IF; + + ELSIF check_max='1' AND r.count = TO_UVEC(c_count_max, c_count_w) THEN -- count max reached + v.count_max := '1'; + END IF; + + IF rst = '1' THEN + v.count_en := '0'; + v.count := (OTHERS=>'0'); + v.count_min := '0'; + v.count_max := '0'; + v.count_init := '0'; + END IF; + + nxt_r <= v; + END PROCESS; + + -------------------------------------------------------------------------------- + -- Register stage + -------------------------------------------------------------------------------- + r <= nxt_r WHEN rising_edge(clk); + + -------------------------------------------------------------------------------- + -- Outputs + -------------------------------------------------------------------------------- + count(31 DOWNTO c_count_w) <= (OTHERS=>'0'); + count(c_count_w-1 DOWNTO 0) <= nxt_r.count; + count_init <= nxt_r.count_init; + count_min <= nxt_r.count_min; + count_max <= nxt_r.count_max; + + +END rtl; diff --git a/cores/base/dp/dp_counter/hdllib.cfg b/cores/base/dp/dp_counter/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fb79f9a247cdd852618c3c2efcc21e5fa8608313 --- /dev/null +++ b/cores/base/dp/dp_counter/hdllib.cfg @@ -0,0 +1,23 @@ +hdl_lib_name = dp_counter +hdl_library_clause_name = dp_counter_lib +hdl_lib_uses_synth = dp_pipeline +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + dp_counter_func_single.vhd + dp_counter_func.vhd + dp_counter.vhd + +test_bench_files = + tb_dp_counter.vhd + tb_tb_dp_counter.vhd + +regression_test_vhdl = + tb_dp_counter_func.vhd + tb_tb_dp_counter.vhd + +[modelsim_project_file] + + +[quartus_project_file] diff --git a/cores/base/dp/dp_counter/tb_dp_counter.vhd b/cores/base/dp/dp_counter/tb_dp_counter.vhd new file mode 100644 index 0000000000000000000000000000000000000000..386a231b946fb2b37d10def31cfcb8562ea15267 --- /dev/null +++ b/cores/base/dp/dp_counter/tb_dp_counter.vhd @@ -0,0 +1,224 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2017 +-- 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: +-- Eric Kooistra, 23 Apr 2018 +-- Purpose: +-- . Test bench for dp_counter. +-- Description: +-- Functional details are verified by tb_dp_counter_func.vhd. This +-- tb_dp_counter.vhd shows the dp_counter with pipelining. +--Usage: +-- . as 10 +-- . run -all + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_str_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + +ENTITY tb_dp_counter IS + GENERIC ( + -- general + g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + -- dut + g_pipeline_src_out : NATURAL := 1; -- Pipeline source outputs (data,valid,sop,eop etc) + g_pipeline_src_in : NATURAL := 0; -- Pipeline source inputs (ready,xon). This will also pipeline src_out. + g_nof_counters : NATURAL := 3; + -- min range = [0,2,1] => (0,1) 'the Python way' + g_range_start : t_nat_natural_arr(9 DOWNTO 0) := (0,0,0,0,0,0,0, 1, 0, 0); + g_range_stop : t_nat_natural_arr(9 DOWNTO 0) := (2,2,2,2,2,2,7,16,16, 1); + g_range_step : t_nat_natural_arr(9 DOWNTO 0) := (1,1,1,1,1,1,2, 2, 2, 1) + ); +END tb_dp_counter; + + +ARCHITECTURE tb OF tb_dp_counter IS + + ------------------------------------------------------------------------------ + -- Clock & reset + ------------------------------------------------------------------------------ + CONSTANT c_clk_period : TIME := 5 ns; + CONSTANT c_max_count_w : NATURAL := 32; + + CONSTANT c_rl : NATURAL := 1; + CONSTANT c_data_w : NATURAL := 32; + CONSTANT c_data_init : NATURAL := 0; + + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC := '1'; + SIGNAL tb_end : STD_LOGIC := '0'; + + ------------------------------------------------------------------------------ + -- dp_counter + ------------------------------------------------------------------------------ + SIGNAL snk_in : t_dp_sosi := c_dp_sosi_rst; + SIGNAL snk_out : t_dp_siso; + SIGNAL src_out : t_dp_sosi; + SIGNAL src_in : t_dp_siso := c_dp_siso_rdy; + SIGNAL count_src_out_arr : t_dp_sosi_arr(g_nof_counters-1 DOWNTO 0); + SIGNAL period : NATURAL; + + FUNCTION calculate_period(g_counter : NATURAL) RETURN NATURAL IS + VARIABLE v_range_period : t_nat_natural_arr(g_counter DOWNTO 0); + VARIABLE v_period : NATURAL := 1; + BEGIN + FOR I IN 0 TO g_counter LOOP + v_range_period(I) := (g_range_stop(I)-1 - g_range_start(I)) / g_range_step(I) + 1; -- Python style range(start, stop, step) + v_period := v_period * v_range_period(I); + END LOOP; + RETURN v_period; + END; + + ------------------------------------------------------------------------------ + -- flow control + ------------------------------------------------------------------------------ + CONSTANT c_in_random_w : NATURAL := 15; + CONSTANT c_out_random_w : NATURAL := 16; -- use different length per random source + CONSTANT c_in_pulse_active : NATURAL := 1; + CONSTANT c_out_pulse_active : NATURAL := 1; + CONSTANT c_in_pulse_period : NATURAL := 2; + CONSTANT c_out_pulse_period : NATURAL := 2; + + SIGNAL in_random : STD_LOGIC_VECTOR(c_in_random_w-1 DOWNTO 0) := TO_UVEC(1, c_in_random_w); + SIGNAL out_random : STD_LOGIC_VECTOR(c_out_random_w-1 DOWNTO 0) := TO_UVEC(1, c_out_random_w); + SIGNAL in_pulse : STD_LOGIC; + SIGNAL out_pulse : STD_LOGIC; + SIGNAL in_pulse_en : STD_LOGIC := '1'; + SIGNAL out_pulse_en : STD_LOGIC := '1'; + + SIGNAL in_en : STD_LOGIC := '0'; + SIGNAL out_ready : STD_LOGIC := '0'; + + +BEGIN + + ------------------------------------------------------------------------------ + -- Flow control + ------------------------------------------------------------------------------ + in_random <= func_common_random(in_random) WHEN rising_edge(clk); + out_random <= func_common_random(out_random) WHEN rising_edge(clk); + + proc_common_gen_duty_pulse(c_in_pulse_active, c_in_pulse_period, '1', rst, clk, in_pulse_en, in_pulse); + proc_common_gen_duty_pulse(c_out_pulse_active, c_out_pulse_period, '1', rst, clk, out_pulse_en, out_pulse); + + in_en <= '1' WHEN g_flow_control_stimuli=e_active ELSE + in_random(in_random'HIGH) WHEN g_flow_control_stimuli=e_random ELSE + in_pulse WHEN g_flow_control_stimuli=e_pulse; + + out_ready <= '1' WHEN g_flow_control_verify=e_active ELSE + out_random(out_random'HIGH) WHEN g_flow_control_verify=e_random ELSE + out_pulse WHEN g_flow_control_verify=e_pulse; + + src_in.ready <= out_ready; + src_in.xon <= NOT src_in.xon WHEN rising_edge(clk); -- should have no effect, only passed on from src_in to snk_out + + ------------------------------------------------------------------------------ + -- Clock & reset + ------------------------------------------------------------------------------ + clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; + rst <= '1', '0' AFTER c_clk_period*7; + + ------------------------------------------------------------------------------ + -- Stimuli: + ------------------------------------------------------------------------------ + + -- Generate snk_in incrementing data with valid + proc_dp_gen_data(c_rl, + c_data_w, + c_data_init, + rst, + clk, + in_en, + snk_out, + snk_in); + + p_stimuli : PROCESS + BEGIN + -- run some more intervals for slowest counter, to more view how the slowest counter behaves + FOR I IN 0 TO 2 LOOP + proc_common_wait_until_hi_lo(clk, count_src_out_arr(g_nof_counters-1).eop); -- wait for carry over + END LOOP; + + tb_end <= '1'; + WAIT; + END PROCESS; + + ------------------------------------------------------------------------------ + -- DUT + ------------------------------------------------------------------------------ + u_dp_counter : ENTITY work.dp_counter + GENERIC MAP ( + g_nof_counters => g_nof_counters, + g_range_start => g_range_start, + g_range_stop => g_range_stop, + g_range_step => g_range_step, + g_pipeline_src_out => g_pipeline_src_out, + g_pipeline_src_in => g_pipeline_src_in + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_in => snk_in, + snk_out => snk_out, + + src_out => src_out, + src_in => src_in, + + count_src_out_arr => count_src_out_arr + ); + + ------------------------------------------------------------------------------ + -- Verification + -- Verify dp_counter using the counter data from the stimuli as reference. + -- Only verify the dp_counter for the highest counter, because that covers + -- also the low level counters. The details of dp_counter_func are tested + -- in tb_dp_counter_func.vhd. + ------------------------------------------------------------------------------ + p_verify : PROCESS(clk) + CONSTANT c_period : NATURAL := calculate_period(g_nof_counters-1); + + VARIABLE v_cnt : NATURAL := 0; + BEGIN + period <= c_period; -- to view c_period in wave window + IF rising_edge(clk) THEN + IF count_src_out_arr(g_nof_counters-1).valid='1' THEN + ASSERT v_cnt = TO_UINT(src_out.data) REPORT "Wrong cnt at valid : " & int_to_str(v_cnt) SEVERITY ERROR; + IF count_src_out_arr(g_nof_counters-1).sop='1' THEN + ASSERT v_cnt MOD c_period = 0 REPORT "Wrong cnt at sop : " & int_to_str(v_cnt) SEVERITY ERROR; + END IF; + IF count_src_out_arr(g_nof_counters-1).eop='1' THEN + ASSERT v_cnt MOD c_period = c_period-1 REPORT "Wrong cnt at eop : " & int_to_str(v_cnt) SEVERITY ERROR; + END IF; + v_cnt := v_cnt + 1; + END IF; + END IF; + END PROCESS; + +END tb; diff --git a/cores/base/dp/dp_counter/tb_dp_counter_func.vhd b/cores/base/dp/dp_counter/tb_dp_counter_func.vhd new file mode 100644 index 0000000000000000000000000000000000000000..3ac3804f058b6f66683b1161b4037339d7b3ab88 --- /dev/null +++ b/cores/base/dp/dp_counter/tb_dp_counter_func.vhd @@ -0,0 +1,249 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2017 +-- 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: +-- . initial, Daniel van der Schuur +-- Pieter Donker, Mar-2018, filled in verify process. +-- Pieter Donker, Apr-2018, add stop test. +-- Purpose: +-- . Test bench for dp_counter_func +-- Description: +-- . dp_counter_func contains *only* the function +-- . so no flow control (taken care of in dp_counter.vhd wrapper) +-- . so no pipelining (taken care of in dp_counter.vhd wrapper) +-- . The above means that this TB does not have to cover flow control or +-- pipelining either, which greatly simplifies things. +--Usage: +-- . as 10 +-- . run -all + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_str_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + +ENTITY tb_dp_counter_func IS + GENERIC ( + g_nof_counters : NATURAL := 2; + -- min range = [0,2,1] => (0,1) 'the python way' + g_range_start : t_nat_natural_arr(9 DOWNTO 0) := (0,0,0,0,0,0,0, 1, 0, 0); + g_range_stop : t_nat_natural_arr(9 DOWNTO 0) := (2,2,2,2,2,2,7,16,16,16); + g_range_step : t_nat_natural_arr(9 DOWNTO 0) := (1,1,1,1,1,1,2, 2, 2, 1) + ); +END tb_dp_counter_func; + + +ARCHITECTURE tb OF tb_dp_counter_func IS + + ------------------------------------------------------------------------------ + -- Clock & reset + ------------------------------------------------------------------------------ + CONSTANT c_clk_period : TIME := 5 ns; + CONSTANT c_max_count_w : NATURAL := 32; + + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC := '1'; + + ------------------------------------------------------------------------------ + -- dp_counter_func + ------------------------------------------------------------------------------ + SIGNAL dp_counter_func_count_en : STD_LOGIC := '0'; + SIGNAL dp_counter_func_count_src_out_arr : t_dp_sosi_arr(g_nof_counters-1 DOWNTO 0); + + -- It is difficult / not possible to avoid the glitches in the dp_counter_func outputs, because + -- dp_counter_func intentionally works combinatorially, so its outputs can vary during a few + -- delta-cycles, before the finalize. Therefore use a pipeline debug signal to view the outputs + -- without the combinatorial glitches in the wave window. + SIGNAL dp_counter_func_count_src_out_arr_p : t_dp_sosi_arr(g_nof_counters-1 DOWNTO 0); + + ------------------------------------------------------------------------------ + -- Verification + ------------------------------------------------------------------------------ + SIGNAL tb_eop_sop_en : STD_LOGIC := '1'; + SIGNAL tb_step_en : STD_LOGIC := '0'; + SIGNAL tb_start_en : STD_LOGIC := '0'; + SIGNAL tb_stopped_en : STD_LOGIC := '0'; + SIGNAL tb_carryover_en : STD_LOGIC := '0'; + SIGNAL tb_end : STD_LOGIC := '0'; + + TYPE t_count_arr IS ARRAY(g_nof_counters-1 DOWNTO 0) OF NATURAL; + SIGNAL tb_count_arr : t_count_arr := (others => 0); + SIGNAL tb_last_count_arr : t_count_arr := (others => 0); + + BEGIN + + ------------------------------------------------------------------------------ + -- Clock & reset + ------------------------------------------------------------------------------ + clk <= (NOT clk) OR tb_end AFTER c_clk_period/2; + rst <= '1', '0' AFTER c_clk_period*7; + + ------------------------------------------------------------------------------ + -- Stimuli: + ------------------------------------------------------------------------------ + + p_stimuli : PROCESS + VARIABLE run_clk_cnt: NATURAL := 1; + BEGIN + -- wait for reset + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 1); + + REPORT "Start tb dp_counter_func"; + + dp_counter_func_count_en <= '1'; -- start counting + proc_common_gen_pulse(clk, tb_start_en); -- check start value + + proc_common_wait_some_cycles(clk, 10); -- wait some time + dp_counter_func_count_en <= '0'; -- stop counting + proc_common_wait_some_cycles(clk, 10); -- wait some time + proc_common_gen_pulse(clk, tb_stopped_en); -- check if counting is stopped + + dp_counter_func_count_en <= '1'; -- start counting again + tb_step_en <= '1'; -- enable step-size check + + IF g_nof_counters>1 THEN + -- issue strobe to check counter carry over + proc_common_wait_until_lo_hi(clk, dp_counter_func_count_src_out_arr(g_nof_counters-2).eop); -- wait for carryover + proc_common_gen_pulse(clk, tb_carryover_en); -- check if dimension carryover is going right + END IF; + + -- keep running for full range counting of the slowest counter + proc_common_wait_until_hi_lo(clk, dp_counter_func_count_src_out_arr(g_nof_counters-1).eop); -- wait for carryover + + -- run some more intervals for slowest counter, to more clearly view how the slowest counter behaves + proc_common_wait_until_hi_lo(clk, dp_counter_func_count_src_out_arr(g_nof_counters-1).eop); -- wait for carryover + proc_common_wait_until_hi_lo(clk, dp_counter_func_count_src_out_arr(g_nof_counters-1).eop); -- wait for carryover + + REPORT "Stop tb dp_counter_func"; + tb_end <= '1'; + WAIT; + END PROCESS; + + ------------------------------------------------------------------------------ + -- dp_counter_func + ------------------------------------------------------------------------------ + u_dp_counter_func : ENTITY work.dp_counter_func + GENERIC MAP ( + g_nof_counters => g_nof_counters, + g_range_start => g_range_start, + g_range_stop => g_range_stop, + g_range_step => g_range_step + ) + PORT MAP ( + rst => rst, + clk => clk, + + count_en => dp_counter_func_count_en, + + count_src_out_arr => dp_counter_func_count_src_out_arr + ); + + -- Add pipeline to removed combinatorial glitches for viewing in the wave window + dp_counter_func_count_src_out_arr_p <= dp_counter_func_count_src_out_arr WHEN rising_edge(clk); + + ------------------------------------------------------------------------------ + -- Verification + ------------------------------------------------------------------------------ + p_verify : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + -- check if direct after start the counters hold the start value + IF tb_start_en = '1' THEN + FOR I IN 0 TO g_nof_counters-1 LOOP + ASSERT tb_count_arr(I) = g_range_start(I) + REPORT "DP : Wrong start of counter " & int_to_str(I) SEVERITY ERROR; + END LOOP; + END IF; + + -- check if dp_counter_func is stopped after dp_counter_func_count_en <= '0' + IF tb_stopped_en = '1' THEN + FOR I IN 0 TO g_nof_counters-1 LOOP + ASSERT tb_count_arr(I) = TO_UINT(dp_counter_func_count_src_out_arr(I).data(c_max_count_w-1 DOWNTO 0)) + REPORT "DP : Counter " & int_to_str(I) & " not stopped after dp_counter_func_count_en <= 0" SEVERITY ERROR; + END LOOP; + END IF; + + -- check dimension carryover + IF tb_carryover_en = '1' THEN + -- all counters except the last one should hold the start value + FOR I IN 0 TO g_nof_counters-2 LOOP + ASSERT tb_count_arr(I) = g_range_start(I) + REPORT "DP : Wrong carryover, counter:" & int_to_str(I) SEVERITY ERROR; + END LOOP; + -- the last counter should hold the start value + step + ASSERT tb_count_arr(g_nof_counters-1) = g_range_start(g_nof_counters-1) + g_range_step(g_nof_counters-1) + REPORT "DP : Wrong carryover, counter:" & int_to_str(g_nof_counters-1) SEVERITY ERROR; + END IF; + + -- check counter values on sop and eop + IF tb_eop_sop_en = '1' THEN + FOR I IN 0 TO g_nof_counters-1 LOOP + -- on eop counter should hold the stop_value-1 + IF dp_counter_func_count_src_out_arr(I).eop = '1' THEN + ASSERT tb_count_arr(I) = ((g_range_stop(I) - 1 - g_range_start(I)) / g_range_step(I)) * g_range_step(I) + g_range_start(I) + REPORT "DP : Wrong stop count on eop, counter:" & int_to_str(I) & + " is:" & int_to_str(tb_count_arr(I)) & + " expected:" & int_to_str(((g_range_stop(I) - 1 - g_range_start(I)) / g_range_step(I)) * g_range_step(I) + g_range_start(I)) SEVERITY ERROR; + END IF; + -- on sop counter should hold the start_value + IF dp_counter_func_count_src_out_arr(I).sop = '1' THEN + ASSERT tb_count_arr(I) = g_range_start(I) + REPORT "DP : Wrong start count on sop, counter:" & int_to_str(I) & + " is:" & int_to_str(tb_count_arr(I)) & + " expected:" & int_to_str(g_range_start(I)) SEVERITY ERROR; + END IF; + END LOOP; + END IF; + + -- check step values + IF tb_step_en = '1' THEN + FOR I IN 0 TO g_nof_counters-1 LOOP + IF tb_count_arr(I) > tb_last_count_arr(I) THEN + ASSERT (tb_last_count_arr(I) + g_range_step(I)) = tb_count_arr(I) + REPORT "DP : Wrong step count, counter:" & int_to_str(I) & + " is:" & int_to_str(tb_count_arr(I)) & + " expected:" & int_to_str(tb_last_count_arr(I) + g_range_step(I)) SEVERITY ERROR; + ELSIF tb_count_arr(I) < tb_last_count_arr(I) THEN + ASSERT g_range_start(I) = tb_count_arr(I) + REPORT "DP : Wrong step count, counter:" & int_to_str(I) & + " is:" & int_to_str(tb_count_arr(I)) & + " expected:" & int_to_str(g_range_start(I)) & ")" SEVERITY ERROR; + END IF; + END LOOP; + END IF; + END IF; + END PROCESS; + + + tb_last_count_arr <= tb_count_arr WHEN rising_edge(clk); + + gen_tb_count_arr : FOR i IN 0 TO g_nof_counters-1 GENERATE + tb_count_arr(I) <= TO_UINT(dp_counter_func_count_src_out_arr(I).data(c_max_count_w-1 DOWNTO 0)) WHEN dp_counter_func_count_en = '1'; + END GENERATE; + +END tb; diff --git a/cores/base/dp/dp_counter/tb_tb_dp_counter.vhd b/cores/base/dp/dp_counter/tb_tb_dp_counter.vhd new file mode 100644 index 0000000000000000000000000000000000000000..5652cfa766ef822d726acff3dc860efe30167d41 --- /dev/null +++ b/cores/base/dp/dp_counter/tb_tb_dp_counter.vhd @@ -0,0 +1,64 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + +-- Purpose: Verify multiple variations of tb_dp_counter +-- Description: +-- Usage: +-- > as 6 +-- > run -all + +ENTITY tb_tb_dp_counter IS +END tb_tb_dp_counter; + + +ARCHITECTURE tb OF tb_tb_dp_counter IS + + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + +BEGIN + + -- -- general + -- g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + -- g_flow_control_verify : t_dp_flow_control_enum := e_pulse; -- always active, random or pulse flow control + -- -- dut + -- g_pipeline_src_out : NATURAL := 1; -- Pipeline source outputs (data,valid,sop,eop etc) + -- g_pipeline_src_in : NATURAL := 0 -- Pipeline source inputs (ready,xon). This will also pipeline src_out. + -- g_nof_counters : NATURAL := 2; + -- -- min range = [0,2,1] => (0,1) 'the Python way' + -- g_range_start : t_nat_natural_arr(9 DOWNTO 0) := (0,0,0,0,0,0,0, 1, 0, 0); + -- g_range_stop : t_nat_natural_arr(9 DOWNTO 0) := (2,2,2,2,2,2,7,16,16,16); + -- g_range_step : t_nat_natural_arr(9 DOWNTO 0) := (1,1,1,1,1,1,2, 2, 2, 1); + + u_act_act_comb : ENTITY work.tb_dp_counter GENERIC MAP (e_active, e_active, 0, 0, 3); + u_act_act_pipe_out : ENTITY work.tb_dp_counter GENERIC MAP (e_active, e_active, 1, 0, 3); + u_act_act_pipe_in : ENTITY work.tb_dp_counter GENERIC MAP (e_active, e_active, 0, 1, 3); + + u_rnd_rnd_comb : ENTITY work.tb_dp_counter GENERIC MAP (e_random, e_random, 0, 0, 3); + u_rnd_rnd_pipe_out : ENTITY work.tb_dp_counter GENERIC MAP (e_random, e_random, 1, 0, 3); + u_rnd_rnd_pipe_in : ENTITY work.tb_dp_counter GENERIC MAP (e_random, e_random, 0, 1, 3); + +END tb; diff --git a/cores/base/dp/dp_pipeline/dp_pipeline.vhd b/cores/base/dp/dp_pipeline/dp_pipeline.vhd new file mode 100644 index 0000000000000000000000000000000000000000..3ecd34351a44511e80201e87ff83eb98d3fb8092 --- /dev/null +++ b/cores/base/dp/dp_pipeline/dp_pipeline.vhd @@ -0,0 +1,156 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, dp_pkg_lib, dp_components_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +-- Purpose: +-- Pipeline the source output by one cycle or by g_pipeline cycles. +-- Description: +-- The dp_pipeline instantiates 0:g_pipeline stages of dp_pipeline_one. +-- The dp_pipeline_one provides a single clock cycle delay of the source +-- output (i.e. sosi). The dp_pipeline_one holds valid sink input in case +-- src_in.ready goes low and makes src_out.valid high again when +-- src_in.ready goes high again, without the need for a valid sink input to +-- push this held data out. +-- The dp_pipeline delays the data, sop, eop by one cycle relative to the +-- valid. However the src_out.valid still has the same phase as the +-- snk_in.valid, because both valids depends on the same src_in.ready. +-- Therefore dp_pipeline cannot be used to delay the valid phase by one +-- cycle. Hence the may purpose of dp_pipeline is to register the sosi. +-- Remarks: +-- . Ready latency = 1 +-- . Without flow control so when src_in.ready = '1' fixed, then the hold +-- logic in dp_pipeline becomes void and dp_pipeline then just pipelines the +-- snk_in sosi. + +ENTITY dp_pipeline IS + GENERIC ( + g_pipeline : NATURAL := 1 -- 0 for wires, > 0 for registers, + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- ST sink + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + -- ST source + src_in : IN t_dp_siso := c_dp_siso_rdy; + src_out : OUT t_dp_sosi + ); +END dp_pipeline; + + +LIBRARY IEEE, dp_pkg_lib, dp_components_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_pipeline_one IS + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- ST sink + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + -- ST source + src_in : IN t_dp_siso := c_dp_siso_rdy; + src_out : OUT t_dp_sosi + ); +END dp_pipeline_one; + + +LIBRARY IEEE, dp_pkg_lib, dp_components_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ARCHITECTURE str OF dp_pipeline IS + + SIGNAL snk_out_arr : t_dp_siso_arr(0 TO g_pipeline); + SIGNAL snk_in_arr : t_dp_sosi_arr(0 TO g_pipeline); + +BEGIN + + -- Input at index 0 + snk_out <= snk_out_arr(0); + snk_in_arr(0) <= snk_in; + + -- Output at index g_pipeline + snk_out_arr(g_pipeline) <= src_in; + src_out <= snk_in_arr(g_pipeline); + + gen_p : FOR I IN 1 TO g_pipeline GENERATE + u_p : ENTITY work.dp_pipeline_one + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out_arr(I-1), + snk_in => snk_in_arr(I-1), + -- ST source + src_in => snk_out_arr(I), + src_out => snk_in_arr(I) + ); + END GENERATE; + +END str; + + +LIBRARY IEEE, dp_pkg_lib, dp_components_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ARCHITECTURE str OF dp_pipeline_one IS + + SIGNAL nxt_src_out : t_dp_sosi; + SIGNAL i_src_out : t_dp_sosi; + +BEGIN + + src_out <= i_src_out; + + -- Pipeline register + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + i_src_out <= c_dp_sosi_rst; + ELSIF rising_edge(clk) THEN + i_src_out <= nxt_src_out; + END IF; + END PROCESS; + + -- Input control + u_hold_input : ENTITY dp_components_lib.dp_hold_input + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in, + -- ST source + src_in => src_in, + next_src_out => nxt_src_out, + pend_src_out => OPEN, + src_out_reg => i_src_out + ); + +END str; diff --git a/cores/base/dp/dp_pipeline/dp_pipeline_arr.vhd b/cores/base/dp/dp_pipeline/dp_pipeline_arr.vhd new file mode 100644 index 0000000000000000000000000000000000000000..f0407d893fd6496e262e0a20381196806a3f325d --- /dev/null +++ b/cores/base/dp/dp_pipeline/dp_pipeline_arr.vhd @@ -0,0 +1,71 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, dp_pkg_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +-- Purpose: +-- Pipeline array of g_nof_streams by g_pipeline cycles. +-- Description: +-- See dp_pipeline. + +ENTITY dp_pipeline_arr IS + GENERIC ( + g_nof_streams : NATURAL := 1; + g_pipeline : NATURAL := 1 -- 0 for wires, > 0 for registers, + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- ST sink + snk_out_arr : OUT t_dp_siso_arr(g_nof_streams-1 DOWNTO 0); + snk_in_arr : IN t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0); + -- ST source + src_in_arr : IN t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_dp_siso_rdy); + src_out_arr : OUT t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0) + ); +END dp_pipeline_arr; + + +ARCHITECTURE str OF dp_pipeline_arr IS + +BEGIN + + gen_nof_streams : FOR I IN 0 TO g_nof_streams-1 GENERATE + u_p : ENTITY work.dp_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out_arr(I), + snk_in => snk_in_arr(I), + -- ST source + src_in => src_in_arr(I), + src_out => src_out_arr(I) + ); + END GENERATE; + +END str; diff --git a/cores/base/dp/dp_pipeline/dp_pipeline_ready.vhd b/cores/base/dp/dp_pipeline/dp_pipeline_ready.vhd new file mode 100644 index 0000000000000000000000000000000000000000..d44b22316b2b436336bebc32d12b0d16372bc160 --- /dev/null +++ b/cores/base/dp/dp_pipeline/dp_pipeline_ready.vhd @@ -0,0 +1,157 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, dp_pkg_lib, dp_components_lib; +USE IEEE.std_logic_1164.all; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +-- Purpose: +-- Pipeline the source input +-- Description: +-- This dp_pipeline_ready provides a single clock cycle delay of the source +-- input (i.e. siso). It does this by first going from RL = g_in_latency --> +-- 0 and then to RL = g_out_latency. +-- Data flow: +-- . out RL > in RL : incr(out RL - in RL) +-- . out RL <= in RL AND out RL = 0 : incr(1) --> adapt(out RL) +-- . out RL <= in RL AND out RL > 0 : adapt(0) --> incr(out RL) +-- Remark: +-- . The g_in_latency may be 0, but for g_in_latency=0 the sosi.ready acts +-- as an acknowledge and that could simply also be registered by the user. + +ENTITY dp_pipeline_ready IS + GENERIC ( + g_in_latency : NATURAL := 1; -- >= 0 + g_out_latency : NATURAL := 1 -- >= 0 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + -- ST sink + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + -- ST source + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END dp_pipeline_ready; + + +ARCHITECTURE str OF dp_pipeline_ready IS + + SIGNAL internal_siso : t_dp_siso; + SIGNAL internal_sosi : t_dp_sosi; + +BEGIN + + gen_out_incr_rl : IF g_out_latency>g_in_latency GENERATE + -- Register siso by incrementing the input RL first + u_incr : ENTITY dp_components_lib.dp_latency_increase + GENERIC MAP ( + g_in_latency => g_in_latency, + g_incr_latency => g_out_latency-g_in_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in, + -- ST source + src_in => src_in, + src_out => src_out + ); + END GENERATE; + + gen_out_rl_0 : IF g_out_latency<=g_in_latency AND g_out_latency=0 GENERATE + -- Register siso by incrementing the input RL first + u_incr : ENTITY dp_components_lib.dp_latency_increase + GENERIC MAP ( + g_in_latency => g_in_latency, + g_incr_latency => 1 + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in, + -- ST source + src_in => internal_siso, + src_out => internal_sosi + ); + + -- Input RL --> 0 + u_adapt : ENTITY dp_components_lib.dp_latency_adapter + GENERIC MAP ( + g_in_latency => g_in_latency+1, + g_out_latency => g_out_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => internal_siso, + snk_in => internal_sosi, + -- ST source + src_in => src_in, + src_out => src_out + ); + END GENERATE; + + gen_out_rl : IF g_out_latency<=g_in_latency AND g_out_latency>0 GENERATE + -- First adapt the input RL --> 0 + u_adapt : ENTITY dp_components_lib.dp_latency_adapter + GENERIC MAP ( + g_in_latency => g_in_latency, + g_out_latency => 0 + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => snk_out, + snk_in => snk_in, + -- ST source + src_in => internal_siso, + src_out => internal_sosi + ); + + -- Register siso by incrementing the internal RL = 0 --> the output RL + u_incr : ENTITY dp_components_lib.dp_latency_increase + GENERIC MAP ( + g_in_latency => 0, + g_incr_latency => g_out_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => internal_siso, + snk_in => internal_sosi, + -- ST source + src_in => src_in, + src_out => src_out + ); + END GENERATE; + +END str; diff --git a/cores/base/dp/dp_pipeline/hdllib.cfg b/cores/base/dp/dp_pipeline/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0ea1977989f467f74cfb23811bdcd0f992e6861f --- /dev/null +++ b/cores/base/dp/dp_pipeline/hdllib.cfg @@ -0,0 +1,25 @@ +hdl_lib_name = dp_pipeline +hdl_library_clause_name = dp_pipeline_lib +hdl_lib_uses_synth = dp_components +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + dp_pipeline.vhd + dp_pipeline_arr.vhd + dp_pipeline_ready.vhd + +test_bench_files = + tb_dp_pipeline.vhd + tb_dp_pipeline_ready.vhd + tb_tb_dp_pipeline.vhd + tb_tb_dp_pipeline_ready.vhd + +regression_test_vhdl = + tb_tb_dp_pipeline.vhd + tb_tb_dp_pipeline_ready.vhd + +[modelsim_project_file] + + +[quartus_project_file] diff --git a/cores/base/dp/dp_pipeline/tb_dp_pipeline.vhd b/cores/base/dp/dp_pipeline/tb_dp_pipeline.vhd new file mode 100644 index 0000000000000000000000000000000000000000..52e7b50b71423f14cddf9c090864fcb513592cd4 --- /dev/null +++ b/cores/base/dp/dp_pipeline/tb_dp_pipeline.vhd @@ -0,0 +1,164 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + +ENTITY tb_dp_pipeline IS + GENERIC ( + g_pipeline : NATURAL := 5 + ); +END tb_dp_pipeline; + + +ARCHITECTURE tb OF tb_dp_pipeline IS + + -- See tb_dp_pkg.vhd for explanation and run time + + -- DUT ready latency + CONSTANT c_dut_latency : NATURAL := 1; -- fixed 1 for dp_pipeline + CONSTANT c_tx_latency : NATURAL := c_dut_latency; -- TX ready latency of TB + CONSTANT c_tx_void : NATURAL := sel_a_b(c_tx_latency, 1, 0); -- used to avoid empty range VHDL warnings when c_tx_latency=0 + CONSTANT c_tx_offset_sop : NATURAL := 3; + CONSTANT c_tx_period_sop : NATURAL := 7; -- sop in data valid cycle 3, 10, 17, ... + CONSTANT c_tx_offset_eop : NATURAL := 5; -- eop in data valid cycle 5, 12, 19, ... + CONSTANT c_tx_period_eop : NATURAL := c_tx_period_sop; + CONSTANT c_tx_offset_sync : NATURAL := 3; -- sync in data valid cycle 3, 20, 37, ... + CONSTANT c_tx_period_sync : NATURAL := 17; + CONSTANT c_rx_latency : NATURAL := c_dut_latency; -- RX ready latency from DUT + CONSTANT c_verify_en_wait : NATURAL := 4+g_pipeline; -- wait some cycles before asserting verify enable + + CONSTANT c_random_w : NATURAL := 19; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL clk : STD_LOGIC := '0'; + SIGNAL rst : STD_LOGIC; + SIGNAL sync : STD_LOGIC; + SIGNAL lfsr1 : STD_LOGIC_VECTOR(c_random_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL lfsr2 : STD_LOGIC_VECTOR(c_random_w DOWNTO 0) := (OTHERS=>'0'); + + SIGNAL cnt_dat : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL cnt_val : STD_LOGIC; + SIGNAL cnt_en : STD_LOGIC; + + SIGNAL tx_data : t_dp_data_arr(0 TO c_tx_latency + c_tx_void) := (OTHERS=>(OTHERS=>'0')); + SIGNAL tx_val : STD_LOGIC_VECTOR(0 TO c_tx_latency + c_tx_void) := (OTHERS=>'0'); + + SIGNAL in_ready : STD_LOGIC; + SIGNAL in_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0) := (OTHERS=>'0'); + SIGNAL in_sync : STD_LOGIC; + SIGNAL in_val : STD_LOGIC; + SIGNAL in_sop : STD_LOGIC; + SIGNAL in_eop : STD_LOGIC; + + SIGNAL in_siso : t_dp_siso; + SIGNAL in_sosi : t_dp_sosi := c_dp_sosi_rst; + SIGNAL out_siso : t_dp_siso; + SIGNAL out_sosi : t_dp_sosi; + + SIGNAL out_ready : STD_LOGIC; + SIGNAL prev_out_ready : STD_LOGIC_VECTOR(0 TO c_rx_latency); + SIGNAL out_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL out_sync : STD_LOGIC; + SIGNAL out_val : STD_LOGIC; + SIGNAL out_sop : STD_LOGIC; + SIGNAL out_eop : STD_LOGIC; + SIGNAL hold_out_sop : STD_LOGIC; + SIGNAL prev_out_data : STD_LOGIC_VECTOR(out_data'RANGE); + + SIGNAL state : t_dp_state_enum; + + SIGNAL verify_en : STD_LOGIC; + SIGNAL verify_done : STD_LOGIC; + + SIGNAL exp_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0) := TO_UVEC(sel_a_b(g_pipeline=0, 18953, 18952), c_dp_data_w); + +BEGIN + + clk <= NOT clk OR tb_end AFTER clk_period/2; + rst <= '1', '0' AFTER clk_period*7; + + -- Sync interval + proc_dp_sync_interval(clk, sync); + + -- Input data + cnt_val <= in_ready AND cnt_en; + + proc_dp_cnt_dat(rst, clk, cnt_val, cnt_dat); + proc_dp_tx_data(c_tx_latency, rst, clk, cnt_val, cnt_dat, tx_data, tx_val, in_data, in_val); + proc_dp_tx_ctrl(c_tx_offset_sync, c_tx_period_sync, in_data, in_val, in_sync); + proc_dp_tx_ctrl(c_tx_offset_sop, c_tx_period_sop, in_data, in_val, in_sop); + proc_dp_tx_ctrl(c_tx_offset_eop, c_tx_period_eop, in_data, in_val, in_eop); + + -- Stimuli control + proc_dp_count_en(rst, clk, sync, lfsr1, state, verify_done, tb_end, cnt_en); + proc_dp_out_ready(rst, clk, sync, lfsr2, out_ready); + + -- Output verify + proc_dp_verify_en(c_verify_en_wait, rst, clk, sync, verify_en); + proc_dp_verify_data("out_sosi.data", c_rx_latency, clk, verify_en, out_ready, out_val, out_data, prev_out_data); + proc_dp_verify_valid(c_rx_latency, clk, verify_en, out_ready, prev_out_ready, out_val); + proc_dp_verify_sop_and_eop(c_rx_latency, FALSE, clk, out_val, out_val, out_sop, out_eop, hold_out_sop); -- Verify that sop and eop come in pairs, no check on valid between eop and sop + proc_dp_verify_ctrl(c_tx_offset_sync, c_tx_period_sync, "sync", clk, verify_en, out_data, out_val, out_sync); + proc_dp_verify_ctrl(c_tx_offset_sop, c_tx_period_sop, "sop", clk, verify_en, out_data, out_val, out_sop); + proc_dp_verify_ctrl(c_tx_offset_eop, c_tx_period_eop, "eop", clk, verify_en, out_data, out_val, out_eop); + + -- Check that the test has ran at all + proc_dp_verify_value(e_equal, clk, verify_done, exp_data, out_data); + + ------------------------------------------------------------------------------ + -- DUT dp_pipeline + ------------------------------------------------------------------------------ + + -- map sl, slv to record + in_ready <= in_siso.ready; -- SISO + in_sosi.data(c_dp_data_w-1 DOWNTO 0) <= in_data; -- SOSI + in_sosi.sync <= in_sync; + in_sosi.valid <= in_val; + in_sosi.sop <= in_sop; + in_sosi.eop <= in_eop; + + out_siso.ready <= out_ready; -- SISO + out_data <= out_sosi.data(c_dp_data_w-1 DOWNTO 0); -- SOSI + out_sync <= out_sosi.sync; + out_val <= out_sosi.valid; + out_sop <= out_sosi.sop; + out_eop <= out_sosi.eop; + + dut : ENTITY work.dp_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline + ) + PORT MAP ( + rst => rst, + clk => clk, + snk_out => in_siso, -- OUT = request to upstream ST source + snk_in => in_sosi, + src_in => out_siso, -- IN = request from downstream ST sink + src_out => out_sosi + ); + +END tb; diff --git a/cores/base/dp/dp_pipeline/tb_dp_pipeline_ready.vhd b/cores/base/dp/dp_pipeline/tb_dp_pipeline_ready.vhd new file mode 100644 index 0000000000000000000000000000000000000000..4c3bb7b24e5943844dda99186f24fe512c656207 --- /dev/null +++ b/cores/base/dp/dp_pipeline/tb_dp_pipeline_ready.vhd @@ -0,0 +1,223 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2011 +-- 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/>. +-- +------------------------------------------------------------------------------- + +-- Purpose: Verify dp_pipeline_ready for different RL +-- Description: +-- Usage: +-- > as 10 +-- > run -all -- signal tb_end will stop the simulation by stopping the clk +-- . The verify procedures check the correct output + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib, dp_components_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + + +ENTITY tb_dp_pipeline_ready IS + GENERIC ( + g_in_en : t_dp_flow_control_enum := e_random; -- always active, random or pulse flow control + g_out_ready : t_dp_flow_control_enum := e_random; -- always active, random or pulse flow control + g_in_latency : NATURAL := 1; -- >= 0 + g_out_latency : NATURAL := 0; -- >= 0 + g_nof_repeat : NATURAL := 50 + ); +END tb_dp_pipeline_ready; + + +ARCHITECTURE tb OF tb_dp_pipeline_ready IS + CONSTANT c_data_w : NATURAL := 16; + CONSTANT c_rl : NATURAL := 1; + CONSTANT c_data_init : INTEGER := 0; + CONSTANT c_frame_len_init : NATURAL := 1; -- >= 1 + CONSTANT c_pulse_active : NATURAL := 1; + CONSTANT c_pulse_period : NATURAL := 7; + CONSTANT c_sync_period : NATURAL := 7; + CONSTANT c_sync_offset : NATURAL := 2; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC := '1'; + + -- Flow control + SIGNAL random_0 : STD_LOGIC_VECTOR(14 DOWNTO 0) := (OTHERS=>'0'); -- use different lengths to have different random sequences + SIGNAL random_1 : STD_LOGIC_VECTOR(15 DOWNTO 0) := (OTHERS=>'0'); -- use different lengths to have different random sequences + SIGNAL pulse_0 : STD_LOGIC; + SIGNAL pulse_1 : STD_LOGIC; + SIGNAL pulse_en : STD_LOGIC := '1'; + + -- Stimuli + SIGNAL in_en : STD_LOGIC := '1'; + SIGNAL in_siso : t_dp_siso; + SIGNAL in_sosi : t_dp_sosi; + SIGNAL adapt_siso : t_dp_siso; + SIGNAL adapt_sosi : t_dp_sosi; + + SIGNAL out_siso : t_dp_siso := c_dp_siso_hold; -- ready='0', xon='1' + SIGNAL out_sosi : t_dp_sosi; + + -- Verification + SIGNAL verify_en : STD_LOGIC := '0'; + SIGNAL verify_done : STD_LOGIC := '0'; + SIGNAL count_eop : NATURAL := 0; + + SIGNAL prev_out_ready : STD_LOGIC_VECTOR(0 TO g_out_latency); + SIGNAL prev_out_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0) := TO_SVEC(c_data_init-1, c_data_w); + SIGNAL out_bsn : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + SIGNAL out_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + SIGNAL out_sync : STD_LOGIC; + SIGNAL out_val : STD_LOGIC; + SIGNAL out_sop : STD_LOGIC; + SIGNAL out_eop : STD_LOGIC; + SIGNAL hold_out_sop : STD_LOGIC; + SIGNAL expected_out_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + +BEGIN + + clk <= (NOT clk) OR tb_end AFTER clk_period/2; + rst <= '1', '0' AFTER clk_period*7; + + random_0 <= func_common_random(random_0) WHEN rising_edge(clk); + random_1 <= func_common_random(random_1) WHEN rising_edge(clk); + + proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period, '1', rst, clk, pulse_en, pulse_0); + proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period+1, '1', rst, clk, pulse_en, pulse_1); + + + ------------------------------------------------------------------------------ + -- STREAM CONTROL + ------------------------------------------------------------------------------ + + in_en <= '1' WHEN g_in_en=e_active ELSE + random_0(random_0'HIGH) WHEN g_in_en=e_random ELSE + pulse_0 WHEN g_in_en=e_pulse; + + out_siso.ready <= '1' WHEN g_out_ready=e_active ELSE + random_1(random_1'HIGH) WHEN g_out_ready=e_random ELSE + pulse_1 WHEN g_out_ready=e_pulse; + + + ------------------------------------------------------------------------------ + -- DATA GENERATION + ------------------------------------------------------------------------------ + + -- Generate data path input data + p_stimuli : PROCESS + VARIABLE v_data_init : NATURAL; + VARIABLE v_frame_len : NATURAL; + VARIABLE v_sync : STD_LOGIC; + BEGIN + v_data_init := c_data_init; + v_frame_len := c_frame_len_init; + in_sosi <= c_dp_sosi_rst; + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 5); + + -- Begin of stimuli + FOR R IN 0 TO g_nof_repeat-1 LOOP + v_sync := sel_a_b(R MOD c_sync_period = c_sync_offset, '1', '0'); + proc_dp_gen_block_data(c_rl, TRUE, c_data_w, c_data_w, v_data_init, 0, 0, v_frame_len, 0, 0, v_sync, TO_DP_BSN(R), clk, in_en, in_siso, in_sosi); + --proc_common_wait_some_cycles(clk, 10); + v_data_init := v_data_init + v_frame_len; + v_frame_len := v_frame_len + 1; + END LOOP; + + -- End of stimuli + expected_out_data <= TO_UVEC(v_data_init-1, c_data_w); + + proc_common_wait_until_high(clk, verify_done); + proc_common_wait_some_cycles(clk, 10); + tb_end <= '1'; + WAIT; + END PROCESS; + + -- proc_dp_gen_block_data() only supports RL=0 or 1, so use a latency adpater to support any g_in_latency + u_input_adapt : ENTITY dp_components_lib.dp_latency_adapter + GENERIC MAP ( + g_in_latency => c_rl, + g_out_latency => g_in_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => in_siso, + snk_in => in_sosi, + -- ST source + src_in => adapt_siso, + src_out => adapt_sosi + ); + + + ------------------------------------------------------------------------------ + -- DATA VERIFICATION + ------------------------------------------------------------------------------ + + + -- Verification logistics + verify_en <= '1' WHEN rising_edge(clk) AND out_sosi.sop='1'; -- enable verify after first output sop + count_eop <= count_eop+1 WHEN rising_edge(clk) AND out_sosi.eop='1' AND((g_out_latency>0) OR + (g_out_latency=0 AND out_siso.ready='1')); -- count number of output eop + verify_done <= '1' WHEN rising_edge(clk) AND count_eop = g_nof_repeat; -- signal verify done after g_nof_repeat frames + + -- Actual verification of the output streams + proc_dp_verify_data("out_sosi.data", g_out_latency, clk, verify_en, out_siso.ready, out_sosi.valid, out_data, prev_out_data); -- Verify that the output is incrementing data, like the input stimuli + proc_dp_verify_valid(g_out_latency, clk, verify_en, out_siso.ready, prev_out_ready, out_sosi.valid); -- Verify that the output valid fits with the output ready latency + proc_dp_verify_sop_and_eop(g_out_latency, clk, out_siso.ready, out_sosi.valid, out_sosi.sop, out_sosi.eop, hold_out_sop); -- Verify that sop and eop come in pairs + proc_dp_verify_value(e_equal, clk, verify_done, expected_out_data, prev_out_data); -- Verify that the stimuli have been applied at all + proc_dp_verify_sync(c_sync_period, c_sync_offset, clk, verify_en, out_sosi.sync, out_sosi.sop, out_sosi.bsn); + + -- Monitoring + out_bsn <= out_sosi.bsn(c_data_w-1 DOWNTO 0); + out_data <= out_sosi.data(c_data_w-1 DOWNTO 0); + out_sync <= out_sosi.sync; + out_val <= out_sosi.valid; + out_sop <= out_sosi.sop; + out_eop <= out_sosi.eop; + + + ------------------------------------------------------------------------------ + -- DUT dp_pipeline_ready + ------------------------------------------------------------------------------ + + pipeline : ENTITY work.dp_pipeline_ready + GENERIC MAP ( + g_in_latency => g_in_latency, + g_out_latency => g_out_latency + ) + PORT MAP ( + rst => rst, + clk => clk, + -- ST sink + snk_out => adapt_siso, + snk_in => adapt_sosi, + -- ST source + src_in => out_siso, + src_out => out_sosi + ); + + +END tb; diff --git a/cores/base/dp/dp_pipeline/tb_tb_dp_pipeline.vhd b/cores/base/dp/dp_pipeline/tb_tb_dp_pipeline.vhd new file mode 100644 index 0000000000000000000000000000000000000000..44c25eb5b35a29785cc8858cafe22c48bad0cf0f --- /dev/null +++ b/cores/base/dp/dp_pipeline/tb_tb_dp_pipeline.vhd @@ -0,0 +1,42 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2013 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; + +-- > as 3 +-- > run -all --> OK + +ENTITY tb_tb_dp_pipeline IS +END tb_tb_dp_pipeline; + + +ARCHITECTURE tb OF tb_tb_dp_pipeline IS + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' +BEGIN + + u_p0 : ENTITY work.tb_dp_pipeline GENERIC MAP (0); + u_p1 : ENTITY work.tb_dp_pipeline GENERIC MAP (1); + u_p2 : ENTITY work.tb_dp_pipeline GENERIC MAP (2); + u_p7 : ENTITY work.tb_dp_pipeline GENERIC MAP (7); + +END tb; diff --git a/cores/base/dp/dp_pipeline/tb_tb_dp_pipeline_ready.vhd b/cores/base/dp/dp_pipeline/tb_tb_dp_pipeline_ready.vhd new file mode 100644 index 0000000000000000000000000000000000000000..15a68bffa37fd4c60cabf991cc33118a3df5a841 --- /dev/null +++ b/cores/base/dp/dp_pipeline/tb_tb_dp_pipeline_ready.vhd @@ -0,0 +1,66 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + + +-- > as 2 +-- > run -all --> OK + +ENTITY tb_tb_dp_pipeline_ready IS +END tb_tb_dp_pipeline_ready; + + +ARCHITECTURE tb OF tb_tb_dp_pipeline_ready IS + + CONSTANT c_nof_repeat : NATURAL := 50; + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + +BEGIN + + -- in_en, src_in.ready, in_latency, out_latency, nof repeat, + -- Random flow control for different RL + u_rnd_rnd_0_0 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 0, 0, c_nof_repeat); + u_rnd_rnd_1_0 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 1, 0, c_nof_repeat); + u_rnd_rnd_0_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 0, 1, c_nof_repeat); + u_rnd_rnd_2_0 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 2, 0, c_nof_repeat); + u_rnd_rnd_0_2 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 0, 2, c_nof_repeat); + u_rnd_rnd_2_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 2, 1, c_nof_repeat); + u_rnd_rnd_1_2 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 1, 2, c_nof_repeat); + u_rnd_rnd_2_2 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 2, 2, c_nof_repeat); + + -- Other flow control for fixed RL + u_act_act_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_active, e_active, 1, 1, c_nof_repeat); + u_act_rnd_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_active, e_random, 1, 1, c_nof_repeat); + u_act_pls_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_active, e_pulse, 1, 1, c_nof_repeat); + + u_rnd_act_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_active, 1, 1, c_nof_repeat); + u_rnd_rnd_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_random, 1, 1, c_nof_repeat); + u_rnd_pls_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_random, e_pulse, 1, 1, c_nof_repeat); + + u_pls_act_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_pulse, e_active, 1, 1, c_nof_repeat); + u_pls_rnd_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_pulse, e_random, 1, 1, c_nof_repeat); + u_pls_pls_1_1 : ENTITY work.tb_dp_pipeline_ready GENERIC MAP (e_pulse, e_pulse, 1, 1, c_nof_repeat); + +END tb; diff --git a/cores/base/dp/dp_pkg/dp_stream_pkg.vhd b/cores/base/dp/dp_pkg/dp_stream_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..1d19e50d666f0b6ce9924ad4e52636f2d21d3fc1 --- /dev/null +++ b/cores/base/dp/dp_pkg/dp_stream_pkg.vhd @@ -0,0 +1,1293 @@ +-------------------------------------------------------------------------------- +-- +-- 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_pkg_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; + +PACKAGE dp_stream_pkg Is + + ------------------------------------------------------------------------------ + -- General DP stream record defintion + ------------------------------------------------------------------------------ + + -- Remarks: + -- * Choose smallest maximum SOSI slv lengths that fit all use cases, because unconstrained record fields slv is not allowed + -- * The large SOSI data field width of 256b has some disadvantages: + -- . about 10% extra simulation time and PC memory usage compared to 72b (measured using tb_unb_tse_board) + -- . a 256b number has 64 hex digits in the Wave window which is awkward because of the leading zeros when typically + -- only 32b are used, fortunately integer representation still works OK (except 0 which is shown as blank). + -- However the alternatives are not attractive, because they affect the implementation of the streaming + -- components that use the SOSI record. Alternatives are e.g.: + -- . define an extra long SOSI data field ldata[255:0] in addition to the existing data[71:0] field + -- . use the array of SOSI records to contain wider data, all with the same SOSI control field values + -- . define another similar SOSI record with data[255:0]. + -- Therefore define data width as 256b, because the disadvantages are acceptable and the benefit is great, because all + -- streaming components can remain as they are. + -- * Added sync and bsn to SOSI to have timestamp information with the data + -- * Added re and im to SOSI to support complex data for DSP + -- * The sosi fields can be labeled in diffent groups: ctrl, info and data as shown in comment at the t_dp_sosi definition. + -- This grouping is useful for functions that operate on a t_dp_sosi signal. + -- * The info fields are valid at the sop or at the eop, but typically they hold their last active value to avoid unnessary + -- toggling and to ease viewing in the wave window. + CONSTANT c_dp_stream_bsn_w : NATURAL := 64; -- 64 is sufficient to count blocks of data for years + CONSTANT c_dp_stream_data_w : NATURAL := 768; -- 72 is sufficient for max word 8 * 9-bit. 576 supports half rate DDR4 bus data width. The current 768 is enough for wide single clock SLVs (e.g. headers) + CONSTANT c_dp_stream_dsp_data_w : NATURAL := 64; -- 64 is sufficient for DSP data, including complex power accumulates + CONSTANT c_dp_stream_empty_w : NATURAL := 16; -- 8 is sufficient for max 256 symbols per data word, still use 16 bit to be able to count c_dp_stream_data_w in bits + CONSTANT c_dp_stream_channel_w : NATURAL := 32; -- 32 is sufficient for several levels of hierarchy in mapping types of streams on to channels + CONSTANT c_dp_stream_error_w : NATURAL := 32; -- 32 is sufficient for several levels of hierarchy in mapping error numbers, e.g. 32 different one-hot encoded errors, bit [0] = 0 = OK + + CONSTANT c_dp_stream_ok : NATURAL := 0; -- SOSI err field OK value + CONSTANT c_dp_stream_err : NATURAL := 1; -- SOSI err field error value /= OK + + CONSTANT c_dp_stream_rl : NATURAL := 1; -- SISO default data path stream ready latency RL = 1 + + TYPE t_dp_siso IS RECORD -- Source In or Sink Out + ready : STD_LOGIC; -- fine cycle based flow control using ready latency RL >= 0 + xon : STD_LOGIC; -- coarse typically block based flow control using xon/xoff + END RECORD; + + TYPE t_dp_sosi IS RECORD -- Source Out or Sink In + sync : STD_LOGIC; -- ctrl + bsn : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0); -- info at sop (block sequence number) + data : STD_LOGIC_VECTOR(c_dp_stream_data_w-1 DOWNTO 0); -- data + re : STD_LOGIC_VECTOR(c_dp_stream_dsp_data_w-1 DOWNTO 0); -- data + im : STD_LOGIC_VECTOR(c_dp_stream_dsp_data_w-1 DOWNTO 0); -- data + valid : STD_LOGIC; -- ctrl + sop : STD_LOGIC; -- ctrl + eop : STD_LOGIC; -- ctrl + empty : STD_LOGIC_VECTOR(c_dp_stream_empty_w-1 DOWNTO 0); -- info at eop + channel : STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0); -- info at sop + err : STD_LOGIC_VECTOR(c_dp_stream_error_w-1 DOWNTO 0); -- info at eop (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'); + CONSTANT c_dp_siso_hold : t_dp_siso := ('0', '1'); + CONSTANT c_dp_siso_rdy : t_dp_siso := ('1', '1'); + CONSTANT c_dp_siso_flush : t_dp_siso := ('1', '0'); + CONSTANT c_dp_sosi_rst : t_dp_sosi := ('0', (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), '0', '0', '0', (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0')); + CONSTANT c_dp_sosi_x : t_dp_sosi := ('X', (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X'), 'X', 'X', 'X', (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X')); + + -- Use integers instead of slv for monitoring purposes (integer range limited to 31 bit plus sign bit) + TYPE t_dp_sosi_integer IS RECORD + sync : STD_LOGIC; + bsn : NATURAL; + data : INTEGER; + re : INTEGER; + im : INTEGER; + valid : STD_LOGIC; + sop : STD_LOGIC; + eop : STD_LOGIC; + empty : NATURAL; + channel : NATURAL; + err : NATURAL; + END RECORD; + + -- Use unsigned instead of slv for monitoring purposes beyond the integer range of t_dp_sosi_integer + TYPE t_dp_sosi_unsigned IS RECORD + sync : STD_LOGIC; + bsn : UNSIGNED(c_dp_stream_bsn_w-1 DOWNTO 0); + data : UNSIGNED(c_dp_stream_data_w-1 DOWNTO 0); + re : UNSIGNED(c_dp_stream_dsp_data_w-1 DOWNTO 0); + im : UNSIGNED(c_dp_stream_dsp_data_w-1 DOWNTO 0); + valid : STD_LOGIC; + sop : STD_LOGIC; + eop : STD_LOGIC; + empty : UNSIGNED(c_dp_stream_empty_w-1 DOWNTO 0); + channel : UNSIGNED(c_dp_stream_channel_w-1 DOWNTO 0); + err : UNSIGNED(c_dp_stream_error_w-1 DOWNTO 0); + END RECORD; + + CONSTANT c_dp_sosi_unsigned_rst : t_dp_sosi_unsigned := ('0', (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), '0', '0', '0', (OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0')); + CONSTANT c_dp_sosi_unsigned_ones : t_dp_sosi_unsigned := ('1', + TO_UNSIGNED(1, c_dp_stream_bsn_w), + TO_UNSIGNED(1, c_dp_stream_data_w), + TO_UNSIGNED(1, c_dp_stream_dsp_data_w), + TO_UNSIGNED(1, c_dp_stream_dsp_data_w), + '1', '1', '1', + TO_UNSIGNED(1, c_dp_stream_empty_w), + TO_UNSIGNED(1, c_dp_stream_channel_w), + TO_UNSIGNED(1, c_dp_stream_error_w)); + + -- Use boolean to define whether a t_dp_siso, t_dp_sosi field is used ('1') or not ('0') + TYPE t_dp_siso_sl IS RECORD + ready : STD_LOGIC; + xon : STD_LOGIC; + END RECORD; + + TYPE t_dp_sosi_sl IS RECORD + sync : STD_LOGIC; + bsn : STD_LOGIC; + data : STD_LOGIC; + re : STD_LOGIC; + im : STD_LOGIC; + valid : STD_LOGIC; + sop : STD_LOGIC; + eop : STD_LOGIC; + empty : STD_LOGIC; + channel : STD_LOGIC; + err : STD_LOGIC; + END RECORD; + + CONSTANT c_dp_siso_sl_rst : t_dp_siso_sl := ('0', '0'); + CONSTANT c_dp_siso_sl_ones : t_dp_siso_sl := ('1', '1'); + CONSTANT c_dp_sosi_sl_rst : t_dp_sosi_sl := ('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'); + CONSTANT c_dp_sosi_sl_ones : t_dp_sosi_sl := ('1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'); + + -- Multi port or multi register array for DP stream records + TYPE t_dp_siso_arr IS ARRAY (INTEGER RANGE <>) OF t_dp_siso; + TYPE t_dp_sosi_arr IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi; + + TYPE t_dp_sosi_integer_arr IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_integer; + TYPE t_dp_sosi_unsigned_arr IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_unsigned; + + TYPE t_dp_siso_sl_arr IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_sl; + TYPE t_dp_sosi_sl_arr IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_sl; + + -- Multi port or multi register slv arrays for DP stream records fields + TYPE t_dp_bsn_slv_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0); + TYPE t_dp_data_slv_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_dp_stream_data_w-1 DOWNTO 0); + TYPE t_dp_dsp_data_slv_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_dp_stream_dsp_data_w-1 DOWNTO 0); + TYPE t_dp_empty_slv_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_dp_stream_empty_w-1 DOWNTO 0); + TYPE t_dp_channel_slv_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0); + TYPE t_dp_error_slv_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_dp_stream_error_w-1 DOWNTO 0); + + -- Multi-dimemsion array types with fixed LS-dimension + TYPE t_dp_siso_2arr_1 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(0 DOWNTO 0); + TYPE t_dp_sosi_2arr_1 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(0 DOWNTO 0); + + -- . 2 dimensional array with 2 fixed LS sosi/siso interfaces (dp_split, dp_concat) + TYPE t_dp_siso_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(1 DOWNTO 0); + TYPE t_dp_sosi_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(1 DOWNTO 0); + + TYPE t_dp_siso_2arr_3 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(2 DOWNTO 0); + TYPE t_dp_sosi_2arr_3 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(2 DOWNTO 0); + + TYPE t_dp_siso_2arr_4 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(3 DOWNTO 0); + TYPE t_dp_sosi_2arr_4 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(3 DOWNTO 0); + + TYPE t_dp_siso_2arr_8 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(7 DOWNTO 0); + TYPE t_dp_sosi_2arr_8 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(7 DOWNTO 0); + + TYPE t_dp_siso_2arr_9 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(8 DOWNTO 0); + TYPE t_dp_sosi_2arr_9 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(8 DOWNTO 0); + + TYPE t_dp_siso_2arr_12 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_arr(11 DOWNTO 0); + TYPE t_dp_sosi_2arr_12 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_arr(11 DOWNTO 0); + + TYPE t_dp_siso_3arr_4_2 IS ARRAY (INTEGER RANGE <>) OF t_dp_siso_2arr_2(3 DOWNTO 0); + TYPE t_dp_sosi_3arr_4_2 IS ARRAY (INTEGER RANGE <>) OF t_dp_sosi_2arr_2(3 DOWNTO 0); + + -- 2-dimensional streaming array type: + -- Note: + -- This t_*_mat is less useful then a t_*_2arr array of arrays, because assignments can only be done per element (i.e. not per row). However for t_*_2arr + -- the arrays dimension must be fixed, so these t_*_2arr types are application dependent and need to be defined where used. + TYPE t_dp_siso_mat IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF t_dp_siso; + TYPE t_dp_sosi_mat IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF t_dp_sosi; + + -- Check sosi.valid against siso.ready + PROCEDURE proc_dp_siso_alert(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi : IN t_dp_sosi; + SIGNAL siso : IN t_dp_siso; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR); + + -- Default RL=1 + PROCEDURE proc_dp_siso_alert(SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi : IN t_dp_sosi; + SIGNAL siso : IN t_dp_siso; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR); + + -- SOSI/SISO array version + PROCEDURE proc_dp_siso_alert(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi_arr : IN t_dp_sosi_arr; + SIGNAL siso_arr : IN t_dp_siso_arr; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR); + + -- SOSI/SISO array version with RL=1 + PROCEDURE proc_dp_siso_alert(SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi_arr : IN t_dp_sosi_arr; + SIGNAL siso_arr : IN t_dp_siso_arr; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR); + + -- Resize functions to fit an integer or an SLV in the corresponding t_dp_sosi field width + -- . Use these functions to assign sosi data TO a record field + -- . Use the range selection [n-1 DOWNTO 0] to assign sosi data FROM a record field to an slv + -- . The unused sosi data field bits could remain undefined 'X', because the unused bits in the fields are not used at all. + -- Typically the sosi data are treated as unsigned in the record field, so extended with '0'. However for interpretating + -- signed data in the simulation wave window it is easier to use sign extension in the record field. Therefore TO_DP_SDATA + -- and RESIZE_DP_SDATA are defined as well. + FUNCTION TO_DP_BSN( n : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION TO_DP_DATA( n : INTEGER) RETURN STD_LOGIC_VECTOR; -- use integer to support 32 bit range, so -1 = 0xFFFFFFFF = +2**32-1 + FUNCTION TO_DP_SDATA( n : INTEGER) RETURN STD_LOGIC_VECTOR; -- use integer to support 32 bit range and signed + FUNCTION TO_DP_UDATA( n : INTEGER) RETURN STD_LOGIC_VECTOR; -- alias of TO_DP_DATA() + FUNCTION TO_DP_DSP_DATA(n : INTEGER) RETURN STD_LOGIC_VECTOR; -- for re and im fields, signed data + FUNCTION TO_DP_DSP_UDATA(n: INTEGER) RETURN STD_LOGIC_VECTOR; -- for re and im fields, unsigned data (useful to carry indices) + FUNCTION TO_DP_EMPTY( n : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION TO_DP_CHANNEL( n : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION TO_DP_ERROR( n : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION RESIZE_DP_BSN( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION RESIZE_DP_DATA( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- set unused MSBits to '0' + FUNCTION RESIZE_DP_SDATA( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- sign extend unused MSBits + FUNCTION RESIZE_DP_XDATA( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- set unused MSBits to 'X' + FUNCTION RESIZE_DP_DSP_DATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- sign extend unused MSBits of re and im fields + FUNCTION RESIZE_DP_EMPTY( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION RESIZE_DP_CHANNEL( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION RESIZE_DP_ERROR( vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + + FUNCTION INCR_DP_DATA( vec : STD_LOGIC_VECTOR; dec : INTEGER; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- unsigned vec(w-1:0) + dec + FUNCTION INCR_DP_SDATA( vec : STD_LOGIC_VECTOR; dec : INTEGER; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- signed vec(w-1:0) + dec + FUNCTION INCR_DP_DSP_DATA(vec : STD_LOGIC_VECTOR; dec : INTEGER; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- signed vec(w-1:0) + dec + + FUNCTION REPLICATE_DP_DATA( seq : STD_LOGIC_VECTOR ) RETURN STD_LOGIC_VECTOR; -- replicate seq as often as fits in c_dp_stream_data_w + FUNCTION UNREPLICATE_DP_DATA(data : STD_LOGIC_VECTOR; seq_w : NATURAL) RETURN STD_LOGIC_VECTOR; -- unreplicate data to width seq_w, return low seq_w bits and set mismatch MSbits bits to '1' + + FUNCTION TO_DP_SOSI_UNSIGNED(sync, valid, sop, eop : STD_LOGIC; bsn, data, re, im, empty, channel, err : UNSIGNED) RETURN t_dp_sosi_unsigned; + + -- Keep part of head data and combine part of tail data, use the other sosi from head_sosi + FUNCTION func_dp_data_shift_first(head_sosi, tail_sosi : t_dp_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail : NATURAL) RETURN t_dp_sosi; + -- Shift and combine part of previous data and this data, use the other sosi from prev_sosi + FUNCTION func_dp_data_shift( prev_sosi, this_sosi : t_dp_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_this : NATURAL) RETURN t_dp_sosi; + -- Shift part of tail data and account for input empty + FUNCTION func_dp_data_shift_last( tail_sosi : t_dp_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail, input_empty : NATURAL) RETURN t_dp_sosi; + + -- Determine resulting empty if two streams are concatenated or split + FUNCTION func_dp_empty_concat(head_empty, tail_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_dp_empty_split(input_empty, head_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR; + + -- Multiplex the t_dp_sosi_arr based on the valid, assuming that at most one input is active valid. + FUNCTION func_dp_sosi_arr_mux(dp : t_dp_sosi_arr) RETURN t_dp_sosi; + + -- Determine the combined logical value of corresponding STD_LOGIC fields in t_dp_*_arr (for all elements or only for the mask[]='1' elements) + FUNCTION func_dp_stream_arr_and(dp : t_dp_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_and(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_and(dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_and(dp : t_dp_sosi_arr; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_or( dp : t_dp_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_or( dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_or( dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC; + FUNCTION func_dp_stream_arr_or( dp : t_dp_sosi_arr; str : STRING) RETURN STD_LOGIC; + + -- Functions to set or get a STD_LOGIC field as a STD_LOGIC_VECTOR to or from an siso or an sosi array + FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_siso_arr; + FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; sl : STD_LOGIC; str : STRING) RETURN t_dp_siso_arr; + FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; sl : STD_LOGIC; str : STRING) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_get(dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC_VECTOR; + FUNCTION func_dp_stream_arr_get(dp : t_dp_sosi_arr; str : STRING) RETURN STD_LOGIC_VECTOR; + + -- Functions to select elements from two siso or two sosi arrays (sel[] = '1' selects a, sel[] = '0' selects b) + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_siso) RETURN t_dp_siso_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_sosi) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_siso_arr; b : t_dp_siso) RETURN t_dp_siso_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_sosi_arr; b : t_dp_sosi) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_siso; b : t_dp_siso_arr) RETURN t_dp_siso_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_sosi; b : t_dp_sosi_arr) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_siso_arr) RETURN t_dp_siso_arr; + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_sosi_arr) RETURN t_dp_sosi_arr; + + -- Fix reversed buses due to connecting TO to DOWNTO range arrays. + FUNCTION func_dp_stream_arr_reverse_range(in_arr : t_dp_sosi_arr) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_reverse_range(in_arr : t_dp_siso_arr) RETURN t_dp_siso_arr; + + -- Functions to combinatorially hold the data fields and to set or reset the control fields in an sosi array + FUNCTION func_dp_stream_arr_combine_data_info_ctrl(dp : t_dp_sosi_arr; info, ctrl : t_dp_sosi) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_set_info( dp : t_dp_sosi_arr; info : t_dp_sosi) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_set_control( dp : t_dp_sosi_arr; ctrl : t_dp_sosi) RETURN t_dp_sosi_arr; + FUNCTION func_dp_stream_arr_reset_control( dp : t_dp_sosi_arr ) RETURN t_dp_sosi_arr; + + -- Reset sosi ctrl and preserve the sosi data (to avoid unnecessary data toggling and to ease data view in Wave window) + FUNCTION func_dp_stream_reset_control(dp : t_dp_sosi) RETURN t_dp_sosi; + + -- Functions to combinatorially determine the maximum and minimum sosi bsn[w-1:0] value in the sosi array (for all elements or only for the mask[]='1' elements) + FUNCTION func_dp_stream_arr_bsn_max(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_dp_stream_arr_bsn_max(dp : t_dp_sosi_arr; w : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_dp_stream_arr_bsn_min(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_dp_stream_arr_bsn_min(dp : t_dp_sosi_arr; w : NATURAL) RETURN STD_LOGIC_VECTOR; + + -- Function to copy the BSN of one valid stream to all output streams. + FUNCTION func_dp_stream_arr_copy_valid_bsn(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR) RETURN t_dp_sosi_arr; + + -- Functions to combinatorially handle channels + -- Note that the *_select and *_remove function are equivalent to dp_demux with g_combined=TRUE + FUNCTION func_dp_stream_channel_set (st_sosi : t_dp_sosi; ch : NATURAL) RETURN t_dp_sosi; -- select channel nr, add the channel field + FUNCTION func_dp_stream_channel_select(st_sosi : t_dp_sosi; ch : NATURAL) RETURN t_dp_sosi; -- select channel nr, skip the channel field + FUNCTION func_dp_stream_channel_remove(st_sosi : t_dp_sosi; ch : NATURAL) RETURN t_dp_sosi; -- skip channel nr + + -- Functions to combinatorially handle the error field + FUNCTION func_dp_stream_error_set(st_sosi : t_dp_sosi; n : NATURAL) RETURN t_dp_sosi; -- force err = 0, is OK + + -- Functions to combinatorially handle the BSN field + FUNCTION func_dp_stream_bsn_set(st_sosi : t_dp_sosi; bsn : STD_LOGIC_VECTOR) RETURN t_dp_sosi; + + -- Functions to combine sosi fields + FUNCTION func_dp_stream_combine_info_and_data(info, data : t_dp_sosi) RETURN t_dp_sosi; + + -- Functions to convert sosi fields + FUNCTION func_dp_stream_slv_to_integer(slv_sosi : t_dp_sosi; w : NATURAL) RETURN t_dp_sosi_integer; + + -- Functions to set the DATA, RE and IM field in a stream. + FUNCTION func_dp_stream_set_data(dp : t_dp_sosi; slv : STD_LOGIC_VECTOR; str : STRING ) RETURN t_dp_sosi; + 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; + + +PACKAGE BODY dp_stream_pkg IS + + -- Check sosi.valid against siso.ready + PROCEDURE proc_dp_siso_alert(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi : IN t_dp_sosi; + SIGNAL siso : IN t_dp_siso; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS + BEGIN + ready_reg(0) <= siso.ready; + -- Register siso.ready in c_ready_latency registers + IF rising_edge(clk) THEN + -- Check DP sink + IF sosi.valid = '1' AND ready_reg(c_ready_latency) = '0' THEN + REPORT "RL ERROR" SEVERITY FAILURE; + END IF; + ready_reg( 1 TO c_ready_latency) <= ready_reg( 0 TO c_ready_latency-1); + END IF; + END proc_dp_siso_alert; + + -- Default RL=1 + PROCEDURE proc_dp_siso_alert(SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi : IN t_dp_sosi; + SIGNAL siso : IN t_dp_siso; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS + BEGIN + proc_dp_siso_alert(1, clk, sosi, siso, ready_reg); + END proc_dp_siso_alert; + + -- SOSI/SISO array version + PROCEDURE proc_dp_siso_alert(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi_arr : IN t_dp_sosi_arr; + SIGNAL siso_arr : IN t_dp_siso_arr; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS + BEGIN + FOR i IN 0 TO sosi_arr'LENGTH-1 LOOP + ready_reg(i*(c_ready_latency+1)) <= siso_arr(i).ready; -- SLV is used as an array: nof_streams*(0..c_ready_latency) + END LOOP; + -- Register siso.ready in c_ready_latency registers + IF rising_edge(clk) THEN + FOR i IN 0 TO sosi_arr'LENGTH-1 LOOP + -- Check DP sink + IF sosi_arr(i).valid = '1' AND ready_reg(i*(c_ready_latency+1)+1) = '0' THEN + REPORT "RL ERROR" SEVERITY FAILURE; + END IF; + ready_reg(i*(c_ready_latency+1)+1 TO i*(c_ready_latency+1)+c_ready_latency) <= ready_reg(i*(c_ready_latency+1) TO i*(c_ready_latency+1)+c_ready_latency-1); + END LOOP; + END IF; + END proc_dp_siso_alert; + + -- SOSI/SISO array version with RL=1 + PROCEDURE proc_dp_siso_alert(SIGNAL clk : IN STD_LOGIC; + SIGNAL sosi_arr : IN t_dp_sosi_arr; + SIGNAL siso_arr : IN t_dp_siso_arr; + SIGNAL ready_reg : INOUT STD_LOGIC_VECTOR) IS + BEGIN + proc_dp_siso_alert(1, clk, sosi_arr, siso_arr, ready_reg); + END proc_dp_siso_alert; + + -- Resize functions to fit an integer or an SLV in the corresponding t_dp_sosi field width + FUNCTION TO_DP_BSN(n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(TO_SVEC(n, 32), c_dp_stream_bsn_w); + END TO_DP_BSN; + + FUNCTION TO_DP_DATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(TO_SVEC(n, 32), c_dp_stream_data_w); + END TO_DP_DATA; + + FUNCTION TO_DP_SDATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_SVEC(TO_SVEC(n, 32), c_dp_stream_data_w); + END TO_DP_SDATA; + + FUNCTION TO_DP_UDATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN TO_DP_DATA(n); + END TO_DP_UDATA; + + FUNCTION TO_DP_DSP_DATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_SVEC(TO_SVEC(n, 32), c_dp_stream_dsp_data_w); + END TO_DP_DSP_DATA; + + FUNCTION TO_DP_DSP_UDATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(TO_SVEC(n, 32), c_dp_stream_dsp_data_w); + END TO_DP_DSP_UDATA; + + FUNCTION TO_DP_EMPTY(n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN TO_UVEC(n, c_dp_stream_empty_w); + END TO_DP_EMPTY; + + FUNCTION TO_DP_CHANNEL(n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN TO_UVEC(n, c_dp_stream_channel_w); + END TO_DP_CHANNEL; + + FUNCTION TO_DP_ERROR(n : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN TO_UVEC(n, c_dp_stream_error_w); + END TO_DP_ERROR; + + FUNCTION RESIZE_DP_BSN(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(vec, c_dp_stream_bsn_w); + END RESIZE_DP_BSN; + + FUNCTION RESIZE_DP_DATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(vec, c_dp_stream_data_w); + END RESIZE_DP_DATA; + + FUNCTION RESIZE_DP_SDATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_SVEC(vec, c_dp_stream_data_w); + END RESIZE_DP_SDATA; + + FUNCTION RESIZE_DP_XDATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_vec : STD_LOGIC_VECTOR(c_dp_stream_data_w-1 DOWNTO 0) := (OTHERS=>'X'); + BEGIN + v_vec(vec'LENGTH-1 DOWNTO 0) := vec; + RETURN v_vec; + END RESIZE_DP_XDATA; + + FUNCTION RESIZE_DP_DSP_DATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_SVEC(vec, c_dp_stream_dsp_data_w); + END RESIZE_DP_DSP_DATA; + + FUNCTION RESIZE_DP_EMPTY(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(vec, c_dp_stream_empty_w); + END RESIZE_DP_EMPTY; + + FUNCTION RESIZE_DP_CHANNEL(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(vec, c_dp_stream_channel_w); + END RESIZE_DP_CHANNEL; + + FUNCTION RESIZE_DP_ERROR(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_UVEC(vec, c_dp_stream_error_w); + END RESIZE_DP_ERROR; + + FUNCTION INCR_DP_DATA(vec : STD_LOGIC_VECTOR; dec : INTEGER; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_DP_DATA(STD_LOGIC_VECTOR(UNSIGNED(vec(w-1 DOWNTO 0)) + dec)); + END INCR_DP_DATA; + + FUNCTION INCR_DP_SDATA(vec : STD_LOGIC_VECTOR; dec : INTEGER; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_DP_SDATA(STD_LOGIC_VECTOR(SIGNED(vec(w-1 DOWNTO 0)) + dec)); + END INCR_DP_SDATA; + + FUNCTION INCR_DP_DSP_DATA(vec : STD_LOGIC_VECTOR; dec : INTEGER; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN RESIZE_DP_DSP_DATA(STD_LOGIC_VECTOR(SIGNED(vec(w-1 DOWNTO 0)) + dec)); + END INCR_DP_DSP_DATA; + + FUNCTION REPLICATE_DP_DATA(seq : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_seq_w : NATURAL := seq'LENGTH; + CONSTANT c_nof_replications : NATURAL := ceil_div(c_dp_stream_data_w, c_seq_w); + CONSTANT c_vec_w : NATURAL := ceil_value(c_dp_stream_data_w, c_seq_w); + VARIABLE v_vec : STD_LOGIC_VECTOR(c_vec_w-1 DOWNTO 0); + BEGIN + FOR I IN 0 TO c_nof_replications-1 LOOP + v_vec((I+1)*c_seq_w-1 DOWNTO I*c_seq_w) := seq; + END LOOP; + RETURN v_vec(c_dp_stream_data_w-1 DOWNTO 0); + END REPLICATE_DP_DATA; + + FUNCTION UNREPLICATE_DP_DATA(data : STD_LOGIC_VECTOR; seq_w :NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_data_w : NATURAL := data'LENGTH; + CONSTANT c_nof_replications : NATURAL := ceil_div(c_data_w, seq_w); + CONSTANT c_vec_w : NATURAL := ceil_value(c_data_w, seq_w); + VARIABLE v_seq : STD_LOGIC_VECTOR(seq_w-1 DOWNTO 0); + VARIABLE v_data : STD_LOGIC_VECTOR(c_vec_w-1 DOWNTO 0); + VARIABLE v_vec : STD_LOGIC_VECTOR(c_vec_w-1 DOWNTO 0); + BEGIN + v_data := RESIZE_UVEC(data, c_vec_w); + v_seq := v_data(seq_w-1 DOWNTO 0); -- low data part is the v_seq + v_vec(seq_w-1 DOWNTO 0) := v_seq; -- keep v_seq at low part of return value + IF c_nof_replications>1 THEN + FOR I IN 1 TO c_nof_replications-1 LOOP + v_vec((I+1)*seq_w-1 DOWNTO I*seq_w) := v_data((I+1)*seq_w-1 DOWNTO I*seq_w) XOR v_seq; -- set return bit to '1' for high part data bits that do not match low part v_seq + END LOOP; + END IF; + RETURN v_vec(c_data_w-1 DOWNTO 0); + END UNREPLICATE_DP_DATA; + + FUNCTION TO_DP_SOSI_UNSIGNED(sync, valid, sop, eop : STD_LOGIC; bsn, data, re, im, empty, channel, err : UNSIGNED) RETURN t_dp_sosi_unsigned IS + VARIABLE v_sosi_unsigned : t_dp_sosi_unsigned; + BEGIN + v_sosi_unsigned.sync := sync; + v_sosi_unsigned.valid := valid; + v_sosi_unsigned.sop := sop; + v_sosi_unsigned.eop := eop; + v_sosi_unsigned.bsn := RESIZE(bsn, c_dp_stream_bsn_w); + v_sosi_unsigned.data := RESIZE(data, c_dp_stream_data_w); + v_sosi_unsigned.re := RESIZE(re, c_dp_stream_dsp_data_w); + v_sosi_unsigned.im := RESIZE(im, c_dp_stream_dsp_data_w); + v_sosi_unsigned.empty := RESIZE(empty, c_dp_stream_empty_w); + v_sosi_unsigned.channel := RESIZE(channel, c_dp_stream_channel_w); + v_sosi_unsigned.err := RESIZE(err, c_dp_stream_error_w); + RETURN v_sosi_unsigned; + END TO_DP_SOSI_UNSIGNED; + + -- Keep part of head data and combine part of tail data + FUNCTION func_dp_data_shift_first(head_sosi, tail_sosi : t_dp_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail : NATURAL) RETURN t_dp_sosi IS + VARIABLE vN : NATURAL := nof_symbols_per_data; + VARIABLE v_sosi : t_dp_sosi; + BEGIN + ASSERT nof_symbols_from_tail<vN REPORT "func_dp_data_shift_first : no symbols from head" SEVERITY FAILURE; + -- use the other sosi from head_sosi + v_sosi := head_sosi; -- I = nof_symbols_from_tail = 0 + FOR I IN 1 TO vN-1 LOOP -- I > 0 + IF nof_symbols_from_tail = I THEN + v_sosi.data(I*symbol_w-1 DOWNTO 0) := tail_sosi.data(vN*symbol_w-1 DOWNTO (vN-I)*symbol_w); + END IF; + END LOOP; + RETURN v_sosi; + END func_dp_data_shift_first; + + + -- Shift and combine part of previous data and this data, + FUNCTION func_dp_data_shift(prev_sosi, this_sosi : t_dp_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_this : NATURAL) RETURN t_dp_sosi IS + VARIABLE vK : NATURAL := nof_symbols_from_this; + VARIABLE vN : NATURAL := nof_symbols_per_data; + VARIABLE v_sosi : t_dp_sosi; + BEGIN + -- use the other sosi from this_sosi if nof_symbols_from_this > 0 else use other sosi from prev_sosi + IF vK>0 THEN + v_sosi := this_sosi; + ELSE + v_sosi := prev_sosi; + END IF; + + -- use sosi data from both if 0 < nof_symbols_from_this < nof_symbols_per_data (i.e. 0 < I < vN) + IF vK<nof_symbols_per_data THEN -- I = vK = nof_symbols_from_this < vN + -- Implementation using variable vK directly instead of via I in a LOOP + -- IF vK > 0 THEN + -- v_sosi.data(vN*symbol_w-1 DOWNTO vK*symbol_w) := prev_sosi.data((vN-vK)*symbol_w-1 DOWNTO 0); + -- v_sosi.data( vK*symbol_w-1 DOWNTO 0) := this_sosi.data( vN *symbol_w-1 DOWNTO (vN-vK)*symbol_w); + -- END IF; + -- Implementaion using LOOP vK rather than VARIABLE vK directly as index to help synthesis and avoid potential multiplier + v_sosi.data := prev_sosi.data; -- I = vK = nof_symbols_from_this = 0 + FOR I IN 1 TO vN-1 LOOP -- I = vK = nof_symbols_from_this > 0 + IF vK = I THEN + v_sosi.data(vN*symbol_w-1 DOWNTO I*symbol_w) := prev_sosi.data((vN-I)*symbol_w-1 DOWNTO 0); + v_sosi.data( I*symbol_w-1 DOWNTO 0) := this_sosi.data( vN *symbol_w-1 DOWNTO (vN-I)*symbol_w); + END IF; + END LOOP; + END IF; + RETURN v_sosi; + END func_dp_data_shift; + + + -- Shift part of tail data and account for input empty + FUNCTION func_dp_data_shift_last(tail_sosi : t_dp_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail, input_empty : NATURAL) RETURN t_dp_sosi IS + VARIABLE vK : NATURAL := nof_symbols_from_tail; + VARIABLE vL : NATURAL := input_empty; + VARIABLE vN : NATURAL := nof_symbols_per_data; + VARIABLE v_sosi : t_dp_sosi; + BEGIN + ASSERT vK > 0 REPORT "func_dp_data_shift_last : no symbols from tail" SEVERITY FAILURE; + ASSERT vK+vL<=vN REPORT "func_dp_data_shift_last : impossible shift" SEVERITY FAILURE; + v_sosi := tail_sosi; + -- Implementation using variable vK directly instead of via I in a LOOP + -- IF vK > 0 THEN + -- v_sosi.data(vN*symbol_w-1 DOWNTO (vN-vK)*symbol_w) <= tail_sosi.data((vK+vL)*symbol_w-1 DOWNTO vL*symbol_w); + -- END IF; + -- Implementation using LOOP vK rather than VARIABLE vK directly as index to help synthesis and avoid potential multiplier + -- Implementation using LOOP vL rather than VARIABLE vL directly as index to help synthesis and avoid potential multiplier + FOR I IN 1 TO vN-1 LOOP + IF vK = I THEN + FOR J IN 0 TO vN-1 LOOP + IF vL = J THEN + v_sosi.data(vN*symbol_w-1 DOWNTO (vN-I)*symbol_w) := tail_sosi.data((I+J)*symbol_w-1 DOWNTO J*symbol_w); + END IF; + END LOOP; + END IF; + END LOOP; + RETURN v_sosi; + END func_dp_data_shift_last; + + + -- Determine resulting empty if two streams are concatenated + -- . both empty must use the same nof symbols per data + FUNCTION func_dp_empty_concat(head_empty, tail_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_a, v_b, v_empty : NATURAL; + BEGIN + v_a := TO_UINT(head_empty); + v_b := TO_UINT(tail_empty); + v_empty := v_a + v_b; + IF v_empty >= nof_symbols_per_data THEN + v_empty := v_empty - nof_symbols_per_data; + END IF; + RETURN TO_UVEC(v_empty, head_empty'LENGTH); + END func_dp_empty_concat; + + FUNCTION func_dp_empty_split(input_empty, head_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_a, v_b, v_empty : NATURAL; + BEGIN + v_a := TO_UINT(input_empty); + v_b := TO_UINT(head_empty); + IF v_a >= v_b THEN + v_empty := v_a - v_b; + ELSE + v_empty := (nof_symbols_per_data + v_a) - v_b; + END IF; + RETURN TO_UVEC(v_empty, head_empty'LENGTH); + END func_dp_empty_split; + + + -- Multiplex the t_dp_sosi_arr based on the valid, assuming that at most one input is active valid. + FUNCTION func_dp_sosi_arr_mux(dp : t_dp_sosi_arr) RETURN t_dp_sosi IS + VARIABLE v_sosi : t_dp_sosi := c_dp_sosi_rst; + BEGIN + FOR I IN dp'RANGE LOOP + IF dp(I).valid='1' THEN + v_sosi := dp(I); + EXIT; + END IF; + END LOOP; + RETURN v_sosi; + END func_dp_sosi_arr_mux; + + + -- Determine the combined logical value of corresponding STD_LOGIC fields in t_dp_*_arr (for all elements or only for the mask[]='1' elements) + FUNCTION func_dp_stream_arr_and(dp : t_dp_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS + VARIABLE v_vec : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); -- set default v_vec such that unmasked input have no influence on operation result + VARIABLE v_any : STD_LOGIC := '0'; + BEGIN + -- map siso field to v_vec + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + v_any := '1'; + IF str="READY" THEN v_vec(I) := dp(I).ready; + ELSIF str="XON" THEN v_vec(I) := dp(I).xon; + ELSE REPORT "Error in func_dp_stream_arr_and for t_dp_siso_arr"; + END IF; + END IF; + END LOOP; + -- do operation on the selected record field + IF v_any='1' THEN + RETURN vector_and(v_vec); -- return AND of the masked input fields + ELSE + RETURN '0'; -- return '0' if no input was masked + END IF; + END func_dp_stream_arr_and; + + FUNCTION func_dp_stream_arr_and(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS + VARIABLE v_vec : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); -- set default v_vec such that unmasked input have no influence on operation result + VARIABLE v_any : STD_LOGIC := '0'; + BEGIN + -- map siso field to v_vec + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + v_any := '1'; + IF str="VALID" THEN v_vec(I) := dp(I).valid; + ELSIF str="SOP" THEN v_vec(I) := dp(I).sop; + ELSIF str="EOP" THEN v_vec(I) := dp(I).eop; + ELSIF str="SYNC" THEN v_vec(I) := dp(I).sync; + ELSE REPORT "Error in func_dp_stream_arr_and for t_dp_sosi_arr"; + END IF; + END IF; + END LOOP; + -- do operation on the selected record field + IF v_any='1' THEN + RETURN vector_and(v_vec); -- return AND of the masked input fields + ELSE + RETURN '0'; -- return '0' if no input was masked + END IF; + END func_dp_stream_arr_and; + + FUNCTION func_dp_stream_arr_and(dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC IS + CONSTANT c_mask : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); + BEGIN + RETURN func_dp_stream_arr_and(dp, c_mask, str); + END func_dp_stream_arr_and; + + FUNCTION func_dp_stream_arr_and(dp : t_dp_sosi_arr; str : STRING) RETURN STD_LOGIC IS + CONSTANT c_mask : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); + BEGIN + RETURN func_dp_stream_arr_and(dp, c_mask, str); + END func_dp_stream_arr_and; + + FUNCTION func_dp_stream_arr_or(dp : t_dp_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS + VARIABLE v_vec : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'0'); -- set default v_vec such that unmasked input have no influence on operation result + VARIABLE v_any : STD_LOGIC := '0'; + BEGIN + -- map siso field to v_vec + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + v_any := '1'; + IF str="READY" THEN v_vec(I) := dp(I).ready; + ELSIF str="XON" THEN v_vec(I) := dp(I).xon; + ELSE REPORT "Error in func_dp_stream_arr_or for t_dp_siso_arr"; + END IF; + END IF; + END LOOP; + -- do operation on the selected record field + IF v_any='1' THEN + RETURN vector_or(v_vec); -- return OR of the masked input fields + ELSE + RETURN '0'; -- return '0' if no input was masked + END IF; + END func_dp_stream_arr_or; + + FUNCTION func_dp_stream_arr_or(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS + VARIABLE v_vec : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'0'); -- set default v_vec such that unmasked input have no influence on operation result + VARIABLE v_any : STD_LOGIC := '0'; + BEGIN + -- map siso field to v_vec + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + v_any := '1'; + IF str="VALID" THEN v_vec(I) := dp(I).valid; + ELSIF str="SOP" THEN v_vec(I) := dp(I).sop; + ELSIF str="EOP" THEN v_vec(I) := dp(I).eop; + ELSIF str="SYNC" THEN v_vec(I) := dp(I).sync; + ELSE REPORT "Error in func_dp_stream_arr_or for t_dp_sosi_arr"; + END IF; + END IF; + END LOOP; + -- do operation on the selected record field + IF v_any='1' THEN + RETURN vector_or(v_vec); -- return OR of the masked input fields + ELSE + RETURN '0'; -- return '0' if no input was masked + END IF; + END func_dp_stream_arr_or; + + FUNCTION func_dp_stream_arr_or(dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC IS + CONSTANT c_mask : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); + BEGIN + RETURN func_dp_stream_arr_or(dp, c_mask, str); + END func_dp_stream_arr_or; + + FUNCTION func_dp_stream_arr_or(dp : t_dp_sosi_arr; str : STRING) RETURN STD_LOGIC IS + CONSTANT c_mask : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); + BEGIN + RETURN func_dp_stream_arr_or(dp, c_mask, str); + END func_dp_stream_arr_or; + + + -- Functions to set or get a STD_LOGIC field as a STD_LOGIC_VECTOR to or from an siso or an sosi array + FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_siso_arr IS + VARIABLE v_dp : t_dp_siso_arr(dp'RANGE) := dp; -- default + VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := slv; -- map to ensure same range as for dp + BEGIN + FOR I IN dp'RANGE LOOP + IF str="READY" THEN v_dp(I).ready := v_slv(I); + ELSIF str="XON" THEN v_dp(I).xon := v_slv(I); + ELSE REPORT "Error in func_dp_stream_arr_set for t_dp_siso_arr"; + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_set; + + FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- default + VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := slv; -- map to ensure same range as for dp + BEGIN + FOR I IN dp'RANGE LOOP + IF str="VALID" THEN v_dp(I).valid := v_slv(I); + ELSIF str="SOP" THEN v_dp(I).sop := v_slv(I); + ELSIF str="EOP" THEN v_dp(I).eop := v_slv(I); + ELSIF str="SYNC" THEN v_dp(I).sync := v_slv(I); + ELSE REPORT "Error in func_dp_stream_arr_set for t_dp_sosi_arr"; + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_set; + + FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; sl : STD_LOGIC; str : STRING) RETURN t_dp_siso_arr IS + VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>sl); + BEGIN + RETURN func_dp_stream_arr_set(dp, v_slv, str); + END func_dp_stream_arr_set; + + FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; sl : STD_LOGIC; str : STRING) RETURN t_dp_sosi_arr IS + VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>sl); + BEGIN + RETURN func_dp_stream_arr_set(dp, v_slv, str); + END func_dp_stream_arr_set; + + FUNCTION func_dp_stream_arr_get(dp : t_dp_siso_arr; str : STRING) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_ctrl : STD_LOGIC_VECTOR(dp'RANGE); + BEGIN + FOR I IN dp'RANGE LOOP + IF str="READY" THEN v_ctrl(I) := dp(I).ready; + ELSIF str="XON" THEN v_ctrl(I) := dp(I).xon; + ELSE REPORT "Error in func_dp_stream_arr_get for t_dp_siso_arr"; + END IF; + END LOOP; + RETURN v_ctrl; + END func_dp_stream_arr_get; + + FUNCTION func_dp_stream_arr_get(dp : t_dp_sosi_arr; str : STRING) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_ctrl : STD_LOGIC_VECTOR(dp'RANGE); + BEGIN + FOR I IN dp'RANGE LOOP + IF str="VALID" THEN v_ctrl(I) := dp(I).valid; + ELSIF str="SOP" THEN v_ctrl(I) := dp(I).sop; + ELSIF str="EOP" THEN v_ctrl(I) := dp(I).eop; + ELSIF str="SYNC" THEN v_ctrl(I) := dp(I).sync; + ELSE REPORT "Error in func_dp_stream_arr_get for t_dp_sosi_arr"; + END IF; + END LOOP; + RETURN v_ctrl; + END func_dp_stream_arr_get; + + + -- Functions to select elements from two siso or two sosi arrays (sel[] = '1' selects a, sel[] = '0' selects b) + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_siso) RETURN t_dp_siso_arr IS + VARIABLE v_dp : t_dp_siso_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a; + ELSE + v_dp(I) := b; + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_siso_arr; b : t_dp_siso) RETURN t_dp_siso_arr IS + VARIABLE v_dp : t_dp_siso_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a(I); + ELSE + v_dp(I) := b; + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_siso; b : t_dp_siso_arr) RETURN t_dp_siso_arr IS + VARIABLE v_dp : t_dp_siso_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a; + ELSE + v_dp(I) := b(I); + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_siso_arr) RETURN t_dp_siso_arr IS + VARIABLE v_dp : t_dp_siso_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a(I); + ELSE + v_dp(I) := b(I); + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_sosi) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a; + ELSE + v_dp(I) := b; + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_sosi_arr; b : t_dp_sosi) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a(I); + ELSE + v_dp(I) := b; + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_dp_sosi; b : t_dp_sosi_arr) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a; + ELSE + v_dp(I) := b(I); + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_dp_sosi_arr) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(sel'RANGE); + BEGIN + FOR I IN sel'RANGE LOOP + IF sel(I)='1' THEN + v_dp(I) := a(I); + ELSE + v_dp(I) := b(I); + END IF; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_select; + + FUNCTION func_dp_stream_arr_reverse_range(in_arr : t_dp_siso_arr) RETURN t_dp_siso_arr IS + VARIABLE v_to_range : t_dp_siso_arr(0 TO in_arr'HIGH); + VARIABLE v_downto_range : t_dp_siso_arr(in_arr'HIGH DOWNTO 0); + BEGIN + FOR i IN in_arr'RANGE LOOP + v_to_range(i) := in_arr(in_arr'HIGH-i); + v_downto_range(i) := in_arr(in_arr'HIGH-i); + END LOOP; + IF in_arr'LEFT>in_arr'RIGHT THEN + RETURN v_downto_range; + ELSIF in_arr'LEFT<in_arr'RIGHT THEN + RETURN v_to_range; + ELSE + RETURN in_arr; + END IF; + END func_dp_stream_arr_reverse_range; + + FUNCTION func_dp_stream_arr_reverse_range(in_arr : t_dp_sosi_arr) RETURN t_dp_sosi_arr IS + VARIABLE v_to_range : t_dp_sosi_arr(0 TO in_arr'HIGH); + VARIABLE v_downto_range : t_dp_sosi_arr(in_arr'HIGH DOWNTO 0); + BEGIN + FOR i IN in_arr'RANGE LOOP + v_to_range(i) := in_arr(in_arr'HIGH-i); + v_downto_range(i) := in_arr(in_arr'HIGH-i); + END LOOP; + IF in_arr'LEFT>in_arr'RIGHT THEN + RETURN v_downto_range; + ELSIF in_arr'LEFT<in_arr'RIGHT THEN + RETURN v_to_range; + ELSE + RETURN in_arr; + END IF; + END func_dp_stream_arr_reverse_range; + + -- Functions to combinatorially hold the data fields and to set or reset the info and control fields in an sosi array + FUNCTION func_dp_stream_arr_combine_data_info_ctrl(dp : t_dp_sosi_arr; info, ctrl : t_dp_sosi) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- hold sosi data + BEGIN + v_dp := func_dp_stream_arr_set_info( v_dp, info); -- set sosi info + v_dp := func_dp_stream_arr_set_control(v_dp, ctrl); -- set sosi ctrl + RETURN v_dp; + END func_dp_stream_arr_combine_data_info_ctrl; + + FUNCTION func_dp_stream_arr_set_info(dp : t_dp_sosi_arr; info : t_dp_sosi) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- hold sosi data + BEGIN + FOR I IN dp'RANGE LOOP -- set sosi info + v_dp(I).bsn := info.bsn; -- sop + v_dp(I).channel := info.channel; -- sop + v_dp(I).empty := info.empty; -- eop + v_dp(I).err := info.err; -- eop + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_set_info; + + FUNCTION func_dp_stream_arr_set_control(dp : t_dp_sosi_arr; ctrl : t_dp_sosi) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- hold sosi data + BEGIN + FOR I IN dp'RANGE LOOP -- set sosi control + v_dp(I).valid := ctrl.valid; + v_dp(I).sop := ctrl.sop; + v_dp(I).eop := ctrl.eop; + v_dp(I).sync := ctrl.sync; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_set_control; + + FUNCTION func_dp_stream_arr_reset_control(dp : t_dp_sosi_arr) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- hold sosi data + BEGIN + FOR I IN dp'RANGE LOOP -- reset sosi control + v_dp(I).valid := '0'; + v_dp(I).sop := '0'; + v_dp(I).eop := '0'; + v_dp(I).sync := '0'; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_reset_control; + + FUNCTION func_dp_stream_reset_control(dp : t_dp_sosi) RETURN t_dp_sosi IS + VARIABLE v_dp : t_dp_sosi := dp; -- hold sosi data + BEGIN + -- reset sosi control + v_dp.valid := '0'; + v_dp.sop := '0'; + v_dp.eop := '0'; + v_dp.sync := '0'; + RETURN v_dp; + END func_dp_stream_reset_control; + + -- Functions to combinatorially determine the maximum and minimum sosi bsn[w-1:0] value in the sosi array (for all elements or only for the mask[]='1' elements) + FUNCTION func_dp_stream_arr_bsn_max(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_bsn : STD_LOGIC_VECTOR(w-1 DOWNTO 0) := (OTHERS=>'0'); -- init max v_bsn with minimum value + BEGIN + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + IF UNSIGNED(v_bsn) < UNSIGNED(dp(I).bsn(w-1 DOWNTO 0)) THEN + v_bsn := dp(I).bsn(w-1 DOWNTO 0); + END IF; + END IF; + END LOOP; + RETURN v_bsn; + END func_dp_stream_arr_bsn_max; + + FUNCTION func_dp_stream_arr_bsn_max(dp : t_dp_sosi_arr; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_mask : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); + BEGIN + RETURN func_dp_stream_arr_bsn_max(dp, c_mask, w); + END func_dp_stream_arr_bsn_max; + + FUNCTION func_dp_stream_arr_bsn_min(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + VARIABLE v_bsn : STD_LOGIC_VECTOR(w-1 DOWNTO 0) := (OTHERS=>'1'); -- init min v_bsn with maximum value + BEGIN + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + IF UNSIGNED(v_bsn) > UNSIGNED(dp(I).bsn(w-1 DOWNTO 0)) THEN + v_bsn := dp(I).bsn(w-1 DOWNTO 0); + END IF; + END IF; + END LOOP; + RETURN v_bsn; + END func_dp_stream_arr_bsn_min; + + FUNCTION func_dp_stream_arr_bsn_min(dp : t_dp_sosi_arr; w : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_mask : STD_LOGIC_VECTOR(dp'RANGE) := (OTHERS=>'1'); + BEGIN + RETURN func_dp_stream_arr_bsn_min(dp, c_mask, w); + END func_dp_stream_arr_bsn_min; + + -- Function to copy the BSN number of one valid stream to all other streams. + FUNCTION func_dp_stream_arr_copy_valid_bsn(dp : t_dp_sosi_arr; mask : STD_LOGIC_VECTOR) RETURN t_dp_sosi_arr IS + VARIABLE v_bsn : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; -- hold sosi data + BEGIN + FOR I IN dp'RANGE LOOP + IF mask(I)='1' THEN + v_bsn := dp(I).bsn; + END IF; + END LOOP; + FOR I IN dp'RANGE LOOP + v_dp(I).bsn := v_bsn; + END LOOP; + RETURN v_dp; + END func_dp_stream_arr_copy_valid_bsn; + + + -- Functions to combinatorially handle channels + FUNCTION func_dp_stream_channel_set(st_sosi : t_dp_sosi; ch : NATURAL) RETURN t_dp_sosi IS + VARIABLE v_rec : t_dp_sosi := st_sosi; + BEGIN + v_rec.channel := TO_UVEC(ch, c_dp_stream_channel_w); + RETURN v_rec; + END func_dp_stream_channel_set; + + FUNCTION func_dp_stream_channel_select(st_sosi : t_dp_sosi; ch : NATURAL) RETURN t_dp_sosi IS + VARIABLE v_rec : t_dp_sosi := st_sosi; + BEGIN + IF UNSIGNED(st_sosi.channel)/=ch THEN + v_rec.valid := '0'; + v_rec.sop := '0'; + v_rec.eop := '0'; + END IF; + RETURN v_rec; + END func_dp_stream_channel_select; + + FUNCTION func_dp_stream_channel_remove(st_sosi : t_dp_sosi; ch : NATURAL) RETURN t_dp_sosi IS + VARIABLE v_rec : t_dp_sosi := st_sosi; + BEGIN + IF UNSIGNED(st_sosi.channel)=ch THEN + v_rec.valid := '0'; + v_rec.sop := '0'; + v_rec.eop := '0'; + END IF; + RETURN v_rec; + END func_dp_stream_channel_remove; + + + FUNCTION func_dp_stream_error_set(st_sosi : t_dp_sosi; n : NATURAL) RETURN t_dp_sosi IS + VARIABLE v_rec : t_dp_sosi := st_sosi; + BEGIN + v_rec.err := TO_UVEC(n, c_dp_stream_error_w); + RETURN v_rec; + END func_dp_stream_error_set; + + + FUNCTION func_dp_stream_bsn_set(st_sosi : t_dp_sosi; bsn : STD_LOGIC_VECTOR) RETURN t_dp_sosi IS + VARIABLE v_rec : t_dp_sosi := st_sosi; + BEGIN + v_rec.bsn := RESIZE_DP_BSN(bsn); + RETURN v_rec; + END func_dp_stream_bsn_set; + + + FUNCTION func_dp_stream_combine_info_and_data(info, data : t_dp_sosi) RETURN t_dp_sosi IS + VARIABLE v_rec : t_dp_sosi := data; -- Sosi data fields + BEGIN + -- Combine sosi data with the sosi info fields + v_rec.sync := info.sync AND data.sop; -- force sync only active at data.sop + v_rec.bsn := info.bsn; + v_rec.channel := info.channel; + v_rec.empty := info.empty; + v_rec.err := info.err; + RETURN v_rec; + END func_dp_stream_combine_info_and_data; + + + FUNCTION func_dp_stream_slv_to_integer(slv_sosi : t_dp_sosi; w : NATURAL) RETURN t_dp_sosi_integer IS + VARIABLE v_rec : t_dp_sosi_integer; + BEGIN + v_rec.sync := slv_sosi.sync; + v_rec.bsn := TO_UINT(slv_sosi.bsn(30 DOWNTO 0)); -- NATURAL'width = 31 bit + v_rec.data := TO_SINT(slv_sosi.data(w-1 DOWNTO 0)); + v_rec.re := TO_SINT(slv_sosi.re(w-1 DOWNTO 0)); + v_rec.im := TO_SINT(slv_sosi.im(w-1 DOWNTO 0)); + v_rec.valid := slv_sosi.valid; + v_rec.sop := slv_sosi.sop; + v_rec.eop := slv_sosi.eop; + v_rec.empty := TO_UINT(slv_sosi.empty); + v_rec.channel := TO_UINT(slv_sosi.channel); + v_rec.err := TO_UINT(slv_sosi.err); + RETURN v_rec; + END func_dp_stream_slv_to_integer; + + FUNCTION func_dp_stream_set_data(dp : t_dp_sosi; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi IS + VARIABLE v_dp : t_dp_sosi := dp; + BEGIN + IF str="DATA" THEN v_dp.data := RESIZE_DP_DATA(slv); + ELSIF str="DSP" THEN v_dp.re := RESIZE_DP_DSP_DATA(slv); + v_dp.im := RESIZE_DP_DSP_DATA(slv); + ELSIF str="RE" THEN v_dp.re := RESIZE_DP_DSP_DATA(slv); + ELSIF str="IM" THEN v_dp.im := RESIZE_DP_DSP_DATA(slv); + ELSIF str="ALL" THEN v_dp.data := RESIZE_DP_DATA(slv); + v_dp.re := RESIZE_DP_DSP_DATA(slv); + v_dp.im := RESIZE_DP_DSP_DATA(slv); + ELSE REPORT "Error in func_dp_stream_set_data for t_dp_sosi"; + END IF; + RETURN v_dp; + END; + + FUNCTION func_dp_stream_set_data(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; + BEGIN + FOR I IN dp'RANGE LOOP + v_dp(I) := func_dp_stream_set_data(dp(I), slv, str); + END LOOP; + RETURN v_dp; + END; + + 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 IS + VARIABLE v_dp : t_dp_sosi_arr(dp'RANGE) := dp; + BEGIN + FOR I IN dp'RANGE LOOP + IF mask(I)='0' THEN + v_dp(I) := func_dp_stream_set_data(dp(I), slv, str); + END IF; + END LOOP; + 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 := (OTHERS=>'0'); + v_src_out_arr(i).re := (OTHERS=>'0'); + v_src_out_arr(i).im := (OTHERS=>'0'); + 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/cores/base/dp/dp_pkg/dp_stream_stimuli.vhd b/cores/base/dp/dp_pkg/dp_stream_stimuli.vhd new file mode 100644 index 0000000000000000000000000000000000000000..3510fa0e2b0725c92c1c463e84a5b18032006af7 --- /dev/null +++ b/cores/base/dp/dp_pkg/dp_stream_stimuli.vhd @@ -0,0 +1,185 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +------------------------------------------------------------------------------- + +-- Purpose: +-- . The dp_stream_stimuli generates as stream of packets with counter data. +-- Description: +-- +-- Remark: +-- . The stimuli empty = 0 because the data in proc_dp_gen_block_data() is +-- generated with one symbol per data (because symbol_w = data_w). +-- +-- Usage: +-- . See tb_dp_example_no_dut for usage example +-- + +LIBRARY IEEE, common_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE work.dp_stream_pkg.ALL; +USE work.tb_dp_pkg.ALL; + + +ENTITY dp_stream_stimuli IS + GENERIC ( + g_instance_nr : NATURAL := 0; + -- flow control + g_random_w : NATURAL := 15; -- use different random width for stimuli and for verify to have different random sequences + g_pulse_active : NATURAL := 1; + g_pulse_period : NATURAL := 2; + g_flow_control : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + -- initializations + g_sync_period : NATURAL := 10; + g_sync_offset : NATURAL := 0; + g_data_init : NATURAL := 0; -- choose some easy to recognize and unique value, data will increment at every valid + g_bsn_init : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := TO_DP_BSN(0); -- X"0877665544332211", bsn will increment at every sop + g_err_init : NATURAL := 247; -- choose some easy to recognize and unique value + g_err_incr : NATURAL := 1; -- when 0 keep fixed at init value, when 1 increment at every sop + g_channel_init : NATURAL := 5; -- choose some easy to recognize and unique value + g_channel_incr : NATURAL := 1; -- when 0 keep fixed at init value, when 1 increment at every sop + -- specific + g_in_dat_w : NATURAL := 32; + g_nof_repeat : NATURAL := 5; + g_pkt_len : NATURAL := 16; + g_pkt_gap : NATURAL := 4 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Generate stimuli + src_in : IN t_dp_siso := c_dp_siso_rdy; + src_out : OUT t_dp_sosi; + + -- End of stimuli + last_snk_in : OUT t_dp_sosi; -- expected verify_snk_in after end of stimuli + last_snk_in_evt : OUT STD_LOGIC; -- trigger verify to verify the last_snk_in + tb_end : OUT STD_LOGIC -- signal end of tb as far as this dp_stream_stimuli is concerned + ); +END dp_stream_stimuli; + + +ARCHITECTURE str OF dp_stream_stimuli IS + + SIGNAL random : STD_LOGIC_VECTOR(g_random_w-1 DOWNTO 0) := TO_UVEC(g_instance_nr, g_random_w); -- use different initialization to have different random sequences per stream + SIGNAL pulse : STD_LOGIC; + SIGNAL pulse_en : STD_LOGIC := '1'; + + SIGNAL stimuli_en : STD_LOGIC := '1'; + SIGNAL src_out_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL i_src_out : t_dp_sosi; + +BEGIN + + src_out <= i_src_out; + + ------------------------------------------------------------------------------ + -- STREAM CONTROL + ------------------------------------------------------------------------------ + + random <= func_common_random(random) WHEN rising_edge(clk); + + proc_common_gen_duty_pulse(g_pulse_active, g_pulse_period, '1', rst, clk, pulse_en, pulse); + + stimuli_en <= '1' WHEN g_flow_control=e_active ELSE + random(random'HIGH) WHEN g_flow_control=e_random ELSE + pulse WHEN g_flow_control=e_pulse; + + ------------------------------------------------------------------------------ + -- DATA GENERATION + ------------------------------------------------------------------------------ + + -- Generate data path input data + p_stimuli_st : PROCESS + VARIABLE v_sosi : t_dp_sosi := c_dp_sosi_rst; + VARIABLE v_last : t_dp_sosi := c_dp_sosi_rst; + BEGIN + -- Initialisations + last_snk_in <= c_dp_sosi_rst; + last_snk_in_evt <= '0'; + tb_end <= '0'; + + -- Adjust initial sosi field values by -1 to compensate for auto increment + v_sosi.bsn := INCR_UVEC(g_bsn_init, -1); + v_sosi.channel := INCR_UVEC(TO_DP_CHANNEL(g_channel_init), -g_channel_incr); + v_sosi.data := INCR_UVEC(TO_DP_DATA(g_data_init), -g_pkt_len); + v_sosi.err := INCR_UVEC(TO_DP_ERROR(g_err_init), -g_err_incr); + + i_src_out <= c_dp_sosi_rst; + proc_common_wait_until_low(clk, rst); + proc_common_wait_some_cycles(clk, 5); + + -- Generate g_nof_repeat packets + FOR I IN 0 TO g_nof_repeat-1 LOOP + -- Auto increment v_sosi field values for this packet + v_sosi.bsn := INCR_UVEC(v_sosi.bsn, 1); + v_sosi.sync := sel_a_b((UNSIGNED(v_sosi.bsn) MOD g_sync_period) = g_sync_offset, '1', '0'); -- insert sync starting at BSN=g_sync_offset and with period g_sync_period + v_sosi.channel := INCR_UVEC(v_sosi.channel, g_channel_incr); + v_sosi.data := INCR_UVEC(v_sosi.data, g_pkt_len); + v_sosi.data := RESIZE_DP_DATA(v_sosi.data(g_in_dat_w-1 DOWNTO 0)); -- wrap when >= 2**g_in_dat_w + v_sosi.err := INCR_UVEC(v_sosi.err, g_err_incr); + + -- Send packet + proc_dp_gen_block_data(g_in_dat_w, TO_UINT(v_sosi.data), g_pkt_len, TO_UINT(v_sosi.channel), TO_UINT(v_sosi.err), v_sosi.sync, v_sosi.bsn, clk, stimuli_en, src_in, i_src_out); + + -- Insert optional gap between the packets + proc_common_wait_some_cycles(clk, g_pkt_gap); + + -- Update v_last.sync + IF v_sosi.sync='1' THEN v_last.sync := '1'; END IF; + END LOOP; + + -- Update v_last control + IF g_nof_repeat>0 THEN + v_last.sop := '1'; + v_last.eop := '1'; + v_last.valid := '1'; + END IF; + + -- Determine and keep last expected sosi field values after end of stimuli + -- . e_qual + v_last.bsn := STD_LOGIC_VECTOR( UNSIGNED(g_bsn_init) + g_nof_repeat-1); + v_last.channel := TO_DP_CHANNEL(g_channel_init + (g_nof_repeat-1)*g_channel_incr); + v_last.err := TO_DP_ERROR(g_err_init + (g_nof_repeat-1)*g_err_incr); + -- . account for g_pkt_len + v_last.data := INCR_UVEC(v_sosi.data, g_pkt_len-1); + v_last.data := RESIZE_DP_DATA(v_last.data(g_in_dat_w-1 DOWNTO 0)); -- wrap when >= 2**g_in_dat_w + last_snk_in <= v_last; + + -- Signal end of stimuli + proc_common_wait_some_cycles(clk, 100); -- latency from stimuli to verify depends on the flow control, so wait sufficiently long for last packet to have passed through + proc_common_gen_pulse(clk, last_snk_in_evt); + proc_common_wait_some_cycles(clk, 50); + tb_end <= '1'; + WAIT; + END PROCESS; + + ------------------------------------------------------------------------------ + -- Auxiliary + ------------------------------------------------------------------------------ + + -- Map to slv to ease monitoring in wave window + src_out_data <= i_src_out.data(g_in_dat_w-1 DOWNTO 0); + +END str; diff --git a/cores/base/dp/dp_pkg/dp_stream_verify.vhd b/cores/base/dp/dp_pkg/dp_stream_verify.vhd new file mode 100644 index 0000000000000000000000000000000000000000..db4c7cb561f312409e6450a00ecab0b2386f08df --- /dev/null +++ b/cores/base/dp/dp_pkg/dp_stream_verify.vhd @@ -0,0 +1,200 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +------------------------------------------------------------------------------- + +-- Purpose: +-- . The dp_stream_verify verifies the stream of packets with counter data that +-- are generated by dp_stimuli_st. +-- Description: +-- The component can verify a stream: +-- . The sosi control fields are verified conform the bus specifications +-- eg. considering the RL, no missing eop, etc. +-- . The sosi data fields are verified based on their previous value under +-- the assumption that they contain incrementing data. Whether a field +-- is checked depends on verify_snk_in_enable. +-- +-- The component also checks whether the stream is active at all. A +-- pulse in verify_expected_snk_in_evt triggers the verification of the +-- corresponding field in snk_in using the expected_snk_in as reference. +-- +-- Usage: +-- . See tb_dp_example_no_dut for usage example +-- + +LIBRARY IEEE, common_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE work.dp_stream_pkg.ALL; +USE work.tb_dp_pkg.ALL; + + +ENTITY dp_stream_verify IS + GENERIC ( + g_instance_nr : NATURAL := 0; + -- flow control + g_random_w : NATURAL := 14; -- use different random width for stimuli and for verify to have different random sequences + g_pulse_active : NATURAL := 1; + g_pulse_period : NATURAL := 2; + g_flow_control : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control + -- initializations + g_sync_period : NATURAL := 10; + g_sync_offset : NATURAL := 7; + g_snk_in_cnt_max : t_dp_sosi_unsigned := c_dp_sosi_unsigned_rst; -- default 0 is no wrap + g_snk_in_cnt_gap : t_dp_sosi_unsigned := c_dp_sosi_unsigned_ones; -- default only accept increment +1 + -- specific + g_in_dat_w : NATURAL := 32; + g_pkt_len : NATURAL := 16 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + -- Verify data + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + + -- During stimuli + verify_snk_in_enable : IN t_dp_sosi_sl; -- enable to verify that the snk_in fields are incrementing + + -- End of stimuli + expected_snk_in : IN t_dp_sosi; -- expected snk_in at verify_expected_snk_in_evt + verify_expected_snk_in_evt : IN t_dp_sosi_sl -- trigger to verify the expected_snk_in + ); +END dp_stream_verify; + + +ARCHITECTURE tb OF dp_stream_verify IS + + CONSTANT c_rl : NATURAL := 1; + CONSTANT c_no_dut : BOOLEAN:= TRUE; + + SIGNAL random : STD_LOGIC_VECTOR(g_random_w-1 DOWNTO 0) := TO_UVEC(g_instance_nr, g_random_w); -- use different initialization to have different random sequences per stream + SIGNAL pulse : STD_LOGIC; + SIGNAL pulse_en : STD_LOGIC := '1'; + + SIGNAL i_snk_out : t_dp_siso := c_dp_siso_rdy; + SIGNAL prev_snk_out : t_dp_siso; + SIGNAL hold_snk_in_data : STD_LOGIC_VECTOR(c_dp_stream_data_w-1 DOWNTO 0); -- used to hold valid data for verify at verify_expected_snk_in_evt + SIGNAL snk_in_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL prev_snk_in : t_dp_sosi; + + SIGNAL hold_snk_in_sop : STD_LOGIC := '0'; + SIGNAL detected_snk_in_ctrl : t_dp_sosi_sl := c_dp_sosi_sl_rst; + SIGNAL verify_snk_in_increment : t_dp_sosi_sl := c_dp_sosi_sl_rst; + SIGNAL verify_snk_in_ctrl : t_dp_sosi_sl := c_dp_sosi_sl_rst; + + SIGNAL exp_size : NATURAL; + SIGNAL cnt_size : NATURAL; + +BEGIN + + snk_out <= i_snk_out; + + ------------------------------------------------------------------------------ + -- STREAM CONTROL + ------------------------------------------------------------------------------ + + random <= func_common_random(random) WHEN rising_edge(clk); + + proc_common_gen_duty_pulse(g_pulse_active, g_pulse_period, '1', rst, clk, pulse_en, pulse); + + i_snk_out.ready <= '1' WHEN g_flow_control=e_active ELSE + random(random'HIGH) WHEN g_flow_control=e_random ELSE + pulse WHEN g_flow_control=e_pulse; + + ------------------------------------------------------------------------------ + -- DATA VERIFICATION + ------------------------------------------------------------------------------ + + -- Detect first sync, sop, eop, valid + detected_snk_in_ctrl.sync <= '1' WHEN snk_in.sync='1' AND rising_edge(clk); + detected_snk_in_ctrl.valid <= '1' WHEN snk_in.valid='1' AND rising_edge(clk); + detected_snk_in_ctrl.sop <= '1' WHEN snk_in.sop='1' AND rising_edge(clk); + detected_snk_in_ctrl.eop <= '1' WHEN snk_in.eop='1' AND rising_edge(clk); + + -- Verify that the stimuli have been applied at all so at least one active sosi sync, sop, eop, valid field has been detected + proc_dp_verify_value("snk_in.sync", clk, verify_expected_snk_in_evt.sync, expected_snk_in.sync, detected_snk_in_ctrl.sync); + proc_dp_verify_value("snk_in.sop", clk, verify_expected_snk_in_evt.sop, expected_snk_in.sop, detected_snk_in_ctrl.sop); + proc_dp_verify_value("snk_in.eop", clk, verify_expected_snk_in_evt.eop, expected_snk_in.eop, detected_snk_in_ctrl.eop); + proc_dp_verify_value("snk_in.valid", clk, verify_expected_snk_in_evt.valid, expected_snk_in.valid, detected_snk_in_ctrl.valid); + + -- Verify that the last sosi data, bsn, channel and err fields are correct + proc_dp_verify_value("snk_in.data", e_equal, clk, verify_expected_snk_in_evt.data, expected_snk_in.data, hold_snk_in_data); + proc_dp_verify_value("snk_in.bsn", e_equal, clk, verify_expected_snk_in_evt.bsn, expected_snk_in.bsn, snk_in.bsn); + proc_dp_verify_value("snk_in.channel", e_equal, clk, verify_expected_snk_in_evt.channel, expected_snk_in.channel, snk_in.channel); + proc_dp_verify_value("snk_in.err", e_equal, clk, verify_expected_snk_in_evt.err, expected_snk_in.err, snk_in.err); + + -- Verify that the output is incrementing data, like the input stimuli + p_verify_snk_in_increment : PROCESS(verify_snk_in_enable, detected_snk_in_ctrl) + BEGIN + verify_snk_in_increment <= verify_snk_in_enable; + verify_snk_in_increment.data <= verify_snk_in_enable.data AND detected_snk_in_ctrl.valid; + verify_snk_in_increment.re <= verify_snk_in_enable.re AND detected_snk_in_ctrl.valid; + verify_snk_in_increment.im <= verify_snk_in_enable.im AND detected_snk_in_ctrl.valid; + verify_snk_in_increment.bsn <= verify_snk_in_enable.bsn AND detected_snk_in_ctrl.sop; + verify_snk_in_increment.channel <= verify_snk_in_enable.channel AND detected_snk_in_ctrl.sop; + verify_snk_in_increment.empty <= verify_snk_in_enable.empty AND detected_snk_in_ctrl.eop; + verify_snk_in_increment.err <= verify_snk_in_enable.err AND detected_snk_in_ctrl.eop; + END PROCESS; + + proc_dp_verify_data("snk_in.data", c_rl, g_snk_in_cnt_max.data, g_snk_in_cnt_gap.data, clk, verify_snk_in_increment.data, i_snk_out.ready, snk_in.valid, snk_in.data, prev_snk_in.data); + proc_dp_verify_data("snk_in.re", c_rl, g_snk_in_cnt_max.re, g_snk_in_cnt_gap.re, clk, verify_snk_in_increment.re, i_snk_out.ready, snk_in.valid, snk_in.re, prev_snk_in.re); + proc_dp_verify_data("snk_in.im", c_rl, g_snk_in_cnt_max.im, g_snk_in_cnt_gap.im, clk, verify_snk_in_increment.im, i_snk_out.ready, snk_in.valid, snk_in.im, prev_snk_in.im); + proc_dp_verify_data("snk_in.bsn", c_rl, g_snk_in_cnt_max.bsn, g_snk_in_cnt_gap.bsn, clk, verify_snk_in_increment.bsn, i_snk_out.ready, snk_in.sop, snk_in.bsn, prev_snk_in.bsn); + proc_dp_verify_data("snk_in.channel", c_rl, g_snk_in_cnt_max.channel, g_snk_in_cnt_gap.channel, clk, verify_snk_in_increment.channel, i_snk_out.ready, snk_in.sop, snk_in.channel, prev_snk_in.channel); + proc_dp_verify_data("snk_in.empty", c_rl, g_snk_in_cnt_max.empty, g_snk_in_cnt_gap.empty, clk, verify_snk_in_increment.empty, i_snk_out.ready, snk_in.eop, snk_in.empty, prev_snk_in.empty); + proc_dp_verify_data("snk_in.err", c_rl, g_snk_in_cnt_max.err, g_snk_in_cnt_gap.err, clk, verify_snk_in_increment.err, i_snk_out.ready, snk_in.eop, snk_in.err, prev_snk_in.err); + + -- Verify that the snk_in control fields are correct + p_verify_snk_in_ctrl: PROCESS(snk_in, verify_snk_in_enable) + BEGIN + verify_snk_in_ctrl.sync <= snk_in.sync AND verify_snk_in_enable.valid AND verify_snk_in_enable.sync; + verify_snk_in_ctrl.sop <= snk_in.sop AND verify_snk_in_enable.valid AND verify_snk_in_enable.sop AND verify_snk_in_enable.eop; + verify_snk_in_ctrl.eop <= snk_in.eop AND verify_snk_in_enable.valid AND verify_snk_in_enable.sop AND verify_snk_in_enable.eop; + verify_snk_in_ctrl.valid <= snk_in.valid AND verify_snk_in_enable.valid; + END PROCESS; + + -- Verify that the output sync occurs when expected + proc_dp_verify_sync(g_sync_period, g_sync_offset, clk, detected_snk_in_ctrl.sop, verify_snk_in_ctrl.sync, verify_snk_in_ctrl.sop, snk_in.bsn); + + -- Verify output packet ctrl + proc_dp_verify_sop_and_eop(clk, verify_snk_in_ctrl.valid, verify_snk_in_ctrl.sop, verify_snk_in_ctrl.eop, hold_snk_in_sop); + + -- Verify output packet block size + exp_size <= g_pkt_len; + + proc_dp_verify_block_size(exp_size, clk, verify_snk_in_ctrl.valid, verify_snk_in_ctrl.sop, verify_snk_in_ctrl.eop, cnt_size); + + -- Verify output ready latency + proc_dp_verify_valid(clk, detected_snk_in_ctrl.valid, i_snk_out.ready, prev_snk_out.ready, verify_snk_in_ctrl.valid); + + ------------------------------------------------------------------------------ + -- Auxiliary + ------------------------------------------------------------------------------ + + -- Map to slv to ease monitoring in wave window + snk_in_data <= snk_in.data(g_in_dat_w-1 DOWNTO 0); + + hold_snk_in_data <= snk_in.data WHEN snk_in.valid='1'; + +END tb; diff --git a/cores/base/dp/dp_pkg/hdllib.cfg b/cores/base/dp/dp_pkg/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..12f4b46bfa651daddb71f635882831a7909c2eac --- /dev/null +++ b/cores/base/dp/dp_pkg/hdllib.cfg @@ -0,0 +1,20 @@ +hdl_lib_name = dp_pkg +hdl_library_clause_name = dp_pkg_lib +hdl_lib_uses_synth = common_pkg +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + dp_stream_pkg.vhd + tb_dp_pkg.vhd + dp_stream_stimuli.vhd + dp_stream_verify.vhd + +test_bench_files = + +regression_test_vhdl = + +[modelsim_project_file] + + +[quartus_project_file] diff --git a/cores/base/dp/dp_pkg/tb_dp_pkg.vhd b/cores/base/dp/dp_pkg/tb_dp_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2dadc92566dc25b3b8ff42bc671d99a047a16f77 --- /dev/null +++ b/cores/base/dp/dp_pkg/tb_dp_pkg.vhd @@ -0,0 +1,2413 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE work.dp_stream_pkg.ALL; + + +PACKAGE tb_dp_pkg IS + + ------------------------------------------------------------------------------ + -- Purpose: + -- + -- Test bench package for applying stimuli to a streaming data path. The + -- input is counter data, the output is verified and an error is reported + -- if a counter value is missing or duplicate. + -- + -- Description: + -- + -- The test is divided into intervals marked by sync to start a new subtest + -- named by state. New subtests can be added by adding an extra sync interval + -- and state name to this package. In each subtest the streaming interface + -- DUT can be verified for different situations by manipulating: + -- . cnt_en : cnt_en not always active when in_ready is asserted + -- . out_ready : out_ready not always active + -- + -- Remarks: + -- . See e.g. tb_dp_pipeline.vhd for how to use the procedures. + -- . To run all stimuli in Modelsim do: + -- > as 10 + -- > run 400 us + ------------------------------------------------------------------------------ + + CONSTANT clk_period : TIME := 10 ns; -- 100 MHz + CONSTANT c_dp_sync_interval : NATURAL := 3000; + CONSTANT c_dp_test_interval : NATURAL := 100; + CONSTANT c_dp_nof_toggle : NATURAL := 40; + CONSTANT c_dp_nof_both : NATURAL := 50; + + -- The test bench uses other field widths than the standard t_dp_sosi record field widths, the assumptions are: + -- . c_dp_data_w < c_dp_stream_data_w + -- . c_dp_data_w > c_dp_stream_empty_w + -- . c_dp_data_w > c_dp_stream_channel_w + -- . c_dp_data_w > c_dp_stream_error_w + CONSTANT c_dp_data_w : NATURAL := c_word_w; -- =32, choose wide enough to avoid out_data wrap around issue for p_verify + CONSTANT c_dp_bsn_w : NATURAL := c_dp_data_w; -- c_dp_stream_bsn_w; + CONSTANT c_dp_empty_w : NATURAL := c_dp_stream_empty_w; + CONSTANT c_dp_channel_w : NATURAL := c_dp_stream_channel_w; + CONSTANT c_dp_channel_user_w : NATURAL := c_dp_stream_channel_w/2; -- support some bits for mux input user streams channel widths + CONSTANT c_dp_channel_mux_w : NATURAL :=(c_dp_stream_channel_w+1)/2; -- support rest bits for the nof input ports of a mux + CONSTANT c_dp_error_w : NATURAL := c_dp_stream_error_w; + + TYPE t_dp_data_arr IS ARRAY (NATURAL RANGE <>) OF STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + + -- The state name tells what kind of test is done in the sync interval + TYPE t_dp_state_enum IS ( + s_idle, + s_both_active, + s_pull_down_out_ready, + s_pull_down_cnt_en, + s_toggle_out_ready, + s_toggle_cnt_en, + s_toggle_both, + s_pulse_cnt_en, + s_chirp_out_ready, + s_random, + s_done + ); + + TYPE t_dp_value_enum IS ( + e_equal, + e_at_least + ); + + -- always active, random or pulse flow control + TYPE t_dp_flow_control_enum IS ( + e_active, + e_random, + e_pulse + ); + + TYPE t_dp_flow_control_enum_arr IS ARRAY (NATURAL RANGE <>) OF t_dp_flow_control_enum; + + CONSTANT c_dp_flow_control_enum_arr : t_dp_flow_control_enum_arr := (e_active, e_random, e_pulse); -- array all possible values that can be iterated over + + ------------------------------------------------------------------------------ + -- Stream source functions + ------------------------------------------------------------------------------ + + -- Block data generator with feedforward throttle control + -- !!! old style: sync before sop + -- !!! used by tb_dp_packetizing, do not use for new DP components + PROCEDURE proc_dp_gen_block_data(CONSTANT c_nof_block_per_sync : IN NATURAL; + CONSTANT c_block_size : IN NATURAL; + CONSTANT c_gap_size : IN NATURAL; + CONSTANT c_throttle_num : IN NATURAL; + CONSTANT c_throttle_den : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync_nr : INOUT NATURAL; + SIGNAL block_nr : INOUT NATURAL; + SIGNAL cnt_sync : OUT STD_LOGIC; + SIGNAL cnt_val : OUT STD_LOGIC; + SIGNAL cnt_dat : INOUT STD_LOGIC_VECTOR); + + -- Block data generator with ready flow control and symbols counter + PROCEDURE proc_dp_gen_block_data(CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_use_data : IN BOOLEAN; -- when TRUE use data field, else use re, im fields, and keep unused fields at 'X' + CONSTANT c_data_w : IN NATURAL; -- data width for the data, re and im fields + CONSTANT c_symbol_w : IN NATURAL; -- c_data_w/c_symbol_w must be an integer + CONSTANT c_symbol_init : IN NATURAL; -- init counter for symbols in data field + CONSTANT c_symbol_re_init : IN NATURAL; -- init counter for symbols in re field + CONSTANT c_symbol_im_init : IN NATURAL; -- init counter for symbols in im field + CONSTANT c_nof_symbols : IN NATURAL; -- nof symbols per frame for the data, re and im fields + CONSTANT c_channel : IN NATURAL; -- channel field + CONSTANT c_error : IN NATURAL; -- error field + CONSTANT c_sync : IN STD_LOGIC; -- when '1' issue sync pulse during this block + CONSTANT c_bsn : IN STD_LOGIC_VECTOR; -- bsn field + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi); + + PROCEDURE proc_dp_gen_block_data(CONSTANT c_data_w : IN NATURAL; -- data width for the data field + CONSTANT c_symbol_init : IN NATURAL; -- init counter for the data in the data field + CONSTANT c_nof_symbols : IN NATURAL; -- nof symbols per frame for the data fields + CONSTANT c_channel : IN NATURAL; -- channel field + CONSTANT c_error : IN NATURAL; -- error field + CONSTANT c_sync : IN STD_LOGIC; -- when '1' issue sync pulse during this block + CONSTANT c_bsn : IN STD_LOGIC_VECTOR; -- bsn field + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi); + + -- Handle stream ready signal, only support RL=0 or 1. + PROCEDURE proc_dp_stream_ready_latency(CONSTANT c_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '1' then active output when ready + CONSTANT c_sync : IN STD_LOGIC; + CONSTANT c_valid : IN STD_LOGIC; + CONSTANT c_sop : IN STD_LOGIC; + CONSTANT c_eop : IN STD_LOGIC; + SIGNAL out_sync : OUT STD_LOGIC; + SIGNAL out_valid : OUT STD_LOGIC; + SIGNAL out_sop : OUT STD_LOGIC; + SIGNAL out_eop : OUT STD_LOGIC); + + -- Initialize the data per symbol + FUNCTION func_dp_data_init(c_data_w, c_symbol_w, init : NATURAL) RETURN STD_LOGIC_VECTOR; + + -- Increment the data per symbol + FUNCTION func_dp_data_incr(c_data_w, c_symbol_w : NATURAL; data : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + + -- Generate a counter data with valid + PROCEDURE proc_dp_gen_data(CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_data_init : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi); + + -- As above but with counter max + PROCEDURE proc_dp_gen_data(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_data_init : IN NATURAL; + CONSTANT c_data_max : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi); + + -- Generate a frame with symbols counter + PROCEDURE proc_dp_gen_frame(CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_symbol_w : IN NATURAL; -- c_data_w/c_symbol_w must be an integer + CONSTANT c_symbol_init : IN NATURAL; + CONSTANT c_nof_symbols : IN NATURAL; + CONSTANT c_bsn : IN NATURAL; + CONSTANT c_sync : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi); + + -- Input data counter + PROCEDURE proc_dp_cnt_dat(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; + SIGNAL cnt_dat : INOUT STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_cnt_dat(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; + SIGNAL cnt_val : INOUT STD_LOGIC; + SIGNAL cnt_dat : INOUT STD_LOGIC_VECTOR); + + -- Transmit data + PROCEDURE proc_dp_tx_data(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL cnt_val : IN STD_LOGIC; + SIGNAL cnt_dat : IN STD_LOGIC_VECTOR; + SIGNAL tx_data : INOUT t_dp_data_arr; + SIGNAL tx_val : INOUT STD_LOGIC_VECTOR; + SIGNAL out_data : OUT STD_LOGIC_VECTOR; + SIGNAL out_val : OUT STD_LOGIC); + + -- Transmit data control (use for sop, eop) + PROCEDURE proc_dp_tx_ctrl(CONSTANT c_offset : IN NATURAL; + CONSTANT c_period : IN NATURAL; + SIGNAL data : IN STD_LOGIC_VECTOR; + SIGNAL valid : IN STD_LOGIC; + SIGNAL ctrl : OUT STD_LOGIC); + + -- Define sync interval + PROCEDURE proc_dp_sync_interval(SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : OUT STD_LOGIC); + + -- Stimuli for cnt_en + PROCEDURE proc_dp_count_en(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL lfsr : INOUT STD_LOGIC_VECTOR; + SIGNAL state : OUT t_dp_state_enum; + SIGNAL done : OUT STD_LOGIC; + SIGNAL tb_end : OUT STD_LOGIC; + SIGNAL cnt_en : OUT STD_LOGIC); + + ------------------------------------------------------------------------------ + -- Stream sink functions + ------------------------------------------------------------------------------ + + -- Stimuli for out_ready + PROCEDURE proc_dp_out_ready(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL lfsr : INOUT STD_LOGIC_VECTOR; + SIGNAL out_ready : OUT STD_LOGIC); + + -- DUT output verify enable + PROCEDURE proc_dp_verify_en(CONSTANT c_delay : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL verify_en : OUT STD_LOGIC); + + PROCEDURE proc_dp_verify_en(CONSTANT c_continuous : IN BOOLEAN; + SIGNAL clk : IN STD_LOGIC; + SIGNAL valid : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL eop : IN STD_LOGIC; + SIGNAL verify_en : OUT STD_LOGIC); + + -- Run and verify for some cycles + PROCEDURE proc_dp_verify_run_some_cycles(CONSTANT nof_pre_clk : IN NATURAL; + CONSTANT nof_verify_clk : IN NATURAL; + CONSTANT nof_post_clk : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : OUT STD_LOGIC); + + -- Verify the expected value + PROCEDURE proc_dp_verify_value(CONSTANT c_str : IN STRING; + CONSTANT mode : IN t_dp_value_enum; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC_VECTOR; + SIGNAL res : IN STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_value(CONSTANT mode : IN t_dp_value_enum; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC_VECTOR; + SIGNAL res : IN STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_value(CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC; + SIGNAL res : IN STD_LOGIC); + + -- Verify output global and local BSN + -- . incrementing or replicated global BSN + -- . incrementing local BSN that starts at 1 + PROCEDURE proc_dp_verify_bsn(CONSTANT c_use_local_bsn : IN BOOLEAN; -- use local BSN or only use global BSN + CONSTANT c_global_bsn_increment : IN POSITIVE; -- increment per global BSN + CONSTANT c_nof_replicated_global_bsn : IN POSITIVE; -- number of replicated global BSN + CONSTANT c_block_per_sync : IN POSITIVE; -- of sop/eop blocks per sync interval + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_sync : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_bsn : IN STD_LOGIC_VECTOR; + SIGNAL verify_en : INOUT STD_LOGIC; -- initialize '0', becomes '1' when bsn verification starts + SIGNAL cnt_replicated_global_bsn : INOUT NATURAL; + SIGNAL prev_out_bsn_global : INOUT STD_LOGIC_VECTOR; + SIGNAL prev_out_bsn_local : INOUT STD_LOGIC_VECTOR); + + -- Verify incrementing data + -- . wrap at c_out_data_max when >0, else no wrap when c_out_data_max=0 + -- . default increment by +1, but also allow an increment by +c_out_data_gap + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_out_data_max : IN UNSIGNED; + CONSTANT c_out_data_gap : IN UNSIGNED; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify the DUT incrementing output data that wraps in range 0 ... c_out_data_max + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_out_data_max : IN UNSIGNED; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify the DUT incrementing output data, fixed increment +1 + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; -- by using sop or eop proc_dp_verify_data() can also be used to verify other SOSI fields like bsn, error, channel, empty + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify incrementing data with RL > 0 or no flow control, support wrap at maximum and increment gap + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_out_data_max : IN UNSIGNED; + CONSTANT c_out_data_gap : IN UNSIGNED; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_out_data_max : IN NATURAL; + CONSTANT c_out_data_gap : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_out_data_max : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify incrementing data with RL > 0 or no flow control, fixed increment +1 + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify the DUT output symbols + PROCEDURE proc_dp_verify_symbols(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_symbol_w : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL out_empty : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR); + + -- Verify the DUT output data with empty + PROCEDURE proc_dp_verify_data_empty(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_last_word : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL out_eop_1 : INOUT STD_LOGIC; + SIGNAL out_eop_2 : INOUT STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL out_data_1 : INOUT STD_LOGIC_VECTOR; + SIGNAL out_data_2 : INOUT STD_LOGIC_VECTOR; + SIGNAL out_data_3 : INOUT STD_LOGIC_VECTOR; + SIGNAL out_empty : IN STD_LOGIC_VECTOR; + SIGNAL out_empty_1 : INOUT STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_other_sosi(CONSTANT c_str : IN STRING; + CONSTANT c_exp_data : IN STD_LOGIC_VECTOR; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL res_data : IN STD_LOGIC_VECTOR); + + PROCEDURE proc_dp_verify_valid(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL prev_out_ready : INOUT STD_LOGIC_VECTOR; + SIGNAL out_val : IN STD_LOGIC); + + PROCEDURE proc_dp_verify_valid(SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL prev_out_ready : INOUT STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC); + + -- Verify the DUT output sync + PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN NATURAL; + CONSTANT c_sync_offset : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL bsn : IN STD_LOGIC_VECTOR); + + -- Verify the DUT output sop and eop + PROCEDURE proc_dp_verify_sop_and_eop(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_verify_valid : IN BOOLEAN; + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL hold_sop : INOUT STD_LOGIC); + + PROCEDURE proc_dp_verify_sop_and_eop(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL hold_sop : INOUT STD_LOGIC); + + PROCEDURE proc_dp_verify_sop_and_eop(SIGNAL clk : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL hold_sop : INOUT STD_LOGIC); + + PROCEDURE proc_dp_verify_block_size(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL alt_size : IN NATURAL; -- alternative size (eg. use exp_size'last_value) + SIGNAL exp_size : IN NATURAL; -- expected size + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL); + + PROCEDURE proc_dp_verify_block_size(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL exp_size : IN NATURAL; -- expected size + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL); + + PROCEDURE proc_dp_verify_block_size(SIGNAL alt_size : IN NATURAL; -- alternative size (eg. use exp_size'last_value) + SIGNAL exp_size : IN NATURAL; -- expected size + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL); + + PROCEDURE proc_dp_verify_block_size(SIGNAL exp_size : IN NATURAL; -- expected size + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL); + + -- Verify the DUT output invalid between frames + PROCEDURE proc_dp_verify_gap_invalid(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : IN STD_LOGIC; + SIGNAL in_sop : IN STD_LOGIC; + SIGNAL in_eop : IN STD_LOGIC; + SIGNAL out_gap : INOUT STD_LOGIC); -- declare initial gap signal = '1' + + -- Verify the DUT output control (use for sop, eop) + PROCEDURE proc_dp_verify_ctrl(CONSTANT c_offset : IN NATURAL; + CONSTANT c_period : IN NATURAL; + CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL data : IN STD_LOGIC_VECTOR; + SIGNAL valid : IN STD_LOGIC; + SIGNAL ctrl : IN STD_LOGIC); + + -- Wait for stream valid + PROCEDURE proc_dp_stream_valid(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_valid : IN STD_LOGIC); + + -- Wait for stream valid AND sop + PROCEDURE proc_dp_stream_valid_sop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_valid : IN STD_LOGIC; + SIGNAL in_sop : IN STD_LOGIC); + + -- Wait for stream valid AND eop + PROCEDURE proc_dp_stream_valid_eop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_valid : IN STD_LOGIC; + SIGNAL in_eop : IN STD_LOGIC); + +END tb_dp_pkg; + + +PACKAGE BODY tb_dp_pkg IS + + ------------------------------------------------------------------------------ + -- PROCEDURE: Block data generator with feedforward throttle control + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_gen_block_data(CONSTANT c_nof_block_per_sync : IN NATURAL; + CONSTANT c_block_size : IN NATURAL; + CONSTANT c_gap_size : IN NATURAL; + CONSTANT c_throttle_num : IN NATURAL; + CONSTANT c_throttle_den : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync_nr : INOUT NATURAL; + SIGNAL block_nr : INOUT NATURAL; + SIGNAL cnt_sync : OUT STD_LOGIC; + SIGNAL cnt_val : OUT STD_LOGIC; + SIGNAL cnt_dat : INOUT STD_LOGIC_VECTOR) IS + CONSTANT c_start_delay : NATURAL := 10; + VARIABLE v_throttle : NATURAL; + BEGIN + sync_nr <= 0; + block_nr <= 0; + + cnt_sync <= '0'; + cnt_val <= '0'; + cnt_dat <= (cnt_dat'RANGE=>'1'); -- -1, so first valid cnt_dat starts at 0 + + -- allow some clock cycles before start after rst release + WAIT UNTIL rst='0'; + FOR I IN 0 TO c_start_delay-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- output first sync + cnt_sync <= '1'; + WAIT UNTIL rising_edge(clk); + cnt_sync <= '0'; + FOR I IN 1 TO c_gap_size-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + WHILE TRUE LOOP + -- output block + IF c_throttle_num >= c_throttle_den THEN + -- no need to throttle, so cnt_val active during whole data block + FOR I IN 0 TO c_block_size-1 LOOP + cnt_val <= '1'; + cnt_dat <= INCR_UVEC(cnt_dat, 1); + WAIT UNTIL rising_edge(clk); + END LOOP; + ELSE + -- throttle cnt_val, so c_throttle_num active cnt_val cycles per c_throttle_den cycles + FOR I IN 0 TO c_block_size/c_throttle_num-1 LOOP + FOR J IN 0 TO c_throttle_num-1 LOOP + cnt_val <= '1'; + cnt_dat <= INCR_UVEC(cnt_dat, 1); + WAIT UNTIL rising_edge(clk); + END LOOP; + FOR J IN 0 TO c_throttle_den-c_throttle_num-1 LOOP + cnt_val <= '0'; + WAIT UNTIL rising_edge(clk); + END LOOP; + END LOOP; + END IF; + cnt_val <= '0'; + -- output sync for next block at first sample of gap + IF block_nr>0 AND ((block_nr + 1) MOD c_nof_block_per_sync)=0 THEN + cnt_sync <= '1'; + sync_nr <= sync_nr+1; + END IF; + WAIT UNTIL rising_edge(clk); + -- output rest of the gap + cnt_sync <= '0'; + FOR I IN 1 TO c_gap_size-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + -- next block + block_nr <= block_nr+1; + END LOOP; + END proc_dp_gen_block_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Block data generator with ready flow control and symbols counter + -- . dependent on in_en and src_in.ready + -- . optional sync pulse at end of frame + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_gen_block_data(CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_use_data : IN BOOLEAN; -- when TRUE use data field, else use re, im fields, and keep unused fields at 'X' + CONSTANT c_data_w : IN NATURAL; -- data width for the data, re and im fields + CONSTANT c_symbol_w : IN NATURAL; -- c_data_w/c_symbol_w must be an integer + CONSTANT c_symbol_init : IN NATURAL; -- init counter for symbols in data field + CONSTANT c_symbol_re_init : IN NATURAL; -- init counter for symbols in re field + CONSTANT c_symbol_im_init : IN NATURAL; -- init counter for symbols in im field + CONSTANT c_nof_symbols : IN NATURAL; -- nof symbols per frame for the data, re and im fields + CONSTANT c_channel : IN NATURAL; -- channel field + CONSTANT c_error : IN NATURAL; -- error field + CONSTANT c_sync : IN STD_LOGIC; -- when '1' issue sync pulse during this block + CONSTANT c_bsn : IN STD_LOGIC_VECTOR; -- bsn field + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi) IS + CONSTANT c_nof_symbols_per_data : NATURAL := c_data_w/c_symbol_w; + CONSTANT c_div : NATURAL := c_nof_symbols / c_nof_symbols_per_data; + CONSTANT c_mod : NATURAL := c_nof_symbols MOD c_nof_symbols_per_data; + CONSTANT c_empty : NATURAL := sel_a_b(c_mod, c_nof_symbols_per_data - c_mod, 0); + CONSTANT c_nof_data : NATURAL := sel_a_b(c_mod, 1, 0) + c_div; + VARIABLE v_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= func_dp_data_init(c_data_w, c_symbol_w, c_symbol_init); + VARIABLE v_re : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= func_dp_data_init(c_data_w, c_symbol_w, c_symbol_re_init); + VARIABLE v_im : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= func_dp_data_init(c_data_w, c_symbol_w, c_symbol_im_init); + BEGIN + src_out <= c_dp_sosi_rst; + IF src_in.xon='1' THEN + -- Generate this block + src_out.bsn <= RESIZE_DP_BSN(c_bsn); + src_out.empty <= TO_DP_EMPTY(c_empty); + src_out.channel <= TO_DP_CHANNEL(c_channel); + src_out.err <= TO_DP_ERROR(c_error); + IF c_use_data=TRUE THEN src_out.data <= RESIZE_DP_DATA(v_data); END IF; + IF c_use_data=FALSE THEN src_out.re <= RESIZE_DP_DSP_DATA(v_re); END IF; + IF c_use_data=FALSE THEN src_out.im <= RESIZE_DP_DSP_DATA(v_im); END IF; + IF c_nof_data>1 THEN + -- . sop + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, c_sync, '1', '1', '0', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + -- . valid + FOR I IN 1 TO c_nof_data-2 LOOP + v_data := func_dp_data_incr(c_data_w, c_symbol_w, v_data); + v_re := func_dp_data_incr(c_data_w, c_symbol_w, v_re); + v_im := func_dp_data_incr(c_data_w, c_symbol_w, v_im); + IF c_use_data=TRUE THEN src_out.data <= RESIZE_DP_DATA(v_data); END IF; + IF c_use_data=FALSE THEN src_out.re <= RESIZE_DP_DSP_DATA(v_re); END IF; + IF c_use_data=FALSE THEN src_out.im <= RESIZE_DP_DSP_DATA(v_im); END IF; + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, '0', '1', '0', '0', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + END LOOP; + + -- . eop + v_data := func_dp_data_incr(c_data_w, c_symbol_w, v_data); + v_re := func_dp_data_incr(c_data_w, c_symbol_w, v_re); + v_im := func_dp_data_incr(c_data_w, c_symbol_w, v_im); + IF c_use_data=TRUE THEN src_out.data <= RESIZE_DP_DATA(v_data); END IF; + IF c_use_data=FALSE THEN src_out.re <= RESIZE_DP_DSP_DATA(v_re); END IF; + IF c_use_data=FALSE THEN src_out.im <= RESIZE_DP_DSP_DATA(v_im); END IF; + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, '0', '1', '0', '1', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + ELSE + -- . sop and eop, frame has only one word + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, c_sync, '1', '1', '1', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + END IF; + ELSE + -- Skip this block + proc_common_wait_some_cycles(clk, c_nof_data); + END IF; + END proc_dp_gen_block_data; + + + PROCEDURE proc_dp_gen_block_data(CONSTANT c_data_w : IN NATURAL; -- data width for the data field + CONSTANT c_symbol_init : IN NATURAL; -- init counter for the data in the data field + CONSTANT c_nof_symbols : IN NATURAL; -- nof symbols per frame for the data fields + CONSTANT c_channel : IN NATURAL; -- channel field + CONSTANT c_error : IN NATURAL; -- error field + CONSTANT c_sync : IN STD_LOGIC; -- when '1' issue sync pulse during this block + CONSTANT c_bsn : IN STD_LOGIC_VECTOR; -- bsn field + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi) IS + BEGIN + proc_dp_gen_block_data(1, TRUE, c_data_w, c_data_w, c_symbol_init, 0, 0, c_nof_symbols, c_channel, c_error, c_sync, c_bsn, clk, in_en, src_in, src_out); + END proc_dp_gen_block_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Handle stream ready signal + -- . output active when src_in is ready and in_en='1' + -- . only support RL=0 or 1, support for RL>1 requires keeping previous ready information in a STD_LOGIC_VECTOR(RL-1 DOWNTO 0). + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_stream_ready_latency(CONSTANT c_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL ready : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; + CONSTANT c_sync : IN STD_LOGIC; + CONSTANT c_valid : IN STD_LOGIC; + CONSTANT c_sop : IN STD_LOGIC; + CONSTANT c_eop : IN STD_LOGIC; + SIGNAL out_sync : OUT STD_LOGIC; + SIGNAL out_valid : OUT STD_LOGIC; + SIGNAL out_sop : OUT STD_LOGIC; + SIGNAL out_eop : OUT STD_LOGIC) IS + BEGIN + -- Default no output + out_sync <= '0'; + out_valid <= '0'; + out_sop <= '0'; + out_eop <= '0'; + + -- Skip cycles until in_en='1' + WHILE in_en='0' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + + -- Active output when ready + -- . RL = 0 + IF c_latency=0 THEN + -- show the available output until acknowledge + out_sync <= c_sync; + out_valid <= c_valid; + out_sop <= c_sop; + out_eop <= c_eop; + WAIT UNTIL rising_edge(clk); + WHILE ready /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + -- ready has acknowledged the valid output + END IF; + + -- . RL = 1 + IF c_latency=1 THEN + -- no valid output until request + WHILE ready /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + -- ready has requested this valid output + out_sync <= c_sync; + out_valid <= c_valid; + out_sop <= c_sop; + out_eop <= c_eop; + WAIT UNTIL rising_edge(clk); + END IF; + + -- Return with no active output + out_sync <= '0'; + out_valid <= '0'; + out_sop <= '0'; + out_eop <= '0'; + END proc_dp_stream_ready_latency; + + + ------------------------------------------------------------------------------ + -- FUNCTION: Initialize the data per symbol + -- . use big endian + -- . if c_data_w=32, c_symbol_w=8, init=3 then return 0x03040506 + ------------------------------------------------------------------------------ + FUNCTION func_dp_data_init(c_data_w, c_symbol_w, init : NATURAL) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_nof_symbols_per_data : NATURAL := c_data_w/c_symbol_w; + VARIABLE v_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + VARIABLE v_sym : STD_LOGIC_VECTOR(c_symbol_w-1 DOWNTO 0); + BEGIN + v_data := (OTHERS=>'0'); + v_sym := TO_UVEC(init, c_symbol_w); + FOR I IN c_nof_symbols_per_data-1 DOWNTO 0 LOOP + v_data((I+1)*c_symbol_w-1 DOWNTO I*c_symbol_w) := v_sym; + v_sym := INCR_UVEC(v_sym, 1); + END LOOP; + RETURN v_data; + END func_dp_data_init; + + + ------------------------------------------------------------------------------ + -- FUNCTION: Increment the data per symbol + -- . use big endian + -- . if c_data_w=32, c_symbol_w=8 then 0x00010203 returns 0x04050607 + -- . the actual data'LENGTH must be >= c_data_w, unused bits become 0 + -- . c_data_w/c_symbol_w must be an integer + ------------------------------------------------------------------------------ + FUNCTION func_dp_data_incr(c_data_w, c_symbol_w : NATURAL; data : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_nof_symbols_per_data : NATURAL := c_data_w/c_symbol_w; + VARIABLE v_data : STD_LOGIC_VECTOR(data'LENGTH-1 DOWNTO 0); + VARIABLE v_sym : STD_LOGIC_VECTOR(c_symbol_w-1 DOWNTO 0); + BEGIN + v_data := (OTHERS=>'0'); + v_sym := data(c_symbol_w-1 DOWNTO 0); + FOR I IN c_nof_symbols_per_data-1 DOWNTO 0 LOOP + v_sym := INCR_UVEC(v_sym, 1); + v_data((I+1)*c_symbol_w-1 DOWNTO I*c_symbol_w) := v_sym; + END LOOP; + RETURN v_data; + END func_dp_data_incr; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate counter data with valid + -- . Output counter data dependent on in_en and src_in.ready + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_gen_data(CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_data_init : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi) IS + VARIABLE v_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= TO_UVEC(c_data_init, c_data_w); + BEGIN + src_out <= c_dp_sosi_rst; + src_out.data <= RESIZE_DP_DATA(v_data); + IF rst='0' THEN + WAIT UNTIL rising_edge(clk); + WHILE TRUE LOOP + src_out.data <= RESIZE_DP_DATA(v_data); + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, '0', '1', '0', '0', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + v_data := INCR_UVEC(v_data, 1); + END LOOP; + END IF; + END proc_dp_gen_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate counter data with valid + -- . Output counter data dependent on in_en and src_in.ready + -- . with maximum count value + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_gen_data(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_data_init : IN NATURAL; + CONSTANT c_data_max : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi) IS + VARIABLE v_cnt : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= TO_UVEC(c_data_init, c_data_w); + BEGIN + src_out <= c_dp_sosi_rst; + src_out.data <= RESIZE_DP_DATA(v_cnt); + IF rst='0' THEN + WAIT UNTIL rising_edge(clk); + WHILE TRUE LOOP + src_out.data <= RESIZE_DP_DATA(v_cnt); + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, '0', '1', '0', '0', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + IF TO_UINT(v_cnt)=c_data_max THEN + v_cnt := TO_UVEC(c_data_init, c_data_w); + ELSE + v_cnt := INCR_UVEC(v_cnt, 1); + END IF; + END LOOP; + END IF; + END proc_dp_gen_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Generate a frame with symbols counter + -- . dependent on in_en and src_in.ready + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_gen_frame(CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_symbol_w : IN NATURAL; -- c_data_w/c_symbol_w must be an integer + CONSTANT c_symbol_init : IN NATURAL; + CONSTANT c_nof_symbols : IN NATURAL; + CONSTANT c_bsn : IN NATURAL; + CONSTANT c_sync : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; -- when '0' then no valid output even when src_in is ready + SIGNAL src_in : IN t_dp_siso; + SIGNAL src_out : OUT t_dp_sosi) IS + CONSTANT c_nof_symbols_per_data : NATURAL := c_data_w/c_symbol_w; + CONSTANT c_div : NATURAL := c_nof_symbols / c_nof_symbols_per_data; + CONSTANT c_mod : NATURAL := c_nof_symbols MOD c_nof_symbols_per_data; + CONSTANT c_empty : NATURAL := sel_a_b(c_mod, c_nof_symbols_per_data - c_mod, 0); + CONSTANT c_nof_data : NATURAL := sel_a_b(c_mod, 1, 0) + c_div; + VARIABLE v_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0):= func_dp_data_init(c_data_w, c_symbol_w, c_symbol_init); + BEGIN + src_out <= c_dp_sosi_rst; + src_out.bsn <= TO_DP_BSN(c_bsn); + src_out.empty <= TO_DP_EMPTY(c_empty); + src_out.data <= RESIZE_DP_DATA(v_data); + IF c_nof_data>1 THEN + -- . sop + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, c_sync, '1', '1', '0', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + -- . valid + FOR I IN 1 TO c_nof_data-2 LOOP + v_data := func_dp_data_incr(c_data_w, c_symbol_w, v_data); + src_out.data <= RESIZE_DP_DATA(v_data); + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, '0', '1', '0', '0', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + END LOOP; + -- . eop + v_data := func_dp_data_incr(c_data_w, c_symbol_w, v_data); + src_out.data <= RESIZE_DP_DATA(v_data); + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, '0', '1', '0', '1', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + ELSE + -- . sop and eop, frame has only one word + proc_dp_stream_ready_latency(c_ready_latency, clk, src_in.ready, in_en, c_sync, '1', '1', '1', src_out.sync, src_out.valid, src_out.sop, src_out.eop); + END IF; + src_out.sync <= '0'; + src_out.valid <= '0'; + src_out.sop <= '0'; + src_out.eop <= '0'; + END proc_dp_gen_frame; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Input data counter + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_cnt_dat(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; + SIGNAL cnt_dat : INOUT STD_LOGIC_VECTOR) IS + BEGIN + IF rst='1' THEN + cnt_dat <= (cnt_dat'RANGE=>'0'); + ELSIF rising_edge(clk) THEN + IF in_en='1' THEN + cnt_dat <= STD_LOGIC_VECTOR(UNSIGNED(cnt_dat)+1); + END IF; + END IF; + END proc_dp_cnt_dat; + + PROCEDURE proc_dp_cnt_dat(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL in_en : IN STD_LOGIC; + SIGNAL cnt_val : INOUT STD_LOGIC; + SIGNAL cnt_dat : INOUT STD_LOGIC_VECTOR) IS + BEGIN + IF rst='1' THEN + cnt_val <= '0'; + cnt_dat <= (cnt_dat'RANGE=>'0'); + ELSIF rising_edge(clk) THEN + cnt_val <= '0'; + IF in_en='1' THEN + cnt_val <= '1'; + cnt_dat <= STD_LOGIC_VECTOR(UNSIGNED(cnt_dat)+1); + END IF; + END IF; + END proc_dp_cnt_dat; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Transmit data + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_tx_data(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL cnt_val : IN STD_LOGIC; + SIGNAL cnt_dat : IN STD_LOGIC_VECTOR; + SIGNAL tx_data : INOUT t_dp_data_arr; + SIGNAL tx_val : INOUT STD_LOGIC_VECTOR; + SIGNAL out_data : OUT STD_LOGIC_VECTOR; + SIGNAL out_val : OUT STD_LOGIC) IS + CONSTANT c_void : NATURAL := sel_a_b(c_ready_latency, 1, 0); -- used to avoid empty range VHDL warnings when c_ready_latency=0 + BEGIN + -- TX data array for output ready latency [c_ready_latency], index [0] for zero latency combinatorial + tx_data(0) <= cnt_dat; + tx_val( 0) <= cnt_val; + + IF rst='1' THEN + tx_data(1 TO c_ready_latency+c_void) <= (1 TO c_ready_latency+c_void=>(OTHERS=>'0')); + tx_val( 1 TO c_ready_latency+c_void) <= (1 TO c_ready_latency+c_void=>'0'); + ELSIF rising_edge(clk) THEN + tx_data(1 TO c_ready_latency+c_void) <= tx_data(0 TO c_ready_latency+c_void-1); + tx_val( 1 TO c_ready_latency+c_void) <= tx_val( 0 TO c_ready_latency+c_void-1); + END IF; + + out_data <= tx_data(c_ready_latency); + out_val <= tx_val(c_ready_latency); + END proc_dp_tx_data; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Transmit data control (use for sop, eop) + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_tx_ctrl(CONSTANT c_offset : IN NATURAL; + CONSTANT c_period : IN NATURAL; + SIGNAL data : IN STD_LOGIC_VECTOR; + SIGNAL valid : IN STD_LOGIC; + SIGNAL ctrl : OUT STD_LOGIC) IS + VARIABLE v_data : INTEGER; + BEGIN + v_data := TO_UINT(data); + ctrl <= '0'; + IF valid='1' AND ((v_data-c_offset) MOD c_period)=0 THEN + ctrl <= '1'; + END IF; + END proc_dp_tx_ctrl; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Define test sync interval + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_sync_interval(SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : OUT STD_LOGIC) IS + BEGIN + sync <= '0'; + FOR I IN 1 TO c_dp_sync_interval-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + sync <= '1'; + WAIT UNTIL rising_edge(clk); + END proc_dp_sync_interval; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Stimuli for cnt_en + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_count_en(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL lfsr : INOUT STD_LOGIC_VECTOR; + SIGNAL state : OUT t_dp_state_enum; + SIGNAL done : OUT STD_LOGIC; + SIGNAL tb_end : OUT STD_LOGIC; + SIGNAL cnt_en : OUT STD_LOGIC) IS + BEGIN + -- The counter operates at zero latency + state <= s_idle; + done <= '0'; + tb_end <= '0'; + cnt_en <= '0'; + WAIT UNTIL rst='0'; + WAIT UNTIL rising_edge(clk); + -- The cnt_val may be asserted for every active in_ready, but als support + -- cnt_val not asserted for every asserted in_ready. + + ---------------------------------------------------------------------------- + -- Interval 1 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_both_active; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 2 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_pull_down_out_ready; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 3 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_pull_down_cnt_en; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 3 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 4 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 5 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 6 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 7 cycle + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 4 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_toggle_out_ready; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 5 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_toggle_cnt_en; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1-1 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1-2 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2-1 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2-2 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1-3 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 3-1 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2-3 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 3-2 toggle + cnt_en <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + cnt_en <= '1'; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 6 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_toggle_both; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + FOR I IN 1 TO c_dp_nof_both LOOP + cnt_en <= '0'; + FOR J IN 1 TO I LOOP WAIT UNTIL rising_edge(clk); END LOOP; + cnt_en <= '1'; + FOR J IN I TO c_dp_nof_both LOOP WAIT UNTIL rising_edge(clk); END LOOP; + END LOOP; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 7 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_pulse_cnt_en; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + FOR I IN 1 TO 15 LOOP + FOR J IN 1 TO 15 LOOP + cnt_en <= '0'; + FOR K IN 1 TO I LOOP WAIT UNTIL rising_edge(clk); END LOOP; + cnt_en <= '1'; + WAIT UNTIL rising_edge(clk); + END LOOP; + FOR J IN 1 TO 20 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + END LOOP; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 8 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_chirp_out_ready; + cnt_en <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 9 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_random; + cnt_en <= '1'; + + FOR I IN 0 TO c_dp_sync_interval - c_dp_test_interval LOOP + lfsr <= func_common_random(lfsr); + cnt_en <= lfsr(lfsr'HIGH); + WAIT UNTIL rising_edge(clk); + END LOOP; + + ---------------------------------------------------------------------------- + -- Done + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + state <= s_done; + WAIT UNTIL rising_edge(clk); + cnt_en <= '0'; + + -- pulse done + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + done <= '1'; + WAIT UNTIL rising_edge(clk); + done <= '0'; + + ---------------------------------------------------------------------------- + -- Testbench end + ---------------------------------------------------------------------------- + -- set tb_end + WAIT UNTIL sync='1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + tb_end <= '1'; + WAIT; + END proc_dp_count_en; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Stimuli for out_ready + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_out_ready(SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL lfsr : INOUT STD_LOGIC_VECTOR; + SIGNAL out_ready : OUT STD_LOGIC) IS + BEGIN + out_ready <= '0'; + WAIT UNTIL rst='0'; + WAIT UNTIL rising_edge(clk); + + ---------------------------------------------------------------------------- + -- Interval 1 : Assert both cnt_en and out_ready + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 2 : Make out_ready low for 1 or more cycles + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 3 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 4 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 5 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 6 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 7 cycle + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 3 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 4 : Toggle out_ready for 1 or more cycles + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1-1 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1-2 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2-1 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2-2 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 1-3 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 3-1 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 2-3 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . 3-2 toggle + out_ready <= '0'; + FOR I IN 1 TO c_dp_nof_toggle LOOP + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '0'; + WAIT UNTIL rising_edge(clk); + WAIT UNTIL rising_edge(clk); + out_ready <= '1'; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 5 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 6 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + FOR I IN 1 TO c_dp_nof_both LOOP + out_ready <= '0'; + FOR J IN I TO c_dp_nof_both LOOP WAIT UNTIL rising_edge(clk); END LOOP; + out_ready <= '1'; + FOR J IN 1 TO I LOOP WAIT UNTIL rising_edge(clk); END LOOP; + END LOOP; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 7 + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 8 : Chirp out_ready + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + -- . slow toggle + out_ready <= '0'; + FOR I IN 0 TO c_dp_nof_toggle LOOP + out_ready <= '0'; + FOR J IN 0 TO I LOOP WAIT UNTIL rising_edge(clk); END LOOP; + out_ready <= '1'; + FOR J IN 0 TO I LOOP WAIT UNTIL rising_edge(clk); END LOOP; + END LOOP; + out_ready <= '1'; + FOR I IN 0 TO c_dp_test_interval LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Interval 9 : Random + ---------------------------------------------------------------------------- + WAIT UNTIL sync='1'; + out_ready <= '1'; + + FOR I IN 0 TO c_dp_sync_interval - c_dp_test_interval LOOP + lfsr <= func_common_random(lfsr); + out_ready <= lfsr(lfsr'HIGH); + WAIT UNTIL rising_edge(clk); + END LOOP; + + ---------------------------------------------------------------------------- + -- Done + ---------------------------------------------------------------------------- + WAIT; + END proc_dp_out_ready; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: DUT output verify enable + ------------------------------------------------------------------------------ + + -- Fixed delay until verify_en active + PROCEDURE proc_dp_verify_en(CONSTANT c_delay : IN NATURAL; + SIGNAL rst : IN STD_LOGIC; + SIGNAL clk : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL verify_en : OUT STD_LOGIC) IS + BEGIN + verify_en <= '0'; + WAIT UNTIL rst='0'; + WAIT UNTIL rising_edge(clk); + + WAIT UNTIL sync='1'; + -- Use c_delay delay before enabling the p_verify. + FOR I IN 0 TO c_delay LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + verify_en <= '1'; + WAIT; + END proc_dp_verify_en; + + + -- Dynamicly depend on first valid data to make verify_en active + PROCEDURE proc_dp_verify_en(CONSTANT c_continuous : IN BOOLEAN; + SIGNAL clk : IN STD_LOGIC; + SIGNAL valid : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL eop : IN STD_LOGIC; + SIGNAL verify_en : OUT STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + IF c_continuous=TRUE THEN + -- Verify across frames (so enable data verify after the first data has been output) + IF valid='1' THEN + verify_en <= '1'; + END IF; + ELSE + -- Verify only per frame (so re-enable data verify after the every sop) + IF eop='1' THEN + verify_en <= '0'; + ELSIF sop='1' THEN + verify_en <= '1'; + END IF; + END IF; + END IF; + END proc_dp_verify_en; + + -- Run and verify for some cycles + PROCEDURE proc_dp_verify_run_some_cycles(CONSTANT nof_pre_clk : IN NATURAL; + CONSTANT nof_verify_clk : IN NATURAL; + CONSTANT nof_post_clk : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : OUT STD_LOGIC) IS + BEGIN + proc_common_wait_some_cycles(clk, nof_pre_clk); + verify_en <= '1'; + proc_common_wait_some_cycles(clk, nof_verify_clk); + verify_en <= '0'; + proc_common_wait_some_cycles(clk, nof_post_clk); + END proc_dp_verify_run_some_cycles; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the expected value + ------------------------------------------------------------------------------ + -- e.g. to check that a test has ran at all + PROCEDURE proc_dp_verify_value(CONSTANT c_str : IN STRING; + CONSTANT mode : IN t_dp_value_enum; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC_VECTOR; + SIGNAL res : IN STD_LOGIC_VECTOR) IS + BEGIN + IF rising_edge(clk) THEN + IF en='1' THEN + IF mode = e_equal AND UNSIGNED(res) /= UNSIGNED(exp) THEN + REPORT "DP : Wrong " & c_str & " result value" SEVERITY ERROR; + END IF; + IF mode = e_at_least AND UNSIGNED(res) < UNSIGNED(exp) THEN + REPORT "DP : Wrong " & c_str & " result value too small" SEVERITY ERROR; + END IF; + END IF; + END IF; + END proc_dp_verify_value; + + PROCEDURE proc_dp_verify_value(CONSTANT mode : IN t_dp_value_enum; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC_VECTOR; + SIGNAL res : IN STD_LOGIC_VECTOR) IS + BEGIN + proc_dp_verify_value("", mode, clk, en, exp, res); + END proc_dp_verify_value; + + PROCEDURE proc_dp_verify_value(CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL en : IN STD_LOGIC; + SIGNAL exp : IN STD_LOGIC; + SIGNAL res : IN STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + IF en='1' THEN + IF res /= exp THEN + REPORT "DP : Wrong " & c_str & " result value" SEVERITY ERROR; + END IF; + END IF; + END IF; + END proc_dp_verify_value; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify output global and local BSN + ------------------------------------------------------------------------------ + -- Verify BSN: + -- . incrementing or replicated global BSN + -- . incrementing local BSN that starts at 1 + -- + -- _ _ _ _ + -- sync __| |____________| |____________| |____________| |____________ + -- _ _ _ _ _ _ _ _ _ _ _ _ + -- sop __| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__ c_block_per_sync = 3 + -- + -- c_use_local_bsn = FALSE: + -- c_nof_replicated_global_bsn = 1 + -- bsn 3 4 5 6 7 8 9 10 11 12 13 14 c_global_bsn_increment = 1 + -- bsn 3 5 7 9 11 13 15 17 19 21 22 23 c_global_bsn_increment = 2 + -- + -- c_use_local_bsn = TRUE: + -- + -- global bsn 3 4 5 6 c_global_bsn_increment = 1, c_nof_replicated_global_bsn = 1 + -- global bsn 3 6 9 12 c_global_bsn_increment = 3, c_nof_replicated_global_bsn = 1 + -- global bsn 3 3 9 9 c_global_bsn_increment = 6, c_nof_replicated_global_bsn = 2 + -- local bsn - 1 2 - 1 2 - 1 2 - 1 2 range 1:c_block_per_sync-1 + -- + -- The verify_en should initially be set to '0' and gets enabled when + -- sufficient BSN history is available to do the verification. + -- + PROCEDURE proc_dp_verify_bsn(CONSTANT c_use_local_bsn : IN BOOLEAN; -- use local BSN or only use global BSN + CONSTANT c_global_bsn_increment : IN POSITIVE; -- increment per global BSN + CONSTANT c_nof_replicated_global_bsn : IN POSITIVE; -- number of replicated global BSN + CONSTANT c_block_per_sync : IN POSITIVE; -- of sop/eop blocks per sync interval + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_sync : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_bsn : IN STD_LOGIC_VECTOR; + SIGNAL verify_en : INOUT STD_LOGIC; -- initialize '0', becomes '1' when bsn verification starts + SIGNAL cnt_replicated_global_bsn : INOUT NATURAL; + SIGNAL prev_out_bsn_global : INOUT STD_LOGIC_VECTOR; + SIGNAL prev_out_bsn_local : INOUT STD_LOGIC_VECTOR) IS + BEGIN + IF rising_edge(clk) THEN + -- out_sop must be active, because only then out_bsn will differ from the previous out_bsn + IF out_sop='1' THEN + IF c_use_local_bsn=FALSE THEN + ------------------------------------------------------------------ + -- Only use global BSN + ------------------------------------------------------------------ + prev_out_bsn_global <= out_bsn; + -- verify + IF out_sync='1' THEN + verify_en <= '1'; + END IF; + IF verify_en='1' THEN + ASSERT UNSIGNED(out_bsn) = UNSIGNED(prev_out_bsn_global)+c_global_bsn_increment REPORT "DP : Wrong BSN increment" SEVERITY ERROR; + END IF; + ELSE + ------------------------------------------------------------------ + -- Use global and local BSN + ------------------------------------------------------------------ + IF out_sync='1' THEN + prev_out_bsn_global <= out_bsn; + IF UNSIGNED(out_bsn) /= UNSIGNED(prev_out_bsn_global) THEN + verify_en <= '1'; -- wait until after last replicated global bsn + cnt_replicated_global_bsn <= 0; + ELSE + cnt_replicated_global_bsn <= cnt_replicated_global_bsn + 1; + END IF; + prev_out_bsn_local <= TO_UVEC(0, prev_out_bsn_global'LENGTH); + ELSE + prev_out_bsn_local <= out_bsn; + END IF; + -- verify + IF verify_en='1' THEN + IF out_sync='1' THEN + IF UNSIGNED(out_bsn) /= UNSIGNED(prev_out_bsn_global) THEN + ASSERT cnt_replicated_global_bsn=c_nof_replicated_global_bsn-1 REPORT "DP : Wrong number of replicated global BSN" SEVERITY ERROR; + ASSERT UNSIGNED(out_bsn)=UNSIGNED(prev_out_bsn_global)+c_global_bsn_increment REPORT "DP : Wrong global BSN increment" SEVERITY ERROR; + ELSE + ASSERT UNSIGNED(out_bsn)=UNSIGNED(prev_out_bsn_global) REPORT "DP : Wrong replicated global BSN" SEVERITY ERROR; + END IF; + ASSERT UNSIGNED(prev_out_bsn_local)=c_block_per_sync-1 REPORT "DP : Wrong last local BSN in sync interval" SEVERITY ERROR; + ELSE + ASSERT UNSIGNED(out_bsn)=UNSIGNED(prev_out_bsn_local)+1 REPORT "DP : Wrong local BSN increment" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_bsn; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output data + ------------------------------------------------------------------------------ + + -- Verify incrementing data + -- . wrap at c_out_data_max when >0, else no wrap when c_out_data_max=0 + -- . default increment by 1, but also allow an increment by c_out_data_gap + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_out_data_max : IN UNSIGNED; + CONSTANT c_out_data_gap : IN UNSIGNED; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; -- only needed when c_ready_latency = 0 + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + BEGIN + IF rising_edge(clk) THEN + -- out_val must be active, because only the out_data will it differ from the previous out_data + IF out_val='1' THEN + -- for ready_latency > 0 out_val indicates new data + -- for ready_latency = 0 out_val only indicates new data when it is confirmed by out_ready + IF c_ready_latency/=0 OR (c_ready_latency=0 AND out_ready='1') THEN + IF c_out_data_max=0 THEN + prev_out_data <= out_data; -- no wrap detection + ELSIF UNSIGNED(out_data)<c_out_data_max THEN + prev_out_data <= out_data; -- no wrap + ELSE + prev_out_data <= TO_SVEC(-1, prev_out_data'LENGTH); -- do wrap + END IF; + IF verify_en='1' THEN + IF UNSIGNED(out_data) /= UNSIGNED(prev_out_data)+1 AND -- check increment +1 + UNSIGNED(out_data) /= UNSIGNED(prev_out_data)+c_out_data_gap AND -- increment +c_out_data_gap + UNSIGNED(out_data) /= UNSIGNED(prev_out_data)+c_out_data_gap-c_out_data_max THEN -- increment +c_out_data_gap wrapped + REPORT "DP : Wrong out_data " & c_str & " count" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_data; + + -- Verify incrementing data that wraps in range 0 ... c_out_data_max + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_out_data_max : IN UNSIGNED; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + BEGIN + proc_dp_verify_data(c_str, c_ready_latency, c_out_data_max, TO_UNSIGNED(1,1), clk, verify_en, out_ready, out_val, out_data, prev_out_data); + END proc_dp_verify_data; + + -- Verify incrementing data + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + BEGIN + proc_dp_verify_data(c_str, c_ready_latency, TO_UNSIGNED(0,1), TO_UNSIGNED(1,1), clk, verify_en, out_ready, out_val, out_data, prev_out_data); + END proc_dp_verify_data; + + -- Verify incrementing data with RL > 0 or no flow control + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_out_data_max : IN UNSIGNED; + CONSTANT c_out_data_gap : IN UNSIGNED; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + BEGIN + -- Use out_val as void signal to pass on to unused out_ready, because a signal input can not connect a constant or variable + proc_dp_verify_data(c_str, 1, c_out_data_max, c_out_data_gap, clk, verify_en, out_val, out_val, out_data, prev_out_data); + END proc_dp_verify_data; + + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_out_data_max : IN NATURAL; + CONSTANT c_out_data_gap : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + CONSTANT c_data_w : NATURAL := out_data'LENGTH; + BEGIN + proc_dp_verify_data(c_str, TO_UNSIGNED(c_out_data_max, c_data_w), TO_UNSIGNED(c_out_data_gap, c_data_w), clk, verify_en, out_val, out_data, prev_out_data); + END proc_dp_verify_data; + + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + CONSTANT c_out_data_max : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + CONSTANT c_data_w : NATURAL := out_data'LENGTH; + BEGIN + proc_dp_verify_data(c_str, TO_UNSIGNED(c_out_data_max, c_data_w), TO_UNSIGNED(1, 1), clk, verify_en, out_val, out_data, prev_out_data); + END proc_dp_verify_data; + + PROCEDURE proc_dp_verify_data(CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + BEGIN + -- Use out_val as void signal to pass on to unused out_ready, because a signal input can not connect a constant or variable + proc_dp_verify_data(c_str, 1, TO_UNSIGNED(0,1), TO_UNSIGNED(1,1), clk, verify_en, out_val, out_val, out_data, prev_out_data); + END proc_dp_verify_data; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify incrementing symbols in data + -- . for c_data_w = c_symbol_w proc_dp_verify_symbols() = proc_dp_verify_data() + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_symbols(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_data_w : IN NATURAL; + CONSTANT c_symbol_w : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL out_empty : IN STD_LOGIC_VECTOR; + SIGNAL prev_out_data : INOUT STD_LOGIC_VECTOR) IS + CONSTANT c_nof_symbols_per_data : NATURAL := c_data_w/c_symbol_w; -- must be an integer + CONSTANT c_empty_w : NATURAL := ceil_log2(c_nof_symbols_per_data); + VARIABLE v_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + VARIABLE v_symbol : STD_LOGIC_VECTOR(c_symbol_w-1 DOWNTO 0); + VARIABLE v_empty : NATURAL; + BEGIN + IF rising_edge(clk) THEN + -- out_val must be active, because only the out_data will it differ from the previous out_data + IF out_val='1' THEN + -- for ready_latency > 0 out_val indicates new data + -- for ready_latency = 0 out_val only indicates new data when it is confirmed by out_ready + IF c_ready_latency/=0 OR (c_ready_latency=0 AND out_ready='1') THEN + prev_out_data <= out_data; + IF verify_en='1' THEN + v_data := prev_out_data(c_data_w-1 DOWNTO 0); + FOR I IN 0 TO c_nof_symbols_per_data-1 LOOP + v_data((I+1)*c_symbol_w-1 DOWNTO I*c_symbol_w) := INCR_UVEC(v_data((I+1)*c_symbol_w-1 DOWNTO I*c_symbol_w), c_nof_symbols_per_data); -- increment each symbol + END LOOP; + IF out_eop='0' THEN + IF UNSIGNED(out_data) /= UNSIGNED(v_data) THEN + REPORT "DP : Wrong out_data symbols count" SEVERITY ERROR; + END IF; + ELSE + v_empty := TO_UINT(out_empty(c_empty_w-1 DOWNTO 0)); + IF UNSIGNED(out_data(c_data_w-1 DOWNTO v_empty*c_symbol_w)) /= UNSIGNED(v_data(c_data_w-1 DOWNTO v_empty*c_symbol_w)) THEN + REPORT "DP : Wrong out_data symbols count at eop" SEVERITY ERROR; + END IF; + IF v_empty>0 THEN + -- adjust prev_out_data for potentially undefined empty symbols in out_data + v_symbol := v_data((v_empty+1)*c_symbol_w-1 DOWNTO v_empty*c_symbol_w); -- last valid symbol + FOR I IN 0 TO c_nof_symbols_per_data-1 LOOP + v_data((I+1)*c_symbol_w-1 DOWNTO I*c_symbol_w) := v_symbol; -- put the last valid symbol at the end of the v_data + v_symbol := INCR_UVEC(v_symbol, -1); -- decrement each symbol towards the beginning of v_data + END LOOP; + prev_out_data <= v_data; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_symbols; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output data with empty + -- . account for stream empty + -- . support last word replace (e.g. by a CRC instead of the count, or use + -- c_last_word=out_data for no replace) + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_data_empty(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_last_word : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL out_eop_1 : INOUT STD_LOGIC; + SIGNAL out_eop_2 : INOUT STD_LOGIC; + SIGNAL out_data : IN STD_LOGIC_VECTOR; + SIGNAL out_data_1 : INOUT STD_LOGIC_VECTOR; + SIGNAL out_data_2 : INOUT STD_LOGIC_VECTOR; + SIGNAL out_data_3 : INOUT STD_LOGIC_VECTOR; + SIGNAL out_empty : IN STD_LOGIC_VECTOR; + SIGNAL out_empty_1 : INOUT STD_LOGIC_VECTOR) IS + VARIABLE v_last_word : STD_LOGIC_VECTOR(out_data'RANGE); + VARIABLE v_ref_data : STD_LOGIC_VECTOR(out_data'RANGE); + VARIABLE v_empty_data : STD_LOGIC_VECTOR(out_data'RANGE); + BEGIN + IF rising_edge(clk) THEN + -- out_val must be active, because only then out_data will differ from the previous out_data + IF out_val='1' THEN + -- for ready_latency > 0 out_val indicates new data + -- for ready_latency = 0 out_val only indicates new data when it is confirmed by out_ready + IF c_ready_latency/=0 OR (c_ready_latency=0 AND out_ready='1') THEN + -- default expected data + out_data_1 <= out_data; + out_data_2 <= out_data_1; + out_data_3 <= out_data_2; + out_empty_1 <= out_empty; + out_eop_1 <= out_eop; + out_eop_2 <= out_eop_1; + IF verify_en='1' THEN + -- assume sufficient valid cycles between eop and sop, so no need to check for out_sop with regard to eop empty + IF out_eop='0' AND out_eop_1='0' AND out_eop_2='0'THEN + -- verify out_data from eop-n to eop-2 and from eop+1 to eop+n, n>2 + v_ref_data := INCR_UVEC(out_data_2, 1); + IF UNSIGNED(out_data_1) /= UNSIGNED(v_ref_data) THEN + REPORT "DP : Wrong out_data count" SEVERITY ERROR; + END IF; + ELSE + -- the empty and crc replace affect data at eop_1 and eop, so need to check data from eop-2 to eop-1 to eop to eop+1 + v_last_word := TO_UVEC(c_last_word, out_data'LENGTH); + IF out_eop='1' THEN + -- verify out_data at eop + CASE TO_INTEGER(UNSIGNED(out_empty)) IS + WHEN 0 => v_empty_data := v_last_word; + WHEN 1 => v_empty_data := v_last_word(3*c_byte_w-1 DOWNTO 0) & c_slv0(1*c_byte_w-1 DOWNTO 0); + WHEN 2 => v_empty_data := v_last_word(2*c_byte_w-1 DOWNTO 0) & c_slv0(2*c_byte_w-1 DOWNTO 0); + WHEN 3 => v_empty_data := v_last_word(1*c_byte_w-1 DOWNTO 0) & c_slv0(3*c_byte_w-1 DOWNTO 0); + WHEN OTHERS => NULL; + END CASE; + IF UNSIGNED(out_data) /= UNSIGNED(v_empty_data) THEN + REPORT "DP : Wrong out_data count at eop" SEVERITY ERROR; + END IF; + ELSIF out_eop_1='1' THEN + -- verify out_data from eop-2 to eop-1 + v_ref_data := INCR_UVEC(out_data_3, 1); + CASE TO_INTEGER(UNSIGNED(out_empty_1)) IS + WHEN 0 => v_empty_data := v_ref_data; + WHEN 1 => v_empty_data := v_ref_data(4*c_byte_w-1 DOWNTO 1*c_byte_w) & v_last_word(4*c_byte_w-1 DOWNTO 3*c_byte_w); + WHEN 2 => v_empty_data := v_ref_data(4*c_byte_w-1 DOWNTO 2*c_byte_w) & v_last_word(4*c_byte_w-1 DOWNTO 2*c_byte_w); + WHEN 3 => v_empty_data := v_ref_data(4*c_byte_w-1 DOWNTO 3*c_byte_w) & v_last_word(4*c_byte_w-1 DOWNTO 1*c_byte_w); + WHEN OTHERS => NULL; + END CASE; + IF UNSIGNED(out_data_2) /= UNSIGNED(v_empty_data) THEN + REPORT "DP : Wrong out_data count at eop-1" SEVERITY ERROR; + END IF; + -- verify out_data from eop-2 to eop+1 + v_ref_data := INCR_UVEC(out_data_3, 3); + IF UNSIGNED(out_data) /= UNSIGNED(v_ref_data) THEN + REPORT "DP : Wrong out_data count at eop+1" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_data_empty; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output other SOSI data + -- . Suited to verify the empty, error, channel fields assuming that these + -- are treated in the same way in parallel to the SOSI data. + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_other_sosi(CONSTANT c_str : IN STRING; + CONSTANT c_exp_data : IN STD_LOGIC_VECTOR; -- use constant to support assignment via FUNCTION return value + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL res_data : IN STD_LOGIC_VECTOR) IS + BEGIN + IF rising_edge(clk) THEN + IF verify_en='1' THEN + IF c_str="bsn" THEN + IF UNSIGNED(c_exp_data(c_dp_bsn_w-1 DOWNTO 0))/=UNSIGNED(res_data(c_dp_bsn_w-1 DOWNTO 0)) THEN + REPORT "DP : Wrong sosi.bsn value" SEVERITY ERROR; + END IF; + ELSIF c_str="empty" THEN + IF UNSIGNED(c_exp_data(c_dp_empty_w-1 DOWNTO 0))/=UNSIGNED(res_data(c_dp_empty_w-1 DOWNTO 0)) THEN + REPORT "DP : Wrong sosi.empty value" SEVERITY ERROR; + END IF; + ELSIF c_str="channel" THEN + IF UNSIGNED(c_exp_data(c_dp_channel_user_w-1 DOWNTO 0))/=UNSIGNED(res_data(c_dp_channel_user_w-1 DOWNTO 0)) THEN + REPORT "DP : Wrong sosi.channel value" SEVERITY ERROR; + END IF; + ELSIF c_str="error" THEN + IF UNSIGNED(c_exp_data(c_dp_error_w-1 DOWNTO 0))/=UNSIGNED(res_data(c_dp_error_w-1 DOWNTO 0)) THEN + REPORT "DP : Wrong sosi.error value" SEVERITY ERROR; + END IF; + ELSE + REPORT "proc_dp_verify_other_sosi : Unknown sosi." & c_str & "field" SEVERITY FAILURE; + END IF; + END IF; + END IF; + END proc_dp_verify_other_sosi; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output valid + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_valid(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL prev_out_ready : INOUT STD_LOGIC_VECTOR; + SIGNAL out_val : IN STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + -- for ready_latency > 0 out_val may only be asserted after out_ready + -- for ready_latency = 0 out_val may always be asserted + prev_out_ready <= (prev_out_ready'RANGE=>'0'); + IF c_ready_latency/=0 THEN + IF c_ready_latency=1 THEN + prev_out_ready(0) <= out_ready; + ELSE + prev_out_ready <= out_ready & prev_out_ready(0 TO c_ready_latency-1); + END IF; + IF verify_en='1' AND out_val='1' THEN + IF prev_out_ready(c_ready_latency-1)/='1' THEN + REPORT "DP : Wrong ready latency between out_ready and out_val" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_valid; + + PROCEDURE proc_dp_verify_valid(SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL prev_out_ready : INOUT STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC) IS + BEGIN + -- Can not reuse: + -- proc_dp_verify_valid(1, clk, verify_en, out_ready, prev_out_ready, out_val); + -- because prev_out_ready needs to map from STD_LOGIC to STD_LOGIC_VECTOR. Therefore copy paste code for RL=1: + IF rising_edge(clk) THEN + -- for ready_latency = 1 out_val may only be asserted after out_ready + prev_out_ready <= out_ready; + IF verify_en='1' AND out_val='1' THEN + IF prev_out_ready/='1' THEN + REPORT "DP : Wrong ready latency between out_ready and out_val" SEVERITY ERROR; + END IF; + END IF; + END IF; + END proc_dp_verify_valid; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output sync + -- . sync is defined such that it can only be active at sop + -- . assume that the sync occures priodically at bsn MOD c_sync_period = c_sync_offset + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN NATURAL; -- BSN sync period + CONSTANT c_sync_offset : IN NATURAL; -- BSN sync offset + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL sync : IN STD_LOGIC; + SIGNAL sop : IN STD_LOGIC; + SIGNAL bsn : IN STD_LOGIC_VECTOR) IS + CONSTANT c_bsn_w : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH); -- use maximally 31 bit of BSN slv to allow calculations with integers + VARIABLE v_expected_sync : BOOLEAN; + BEGIN + IF rising_edge(clk) THEN + IF verify_en='1' THEN + v_expected_sync := (TO_UINT(bsn(c_bsn_w-1 DOWNTO 0))-c_sync_offset) MOD c_sync_period = 0; + -- Check for unexpected sync + IF sync='1' THEN + ASSERT v_expected_sync = TRUE + REPORT "Error: Unexpected sync at BSN" SEVERITY ERROR; + ASSERT sop = '1' + REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR; + END IF; + -- Check for missing sync + IF sop='1' AND v_expected_sync=TRUE THEN + ASSERT sync = '1' + REPORT "Error: Missing sync" SEVERITY ERROR; + END IF; + END IF; + END IF; + END proc_dp_verify_sync; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output sop and eop + ------------------------------------------------------------------------------ + -- sop and eop in pairs, valid during packet and invalid between packets + PROCEDURE proc_dp_verify_sop_and_eop(CONSTANT c_ready_latency : IN NATURAL; + CONSTANT c_verify_valid : IN BOOLEAN; + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL hold_sop : INOUT STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + IF out_val='0' THEN + IF out_sop='1' THEN REPORT "DP : Wrong active sop during invalid" SEVERITY ERROR; END IF; + IF out_eop='1' THEN REPORT "DP : Wrong active eop during invalid" SEVERITY ERROR; END IF; + ELSE + -- for ready_latency > 0 out_val indicates new data + -- for ready_latency = 0 out_val only indicates new data when it is confirmed by out_ready + IF c_ready_latency/=0 OR (c_ready_latency=0 AND out_ready='1') THEN + IF out_sop='1' THEN + hold_sop <= '1'; + IF hold_sop='1' THEN + REPORT "DP : Unexpected sop without eop" SEVERITY ERROR; + END IF; + END IF; + IF out_eop='1' THEN + hold_sop <= '0'; + IF hold_sop='0' AND out_sop='0' THEN + REPORT "DP : Unexpected eop without sop" SEVERITY ERROR; + END IF; + END IF; + -- out_val='1' + IF c_verify_valid=TRUE AND out_sop='0' AND hold_sop='0' THEN + REPORT "DP : Unexpected valid in gap between eop and sop" SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_sop_and_eop; + + PROCEDURE proc_dp_verify_sop_and_eop(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL hold_sop : INOUT STD_LOGIC) IS + BEGIN + proc_dp_verify_sop_and_eop(c_ready_latency, TRUE, clk, out_ready, out_val, out_sop, out_eop, hold_sop); + END proc_dp_verify_sop_and_eop; + + PROCEDURE proc_dp_verify_sop_and_eop(SIGNAL clk : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL hold_sop : INOUT STD_LOGIC) IS + BEGIN + -- Use out_val as void signal to pass on to unused out_ready, because a signal input can not connect a constant or variable + proc_dp_verify_sop_and_eop(1, TRUE, clk, out_val, out_val, out_sop, out_eop, hold_sop); + END proc_dp_verify_sop_and_eop; + + PROCEDURE proc_dp_verify_block_size(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL alt_size : IN NATURAL; -- alternative size + SIGNAL exp_size : IN NATURAL; -- expected size + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL) IS + BEGIN + IF rising_edge(clk) THEN + IF out_val='1' THEN + -- for ready_latency > 0 out_val indicates new data + -- for ready_latency = 0 out_val only indicates new data when it is confirmed by out_ready + IF c_ready_latency/=0 OR (c_ready_latency=0 AND out_ready='1') THEN + IF out_sop='1' THEN + cnt_size <= 1; + ELSIF out_eop='1' THEN + cnt_size <= 0; + IF cnt_size/=alt_size-1 AND cnt_size/=exp_size-1 THEN + REPORT "DP : Unexpected block size" SEVERITY ERROR; + END IF; + ELSE + cnt_size <= cnt_size+1; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_block_size; + + PROCEDURE proc_dp_verify_block_size(CONSTANT c_ready_latency : IN NATURAL; + SIGNAL exp_size : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_ready : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL) IS + BEGIN + proc_dp_verify_block_size(c_ready_latency, exp_size, exp_size, clk, out_ready, out_val, out_sop, out_eop, cnt_size); + END proc_dp_verify_block_size; + + PROCEDURE proc_dp_verify_block_size(SIGNAL alt_size : IN NATURAL; -- alternative size + SIGNAL exp_size : IN NATURAL; -- expected size + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL) IS + BEGIN + -- Use out_val as void signal to pass on to unused out_ready, because a signal input can not connect a constant or variable + proc_dp_verify_block_size(1, alt_size, exp_size, clk, out_val, out_val, out_sop, out_eop, cnt_size); + END proc_dp_verify_block_size; + + PROCEDURE proc_dp_verify_block_size(SIGNAL exp_size : IN NATURAL; + SIGNAL clk : IN STD_LOGIC; + SIGNAL out_val : IN STD_LOGIC; + SIGNAL out_sop : IN STD_LOGIC; + SIGNAL out_eop : IN STD_LOGIC; + SIGNAL cnt_size : INOUT NATURAL) IS + BEGIN + -- Use out_val as void signal to pass on to unused out_ready, because a signal input can not connect a constant or variable + proc_dp_verify_block_size(1, exp_size, exp_size, clk, out_val, out_val, out_sop, out_eop, cnt_size); + END proc_dp_verify_block_size; + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output invalid between frames + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_gap_invalid(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_val : IN STD_LOGIC; + SIGNAL in_sop : IN STD_LOGIC; + SIGNAL in_eop : IN STD_LOGIC; + SIGNAL out_gap : INOUT STD_LOGIC) IS + BEGIN + IF rising_edge(clk) THEN + IF in_eop='1' THEN + out_gap <= '1'; + ELSIF in_sop='1' THEN + out_gap <= '0'; + ELSIF in_val='1' AND out_gap='1' THEN + REPORT "DP : Wrong valid in gap between eop and sop" SEVERITY ERROR; + END IF; + END IF; + END proc_dp_verify_gap_invalid; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Verify the DUT output control (use for sop, eop) + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_verify_ctrl(CONSTANT c_offset : IN NATURAL; + CONSTANT c_period : IN NATURAL; + CONSTANT c_str : IN STRING; + SIGNAL clk : IN STD_LOGIC; + SIGNAL verify_en : IN STD_LOGIC; + SIGNAL data : IN STD_LOGIC_VECTOR; + SIGNAL valid : IN STD_LOGIC; + SIGNAL ctrl : IN STD_LOGIC) IS + VARIABLE v_data : INTEGER; + BEGIN + IF rising_edge(clk) THEN + IF verify_en='1' THEN + v_data := TO_UINT(data); + IF ((v_data-c_offset) MOD c_period)=0 THEN + IF valid='1' AND ctrl/='1' THEN + REPORT "DP : Wrong data control, missing " & c_str SEVERITY ERROR; + END IF; + ELSE + IF ctrl='1' THEN + REPORT "DP : Wrong data control, unexpected " & c_str SEVERITY ERROR; + END IF; + END IF; + END IF; + END IF; + END proc_dp_verify_ctrl; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Wait for stream valid + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_stream_valid(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_valid : IN STD_LOGIC) IS + BEGIN + WAIT UNTIL rising_edge(clk); + WHILE in_valid /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_dp_stream_valid; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Wait for stream valid AND sop + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_stream_valid_sop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_valid : IN STD_LOGIC; + SIGNAL in_sop : IN STD_LOGIC) IS + BEGIN + WAIT UNTIL rising_edge(clk); + WHILE in_valid /= '1' AND in_sop /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_dp_stream_valid_sop; + + + ------------------------------------------------------------------------------ + -- PROCEDURE: Wait for stream valid AND eop + ------------------------------------------------------------------------------ + PROCEDURE proc_dp_stream_valid_eop(SIGNAL clk : IN STD_LOGIC; + SIGNAL in_valid : IN STD_LOGIC; + SIGNAL in_eop : IN STD_LOGIC) IS + BEGIN + WAIT UNTIL rising_edge(clk); + WHILE in_valid /= '1' AND in_eop /= '1' LOOP + WAIT UNTIL rising_edge(clk); + END LOOP; + END proc_dp_stream_valid_eop; + +END tb_dp_pkg; diff --git a/cores/base/dp/dp_repack_data/dp_repack_data.vhd b/cores/base/dp/dp_repack_data/dp_repack_data.vhd new file mode 100644 index 0000000000000000000000000000000000000000..0e052cb06330c2c48656f8bd71e297e8d375931b --- /dev/null +++ b/cores/base/dp/dp_repack_data/dp_repack_data.vhd @@ -0,0 +1,748 @@ +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +-------------------------------------------------------------------------------- + +-- Purpose: +-- The dp_repack_data works both as packer and as unpacker. +-- +-- Block diagram: +-- +-- A) Functional +-- The drawing shows g_in_nof_words=4 and g_out_nof_words=2 as example: +-- +-- dp_repack_in dp_repack_out +-- ___ ___ +-- | | pack_sosi | |--> src_out +-- | 3 |--------------------->| 1 | +-- | | | | +-- | 2 | ^ | | ^ +-- | | |valid | | |shift +-- | 1 | |flush | 0 | | +-- | | | | | | +-- | 0 | | | +-- snk_in -->|___| pack_siso |___| +-- snk_out.ready <-- <--------------------- <-- src_in.ready +-- snk_out.xon <------------------------------------- src_in.xon +-- +-- +-- B) Flow control +-- +-- RL=1 RL=1 RL=1 +-- . . . +-- . /-----------------------------------------\ +-- . | . _____ . | +-- . | /------\ nxt_r | | r | +-- . \-->| |---*-------*----->|p_reg|-----*---> src_out +-- . | | | | |_____| +-- snk_in -------->|p_comb|<--|-------|--------------*-------- src_in +-- | | | | | +-- | | | v | +-- | | | /-------\ | +-- | | | |p_flow | | +-- \------/ | \-------/ | +-- | | | +-- nxt_r.hold_out.valid | | | +-- v | | +-- /| |r_snk_out | +-- |0|------/ | +-- snk_out <------------------| | | +-- |1|---------------------/ +-- \| +-- +-- Description: +-- The dp_repack_data repacks g_in_nof_words of width g_in_dat_w into +-- g_out_nof_words of width g_out_dat_w. +-- +-- . g_in_bypass, g_out_bypass +-- The dp_repack_in and dp_repack_out can be bypassed to save logic and to +-- avoid the pipeline stage. Default both are FALSE, but they can be set +-- TRUE if: +-- +-- . g_in_bypass =TRUE if g_in_nof_words=g_out_nof_words or g_in_nof_words=1 +-- . g_out_bypass=TRUE if g_in_nof_words=g_out_nof_words or g_out_nof_words=1 +-- +-- Both the dp_repack_in and dp_repack_out stage do work correctly independent +-- of the g_*_bypass setting. When g_*_bypass=FALSE then they merely +-- add a transparant pipeline delay. It is important that they also work for +-- g_*_bypass=FALSE because that gives confidence that their implementation +-- structure is ok. +-- +-- . g_in_nof_words and input block size +-- The input block size in words is indicated by snk_in.sop and snk_in.eop. +-- Each subsection of g_in_nof_words is packed into g_out_nof_words. The +-- input block size does not have to be a multiple of g_in_nof_words. When +-- the snk_in.eop occurs the last repack is initiated without need for input +-- data padding. If the block length is an integer multiple of +-- g_in_nof_words then the dp_repack_data introduces no gaps between blocks. +-- If the block length is a fractional multiple of g_in_nof_words then there +-- will be a gap after the block due to that the dp_repack_in needs to +-- shift up the last subsection for the 'missing' input words. +-- +-- . g_in_dat_w*g_in_nof_words <, =, > g_in_dat_w*g_in_nof_words +-- . = : no subsection zero padding +-- . < : the subsections will be zero padded +-- . > : then the input must have sufficient zero padded bits per +-- subsection that can be stripped without data loss. +-- +-- . Resolution of the empty field +-- The g_in_symbol_w is used to define the resolution of snk_in.empty and +-- the g_out_symbol_w is used to define the resolution of src_out.empty. If +-- they are 1 then the resolution is in number of bits, because the symbol +-- size is then 1 bit. Their value has no effect on the packed data it self, +-- only on the meaning of the empty field. Hence if the empty field is not +-- used, then the setting of g_in_symbol_w and g_out_symbol_w is dont care. +-- +-- Remarks: +-- . Originally reused from LOFAR rad_repack.vhd and rad_repack(rtl).vhd. This +-- dp_repack_data still uses the shift in input register in and the shift out +-- output register, but no longer the intermediate buffer register. +-- Using shift in and shift out may ease timing closure because the routing +-- is more local compared to using a demultiplexer to put the input data in +-- the input register and a multiplexer to get the data directly from the +-- output register. For the demultiplexer / multiplexer it would be possible +-- to only use one internal register. +-- Using shift up is sufficient, the shift down option is not needed. With +-- shift up the data is input a [0] and output the high index. +-- Instead of requiring an snk_in.valid duty cycle this dp_repack_data uses +-- snk_out.ready flow control and can handle src_in.ready flow control. +-- +-- . To pack ETH/IP/UDP header slv of 14 + 20 + 8 = 42 octets into 32 bit words +-- use: +-- u_dp_repack_data : ENTITY .dp_repack_data +-- GENERIC MAP ( +-- g_in_bypass => TRUE, +-- g_in_dat_w => 8 * 42, +-- g_in_nof_words => 1, +-- g_in_symbol_w => 8, +-- g_out_bypass => FALSE, +-- g_out_dat_w => 32, +-- g_out_nof_words => 11, +-- g_out_symbol_w => 8 +-- ) +-- The src_out.empty will be 2, because: +-- (g_out_dat_w*g_out_nof_words-g_in_dat_w*g_in_nof_words)/g_out_symbol_w +-- = (32*11 - 42*8*1)/ 8 = 2 octet symbols +-- +-- Design steps: +-- * In total the development took 5 days. On day 3 I was in distress because +-- I could not get it to work so I needed to rethink. After changing to the +-- new flow control scheme that uses nxt_r the design was gradually improved +-- by getting the dp_repack_data instances in tb_tb_dp_repack_data to work one +-- by one. First only for e_active stimuli and later also for e_random and +-- e_pulse. Initially about 80 % of the functionality was implemented but +-- and subsequently each feature was verified starting with the basic +-- features and then themore detailed features. This step by step approach +-- makes that the bugs appear one by one instead of all together. Without a +-- step by step approach the bugs are too big to solve. +-- . First cases with g_in_nof_words=1 and g_out_nof_words were made to work +-- for different g_pkt_len <, =, > g_in_nof_words. +-- . Then the empty functionality for g_pkt_len MOD g_in_nof_words /= 0 was +-- added. +-- . Tried g_out_dat_w=1 which makes dp_repack_data a serializer/deserializer. +-- . Then apply external flow control using c_dp_flow_control_enum_arr in +-- the tb_tb_dp_repack_data was verified resulting in small corrections. +-- . Then verified g_in_dat_w * g_in_nof_words > or < g_out_dat_w * +-- g_out_nof_words which require padding in the subsection. The > case +-- occurs for packing and the < case then occurs for unpacking. +-- . Added g_bypass to force using wires instead of a void dp_repack_in or +-- dp_repack_out stage. +-- . Verified g_in_symbol_w and g_out_symbol_w /= 1. +-- * The development used the tb_dp_repack_data testbench that does a pack and +-- an unpack to be able to verify the data. The c_no_unpack and +-- c_enable_repack_in and c_enable_repack_out parameters in the tb are +-- useful to be able to isolate a component for debugging. + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_repack_in IS + GENERIC ( + g_bypass : BOOLEAN := FALSE; + g_in_dat_w : NATURAL; + g_in_nof_words : NATURAL; + g_in_symbol_w : NATURAL := 1 -- default 1 for snk_in.empty in nof bits, else use power of 2 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END dp_repack_in; + + +ARCHITECTURE rtl OF dp_repack_in IS + + CONSTANT c_in_buf_dat_w : NATURAL := g_in_dat_w * g_in_nof_words; + CONSTANT c_bit_cnt_max : NATURAL := c_in_buf_dat_w; + CONSTANT c_in_empty_lo : NATURAL := true_log2(g_in_symbol_w); + + TYPE t_dat_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + + TYPE t_reg IS RECORD + dat_arr : t_dat_arr(g_in_nof_words-1 DOWNTO 0); -- internally use dat_arr[] to represent v.src_out.data + src_out : t_dp_sosi; -- sosi output + hold_out : t_dp_sosi; -- hold snk_in.sync/sop/eop until end of section and then hold valid src_out until src_in.ready + flush : STD_LOGIC; -- shift when snk_in.valid or flush in case the last subsection has < g_in_nof_words + dat_bit_cnt : NATURAL RANGE 0 TO c_bit_cnt_max; -- actual nof bits in subsection + pack_bit_cnt : NATURAL RANGE 0 TO c_bit_cnt_max; -- count nof bits in subsection + END RECORD; + + SIGNAL data_vec : STD_LOGIC_VECTOR(c_in_buf_dat_w-1 DOWNTO 0); + + SIGNAL r_snk_out : t_dp_siso := c_dp_siso_rdy; + SIGNAL r : t_reg; + SIGNAL nxt_r : t_reg; + + -- Debug signals + SIGNAL snk_in_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL i_src_out : t_dp_sosi; + SIGNAL src_out_data : STD_LOGIC_VECTOR(c_in_buf_dat_w-1 DOWNTO 0); + + SIGNAL dbg_g_in_dat_w : NATURAL := g_in_dat_w; + SIGNAL dbg_in_nof_words : NATURAL := g_in_nof_words; + SIGNAL dbg_in_symbol_w : NATURAL := g_in_symbol_w; + SIGNAL dbc_in_buf_dat_w : NATURAL := c_in_buf_dat_w; + +BEGIN + + snk_in_data <= snk_in.data(g_in_dat_w-1 DOWNTO 0); + + src_out <= i_src_out; + src_out_data <= i_src_out.data(c_in_buf_dat_w-1 DOWNTO 0); + + gen_bypass : IF g_bypass=TRUE GENERATE + snk_out <= src_in; + i_src_out <= snk_in; + END GENERATE; + + no_bypass : IF g_bypass=FALSE GENERATE + + p_comb : PROCESS(rst, r, snk_in, data_vec, src_in) + VARIABLE v : t_reg; + BEGIN + ------------------------------------------------------------------------ + -- Default + v := r; + v.src_out.sync := '0'; + v.src_out.valid := '0'; + v.src_out.sop := '0'; + v.src_out.eop := '0'; + + -------------------------------------------------------------------------- + -- Function + IF r.hold_out.valid='0' THEN + + -- Clear hold_out for new output valid (= new subsection) + IF r.src_out.valid='1' THEN + v.hold_out := c_dp_sosi_rst; + END IF; + + -- Capture the snk_in block info that is valid at sop and eop + IF snk_in.sop='1' THEN + v.hold_out.sop := '1'; + v.hold_out.sync := snk_in.sync; + v.src_out.bsn := snk_in.bsn; + v.src_out.channel := snk_in.channel; + END IF; + IF snk_in.eop='1' THEN + v.hold_out.eop := '1'; + v.hold_out.empty := SHIFT_UVEC(snk_in.empty, -c_in_empty_lo); -- use snk_in.empty as offset for src_out.empty in nof bits + v.src_out.err := snk_in.err; + END IF; + + -- Capture the data per subsection in a block + IF snk_in.valid='1' OR r.flush='1' THEN + -- shift in during block + v.dat_arr(g_in_nof_words-1 DOWNTO 1) := r.dat_arr(g_in_nof_words-2 DOWNTO 0); -- shift up from low to high and shift in at index 0 + IF r.flush='1' THEN + v.dat_arr(0) := (OTHERS=>'0'); -- shift in data=0 for flush + ELSE + v.dat_arr(0) := snk_in.data(g_in_dat_w-1 DOWNTO 0); -- shift in valid data + END IF; + + -- pack subsection + IF r.pack_bit_cnt<c_in_buf_dat_w-g_in_dat_w THEN + v.pack_bit_cnt := r.pack_bit_cnt + g_in_dat_w; + + -- early end of pack subsection + IF snk_in.eop='1' THEN + v.flush := '1'; -- enable flush in case eop occurs before end of pack subsection + v.dat_bit_cnt := v.pack_bit_cnt; -- capture the current subsection pack_bit_cnt + END IF; + ELSE -- r.pack_bit_cnt=c_in_buf_dat_w-g_in_dat_w + -- default end of pack subsection + v.pack_bit_cnt := 0; + v.flush := '0'; + IF r.flush='0' THEN + v.dat_bit_cnt := c_in_buf_dat_w; -- set default subsection pack_bit_cnt + END IF; + + v.hold_out.valid := '1'; -- the function has new data to output + END IF; + END IF; + + -- pass on the v.dat_arr as data vector + v.src_out.data := RESIZE_DP_DATA(data_vec); + + -- pass on dat_bit_cnt via DP empty field + v.src_out.empty := INCR_UVEC(v.hold_out.empty, c_in_buf_dat_w - v.dat_bit_cnt); + + -- output input stage into output stage when ready, else hold_out.valid to signal pending output + IF v.hold_out.valid='1' THEN + IF src_in.ready='1' THEN + v.src_out.valid := '1'; + v.src_out.sync := v.hold_out.sync; + v.src_out.sop := v.hold_out.sop; + v.src_out.eop := v.hold_out.eop; + v.hold_out.valid := '0'; + END IF; + END IF; + ELSE + -- pending output + IF src_in.ready='1' THEN + v.src_out.valid := '1'; + v.src_out.sync := r.hold_out.sync; + v.src_out.sop := r.hold_out.sop; + v.src_out.eop := r.hold_out.eop; + v.hold_out.valid := '0'; + END IF; + END IF; + + ------------------------------------------------------------------------ + -- Reset and nxt_r + IF rst = '1' THEN + v.src_out := c_dp_sosi_rst; + v.hold_out := c_dp_sosi_rst; + v.flush := '0'; + v.dat_bit_cnt := 0; + v.pack_bit_cnt := 0; + END IF; + + nxt_r <= v; + END PROCESS; + + -------------------------------------------------------------------------- + -- p_reg + r <= nxt_r WHEN rising_edge(clk); + + -------------------------------------------------------------------------- + -- Wires + p_data_vec : PROCESS(nxt_r) + BEGIN + FOR I IN 0 TO g_in_nof_words-1 LOOP + data_vec((I+1)*g_in_dat_w-1 DOWNTO I*g_in_dat_w) <= nxt_r.dat_arr(I); + END LOOP; + END PROCESS; + + -------------------------------------------------------------------------- + -- Wired output + i_src_out <= r.src_out; + + -------------------------------------------------------------------------- + -- Flow control + + -- local function flow control + p_flow : PROCESS(nxt_r) + BEGIN + r_snk_out <= c_dp_siso_rdy; + IF nxt_r.flush='1' THEN + r_snk_out.ready <= '0'; -- input shift in stage function is always ready except when flushing + END IF; + END PROCESS; + + -- combined local and remote src_in flow control + snk_out.ready <= r_snk_out.ready WHEN nxt_r.hold_out.valid='0' ELSE src_in.ready; -- if there is pending output then the src_in ready determines the flow control + snk_out.xon <= src_in.xon; -- just pass on the xon/off frame flow control + + END GENERATE; + +END rtl; + + + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_repack_out IS + GENERIC ( + g_bypass : BOOLEAN := FALSE; + g_in_buf_dat_w : NATURAL; + g_out_dat_w : NATURAL; + g_out_nof_words : NATURAL; + g_out_symbol_w : NATURAL := 1 -- default 1 for snk_in.empty in nof bits, else use power of 2 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END dp_repack_out; + +ARCHITECTURE rtl OF dp_repack_out IS + + CONSTANT c_out_buf_dat_w : NATURAL := g_out_dat_w * g_out_nof_words; + CONSTANT c_out_buf_dat_lo : NATURAL := sel_a_b(c_out_buf_dat_w > g_in_buf_dat_w, c_out_buf_dat_w - g_in_buf_dat_w, 0); -- pack into subsection with 0 or more padding bits + CONSTANT c_snk_in_dat_lo : NATURAL := sel_a_b(c_out_buf_dat_w < g_in_buf_dat_w, g_in_buf_dat_w - c_out_buf_dat_w, 0); -- unpack from subsection that has 0 or more padding bits + CONSTANT c_bit_cnt_max : NATURAL := c_out_buf_dat_w; + CONSTANT c_out_empty_lo : NATURAL := true_log2(g_out_symbol_w); + + TYPE t_dat_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + + TYPE t_reg IS RECORD + dat_arr : t_dat_arr(g_out_nof_words-1 DOWNTO 0); + src_out : t_dp_sosi; + hold_out : t_dp_sosi; -- hold src_out valid and sync/sop/eop until src_in.ready + shift : STD_LOGIC; -- shift out the dat_arr + dat_bit_cnt : NATURAL RANGE 0 TO c_bit_cnt_max; -- actual nof bits in subsection + pack_bit_cnt : NATURAL RANGE 0 TO c_bit_cnt_max; -- count nof bits in subsection + empty_bit_cnt : NATURAL RANGE 0 TO c_bit_cnt_max; -- empty nof bits in subsection + eos : STD_LOGIC; -- end of subsection + END RECORD; + + SIGNAL data_vec : STD_LOGIC_VECTOR(c_out_buf_dat_w-1 DOWNTO 0) := (OTHERS=>'0'); + + SIGNAL r_snk_out : t_dp_siso := c_dp_siso_rdy; + SIGNAL r : t_reg; + SIGNAL nxt_r : t_reg; + + -- Debug signals + SIGNAL snk_in_data : STD_LOGIC_VECTOR(g_in_buf_dat_w-1 DOWNTO 0); + SIGNAL i_src_out : t_dp_sosi; + SIGNAL src_out_data : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + + SIGNAL dbg_g_in_buf_dat_w : NATURAL := g_in_buf_dat_w; + SIGNAL dbg_g_out_dat_w : NATURAL := g_out_dat_w; + SIGNAL dbg_out_nof_words : NATURAL := g_out_nof_words; + SIGNAL dbg_out_symbol_w : NATURAL := g_out_symbol_w; + SIGNAL dbc_out_buf_dat_w : NATURAL := c_out_buf_dat_w; + SIGNAL dbc_out_buf_dat_lo : NATURAL := c_out_buf_dat_lo; + SIGNAL dbc_snk_in_dat_lo : NATURAL := c_snk_in_dat_lo; + +BEGIN + + snk_in_data <= snk_in.data(g_in_buf_dat_w-1 DOWNTO 0); + + src_out <= i_src_out; + src_out_data <= i_src_out.data(g_out_dat_w-1 DOWNTO 0); + + gen_bypass : IF g_bypass=TRUE GENERATE + snk_out <= src_in; + + p_src_out : PROCESS(snk_in) + BEGIN + i_src_out <= snk_in; + IF c_snk_in_dat_lo>0 THEN + i_src_out.data <= SHIFT_UVEC(snk_in.data, c_snk_in_dat_lo); + i_src_out.empty <= INCR_UVEC( snk_in.empty, -c_snk_in_dat_lo); + END IF; + IF c_out_buf_dat_lo>0 THEN + i_src_out.data <= SHIFT_UVEC(snk_in.data, -c_out_buf_dat_lo); + i_src_out.empty <= INCR_UVEC( snk_in.empty, c_out_buf_dat_lo); + END IF; + END PROCESS; + END GENERATE; + + no_bypass : IF g_bypass=FALSE GENERATE + + p_comb : PROCESS(rst, snk_in, r, data_vec, src_in) + VARIABLE v : t_reg; + BEGIN + ------------------------------------------------------------------------ + -- Default + v := r; + v.src_out.sync := '0'; + v.src_out.valid := '0'; + v.src_out.sop := '0'; + v.src_out.eop := '0'; + + ------------------------------------------------------------------------ + -- Function + IF r.hold_out.valid='0' THEN + + -- Clear hold_out for new output valid + IF r.src_out.sop='1' THEN + v.hold_out.sync := '0'; + v.hold_out.sop := '0'; + END IF; + IF r.src_out.eop='1' THEN + v.hold_out.eop := '0'; + END IF; + + -- Capture the snk_in block info that is valid at sop and eop + IF snk_in.sop='1' THEN + v.hold_out.sop := '1'; + v.hold_out.sync := snk_in.sync; + v.src_out.bsn := snk_in.bsn; + v.src_out.channel := snk_in.channel; + END IF; + IF snk_in.eop='1' THEN + v.hold_out.eop := '1'; -- local function will calculate src_out.empty based on snk_in.empty + v.src_out.err := snk_in.err; + END IF; + + IF r.shift='1' THEN + -- shift out rest of subsection + v.hold_out.valid := '1'; + + v.dat_arr(g_out_nof_words-1 DOWNTO 1) := r.dat_arr(g_out_nof_words-2 DOWNTO 0); -- shift up from low to high and shift out at high index + v.dat_arr(0) := (OTHERS=>'0'); -- shift in data=0 + + v.pack_bit_cnt := r.pack_bit_cnt - g_out_dat_w; + + -- end of pack subsection + IF v.pack_bit_cnt<=r.empty_bit_cnt THEN + v.eos := '1'; -- end of subsection, so ready for new snk_in + v.shift := '0'; -- stop shifting + END IF; + + ELSIF snk_in.valid='1' THEN + -- start of pack subsection + v.hold_out.valid := '1'; + + FOR I IN 0 TO g_out_nof_words-1 LOOP + v.dat_arr(I) := data_vec((I+1)*g_out_dat_w-1 DOWNTO I*g_out_dat_w); + END LOOP; + + v.dat_bit_cnt := g_in_buf_dat_w - c_snk_in_dat_lo; -- default dat_bit_cnt per subsection + IF snk_in.eop='1' THEN + v.dat_bit_cnt := g_in_buf_dat_w - TO_UINT(snk_in.empty); -- pass on last subsection dat_bit_cnt info via DP empty field + END IF; + + v.pack_bit_cnt := c_out_buf_dat_w - g_out_dat_w; + v.empty_bit_cnt := c_out_buf_dat_w - v.dat_bit_cnt; + v.eos := '0'; + v.shift := '1'; + + -- end of pack subsection + IF v.pack_bit_cnt<=v.empty_bit_cnt THEN + v.eos := '1'; -- end of subsection, so ready for new snk_in + v.shift := '0'; + END IF; + END IF; + + -- fill in local empty if this is the last subsection of a block + IF v.eos='1' THEN + IF v.hold_out.eop='1' THEN + v.src_out.empty := TO_DP_EMPTY(v.empty_bit_cnt - v.pack_bit_cnt); -- in nof bits + v.src_out.empty := SHIFT_UVEC(v.src_out.empty, c_out_empty_lo); -- in nof symbols + END IF; + END IF; + + -- pass on the v.dat_arr as data vector + v.src_out.data := RESIZE_DP_DATA(v.dat_arr(g_out_nof_words-1)); + + -- output valid data when ready, else hold_out.valid to signal pending output + IF v.hold_out.valid='1' THEN + IF src_in.ready='1' THEN + v.src_out.valid := '1'; + v.src_out.sync := v.hold_out.sync; + v.src_out.sop := v.hold_out.sop; + v.src_out.eop := v.hold_out.eop AND v.eos; -- output eop at end of subsection + v.hold_out.valid := '0'; + END IF; + END IF; + + ELSE + -- pending output + IF src_in.ready='1' THEN + v.src_out.valid := '1'; + v.src_out.sync := r.hold_out.sync; + v.src_out.sop := r.hold_out.sop; + v.src_out.eop := r.hold_out.eop AND r.eos; -- output eop at end of subsection + v.hold_out.valid := '0'; + END IF; + END IF; + + ------------------------------------------------------------------------ + -- Reset and nxt_r + IF rst = '1' THEN + v.src_out := c_dp_sosi_rst; + v.hold_out := c_dp_sosi_rst; + v.shift := '0'; + v.dat_bit_cnt := 0; + v.pack_bit_cnt := 0; + v.empty_bit_cnt := 0; + v.eos := '0'; + END IF; + + nxt_r <= v; + END PROCESS; + + -------------------------------------------------------------------------- + -- p_reg + r <= nxt_r WHEN rising_edge(clk); + + -------------------------------------------------------------------------- + -- Wires + data_vec(c_out_buf_dat_w-1 DOWNTO c_out_buf_dat_lo) <= snk_in.data(g_in_buf_dat_w-1 DOWNTO c_snk_in_dat_lo); + + -------------------------------------------------------------------------- + -- Wired output + i_src_out <= r.src_out; + + -------------------------------------------------------------------------- + -- Flow control + + -- local function flow control + p_flow : PROCESS(nxt_r) + BEGIN + r_snk_out <= c_dp_siso_rdy; + IF nxt_r.shift='1' AND nxt_r.eos='0' THEN + r_snk_out.ready <= '0'; -- output shift out stage function is only ready when it is not shifting or at the end of the subsection + END IF; + END PROCESS; + + -- combined local and remote src_in flow control + snk_out.ready <= r_snk_out.ready WHEN nxt_r.hold_out.valid='0' ELSE src_in.ready; -- if there is pending output then the src_in ready determines the flow control + snk_out.xon <= src_in.xon; -- just pass on the xon/off frame flow control + + END GENERATE; + +END rtl; + + + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; + +ENTITY dp_repack_data IS + GENERIC ( + g_enable_repack_in : BOOLEAN := TRUE; + g_enable_repack_out : BOOLEAN := TRUE; + g_in_bypass : BOOLEAN := FALSE; + g_in_dat_w : NATURAL; + g_in_nof_words : NATURAL; + g_in_symbol_w : NATURAL := 1; -- default 1 for snk_in.empty in nof bits, else use power of 2 + g_out_bypass : BOOLEAN := FALSE; + g_out_dat_w : NATURAL; + g_out_nof_words : NATURAL; + g_out_symbol_w : NATURAL := 1 -- default 1 for src_out.empty in nof bits, else use power of 2 + ); + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_out : OUT t_dp_siso; + snk_in : IN t_dp_sosi; + + src_in : IN t_dp_siso; + src_out : OUT t_dp_sosi + ); +END dp_repack_data; + + +ARCHITECTURE str OF dp_repack_data IS + + CONSTANT c_in_buf_dat_w : NATURAL := g_in_dat_w * g_in_nof_words; + + SIGNAL snk_in_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL i_snk_out : t_dp_siso; + + SIGNAL pack_siso : t_dp_siso; + SIGNAL pack_sosi : t_dp_sosi; + SIGNAL pack_sosi_data : STD_LOGIC_VECTOR(c_in_buf_dat_w-1 DOWNTO 0); + + SIGNAL i_src_out : t_dp_sosi; + SIGNAL src_out_data : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + + SIGNAL snk_out_ready_reg : STD_LOGIC_VECTOR(0 TO c_dp_stream_rl); + SIGNAL pack_ready_reg : STD_LOGIC_VECTOR(0 TO c_dp_stream_rl); + +BEGIN + + snk_out <= i_snk_out; + src_out <= i_src_out; + + snk_in_data <= snk_in.data(g_in_dat_w-1 DOWNTO 0); + pack_sosi_data <= pack_sosi.data(c_in_buf_dat_w-1 DOWNTO 0); + src_out_data <= i_src_out.data(g_out_dat_w-1 DOWNTO 0); + + no_dp_repack_in : IF g_enable_repack_in=FALSE GENERATE + i_snk_out <= pack_siso; + pack_sosi <= snk_in; + END GENERATE; + + gen_dp_repack_in : IF g_enable_repack_in=TRUE GENERATE + u_dp_repack_in : ENTITY work.dp_repack_in + GENERIC MAP ( + g_bypass => g_in_bypass, + g_in_dat_w => g_in_dat_w, + g_in_nof_words => g_in_nof_words, + g_in_symbol_w => g_in_symbol_w + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_out => i_snk_out, + snk_in => snk_in, + + src_in => pack_siso, + src_out => pack_sosi + ); + END GENERATE; + + no_dp_repack_out : IF g_enable_repack_out=FALSE GENERATE + pack_siso <= src_in; + i_src_out <= pack_sosi; + END GENERATE; + + gen_dp_repack_out : IF g_enable_repack_out=TRUE GENERATE + u_dp_repack_out : ENTITY work.dp_repack_out + GENERIC MAP ( + g_bypass => g_out_bypass, + g_in_buf_dat_w => c_in_buf_dat_w, + g_out_dat_w => g_out_dat_w, + g_out_nof_words => g_out_nof_words, + g_out_symbol_w => g_out_symbol_w + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_out => pack_siso, + snk_in => pack_sosi, + + src_in => src_in, + src_out => i_src_out + ); + END GENERATE; + + -- Simulation only: internal stream RL verification + proc_dp_siso_alert(clk, snk_in, i_snk_out, snk_out_ready_reg); + proc_dp_siso_alert(clk, pack_sosi, pack_siso, pack_ready_reg); + +END str; diff --git a/cores/base/dp/dp_repack_data/hdllib.cfg b/cores/base/dp/dp_repack_data/hdllib.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bb4139616f9d019c159f9b9a2441177de1533e0a --- /dev/null +++ b/cores/base/dp/dp_repack_data/hdllib.cfg @@ -0,0 +1,20 @@ +hdl_lib_name = dp_repack_data +hdl_library_clause_name = dp_repack_data_lib +hdl_lib_uses_synth = dp_pkg +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + dp_repack_data.vhd + +test_bench_files = + tb_dp_repack_data.vhd + tb_tb_dp_repack_data.vhd + +regression_test_vhdl = + tb_tb_dp_repack_data.vhd + +[modelsim_project_file] + + +[quartus_project_file] diff --git a/cores/base/dp/dp_repack_data/tb_dp_repack_data.vhd b/cores/base/dp/dp_repack_data/tb_dp_repack_data.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2d2d5f95684a228a8782e1b21fbd7ef157fa2f7a --- /dev/null +++ b/cores/base/dp/dp_repack_data/tb_dp_repack_data.vhd @@ -0,0 +1,317 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +------------------------------------------------------------------------------- + +-- Purpose: +-- . Test bench for dp_repack_data +-- Description: +-- c_no_unpack +-- . +-- g_in_dat_w g_pack_dat_w . g_in_dat_w +-- g_in_nof_words g_pack_nof_words . g_in_nof_words +-- . . . . +-- . u_pack . u_unpack . . +-- . ______________ . ______________ . +-- . |dp_repack_data| . |dp_repack_data| . +-- stimuli_src ---->| |----------->| |----> verify_snk +-- | in out | pack_src | in out | +-- |______________| |______________| +-- +-- Usage: +-- > as 10 +-- > run -all +-- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE common_pkg_lib.common_lfsr_sequences_pkg.ALL; +USE common_pkg_lib.tb_common_pkg.ALL; +USE dp_pkg_lib.dp_stream_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + + +ENTITY tb_dp_repack_data IS + GENERIC ( + -- general + g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always e_active, e_random or e_pulse flow control + g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always e_active, e_random or e_pulse flow control + -- specific + g_in_dat_w : NATURAL := 8 * 42; + g_in_nof_words : NATURAL := 1; + g_pack_dat_w : NATURAL := 32; + g_pack_nof_words : NATURAL := 11; + g_in_bypass : BOOLEAN := TRUE; -- can use TRUE when g_in_nof_words=1 or g_in_nof_words=g_out_nof_words + g_pack_bypass : BOOLEAN := FALSE; -- can use TRUE when g_out_nof_words=1 or g_in_nof_words=g_out_nof_words + g_in_symbol_w : NATURAL := 8; -- default 1 for snk_in.empty in nof bits, else use power of 2 + g_pack_symbol_w : NATURAL := 8; -- default 1 for src_out.empty in nof bits, else use power of 2 + g_nof_repeat : NATURAL := 10; + g_pkt_len : NATURAL := 1; -- if not a multiple of g_in_nof_words then the input stage flush creates gap between blocks + g_pkt_gap : NATURAL := 0 + ); +END tb_dp_repack_data; + + +ARCHITECTURE tb OF tb_dp_repack_data IS + + CONSTANT c_no_unpack : BOOLEAN := FALSE; + CONSTANT c_enable_repack_in : BOOLEAN := TRUE; + CONSTANT c_enable_repack_out : BOOLEAN := TRUE; + + -- dp_stream_stimuli + CONSTANT c_stimuli_pulse_active : NATURAL := 3; --g_in_nof_words; + CONSTANT c_stimuli_pulse_period : NATURAL := 7; + + CONSTANT c_data_init : NATURAL := 0; + CONSTANT c_bsn_init : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := X"0000000000000000"; -- X"0877665544332211" + CONSTANT c_err_init : NATURAL := 247; + CONSTANT c_channel_init : NATURAL := 5; -- fixed + + -- dp_stream_verify + CONSTANT c_verify_pulse_active : NATURAL := 1; + CONSTANT c_verify_pulse_period : NATURAL := 5; + + CONSTANT c_data_max : UNSIGNED(g_in_dat_w-1 DOWNTO 0) := (OTHERS=>'1'); + CONSTANT c_dsp_max : UNSIGNED(g_in_dat_w-1 DOWNTO 0) := (OTHERS=>'1'); + + --CONSTANT c_verify_snk_in_cnt_max : t_dp_sosi_unsigned := c_dp_sosi_unsigned_rst; -- default 0 is no wrap + CONSTANT c_verify_snk_in_cnt_max : t_dp_sosi_unsigned := TO_DP_SOSI_UNSIGNED('0', '0', '0', '0', c_data_max, c_dsp_max, c_dsp_max, c_unsigned_0, c_unsigned_0, c_unsigned_0, c_unsigned_0); + CONSTANT c_verify_snk_in_cnt_gap : t_dp_sosi_unsigned := c_dp_sosi_unsigned_ones; -- default only accept increment +1 + + CONSTANT c_expected_pkt_len : NATURAL := sel_a_b(c_no_unpack, g_pkt_len * g_pack_nof_words / g_in_nof_words, g_pkt_len); + -- both + CONSTANT c_sync_period : NATURAL := 10; + CONSTANT c_sync_offset : NATURAL := 7; + + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC := '1'; + SIGNAL tb_end : STD_LOGIC := '0'; + + SIGNAL stimuli_src_in : t_dp_siso := c_dp_siso_rdy; + SIGNAL stimuli_src_out : t_dp_sosi; + SIGNAL stimuli_src_out_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + + SIGNAL verify_snk_in_enable : t_dp_sosi_sl := c_dp_sosi_sl_rst; + SIGNAL last_snk_in : t_dp_sosi; + SIGNAL last_snk_in_evt : STD_LOGIC; + SIGNAL verify_last_snk_in_evt : t_dp_sosi_sl := c_dp_sosi_sl_rst; + + SIGNAL verify_snk_out : t_dp_siso := c_dp_siso_rdy; + SIGNAL verify_snk_in : t_dp_sosi; + SIGNAL verify_snk_in_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + + -- specific + SIGNAL pack_src_in : t_dp_siso; + SIGNAL pack_src_out : t_dp_sosi; + SIGNAL pack_src_out_data : STD_LOGIC_VECTOR(g_pack_dat_w-1 DOWNTO 0); + + SIGNAL unpack_src_in : t_dp_siso; + SIGNAL unpack_src_out : t_dp_sosi; + SIGNAL unpack_src_out_data : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + +BEGIN + + clk <= (NOT clk) OR tb_end AFTER clk_period/2; + rst <= '1', '0' AFTER clk_period*7; + + ------------------------------------------------------------------------------ + -- DATA GENERATION + ------------------------------------------------------------------------------ + + u_dp_stream_stimuli : ENTITY dp_pkg_lib.dp_stream_stimuli + GENERIC MAP ( + g_instance_nr => 0, -- only one stream so choose index 0 + -- flow control + g_random_w => 15, -- use different random width for stimuli and for verify to have different random sequences + g_pulse_active => c_stimuli_pulse_active, + g_pulse_period => c_stimuli_pulse_period, + g_flow_control => g_flow_control_stimuli, -- always active, random or pulse flow control + -- initializations + g_sync_period => c_sync_period, + g_sync_offset => c_sync_offset, + g_data_init => c_data_init, + g_bsn_init => c_bsn_init, + g_err_init => c_err_init, + g_channel_init => c_channel_init, + -- specific + g_in_dat_w => g_in_dat_w, + g_nof_repeat => g_nof_repeat, + g_pkt_len => g_pkt_len, + g_pkt_gap => g_pkt_gap + ) + PORT MAP ( + rst => rst, + clk => clk, + + -- Generate stimuli + src_in => stimuli_src_in, + src_out => stimuli_src_out, + + -- End of stimuli + last_snk_in => last_snk_in, -- expected verify_snk_in after end of stimuli + last_snk_in_evt => last_snk_in_evt, -- trigger verify to verify the last_snk_in + tb_end => tb_end -- signal end of tb as far as this dp_stream_stimuli is concerned + ); + + + ------------------------------------------------------------------------------ + -- DATA VERIFICATION + ------------------------------------------------------------------------------ + + -- Select fields that need to be verified + -- . during the test + verify_snk_in_enable.sync <= '1'; + verify_snk_in_enable.bsn <= '1'; + verify_snk_in_enable.data <= '1' WHEN c_no_unpack=FALSE ELSE '0'; + verify_snk_in_enable.re <= '0'; + verify_snk_in_enable.im <= '0'; + verify_snk_in_enable.valid <= '1'; + verify_snk_in_enable.sop <= '1'; + verify_snk_in_enable.eop <= '1'; + verify_snk_in_enable.empty <= '0'; + verify_snk_in_enable.channel <= '1'; + verify_snk_in_enable.err <= '1'; + + -- . after the test + verify_last_snk_in_evt.sync <= last_snk_in_evt; + verify_last_snk_in_evt.bsn <= last_snk_in_evt; + verify_last_snk_in_evt.data <= last_snk_in_evt WHEN c_no_unpack=FALSE ELSE '0'; + verify_last_snk_in_evt.re <= '0'; + verify_last_snk_in_evt.im <= '0'; + verify_last_snk_in_evt.valid <= last_snk_in_evt; + verify_last_snk_in_evt.sop <= last_snk_in_evt; + verify_last_snk_in_evt.eop <= last_snk_in_evt; + verify_last_snk_in_evt.empty <= '0'; + verify_last_snk_in_evt.channel <= last_snk_in_evt; + verify_last_snk_in_evt.err <= last_snk_in_evt; + + u_dp_stream_verify : ENTITY dp_pkg_lib.dp_stream_verify + GENERIC MAP ( + g_instance_nr => 0, -- only one stream so choose index 0 + -- flow control + g_random_w => 14, -- use different random width for stimuli and for verify to have different random sequences + g_pulse_active => c_verify_pulse_active, + g_pulse_period => c_verify_pulse_period, + g_flow_control => g_flow_control_verify, -- always active, random or pulse flow control + -- initializations + g_sync_period => c_sync_period, + g_sync_offset => c_sync_offset, + g_snk_in_cnt_max => c_verify_snk_in_cnt_max, + g_snk_in_cnt_gap => c_verify_snk_in_cnt_gap, + -- specific + g_in_dat_w => g_in_dat_w, + g_pkt_len => c_expected_pkt_len + ) + PORT MAP ( + rst => rst, + clk => clk, + + -- Verify data + snk_out => verify_snk_out, + snk_in => verify_snk_in, + + -- During stimuli + verify_snk_in_enable => verify_snk_in_enable, -- enable verify to verify that the verify_snk_in fields are incrementing + + -- End of stimuli + expected_snk_in => last_snk_in, -- expected verify_snk_in after end of stimuli + verify_expected_snk_in_evt => verify_last_snk_in_evt -- trigger verify to verify the last_snk_in + ); + + ------------------------------------------------------------------------------ + -- DUT Pack + ------------------------------------------------------------------------------ + + u_pack : ENTITY work.dp_repack_data + GENERIC MAP ( + g_enable_repack_in => c_enable_repack_in, + g_enable_repack_out => c_enable_repack_out, + g_in_bypass => g_in_bypass, + g_in_dat_w => g_in_dat_w, + g_in_nof_words => g_in_nof_words, + g_in_symbol_w => g_in_symbol_w, + g_out_bypass => g_pack_bypass, + g_out_dat_w => g_pack_dat_w, + g_out_nof_words => g_pack_nof_words, + g_out_symbol_w => g_pack_symbol_w + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_out => stimuli_src_in, + snk_in => stimuli_src_out, + + src_in => pack_src_in, + src_out => pack_src_out + ); + + pack_src_out_data <= pack_src_out.data(g_pack_dat_w-1 DOWNTO 0); + + ------------------------------------------------------------------------------ + -- DUT Unpack + ------------------------------------------------------------------------------ + + no_unpack : IF c_no_unpack=TRUE GENERATE + pack_src_in <= unpack_src_in; + unpack_src_out <= pack_src_out; + END GENERATE; + + gen_unpack : IF c_no_unpack=FALSE GENERATE + u_unpack : ENTITY work.dp_repack_data + GENERIC MAP ( + g_enable_repack_in => c_enable_repack_out, + g_enable_repack_out => c_enable_repack_in, + g_in_bypass => g_pack_bypass, + g_in_dat_w => g_pack_dat_w, + g_in_nof_words => g_pack_nof_words, + g_in_symbol_w => g_pack_symbol_w, + g_out_bypass => g_in_bypass, + g_out_dat_w => g_in_dat_w, + g_out_nof_words => g_in_nof_words, + g_out_symbol_w => g_in_symbol_w + ) + PORT MAP ( + rst => rst, + clk => clk, + + snk_out => pack_src_in, + snk_in => pack_src_out, + + src_in => unpack_src_in, + src_out => unpack_src_out + ); + END GENERATE; + + unpack_src_out_data <= unpack_src_out.data(g_in_dat_w-1 DOWNTO 0); + + unpack_src_in <= verify_snk_out; + verify_snk_in <= unpack_src_out; + + ------------------------------------------------------------------------------ + -- Auxiliary + ------------------------------------------------------------------------------ + + -- Map to slv to ease monitoring in wave window + stimuli_src_out_data <= stimuli_src_out.data(g_in_dat_w-1 DOWNTO 0); + verify_snk_in_data <= verify_snk_in.data(g_in_dat_w-1 DOWNTO 0); + +END tb; diff --git a/cores/base/dp/dp_repack_data/tb_tb_dp_repack_data.vhd b/cores/base/dp/dp_repack_data/tb_tb_dp_repack_data.vhd new file mode 100644 index 0000000000000000000000000000000000000000..62f5bdb7c4f788ef5e90c96955a5919678652ffc --- /dev/null +++ b/cores/base/dp/dp_repack_data/tb_tb_dp_repack_data.vhd @@ -0,0 +1,166 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2015 +-- 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/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_pkg_lib, dp_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE dp_pkg_lib.tb_dp_pkg.ALL; + +-- Purpose: Verify multiple variations of tb_dp_repack_data +-- Description: +-- Usage: +-- > as 6 +-- > run -all + +ENTITY tb_tb_dp_repack_data IS +END tb_tb_dp_repack_data; + + +ARCHITECTURE tb OF tb_tb_dp_repack_data IS + + CONSTANT c_nof_repeat : NATURAL := 5; + + CONSTANT c_flow : t_dp_flow_control_enum_arr := c_dp_flow_control_enum_arr; + CONSTANT c_bool : t_nat_boolean_arr := c_nat_boolean_arr; + + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' + +BEGIN + + -- -- general + -- g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always e_active, e_random or e_pulse flow control + -- g_flow_control_verify : t_dp_flow_control_enum := e_active; -- always e_active, e_random or e_pulse flow control + -- -- specific + -- g_in_dat_w : NATURAL := 5; + -- g_in_nof_words : NATURAL := 2; + -- g_pack_dat_w : NATURAL := 16; + -- g_pack_nof_words : NATURAL := 1; + -- g_in_bypass : BOOLEAN := FALSE; -- can use TRUE when g_in_nof_words=1 or g_in_nof_words=g_out_nof_words + -- g_pack_bypass : BOOLEAN := FALSE; -- can use TRUE when g_out_nof_words=1 or g_in_nof_words=g_out_nof_words + -- g_in_symbol_w : NATURAL := 1; -- default 1 for snk_in.empty in nof bits, else use power of 2 + -- g_pack_symbol_w : NATURAL := 1; -- default 1 for src_out.empty in nof bits, else use power of 2 + -- g_nof_repeat : NATURAL := 10; + -- g_pkt_len : NATURAL := 11; -- if not a multiple of g_in_nof_words then the input stage flush creates gap between blocks + -- g_pkt_gap : NATURAL := 0 + + g_flow_control_stimuli : FOR I IN 0 TO 2 GENERATE -- 0 = e_active, 1 = e_random, 2 = e_pulse + g_flow_control_verify : FOR J IN 0 TO 2 GENERATE -- 0 = e_active, 1 = e_random, 2 = e_pulse + + ------------------------------------------------------------------------- + -- Tests that can use bypass + -- . g_in_nof_words = 1 + -- . g_out_nof_words = 1 + -- . g_in_nof_words = g_in_nof_words >= 1 + ------------------------------------------------------------------------- + + gen_bool_bypass : FOR K IN 0 TO 1 GENERATE + gen_bool_bypass : FOR L IN 0 TO 1 GENERATE + -- no repack, g_in_nof_words = g_out_nof_words = 1 + u_16_1_16_1_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 16, 1, 16, 1, c_bool(K), c_bool(L), 1, 1, c_nof_repeat, 10, 0); -- g_pkt_len > g_in_nof_words + u_16_1_16_1_len_3_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 16, 1, 16, 1, c_bool(K), c_bool(L), 1, 1, c_nof_repeat, 3, 0); -- g_pkt_len > g_in_nof_words, odd + u_16_1_16_1_len_2_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 16, 1, 16, 1, c_bool(K), c_bool(L), 1, 1, c_nof_repeat, 2, 0); -- g_pkt_len > g_in_nof_words, even + u_16_1_16_1_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 16, 1, 16, 1, c_bool(K), c_bool(L), 1, 1, c_nof_repeat, 1, 0); -- g_pkt_len = g_in_nof_words + + u_16_1_16_1_len_1_gap_1 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 16, 1, 16, 1, c_bool(K), c_bool(L), 1, 1, c_nof_repeat, 1, 1); -- g_pkt_gap > 0 + END GENERATE; + + -- no repack, g_in_nof_words = g_out_nof_words > 1 + u_16_3_16_3_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 16, 3, 16, 3, c_bool(K), c_bool(K), 1, 1, c_nof_repeat, 10, 0); + + -- g_in_nof_words > g_pack_nof_words can use always active stimuli except when g_pkt_len MOD g_in_nof_words /= 0, because then the input stage needs to flush + u_8_4_32_1_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 1, 0); -- g_pkt_len < g_in_nof_words + u_8_4_32_1_len_2_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 2, 0); -- g_pkt_len = g_in_nof_words + u_8_4_32_1_len_3_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 3, 0); -- g_pkt_len > g_in_nof_words, MOD /= 0 + u_8_4_32_1_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 10, 0); -- g_pkt_len > g_in_nof_words, MOD /= 0 + u_8_4_32_1_len_11_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 11, 0); -- g_pkt_len > g_in_nof_words, MOD /= 0 + u_8_4_32_1_len_12_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 12, 0); -- g_pkt_len > g_in_nof_words, MOD = 0 + + u_8_4_32_1_len_12_gap_2 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 4, 32, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 12, 2); -- g_pkt_gap > 0 + + -- g_in_nof_words < g_pack_nof_words will apply backpressure, because the output stage needs to output more + u_32_1_8_4_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 32, 1, 8, 4, c_bool(K), FALSE, 1, 1, c_nof_repeat, 1, 0); -- g_pkt_len = g_in_nof_words + u_32_1_8_4_len_2_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 32, 1, 8, 4, c_bool(K), FALSE, 1, 1, c_nof_repeat, 2, 0); -- g_pkt_len > g_in_nof_words + u_32_1_8_4_len_3_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 32, 1, 8, 4, c_bool(K), FALSE, 1, 1, c_nof_repeat, 3, 0); -- g_pkt_len > g_in_nof_words + u_32_1_8_4_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 32, 1, 8, 4, c_bool(K), FALSE, 1, 1, c_nof_repeat, 10, 0); -- g_pkt_len > g_in_nof_words + + u_32_1_8_4_len_11_gap_1 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 32, 1, 8, 4, c_bool(K), FALSE, 1, 1, c_nof_repeat, 11, 1); -- g_pkt_gap > 0 + + -- g_in_dat_w MOD 8 /= 0, g_in_nof_words=1 + u_14_1_8_2_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 14, 1, 8, 2, c_bool(K), FALSE, 1, 1, c_nof_repeat, 10, 0); -- repack with subsection padding, even multiple of g_in_nof_words + u_14_1_8_2_len_11_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 14, 1, 8, 2, c_bool(K), FALSE, 1, 1, c_nof_repeat, 11, 0); -- repack with subsection padding, odd multiple of g_in_nof_words + + -- g_in_dat_w MOD 8 /= 0, g_out_nof_words=1 + u_5_2_16_1_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 5, 2, 16, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 10, 0); -- repack with subsection padding, integer multiple of g_in_nof_words + u_5_2_16_1_len_11_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 5, 2, 16, 1, FALSE, c_bool(K), 1, 1, c_nof_repeat, 11, 0); -- repack with subsection padding, fractional multiple of g_in_nof_words + + -- g_in_nof_words=1, g_pack_nof_words>1 + u_8_1_4_2_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 1, 4, 2, c_bool(K), FALSE, 1, 1, c_nof_repeat, 10, 0); + u_512_1_32_16_len_1_gap_20 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 512, 1, 32, 16, c_bool(K), FALSE, 1, 1, c_nof_repeat, 1, 20); -- pack a larger header slv into g_pack_dat_w words + + -- serialize to and deserialize from g_pack_dat_w=1 bit + u_8_1_1_8_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 8, 1, 1, 8, c_bool(K), FALSE, 1, 1, c_nof_repeat, 10, 0); -- g_pack_dat_w=1 + u_32_1_1_32_len_10_gap_7 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 32, 1, 1, 32, c_bool(K), FALSE, 1, 1, c_nof_repeat, 10, 7); -- g_pack_dat_w=1 + + -- g_in_symbol_w /= 1, g_out_symbol_w /= 1 + u_20_1_8_3_symbol_1_4_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 20, 1, 8, 3, c_bool(K), FALSE, 1, 4, c_nof_repeat, 10, 0); -- no repack + u_20_1_8_3_symbol_4_1_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 20, 1, 8, 3, c_bool(K), FALSE, 4, 1, c_nof_repeat, 10, 0); -- no repack + u_20_1_8_3_symbol_4_4_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 20, 1, 8, 3, c_bool(K), FALSE, 4, 4, c_nof_repeat, 10, 0); -- no repack + + -- pack ETH/IP/UDP header, g_in_symbol_w = 8, g_out_symbol_w = 8 + u_336_1_32_11_symbol_8_8_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 336, 1, 32, 11, c_bool(K), FALSE, 8, 8, c_nof_repeat, 1, 0); --pack to 32 bit --> empty = 2 + u_336_1_64_6_symbol_8_8_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 336, 1, 64, 6, c_bool(K), FALSE, 8, 8, c_nof_repeat, 1, 0); --pack to 64 bit --> empty = 6 + END GENERATE; + + ------------------------------------------------------------------------- + -- Tests that cannot use bypass + ------------------------------------------------------------------------- + + -- g_in_nof_words > 1 and g_pack_nof_words > 1 + u_24_2_16_3_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 24, 2, 16, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 1, 0); -- g_pkt_len < g_in_nof_words + u_24_2_16_3_len_2_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 24, 2, 16, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 2, 0); -- g_pkt_len = g_in_nof_words + u_24_2_16_3_len_3_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 24, 2, 16, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 3, 0); -- g_pkt_len = fractional multiple of g_in_nof_words + u_24_2_16_3_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 24, 2, 16, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 10, 0); -- g_pkt_len = integer multiple of g_in_nof_words + u_24_2_16_3_len_11_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 24, 2, 16, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 11, 0); -- g_pkt_len = fractional multiple of g_in_nof_words + + u_24_2_16_3_len_11_gap_3 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 24, 2, 16, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 11, 3); -- g_pkt_gap > 0 + + -- g_in_dat_w MOD 8 /= 0 + u_6_5_10_3_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 1, 0); -- g_pkt_len < g_in_nof_words + u_6_5_10_3_len_2_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 2, 0); -- g_pkt_len < g_in_nof_words + u_6_5_10_3_len_3_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 3, 0); -- g_pkt_len < g_in_nof_words + u_6_5_10_3_len_4_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 4, 0); -- g_pkt_len < g_in_nof_words + u_6_5_10_3_len_5_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 5, 0); -- g_pkt_len = g_in_nof_words + u_6_5_10_3_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 10, 0); -- g_pkt_len = integer multiple of g_in_nof_words + u_6_5_10_3_len_11_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 11, 0); -- g_pkt_len = fractional multiple of g_in_nof_words + + u_6_5_10_3_len_21_gap_3 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 6, 5, 10, 3, FALSE, FALSE, 1, 1, c_nof_repeat, 21, 3); -- g_pkt_gap > 0 + + -- subsection padding, g_in_dat_w * g_in_nof_words < g_pack_dat_w * g_pack_nof_words + u_18_2_8_5_len_1_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 18, 2, 8, 5, FALSE, FALSE, 1, 1, c_nof_repeat, 1, 0); -- g_pkt_len < g_in_nof_words + u_18_2_8_5_len_2_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 18, 2, 8, 5, FALSE, FALSE, 1, 1, c_nof_repeat, 2, 0); -- g_pkt_len = g_in_nof_words + u_18_2_8_5_len_3_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 18, 2, 8, 5, FALSE, FALSE, 1, 1, c_nof_repeat, 3, 0); -- g_pkt_len = fractional multiple of g_in_nof_words + u_18_2_8_5_len_10_gap_0 : ENTITY work.tb_dp_repack_data GENERIC MAP (c_flow(I), c_flow(J), 18, 2, 8, 5, FALSE, FALSE, 1, 1, c_nof_repeat, 10, 0); -- g_pkt_len = integer multiple of g_in_nof_words + + END GENERATE; + END GENERATE; + +END tb; diff --git a/cores/copy_source_files.py b/cores/copy_source_files.py new file mode 100644 index 0000000000000000000000000000000000000000..09c28c4d0156f20179cd5a514e4bcd8738110dc2 --- /dev/null +++ b/cores/copy_source_files.py @@ -0,0 +1,63 @@ +# Author: +# . Daniel van der Schuur +# Purpose: +# . Copy the VHD files from the horizontal $RADIOHDL/libraries libraries to +# the vertical libraries located here. +# Description: +# . This script scans the hdllib.cfg files located here, then searches +# for the listed .vhd file in the horizontal library dirs, and copies it. +# Remark: +# . This script could be adapted to call 'svn move' for full migration. + +import os +from shutil import copyfile + +""" +Recursively find occurences of fname in dirname, returns them as list. +""" +def find_files(fname, dirname): + found_files = [] + for root, dirs, files in os.walk(dirname): + for file in files: + if file==fname: + found_files.append(os.path.join(root, file)) + if fname[0]=='*' and file.endswith(fname[1:]): + found_files.append(os.path.join(root, file)) + return found_files + +############################################################################### +# Find the .vhd file listed in the hdllib.cfg and copy it here +############################################################################### +if __name__== '__main__': + + for hdllib_file in find_files('hdllib.cfg', '.') : + + # Set the current target path (hdllib.cfg dir) + target_path = os.path.abspath(os.path.dirname(hdllib_file))+'/' + + # Read the hdllib.cfg file into a list + with open(hdllib_file) as f: + lines = f.readlines() + + # Get the paths to the required .VHD files + for line in lines: + if '.vhd' in line: + vhd_file = line.strip('\n').strip(' ') + + # Find the VHD file in the horizontal RadioHDL libraries + found_files = find_files(vhd_file, os.environ['RADIOHDL']+'/libraries') + + # Copy the VHD file here + if len(found_files)>1: + print 'Warning: multiple files found:' + print vhd_file, found_files + + try: + print 'Copying', found_files[0], 'to', target_path+vhd_file + copyfile(found_files[0], target_path+vhd_file) + except: + print 'Warning: could not copy', vhd_file, found_files, 'to', target_path + + + + diff --git a/cores/edit_source_files.py b/cores/edit_source_files.py new file mode 100644 index 0000000000000000000000000000000000000000..bbe54c491725c8004de8c6d134812c2c28984533 --- /dev/null +++ b/cores/edit_source_files.py @@ -0,0 +1,134 @@ +# Author: +# . Daniel van der Schuur +# Purpose: +# . Edit the (copied) source files to make them include and use vertical +# libraries instead of horizontal ones. +# Description: +# . The included horizontal libraries at the top are replaced by vertical ones; +# . The horizontal libraries named in the instantiations are replaced with +# vertical ones. + +from copy_source_files import find_files +import os + +REPLACE_LIST = [ + +# common_pkg_lib +['common_lib.common_pkg','common_pkg_lib.common_pkg'], +['work.common_pkg','common_pkg_lib.common_pkg'], + +['common_lib.common_str_pkg','common_pkg_lib.common_str_pkg'], +['work.common_str_pkg','common_pkg_lib.common_str_pkg'], + +['common_lib.common_lfsr_sequences_pkg','common_pkg_lib.common_lfsr_sequences_pkg'], +['work.common_lfsr_sequences_pkg','common_pkg_lib.common_lfsr_sequences_pkg'], + +['common_lib.tb_common_pkg','common_pkg_lib.tb_common_pkg'], +['work.tb_common_pkg','common_pkg_lib.tb_common_pkg'], + +# dp_pkg_lib +['dp_lib.dp_stream_pkg','dp_pkg_lib.dp_stream_pkg'], +['work.dp_stream_pkg','dp_pkg_lib.dp_stream_pkg'], + +['dp_lib.tb_dp_pkg','dp_pkg_lib.tb_dp_pkg'], +['work.tb_dp_pkg','dp_pkg_lib.tb_dp_pkg'], + +['dp_lib.dp_stream_stimuli','dp_pkg_lib.dp_stream_stimuli'], +['work.dp_stream_stimuli','dp_pkg_lib.dp_stream_stimuli'], + +['dp_lib.dp_stream_verify','dp_pkg_lib.dp_stream_verify'], +['work.dp_stream_verify','dp_pkg_lib.dp_stream_verify'], + +# common_components_lib +['common_lib.common_switch','common_components_lib.common_switch'], +['work.common_switch','common_components_lib.common_switch'], + +# dp_components_lib +['dp_lib.dp_hold_input','dp_components_lib.dp_hold_input'], +['work.dp_hold_input','dp_components_lib.dp_hold_input'], + +['dp_lib.dp_hold_ctrl','dp_components_lib.dp_hold_ctrl'], +['work.dp_hold_ctrl','dp_components_lib.dp_hold_ctrl'], + +['dp_lib.dp_latency_increase','dp_components_lib.dp_latency_increase'], +['work.dp_latency_increase','dp_components_lib.dp_latency_increase'], + +['dp_lib.dp_latency_adapter','dp_components_lib.dp_latency_adapter'], +['work.dp_latency_adapter','dp_components_lib.dp_latency_adapter'], + +# dp_pipeline_lib +['dp_lib.dp_pipeline','dp_pipeline_lib.dp_pipeline'], +['work.dp_pipeline','dp_pipeline_lib.dp_pipeline'], + +['dp_lib.dp_pipeline_ready','dp_pipeline_lib.dp_pipeline_ready'], +['work.dp_pipeline_ready','dp_pipeline_lib.dp_pipeline_ready'], + +# Delete any remaining references to horizontal libs +[', common_lib', ''], +['common_lib', ''], +[', dp_lib', ''], +['dp_lib', ''] + +] + +for vhd_file in find_files('*.vhd', '.'): + + core_lib = os.path.dirname(vhd_file).split('/')[-1]+'_lib' + print 'Editing', vhd_file + + # Read the VHDL file into a list + with open(vhd_file) as f: + lines = f.readlines() + newlines= [] + + # Remove strings from VHDL file + for line in lines: + newline = line + for item in REPLACE_LIST: + replace=True + if 'work.' in line: + libname=item[1].split('.')[0] + if libname==core_lib: + replace=False + if replace==True: + newline = newline.replace(item[0], item[1]) + + newlines.append(newline) + + # find all lines with '_lib' + lib_lines = [] + for line in newlines: + if '_lib' in line: + lib_lines.append(line) + # extract full library name from that line + libs = [] + for line in lib_lines: + if 'USE' in line: + libname = line.split('.')[0].strip('USE ') + if not libname==core_lib: # don't add the work lib as dependency + libs.append(libname) + elif 'ENTITY' in line: + libname = line.split('.')[0].split(' ')[-1] + if not libname==core_lib: # don't add the work lib as dependency + libs.append(libname) + + unique_libs = list(set(libs)) + lib_str = str(unique_libs).strip('[]').replace("'", '') + + # Replace the LIBRARY declaration with an updated, complete one + final_lines = [] + for line in newlines: + final_line = line + if 'LIBRARY' in line and len(unique_libs)>0: + final_line = 'LIBRARY IEEE, '+ lib_str + ';\n' + + final_lines.append(final_line.replace('\r', '')) + + + with open(vhd_file, 'wb') as f: + f.writelines(final_lines) + + f.close() + + + diff --git a/cores/readme.txt b/cores/readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..e6c81efd58fa6e4b000217f6b5d627d9556e5db8 --- /dev/null +++ b/cores/readme.txt @@ -0,0 +1,41 @@ +(Daniel van der Schuur, 17 Oct. 2018) + +Summary +======= +This directory contains vertically grouped versions of the horizontal RadioHDL +HDL libraries, intended to be uploaded to OpenCores. It cannot harm the current +horizontal libraries, yet it syncs the vertical libs with the horizontal ones, +and provides a clean migration possibility. + +Introduction +============ +This 'cores' directory contains VHDL libraries from the RadioHDL repository, but +organized differently: +- The RadioHDL repository has the HDL files ordered in horizontal libraries, + grouped together based on hierarchical layer. E.g. all technology-specific + HDL files are in the 'technology'-library, and all 'common' HDL files are + grouped together in one very large library as well. +- This cores repository contains the same (though slightly modifed) HDL files, + but grouped together vertically. For example, the dp_mux and dp_demux belong + to the very large horizontal 'dp'-library, but here they belong to the + 'dp_mux' library. + +The original horizontal organization is however still maintained as directory +structure; e.g. all vertical 'dp' cores are in the 'dp' directory. + +copy_source_files.py +==================== +This script copies the source files from the horizontal RadioHDL repository, +with the purpose of: +1) Keeping this 'cores' library in sync with our 'official' RadioHDL library, + without risk of doing damage to it. +2) Provide option to migrate to this vertical for by calling 'svn move' + +edit_source_files.py +==================== +This script edits the copied files such that: +1) the included horizontal libraries at the top are replaced by vertical ones; +2) the horizontal libraries named in the instantiations are replaced with + vertical ones. + +