diff --git a/casper_correlator/correlator_pkg.vhd b/casper_correlator/correlator_pkg.vhd new file mode 100644 index 00000000..c46d9af0 --- /dev/null +++ b/casper_correlator/correlator_pkg.vhd @@ -0,0 +1,50 @@ +library ieee; +use ieee.std_logic_1164.all; + +package correlator_pkg is + + CONSTANT c_cross_mult_nof_input_streams : NATURAL := 12; + CONSTANT c_cross_mult_aggregation_per_stream : NATURAL := 3; + CONSTANT c_cross_mult_input_bit_width : NATURAL := 5; + CONSTANT c_cross_mult_output_bit_width : NATURAL := 11; + + CONSTANT c_cross_mult_input_cbit_width : NATURAL := c_cross_mult_input_bit_width * 2; --COMPLEX + CONSTANT c_cross_mult_output_cbit_width : NATURAL := c_cross_mult_output_bit_width * 2; --COMPLEX + CONSTANT c_cross_mult_total_streams : NATURAL := c_cross_mult_nof_input_streams * c_cross_mult_aggregation_per_stream; + CONSTANT c_cross_mult_nof_output_streams :NATURAL := (c_cross_mult_nof_input_streams+1)*c_cross_mult_nof_input_streams / 2; + CONSTANT c_cross_mult_nof_cmults : NATURAL := c_cross_mult_nof_output_streams * c_cross_mult_aggregation_per_stream; + + TYPE s_cross_mult_din is ARRAY (0 TO c_cross_mult_nof_input_streams - 1) OF std_logic_vector((c_cross_mult_aggregation_per_stream * c_cross_mult_input_cbit_width) - 1 downto 0); + TYPE s_cross_mult_out_bus_expand is ARRAY (0 TO (c_cross_mult_total_streams) - 1) OF std_logic_vector(c_cross_mult_input_cbit_width - 1 downto 0); + TYPE s_cross_mult_cmult_in is ARRAY(0 TO (c_cross_mult_nof_cmults - 1)) OF std_logic_vector(c_cross_mult_input_cbit_width - 1 downto 0); + TYPE s_cross_mult_out is ARRAY (0 TO c_cross_mult_nof_output_streams - 1) OF std_logic_vector((c_cross_mult_aggregation_per_stream * c_cross_mult_output_cbit_width) - 1 downto 0); + TYPE s_cross_mult_cmult_out is ARRAY (0 TO c_cross_mult_nof_cmults - 1) OF std_logic_vector(c_cross_mult_output_cbit_width - 1 DOWNTO 0); + TYPE s_cmult_inpt is ARRAY(0 TO 1) OF INTEGER RANGE 0 TO c_cross_mult_total_streams - 1; + TYPE s_cmult_map is ARRAY(0 TO c_cross_mult_nof_cmults - 1) OF s_cmult_inpt; + + FUNCTION gen_inpt_to_mult_mapping(nof_aggregation : NATURAL; nof_streams : NATURAL) return s_cmult_map; + +end package correlator_pkg; + +package body correlator_pkg is + +function gen_inpt_to_mult_mapping(nof_aggregation : NATURAL; nof_streams : NATURAL) + return s_cmult_map is + variable mapping : s_cmult_map; + variable mult : INTEGER := 0; + variable aa : INTEGER := 0; + begin + mult := 0; + FOR a IN 0 TO nof_aggregation - 1 LOOP + aa := a * nof_streams; + FOR s IN aa TO (aa + nof_streams - 1) LOOP + FOR ss IN s TO (aa + nof_streams - 1) LOOP + mapping(mult) := (s, ss); + mult := mult + 1; + END LOOP; + END LOOP; + END LOOP; + return mapping; + end function gen_inpt_to_mult_mapping; + +end package body correlator_pkg; diff --git a/casper_correlator/cross_multiplier.vhd b/casper_correlator/cross_multiplier.vhd new file mode 100644 index 00000000..1b8e0b2f --- /dev/null +++ b/casper_correlator/cross_multiplier.vhd @@ -0,0 +1,85 @@ +library ieee, common_pkg_lib, casper_multiplier_lib; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use common_pkg_lib.common_pkg.all; +use work.correlator_pkg.all; + +entity cross_multiplier is + generic( + g_use_gauss : BOOLEAN := FALSE; + g_use_dsp : BOOLEAN := TRUE; + g_pipeline_input : NATURAL := 1; --! 0 or 1 + g_pipeline_product : NATURAL := 1; --! 0 or 1 + g_pipeline_adder : NATURAL := 1; --! 0 or 1 + g_pipeline_round : NATURAL := 1; --! 0 or 1 + g_pipeline_output : NATURAL := 0; --! >= 0 + ovflw_behav : BOOLEAN := FALSE; + quant_behav : NATURAL := 0 + ); + port( + clk : in std_logic; + ce : in std_logic; + sync_in : in std_logic; + sync_out : out std_logic; + din : in s_cross_mult_din; + dout : out s_cross_mult_out + ); +end entity cross_multiplier; + +architecture RTL of cross_multiplier is + + signal s_out_bus_expand : s_cross_mult_out_bus_expand := (others => (others => '0')); + signal s_out_cmults : s_cross_mult_cmult_out := (others => (others => '0')); + signal s_a_cmult_in, s_b_cmult_in : s_cross_mult_cmult_in := (others => (others => '0')); + signal s_out : s_cross_mult_out := (others=>(others=>'0')); + + signal s_cmult_input_map : s_cmult_map := gen_inpt_to_mult_mapping(c_cross_mult_aggregation_per_stream, c_cross_mult_nof_input_streams); + +begin + + gen_expand : FOR j IN 0 TO c_cross_mult_aggregation_per_stream - 1 GENERATE --SPLIT the aggregation + gen_bus_expand : FOR i IN 0 TO c_cross_mult_nof_input_streams - 1 GENERATE -- FOR each stream + s_out_bus_expand(c_cross_mult_nof_input_streams*j + i) <= din(i)((j + 1) * c_cross_mult_input_cbit_width - 1 DOWNTO j * c_cross_mult_input_cbit_width); + END GENERATE; + END GENERATE; + + gen_cmult : FOR m IN 0 TO c_cross_mult_nof_cmults - 1 GENERATE + s_a_cmult_in(m) <= s_out_bus_expand(s_cmult_input_map(m)(0)); + s_b_cmult_in(m) <= s_out_bus_expand(s_cmult_input_map(m)(1)); + cmult_inst : entity casper_multiplier_lib.cmult + generic map( + g_use_ip => FALSE, + g_a_bw => c_cross_mult_input_bit_width, + g_b_bw => c_cross_mult_input_bit_width, + g_ab_bw => c_cross_mult_output_bit_width, + g_conjugate_b => TRUE, + g_use_gauss => g_use_gauss, + g_use_dsp => g_use_dsp, + g_round_method => quant_behav, + g_ovflw_method => ovflw_behav, + g_pipeline_input => g_pipeline_input, + g_pipeline_product => g_pipeline_product, + g_pipeline_adder => g_pipeline_adder, + g_pipeline_round => g_pipeline_round, + g_pipeline_output => g_pipeline_output + ) + port map( + clk => clk, + ce => ce, + rst => '0', + in_a => s_a_cmult_in(m), + in_b => s_b_cmult_in(m), + in_val => '1', + out_ab => s_out_cmults(m), + out_val => open + ); + + END GENERATE; + + gen_pack : FOR n IN 0 TO c_cross_mult_nof_output_streams - 1 GENERATE + gen_per_aggregation : FOR p IN 0 TO c_cross_mult_aggregation_per_stream - 1 GENERATE + s_out(n)((p+1)*c_cross_mult_output_cbit_width - 1 DOWNTO p*c_cross_mult_output_cbit_width) <= s_out_cmults(2*n + p)(c_cross_mult_output_cbit_width -1 DOWNTO 0); + END GENERATE; + END GENERATE; + dout <= s_out; +end architecture RTL; diff --git a/casper_correlator/run.py b/casper_correlator/run.py new file mode 100644 index 00000000..adf2184d --- /dev/null +++ b/casper_correlator/run.py @@ -0,0 +1,195 @@ +#Author: Talon Myburgh +#Company: Mydon Solutions + +from vunit import VUnit +import numpy as np +import itertools +import glob +from os.path import dirname, join, abspath + +# Function for package mangling. +def manglePkg(file_name, line_number, new_line): + with open(file_name, 'r') as file: + lines = file.readlines() + lines[line_number] = new_line + with open(file_name, 'w') as file: + lines = file.writelines(lines) + + +def mapping(aggre, inpts): + mult_map = {} + mult = 0 + for a in range(aggre): + aa = a * inpts + for s in range(aa, aa + inpts): + for ss in range(s, aa + inpts): + mult_map[mult] = (s, ss) + mult += 1 + return mult_map + +def cross_mult(c_val_dict): + ans_dict = {} + for test, val in c_val_dict.items(): + tests = test.split(':') + aggregations = int(tests[1]) + streams = int(tests[0]) + nof_cmults = aggregations * int(((streams +1)*streams)/2) + answers = np.zeros(nof_cmults,dtype=np.complex64) + mult_map = mapping(aggregations,streams) + for ans in range(nof_cmults): + a = val[mult_map[ans][0]] + b = np.conj(val[mult_map[ans][1]]) + answers[ans] = a * b + ans_dict[test] = answers + return ans_dict + +def turn_cint_to_int(number:complex, cin_bwidth:int): + if number.real >= 0: + real = int(number.real) + else: + real = int(number.real) + 2**32 + + if number.imag >= 0: + imag = int(number.imag) + else: + imag = int(number.imag) + 2**32 + real_binary = bin(real & (2**cin_bwidth - 1))[2:].zfill(cin_bwidth) + imag_binary = bin(imag & (2**cin_bwidth - 1))[2:].zfill(cin_bwidth) + binary = real_binary + imag_binary + return int(binary, 2) + +def split_int_gen_complexint(number, bitwidth): + if number < 0: + number += 2**32 + binary = bin(number & (2**bitwidth-1))[2:].zfill(bitwidth) + first_half = binary[:bitwidth//2] + second_half = binary[bitwidth//2:] + if first_half: + first_int = int(first_half, 2) + if first_int >= 2**(bitwidth//2-1): + first_int -= 2**(bitwidth//2) + else: + first_int = 0 + if second_half: + second_int = int(second_half, 2) + if second_int >= 2**(bitwidth//2-1): + second_int -= 2**(bitwidth//2) + else: + second_int = 0 + return first_int + second_int*1j + +# Create VUnit instance by parsing command line arguments +vu = VUnit.from_argv() +vu.add_vhdl_builtins() +script_dir = abspath(dirname(__file__)) + +aggregations = np.random.randint(2 , 8, 1) +streams = np.random.randint(2, 20, 1) +inpt_bitwidths = int(np.random.randint(2, 10, 1)) + +package_vals = [f' CONSTANT c_cross_mult_nof_input_streams : NATURAL := {int(streams)};\n', +f' CONSTANT c_cross_mult_aggregation_per_stream : NATURAL := {int(aggregations)};\n', +f' CONSTANT c_cross_mult_input_bit_width : NATURAL := {int(inpt_bitwidths)};\n', +f' CONSTANT c_cross_mult_output_bit_width : NATURAL := {2*int(inpt_bitwidths)+1};\n'] + +manglePkg(join(script_dir,'correlator_pkg.vhd'), slice(5,9),package_vals) + +# COMMON COMPONENTS Library +common_components_lib = vu.add_library("common_components_lib",allow_duplicate=True) +common_components_lib.add_source_files(script_dir + "/../common_components/common_pipeline.vhd") +common_components_lib.add_source_files(script_dir + "/../common_components/common_pipeline_sl.vhd") + +# COMMON PACKAGE Library +common_pkg_lib = vu.add_library("common_pkg_lib",allow_duplicate = True) +common_pkg_lib.add_source_files(script_dir + "/../common_pkg/*.vhd") +common_pkg_lib.add_source_files(script_dir + "/../common_pkg/tb_common_pkg.vhd") + +# TECHNOLOGY Library +technology_lib = vu.add_library("technology_lib",allow_duplicate = True) +technology_lib.add_source_files(script_dir + "/../technology/technology_select_pkg.vhd") + +# XPM Multiplier library +ip_xpm_mult_lib = vu.add_library("ip_xpm_mult_lib", allow_duplicate=True) +ip_xpm_mult_lib.add_source_files(script_dir + "/../ip_xpm/mult/*.vhd") + +# STRATIXIV Multiplier library +ip_stratixiv_mult_lib = vu.add_library("ip_stratixiv_mult_lib", allow_duplicate=True) +ip_stratixiv_mult_lib.add_source_files(script_dir + "/../ip_stratixiv/mult/*rtl.vhd") + +# CASPER MUlTIPLIER Library +casper_multiplier_lib = vu.add_library("casper_multiplier_lib") +casper_multiplier_lib.add_source_file(join(script_dir, "../casper_multiplier/tech_mult_component.vhd")) +tech_complex_mult = casper_multiplier_lib.add_source_file(join(script_dir, "../casper_multiplier/tech_complex_mult.vhd")) +casper_multiplier_lib.add_source_file(join(script_dir, "../casper_multiplier/tech_agilex_versal_cmult.vhd")) +casper_multiplier_lib.add_source_file(join(script_dir, "../casper_multiplier/common_complex_mult.vhd")) +casper_multiplier_lib.add_source_file(join(script_dir, "../casper_multiplier/cmult.vhd")) + +# CASPER CORRELATOR Library +casper_correlator_lib = vu.add_library("casper_correlator_lib",allow_duplicate=True) +casper_correlator_lib.add_source_files(join(script_dir,'*.vhd')) + +TB_GENERATED = casper_correlator_lib.test_bench("tb_tb_vu_cross_multiplier") + +value_dict = {} +# Here we generate the test values. Note that these values are all taken as complex where real and imag are join (i.e. 85 = 5+5j) +for s, a in itertools.product(streams, aggregations): + print(f""" + Generating test for values: + bitwidth = {inpt_bitwidths} + nof streams = {s} + nof aggregations = {a}""") + max_val = int(2**(2*inpt_bitwidths) -1) + min_val = 0 + value_dict[f"{s}:{a}"] = np.random.randint(min_val, max_val, size=(s,a)) + +generics_dict = {} +#Turn this into strings so they can be passed to generic g_values +for key,val in value_dict.items(): + strval = ', '.join(map(str, val.flatten(order = 'F'))) + generics_dict[key] = strval + +#Here we must construct the complex values for testing +c_dict = {} +for key,val in value_dict.items(): + values = val.flatten(order = 'F') #now we've flattened across aggregations which is how the module works and what the mapping expects + # print(values) + c_val = np.zeros(values.shape, dtype=np.complex64) + for i,v in enumerate(values): + c_val[i] = split_int_gen_complexint(int(v),2*int(inpt_bitwidths)) + c_dict[key] = c_val + +cross_mult_result = cross_mult(c_dict) + +result_dict = {} +for test, val in cross_mult_result.items(): + tests = test.split(':') + stream = int(tests[0]) + aggre = int(tests[1]) + int_val = np.zeros(val.shape,dtype=np.int64) + for i,v in enumerate(val): + int_val[i] = turn_cint_to_int(v ,2*int(inpt_bitwidths) + 1) + result_dict[test] = int_val#.reshape(int_val.size//aggre,aggre,order='F') + +generics_result = {} +#convert result dict to set of strings for generics: +for key, result in result_dict.items(): + strval = ', '.join(map(str, result))#.flatten())) + generics_result[key] = strval + +for key, val in generics_dict.items(): + vals = key.split(':') + streams = int(vals[0]) + aggregations = int(vals[1]) + config_name = f"Streams={streams},Aggregations={aggregations},inputbitwidth={inpt_bitwidths}" + TB_GENERATED.add_config( + name = config_name, + generics = { + 'g_values' : val, + 'g_results' : generics_result[key] + } + ) + +# RUN +vu.set_compile_option("ghdl.a_flags", ["-frelaxed", "-Wno-hide"]) +vu.set_sim_option("ghdl.elab_flags", ["-frelaxed","--syn-binding"]) +vu.main() diff --git a/casper_correlator/tb_cross_multiplier.vhd b/casper_correlator/tb_cross_multiplier.vhd new file mode 100644 index 00000000..125e9709 --- /dev/null +++ b/casper_correlator/tb_cross_multiplier.vhd @@ -0,0 +1,121 @@ +-- A VHDL testbench for cross_multiplier.vhd. +-- @author: Talon Myburgh +-- @company: Mydon Solutions + +LIBRARY IEEE, common_pkg_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_pkg_lib.common_pkg.ALL; +USE STD.TEXTIO.ALL; +USE work.correlator_pkg.all; + +entity tb_cross_multiplier is + generic( + g_values : t_natural_matrix(0 TO c_cross_mult_nof_input_streams - 1,0 TO c_cross_mult_aggregation_per_stream - 1); --:= ((11, 27), (10, 16), (-26, 26)); + g_results : t_natural_matrix (0 TO c_cross_mult_nof_output_streams - 1, 0 TO c_cross_mult_aggregation_per_stream - 1)-- := ((247808, 225280), (137205, 204800), (124918, 75776), (51200, 163840), (61440, 524288), (196608, 73728)) + ); + port( + o_clk : out std_logic; + o_tb_end : out std_logic; + o_test_msg : out STRING(1 to 200); + o_test_pass : out BOOLEAN := True + ); +end entity tb_cross_multiplier; + +architecture rtl of tb_cross_multiplier is + + CONSTANT clk_period : TIME := 10 ns; + CONSTANT c_pipeline_input : NATURAL := 1; + CONSTANT c_pipeline_product : NATURAL := 1; + CONSTANT c_pipeline_adder : NATURAL := 1; + CONSTANT c_pipeline_round : NATURAL := 1; + CONSTANT c_pipeline_output : NATURAL := 0; + + SIGNAL clk : STD_LOGIC := '0'; + SIGNAL ce : STD_LOGIC; + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL din : s_cross_mult_din := (others => (others => '0')); + SIGNAL dout : s_cross_mult_out := (others => (others => '0')); + SIGNAL exp_dout : s_cross_mult_out := (others => (others => '0')); + + function populate_din + return s_cross_mult_din IS + VARIABLE pop_input : s_cross_mult_din; + VARIABLE max_width : NATURAL := c_cross_mult_aggregation_per_stream * c_cross_mult_input_cbit_width; + BEGIN + FOR i IN 0 TO c_cross_mult_nof_input_streams - 1 LOOP + FOR j IN 0 TO c_cross_mult_aggregation_per_stream - 1 LOOP + pop_input(i)(max_width - c_cross_mult_input_cbit_width*j - 1 DOWNTO max_width - c_cross_mult_input_cbit_width*(j+1)) := TO_UVEC(g_values(i, c_cross_mult_aggregation_per_stream-j-1),c_cross_mult_input_cbit_width); + END LOOP; + END LOOP; + RETURN pop_input; + END FUNCTION; + + function populate_exp_dout + return s_cross_mult_out IS + VARIABLE pop_output : s_cross_mult_out; + BEGIN + FOR i IN 0 TO c_cross_mult_nof_output_streams - 1 LOOP + FOR j IN 0 TO c_cross_mult_aggregation_per_stream - 1 LOOP + pop_output(i)((j+1)*c_cross_mult_output_cbit_width -1 DOWNTO j*c_cross_mult_output_cbit_width) := TO_UVEC(g_results(i,j),c_cross_mult_output_cbit_width); + END LOOP; + END LOOP; + RETURN pop_output; + END FUNCTION; + +begin + clk <= NOT clk OR tb_end AFTER clk_period / 2; + + o_clk <= clk; + o_tb_end <= tb_end; + + --------------------------------------------------------------------- + -- Stimulus process + --------------------------------------------------------------------- + p_stimuli_verify : PROCESS + VARIABLE v_test_msg : STRING(1 to o_test_msg'length) := (OTHERS => '.'); + VARIABLE v_test_pass : BOOLEAN := True; + BEGIN + WAIT UNTIL rising_edge(clk); + WAIT FOR 5 * clk_period; + din <= populate_din; + exp_dout <= populate_exp_dout; + ce <= '1'; + WAIT FOR (c_pipeline_input + c_pipeline_product + c_pipeline_adder + c_pipeline_round + c_pipeline_output + 1)*clk_period; + FOR i IN 0 TO c_cross_mult_nof_output_streams - 1 LOOP + v_test_pass := v_test_pass and (dout(i) = exp_dout(i)); + if not v_test_pass then + v_test_msg := pad("4DSP RE cmult wrong RTL result#" & integer'image(i) & ", expected: " & to_hstring(exp_dout(i)) & " but got: " & to_hstring(dout(i)), o_test_msg'length, '.'); + o_test_msg <= v_test_msg; + report "Error: " & v_test_msg severity failure; + end if; + END LOOP; + o_test_msg <= v_test_msg; + o_test_pass <= v_test_pass; + tb_end <= '1'; + WAIT; + + END PROCESS; +--------------------------------------------------------------------- +-- cross multiplier module +--------------------------------------------------------------------- +cross_mult : entity work.cross_multiplier + generic map( + g_use_gauss => FALSE, + g_use_dsp => TRUE, + g_pipeline_input => c_pipeline_input, + g_pipeline_product => c_pipeline_product, + g_pipeline_adder => c_pipeline_adder, + g_pipeline_round => c_pipeline_round, + g_pipeline_output => c_pipeline_output, + ovflw_behav => FALSE, + quant_behav => 0 + ) + port map( + clk => clk, + ce => ce, + din => din, + dout => dout + ); + +end architecture; \ No newline at end of file diff --git a/casper_correlator/tb_tb_vu_cross_multiplier.vhd b/casper_correlator/tb_tb_vu_cross_multiplier.vhd new file mode 100644 index 00000000..43ea02d6 --- /dev/null +++ b/casper_correlator/tb_tb_vu_cross_multiplier.vhd @@ -0,0 +1,98 @@ +-- @author: Talon Myburgh +-- @company: Mydon Solutions + +library ieee, common_pkg_lib, vunit_lib; +use IEEE.std_logic_1164.all; +use common_pkg_lib.common_pkg.all; +use work.correlator_pkg.all; +context vunit_lib.vunit_context; + +entity tb_tb_vu_cross_multiplier is + GENERIC( + g_values : string; -- CSV matrix of integers where ; demarcate new rows + g_results : string; + runner_cfg : string + ); +end tb_tb_vu_cross_multiplier; + +architecture tb of tb_tb_vu_cross_multiplier is + + SIGNAL rst : STD_LOGIC := '0'; + SIGNAL clk : STD_LOGIC; + SIGNAL tb_end : STD_LOGIC; + SIGNAL test_msg : STRING(1 to 200); + SIGNAL test_pass : BOOLEAN; + + impure function decodeinpt(encoded_natural_matrix : string) return t_natural_matrix is + VARIABLE parts : lines_t := split(encoded_natural_matrix, ","); + VARIABLE row : NATURAL := 0; + VARIABLE col : NATURAL := 0; + VARIABLE new_row : BOOLEAN := FALSE; + variable return_value : t_natural_matrix(0 TO c_cross_mult_nof_input_streams - 1,0 TO c_cross_mult_aggregation_per_stream - 1); + begin + + for i in parts'range loop + + return_value(row, col) := integer'value(parts(i).all); + new_row := (col = c_cross_mult_aggregation_per_stream - 1) AND (row /= c_cross_mult_nof_input_streams - 1); + if new_row then + row := row + 1; + col := 0; + else + col := col + 1; + end if; + end loop; + return return_value; + end; + impure function decodeoutpt(encoded_natural_matrix : string) return t_natural_matrix is + VARIABLE parts : lines_t := split(encoded_natural_matrix, ","); + VARIABLE row : NATURAL := 0; + VARIABLE col : NATURAL := 0; + VARIABLE new_row : BOOLEAN := FALSE; + variable return_value : t_natural_matrix (0 TO c_cross_mult_nof_output_streams - 1, 0 TO c_cross_mult_aggregation_per_stream - 1); + begin + for i in parts'range loop + + return_value(row, col) := integer'value(parts(i).all); + new_row := (col = c_cross_mult_aggregation_per_stream - 1) AND (row /= c_cross_mult_nof_output_streams - 1); + if new_row then + row := row + 1; + col := 0; + else + col := col + 1; + end if; + end loop; + return return_value; + end; + + CONSTANT c_values : t_natural_matrix := decodeinpt(g_values); + CONSTANT c_results : t_natural_matrix := decodeoutpt(g_results); +BEGIN + tb_ut : ENTITY work.tb_cross_multiplier + GENERIC MAP( + g_values => c_values, + g_results => c_results + ) + PORT MAP( + o_clk => clk, + o_tb_end => tb_end, + o_test_msg => test_msg, + o_test_pass => test_pass + ); + + p_vunit : PROCESS + BEGIN + test_runner_setup(runner, runner_cfg); + wait until tb_end = '1'; + test_runner_cleanup(runner); + wait; + END PROCESS; + + p_verify : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + check(test_pass, "Test Failed: " & test_msg); + END IF; + + END PROCESS; +END tb; \ No newline at end of file diff --git a/casper_correlator/top_cross_mult.vhd b/casper_correlator/top_cross_mult.vhd new file mode 100644 index 00000000..8ca3c8c3 --- /dev/null +++ b/casper_correlator/top_cross_mult.vhd @@ -0,0 +1,66 @@ +library ieee, common_pkg_lib, casper_multiplier_lib; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use common_pkg_lib.common_pkg.all; +use work.correlator_pkg.all; + +entity cross_multiplier_top is + generic( + g_use_gauss : BOOLEAN := FALSE; + g_use_dsp : BOOLEAN := TRUE; + g_pipeline_input : NATURAL := 1; --! 0 or 1 + g_pipeline_product : NATURAL := 1; --! 0 or 1 + g_pipeline_adder : NATURAL := 1; --! 0 or 1 + g_pipeline_round : NATURAL := 1; --! 0 or 1 + g_pipeline_output : NATURAL := 0; --! >= 0 + ovflw_behav : BOOLEAN := FALSE; + quant_behav : NATURAL := 0 + ); + port( + clk : in std_logic; + ce : in std_logic; + sync_in : in std_logic; + sync_out : out std_logic; + din_0 : in STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_input_cbit_width) - 1 downto 0); + din_1 : in STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_input_cbit_width) - 1 downto 0); + dout_0 : out STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_output_cbit_width) - 1 downto 0); + dout_1 : out STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_output_cbit_width) - 1 downto 0); + dout_2 : out STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_output_cbit_width) - 1 downto 0) + ); + +end entity cross_multiplier_top; + +architecture RTL of cross_multiplier_top is + + SIGNAL s_din : s_cross_mult_din; + SIGNAL s_dout : s_cross_mult_out; +begin + + u_cross_mult : entity work.cross_multiplier + generic map ( + g_use_gauss => g_use_gauss, + g_use_dsp => g_use_dsp, + g_pipeline_input => g_pipeline_input, + g_pipeline_product => g_pipeline_product, + g_pipeline_adder => g_pipeline_adder, + g_pipeline_round => g_pipeline_round, + g_pipeline_output => g_pipeline_output, + ovflw_behav => ovflw_behav, + quant_behav => quant_behav + ) + port map ( + clk => clk, + ce => ce, + sync_in => sync_in, + sync_out => sync_out, + din => s_din, + dout => s_dout + ); + + s_din(0) <= din_0; + s_din(1) <= din_1; + dout_0 <= s_dout(0); + dout_1 <= s_dout(1); + dout_2 <= s_dout(2); + +end architecture; \ No newline at end of file diff --git a/casper_multiplier/cmult.vhd b/casper_multiplier/cmult.vhd new file mode 100644 index 00000000..06435e14 --- /dev/null +++ b/casper_multiplier/cmult.vhd @@ -0,0 +1,122 @@ +library ieee, common_pkg_lib, casper_multiplier_lib; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use common_pkg_lib.common_pkg.all; + +entity cmult is + generic( + g_use_ip : BOOLEAN := FALSE; -- Use IP component when TRUE, else rtl component when FALSE + g_a_bw : NATURAL := 18; + g_b_bw : NATURAL := 18; + g_ab_bw : NATURAL := 36; + g_conjugate_b : BOOLEAN := FALSE; + g_use_gauss : BOOLEAN := FALSE; --! Use 4DSP variant or 3DSP variant + g_use_dsp : BOOLEAN := TRUE; + g_round_method : NATURAL := 1; + g_ovflw_method : BOOLEAN := FALSE; + g_pipeline_input : NATURAL := 1; --! 0 or 1 + g_pipeline_product : NATURAL := 0; --! 0 or 1 + g_pipeline_adder : NATURAL := 1; --! 0 or 1 + g_pipeline_round : NATURAL := 1; --! 0 or 1 + g_pipeline_output : NATURAL := 0 --! >= 0 + ); + port( + clk : in std_logic; + ce : in std_logic; + rst : in std_logic := '0'; + in_a : in std_logic_vector(2 * g_a_bw - 1 downto 0); + in_b : in std_logic_vector(2 * g_b_bw - 1 downto 0); + in_val : in std_logic := '1'; + out_ab : out std_logic_vector(2 * g_ab_bw - 1 downto 0); + out_val : out std_logic + ); +end entity cmult; + +architecture Behaviour of cmult is + + constant round_mode : t_rounding_mode := t_rounding_mode'val(g_round_method); + constant c_prod_w : NATURAL := g_a_bw + g_b_bw + 1; + constant c_a_complex_w : NATURAL := g_a_bw * 2; + constant c_b_complex_w : NATURAL := g_b_bw * 2; + constant c_use_variant : STRING := sel_a_b(g_use_gauss, "3DSP", "4DSP"); + constant c_use_dsp : STRING := sel_a_b(g_use_dsp, "YES", "NO"); + + signal s_in_a_re : std_logic_vector(g_a_bw - 1 DOWNTO 0); + signal s_in_a_im : std_logic_vector(g_a_bw - 1 DOWNTO 0); + signal s_in_b_re : std_logic_vector(g_b_bw - 1 DOWNTO 0); + signal s_in_b_im : std_logic_vector(g_b_bw - 1 DOWNTO 0); + + signal s_out_ab_re : std_logic_vector(c_prod_w - 1 DOWNTO 0); + signal s_out_ab_im : std_logic_vector(c_prod_w - 1 DOWNTO 0); + signal s_round_re : std_logic_vector(g_ab_bw - 1 DOWNTO 0); + signal s_round_im : std_logic_vector(g_ab_bw - 1 DOWNTO 0); + +begin + ------------------------------------------------------ + -- Split A and B into separate re/im parts + ------------------------------------------------------ + s_in_a_re <= in_a(c_a_complex_w - 1 DOWNTO g_a_bw); + s_in_a_im <= in_a(g_a_bw - 1 DOWNTO 0); + s_in_b_re <= in_b(c_b_complex_w - 1 DOWNTO g_b_bw); + s_in_b_im <= in_b(g_b_bw - 1 DOWNTO 0); + + ------------------------------------------------------ + -- Feed into common_complex_mult + ------------------------------------------------------ + common_complex_mult_inst : entity casper_multiplier_lib.common_complex_mult + generic map( + g_use_ip => g_use_ip, + g_use_variant => c_use_variant, + g_use_dsp => c_use_dsp, + g_in_a_w => g_a_bw, + g_in_b_w => g_b_bw, + g_out_p_w => c_prod_w, + g_conjugate_b => g_conjugate_b, + g_pipeline_input => g_pipeline_input, + g_pipeline_product => g_pipeline_product, + g_pipeline_adder => g_pipeline_adder, + g_pipeline_output => g_pipeline_output + ) + port map( + rst => rst, + clk => clk, + clken => ce, + in_ar => s_in_a_re, + in_ai => s_in_a_im, + in_br => s_in_b_re, + in_bi => s_in_b_im, + in_val => in_val, + out_pr => s_out_ab_re, + out_pi => s_out_ab_im, + out_val => out_val + ); + + ------------------------------------------------------------------------------ + -- Round cmult output output + ------------------------------------------------------------------------------ + + gen_resize : if g_ab_bw >= c_prod_w GENERATE + gen_comb_resize : if g_pipeline_round = 0 generate + s_round_re <= RESIZE_SVEC(s_out_ab_re, g_ab_bw); + s_round_im <= RESIZE_SVEC(s_out_ab_im, g_ab_bw); + end generate; + gen_reg_resize : if g_pipeline_round = 1 generate + s_round_re <= RESIZE_SVEC(s_out_ab_re, g_ab_bw) when rising_edge(clk); + s_round_im <= RESIZE_SVEC(s_out_ab_im, g_ab_bw) when rising_edge(clk); + end generate; + END GENERATE; + + gen_round : if g_ab_bw < c_prod_w GENERATE + gen_comb_round : if g_pipeline_round = 0 generate + s_round_re <= s_round(vec => s_out_ab_re, n => c_prod_w - g_ab_bw, clip => g_ovflw_method, round_style => round_mode); + s_round_im <= s_round(vec => s_out_ab_im, n => c_prod_w - g_ab_bw, clip => g_ovflw_method, round_style => round_mode); + end generate; + gen_reg_round : if g_pipeline_round = 1 generate + s_round_re <= s_round(vec => s_out_ab_re, n => c_prod_w - g_ab_bw, clip => g_ovflw_method, round_style => round_mode) when rising_edge(clk); + s_round_im <= s_round(vec => s_out_ab_im, n => c_prod_w - g_ab_bw, clip => g_ovflw_method, round_style => round_mode) when rising_edge(clk); + end generate; + END GENERATE; + + out_ab(2 * g_ab_bw - 1 DOWNTO 0) <= std_logic_vector(unsigned(s_round_re(g_ab_bw - 1 DOWNTO 0)) & unsigned(s_round_im(g_ab_bw - 1 DOWNTO 0))); +end architecture Behaviour; + diff --git a/casper_multiplier/run.py b/casper_multiplier/run.py index e880a3db..6704361d 100644 --- a/casper_multiplier/run.py +++ b/casper_multiplier/run.py @@ -2,7 +2,7 @@ from itertools import product from random import sample, choice import glob -from os.path import dirname +from os.path import dirname, abspath # Create VUnit instance by parsing command line arguments vu = VUnit.from_argv() vu.add_vhdl_builtins() @@ -63,7 +63,7 @@ def generate_tests(obj, in_dat_w, inp_pipeline,product_pipeline, out_pipeline, c g_pipeline_output=out_pipeline, g_a_val_min=a_v_min, g_a_val_max=a_v_max, g_b_val_min=b_v_min, g_b_val_max=b_v_max) ) -script_dir = dirname(__file__) +script_dir = abspath(dirname(__file__)) # CASPER MUlTIPLIER Library casper_multiplier_lib = vu.add_library("casper_multiplier_lib", allow_duplicate=True) diff --git a/common_pkg/common_pkg.vhd b/common_pkg/common_pkg.vhd index 6a8183e3..2d604a67 100644 --- a/common_pkg/common_pkg.vhd +++ b/common_pkg/common_pkg.vhd @@ -133,6 +133,7 @@ PACKAGE common_pkg IS 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_natural_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF NATURAL; 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; diff --git a/docs/source/index.rst b/docs/source/index.rst index eef7ca83..11be3b3d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,6 +12,7 @@ IP Cores: accumulators delay flow_control + multiplier misc r2sdf_fft wideband_fft diff --git a/docs/source/multipliers.rst b/docs/source/multipliers.rst new file mode 100644 index 00000000..b55ca678 --- /dev/null +++ b/docs/source/multipliers.rst @@ -0,0 +1,86 @@ +################### +Multiplier Library +################### +.. _multiplier: + +******* +Purpose +******* +.. _multiplier_purpose: + +The multiplier library contains the cmult (complex multiplication) HDL modules wrapped for Simulink. All CASPER +multiplier modules have been bundled into the cmult block with the exception of the cmult BRAM modules which +supports small bitwidths alone. + +============== +Cmult +============== +This cmult wraps the ASTRON common_complex_mult module. As this does not do the data replication that the original CASPER +complex multiplier does, this complex multiplier requires a latency of at least 4 (cumulatively). + +----- +Ports +----- ++----------------+--------------------------+---------------------------+----------------------------------------------------------------+ +| Signal | Type | Size | Description | ++================+==========================+===========================+================================================================+ +| rst | std_logic | 1 | This port drives the reset of all pipelines in the block. | ++----------------+--------------------------+---------------------------+----------------------------------------------------------------+ +| in_a | std_logic_vector | 2*(a_bw) | The first complex vector input. a_bw is the bitwidth specified | +| | | | for a in the parameter list. Real part occupies MSB while imag | +| | | | part occupies LSB. | ++----------------+--------------------------+---------------------------+----------------------------------------------------------------+ +| in_b | std_logic_vector | 2*(b_bw) | The second complex vector input. b_bw is the bitwidth | +| | | | specified for a in the parameter list. Real part occupies | +| | | | MSB while imag part occupies LSB. | ++----------------+--------------------------+---------------------------+----------------------------------------------------------------+ +| in_val | std_logic | 1 | Valid signal to drive multiply. | ++----------------+--------------------------+---------------------------+----------------------------------------------------------------+ + +---------- +Parameters +---------- +'a' and 'b' are taken to be complex vectors. The bitwidths and binary points specified in the mask are for +the real and imaginary parts of the complex vectors. The output is also a complex vector where the 'ab' bitwidth +and binary point are again for the real and imaginary parts. Hence, inputs and outputs are expected to be double +bitwidths specified. +The complex vector has the real part in the MSB and the imaginary part in the LSB. ++----------------+---------+----------+----------------------------------------------------------------+ +| Generic | Type | Value | Description | ++================+=========+==========+================================================================+ +| 'a' nof bits | Natural | 18 | The bitwidth of the 'a' real part. | ++----------------+---------+----------+----------------------------------------------------------------+ +| 'a' bin pt | Natural | 17 | The binary point position for the 'a' real part. | ++----------------+---------+----------+----------------------------------------------------------------+ +| 'b' nof bits | Natural | 18 | The bitwidth of the 'b' real part. | ++----------------+---------+----------+----------------------------------------------------------------+ +| 'b' bin pt | Natural | 17 | The binary point position for the 'b' real part. | ++----------------+---------+----------+----------------------------------------------------------------+ +| 'ab' nof bits | Natural | 36 | Output bitwidth for the 'ab' real part. | ++----------------+---------+----------+----------------------------------------------------------------+ +| 'ab' bin pt | Natural | 34 | The binary point position for the 'ab' real part. | ++----------------+---------+----------+----------------------------------------------------------------+ +| Quantisation | String | Truncate | The rounding method used if 'ab' bitwidth < a_bw + b_bw + 1. | ++----------------+---------+----------+----------------------------------------------------------------+ +| Overflow | String | Wrap | The overflow method used. | ++----------------+---------+----------+----------------------------------------------------------------+ +| Conjugate | Boolean | False | If True, conjugate the 'b' entry before complex multiplication.| ++----------------+---------+----------+----------------------------------------------------------------+ +| input_lat | Natural | 1 | Can be 0 or 1. 0 means no registering of input, 1 means 1 clk | +| | | | of input register. | ++----------------+---------+----------+----------------------------------------------------------------+ +| prod _lat | Natural | 1 | Can be 0 or 1. 0 means no registering of product output, 1 | +| | | | means 1 clk of product register. | ++----------------+---------+----------+----------------------------------------------------------------+ +| adder_lat | Natural | 1 | Can be 0 or 1. 0 means no registering of adder output, 1 means | +| | | | 1 clk of adder register. | ++----------------+---------+----------+----------------------------------------------------------------+ +| output_lat | Natural | 1 | Can be 0 or greater. 0 means no registering of output, | +| | | | greater means output_lat*clk of output registering. | ++----------------+---------+----------+----------------------------------------------------------------+ +| gaussian_cmult | Boolean | False | Use gaussian Cmult to implement 3DSP, normal to use 4DSP. | ++----------------+---------+----------+----------------------------------------------------------------+ +| IP implement | Boolean | False | Intel only - use ip cmult instead of behavioural HDL. | ++----------------+---------+----------+----------------------------------------------------------------+ +| Use DSP | Boolean | True | If true, use directive to implement multiplication using DSP48.| ++----------------+---------+----------+----------------------------------------------------------------+ diff --git a/wrappers/simulink/MULTIPLIERS.slx b/wrappers/simulink/MULTIPLIERS.slx new file mode 100644 index 00000000..4c402afe Binary files /dev/null and b/wrappers/simulink/MULTIPLIERS.slx differ diff --git a/wrappers/simulink/cmult_blk.slx b/wrappers/simulink/cmult_blk.slx new file mode 100644 index 00000000..1673d4eb Binary files /dev/null and b/wrappers/simulink/cmult_blk.slx differ diff --git a/wrappers/simulink/cmult_config.m b/wrappers/simulink/cmult_config.m new file mode 100644 index 00000000..c9d0ef23 --- /dev/null +++ b/wrappers/simulink/cmult_config.m @@ -0,0 +1,175 @@ +function cmult_config(this_block) + + this_block.setTopLevelLanguage('VHDL'); + + this_block.setEntityName('cmult'); + filepath = fileparts(which('cmult_config')); + + function boolval = checkbox2bool(bxval) + if strcmp(bxval, 'on') + boolval= true; + elseif strcmp(bxval, 'off') + boolval= false; + end + end + function strval = bool2str(boolval) + if boolval + strval = 'TRUE'; + else + strval = 'FALSE'; + end + end + function nat = quant2nat(quant) + if strcmp(quant,"Truncate") + nat = '0'; + elseif strcmp(quant,"Round-Even") + nat = '1'; + else + nat = '2'; + end + end + function boolstr = str_clip(ovflw) + if strcmp(ovflw,"Wrap") + boolstr = 'FALSE'; + else + boolstr = 'TRUE'; + end + end + + cmult_blk = this_block.blockName; + cmult_parent = get_param(cmult_blk,'Parent'); + %Extract block parameters + is_async = checkbox2bool(get_param(cmult_parent, 'is_async')); + in_a_bw = get_param(cmult_parent, 'in_a_bw'); + in_b_bw = get_param(cmult_parent, 'in_b_bw'); + out_ab_bw = get_param(cmult_parent, 'out_ab_bw'); + out_ab_bp = get_param(cmult_parent, 'out_ab_bp'); + quant_method = quant2nat(get_param(cmult_parent, 'quant_method')); + ovflw_method = str_clip(get_param(cmult_parent, 'ovflw_method')); + conjugate = checkbox2bool(get_param(cmult_parent, 'conjugate')); + pipeline_input = get_param(cmult_parent, 'pipeline_input'); + pipeline_product = get_param(cmult_parent, 'pipeline_product'); + pipeline_adder = get_param(cmult_parent, 'pipeline_adder'); + pipeline_output = get_param(cmult_parent, 'pipeline_output'); + pipeline_round = get_param(cmult_parent, 'pipeline_round'); + use_gauss = checkbox2bool(get_param(cmult_parent, 'use_gauss')); + use_ip = checkbox2bool(get_param(cmult_parent, 'use_ip')); + use_dsp = checkbox2bool(get_param(cmult_parent, 'use_dsp')); + + this_block.addSimulinkInport('in_a'); + in_a_port = this_block.port('in_a'); + this_block.addSimulinkInport('in_b'); + in_b_port = this_block.port('in_b'); + + if is_async + this_block.addSimulinkInport('rst'); + rst_port = this_block.port('rst'); + + this_block.addSimulinkInport('in_val'); + in_val_port = this_block.port('in_val'); + end + + this_block.addSimulinkOutport('out_ab'); + out_ab_port = this_block.port('out_ab'); + out_ab_port.setType(sprintf('Ufix_%d_0',2*str2double(out_ab_bw))); + + if is_async + this_block.addSimulinkOutport('out_val'); + out_val_port = this_block.port('out_val'); + out_val_port.setType('Bool'); + out_val_port.useHDLVector(false); + end + + % ----------------------------- + if (this_block.inputTypesKnown) + % do input type checking, dynamic output type and generic setup in this code block. + if is_async + if (rst_port.width ~= 1) + this_block.setError('Input data type for port "rst" must have width=1.'); + end + rst_port.setType('Bool'); + rst_port.useHDLVector(false); + + if (in_val_port.width ~= 1) + this_block.setError('Input data type for port "in_val" must have width=1.'); + end + in_val_port.setType('Bool'); + in_val_port.useHDLVector(false); + end + if in_a_port.getWidth ~= 2*str2double(in_a_bw) + this_block.setError(sprintf('Input data width for port "in_a" must be %d.',2*str2double(in_a_bw))); + end + if in_b_port.getWidth ~= 2*str2double(in_b_bw) + this_block.setError(sprintf('Input data width for port "in_b" must be %d.',2*str2double(in_b_bw))); + end + + end % if(inputTypesKnown) + % ----------------------------- + + % ----------------------------- + if (this_block.inputRatesKnown) + setup_as_single_rate(this_block,'clk','ce') + end % if(inputRatesKnown) + % ----------------------------- + + this_block.addGeneric('g_use_ip','BOOLEAN',bool2str(use_ip)); + this_block.addGeneric('g_a_bw','NATURAL',in_a_bw); + this_block.addGeneric('g_b_bw','NATURAL',in_b_bw); + this_block.addGeneric('g_ab_bw','NATURAL',out_ab_bw); + this_block.addGeneric('g_conjugate_b','BOOLEAN',bool2str(conjugate)); + this_block.addGeneric('g_use_gauss','BOOLEAN',bool2str(use_gauss)); + this_block.addGeneric('g_use_dsp','BOOLEAN',bool2str(use_dsp)); + this_block.addGeneric('g_round_method','NATURAL',quant_method); + this_block.addGeneric('g_ovflw_method','BOOLEAN',ovflw_method); + this_block.addGeneric('g_pipeline_input','NATURAL',pipeline_input); + this_block.addGeneric('g_pipeline_product','NATURAL',pipeline_product); + this_block.addGeneric('g_pipeline_adder','NATURAL',pipeline_adder); + this_block.addGeneric('g_pipeline_round','NATURAL',pipeline_round); + this_block.addGeneric('g_pipeline_output','NATURAL',pipeline_output); + + this_block.addFileToLibrary([filepath '/../../casper_multiplier/cmult.vhd'],'xil_defaultlib'); + this_block.addFileToLibrary([filepath '/../../ip_xpm/mult/ip_cmult_rtl_4dsp.vhd'],'ip_xpm_mult_lib'); + this_block.addFileToLibrary([filepath '/../../ip_xpm/mult/ip_cmult_rtl_3dsp.vhd'],'ip_xpm_mult_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/common_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/common_str_pkg.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/common_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_agilex_versal_cmult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_mult_component.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../common_components/common_pipeline_sl.vhd'],'common_components_lib'); + this_block.addFileToLibrary([filepath '/../../common_components/common_pipeline.vhd'],'common_components_lib'); + this_block.addFileToLibrary([filepath '/../../technology/technology_select_pkg.vhd'],'technology_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/common_pkg.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/fixed_pkg_c.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/fixed_float_types_c.vhd'],'common_pkg_lib'); +return; +end + +% ------------------------------------------------------------ + +function setup_as_single_rate(block,clkname,cename) + inputRates = block.inputRates; + uniqueInputRates = unique(inputRates); + if (length(uniqueInputRates)==1 & uniqueInputRates(1)==Inf) + block.addError('The inputs to this block cannot all be constant.'); + return; + end + if (uniqueInputRates(end) == Inf) + hasConstantInput = true; + uniqueInputRates = uniqueInputRates(1:end-1); + end + if (length(uniqueInputRates) ~= 1) + block.addError('The inputs to this block must run at a single rate.'); + return; + end + theInputRate = uniqueInputRates(1); + for i = 1:block.numSimulinkOutports + block.outport(i).setRate(theInputRate); + end + block.addClkCEPair(clkname,cename,theInputRate); + return; +end +% ------------------------------------------------------------ + diff --git a/wrappers/simulink/cmult_subsys2bb_knit.m b/wrappers/simulink/cmult_subsys2bb_knit.m new file mode 100644 index 00000000..59e25c2d --- /dev/null +++ b/wrappers/simulink/cmult_subsys2bb_knit.m @@ -0,0 +1,61 @@ +function cmult_subsys2bb_knit() + + function boolval = checkbox2bool(bxval) + if strcmp(bxval, 'on') + boolval= true; + elseif strcmp(bxval, 'off') + boolval= false; + end + end + + subsysblk = gcb; + cmult_bb = [subsysblk '/cmult']; + is_async = checkbox2bool(get_param(subsysblk,'is_async')); + + inprts = find_system(subsysblk,'LookUnderMasks','on','BlockType','Inport'); + outprts = find_system(subsysblk,'LookUnderMasks','on','BlockType','Outport'); + rst = find(contains(inprts,'rst')); + in_val = find(contains(inprts,'in_val')); + out_val = find(contains(outprts,'out_val')); + + bbports = get_param(cmult_bb,'PortHandles'); + if(is_async) + if(isempty(rst) && isempty(in_val) && isempty(out_val)) + rst_str = [subsysblk '/rst']; + in_val_str = [subsysblk '/in_val']; + out_val_str = [subsysblk '/out_val']; + + add_block('simulink/Commonly Used Blocks/In1',rst_str); + rst_ph = get_param(rst_str,'PortHandles'); + add_line(subsysblk, rst_ph.Outport(1),bbports.Inport(3)); + + add_block('simulink/Commonly Used Blocks/In1',in_val_str); + in_val_ph = get_param(in_val_str,'PortHandles'); + add_line(subsysblk, in_val_ph.Outport(1),bbports.Inport(4)); + + add_block('simulink/Commonly Used Blocks/Out1',out_val_str); + out_val_ph = get_param(out_val_str,'PortHandles'); + add_line(subsysblk, bbports.Outport(2), out_val_ph.Inport(1)); + end + else + if(isempty(rst) && isempty(in_val)) + %do nothing + else + rst_str = [subsysblk '/rst']; + in_val_str = [subsysblk '/in_val']; + out_val_str = [subsysblk '/out_val']; + + rst_lh = get_param(rst_str,'LineHandles'); + delete_line(rst_lh.Outport(1)); + delete_block(rst_str); + + in_val_lh = get_param(in_val_str,'LineHandles'); + delete_line(in_val_lh.Outport(1)); + delete_block(in_val_str); + + out_val_lh = get_param(out_val_str,'LineHandles'); + delete_line(out_val_lh.Inport(1)); + delete_block(out_val_str); + end + end +end \ No newline at end of file diff --git a/wrappers/simulink/cross_mult_config.m b/wrappers/simulink/cross_mult_config.m new file mode 100644 index 00000000..107756f4 --- /dev/null +++ b/wrappers/simulink/cross_mult_config.m @@ -0,0 +1,137 @@ + +function cross_multiplier_config(this_block) + + this_block.setTopLevelLanguage('VHDL'); + + this_block.setEntityName('cross_multiplier_top'); + + filepath = fileparts(which('cross_multiplier_config')); + + cross_mult = this_block.blockName; + cross_mult_parent = get_param(cross_mult, 'Parent'); + + function boolval = checkbox2bool(bxval) + if strcmp(bxval, 'on') + boolval= true; + elseif strcmp(bxval, 'off') + boolval= false; + end + end + + function strboolval = bool2str(bval) + if bval + strboolval = 'TRUE'; + elseif ~bval + strboolval = 'FALSE'; + end + end + + nof_streams = get_param(cross_mult_parent, 'nof_streams'); + dbl_nof_streams = str2double(nof_streams); + nof_aggregations =get_param(cross_mult_parent, 'aggregation'); + dbl_nof_aggregations = str2double(nof_aggregations); + in_bit_width =str2double(get_param(cross_mult_parent, 'in_bit_w')); + out_bit_width =str2double(get_param(cross_mult_parent, 'out_bit_w')); + use_dsp = bool2str(checkbox2bool(get_param(cross_mult_parent,'use_dsp'))); + use_gauss = bool2str(checkbox2bool(get_param(cross_mult_parent,'use_gauss'))); + input_latency = get_param(cross_mult_parent, 'pipeline_input'); + product_latency = get_param(cross_mult_parent, 'pipeline_product'); + adder_latency = get_param(cross_mult_parent, 'pipeline_adder'); + round_latency = get_param(cross_mult_parent, 'pipeline_round'); + output_latency = get_param(cross_mult_parent, 'pipeline_output'); + ovflw_behav = bool2str(checkbox2bool(get_param(cross_mult_parent,'ovflw_behav'))); + quant_behav = get_param(cross_mult_parent, 'quant_behav'); + + nof_outputs = ((dbl_nof_streams+1) * (dbl_nof_streams))/2; + din_type = sprintf('Ufix_%d_0',dbl_nof_aggregations*2*in_bit_width); + dout_type = sprintf('Ufix_%d_0',dbl_nof_aggregations*2*out_bit_width); + + %Generate the vhdl top file + vhdlfile = cross_multiplier_code_gen(dbl_nof_streams,dbl_nof_aggregations,in_bit_width, out_bit_width); + + this_block.addSimulinkInport('sync_in'); + sync_in_port = this_block.port('sync_in'); + sync_in_port.setType('Bool'); + sync_in_port.useHDLVector(false); + + this_block.addSimulinkOutport('sync_out'); + sync_out_port = this_block.port('sync_out'); + sync_out_port.setType('Bool'); + sync_out_port.useHDLVector(false); + + for s = 0:1:dbl_nof_streams-1 + name = sprintf('din_%d',s); + this_block.addSimulinkInport(name); + this_block.port(name).setType(din_type); + end + for o = 0:1:nof_outputs-1 + name = sprintf('dout_%d',o); + this_block.addSimulinkOutport(name); + this_block.port(name).setType(dout_type); + end + + % ----------------------------- + if (this_block.inputRatesKnown) + setup_as_single_rate(this_block,'clk','ce') + end % if(inputRatesKnown) + % ----------------------------- + + uniqueInputRates = unique(this_block.getInputRates); + + this_block.addGeneric('g_use_gauss','BOOLEAN',use_gauss); + this_block.addGeneric('g_use_dsp','BOOLEAN',use_dsp); + this_block.addGeneric('g_pipeline_input','NATURAL',input_latency); + this_block.addGeneric('g_pipeline_product','NATURAL',product_latency); + this_block.addGeneric('g_pipeline_adder','NATURAL',adder_latency); + this_block.addGeneric('g_pipeline_round','NATURAL',round_latency); + this_block.addGeneric('g_pipeline_output','NATURAL',output_latency); + this_block.addGeneric('ovflw_behav','BOOLEAN',ovflw_behav); + this_block.addGeneric('quant_behav','NATURAL',quant_behav); + + srcloc = fileparts(vhdlfile); + + this_block.addFileToLibrary(vhdlfile,'xil_defaultlib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/fixed_float_types_c.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/fixed_pkg_c.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/common_pkg.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([filepath '/../../technology/technology_select_pkg.vhd'],'technology_lib'); + this_block.addFileToLibrary([filepath '/../../common_components/common_pipeline.vhd'],'common_components_lib'); + this_block.addFileToLibrary([filepath '/../../common_components/common_pipeline_sl.vhd'],'common_components_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_mult_component.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_agilex_versal_cmult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/tech_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/common_complex_mult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../casper_multiplier/cmult.vhd'],'casper_multiplier_lib'); + this_block.addFileToLibrary([filepath '/../../common_pkg/common_str_pkg.vhd'],'common_pkg_lib'); + this_block.addFileToLibrary([srcloc '/correlator_pkg.vhd'],'casper_correlator_lib'); + this_block.addFileToLibrary([filepath '/../../ip_xpm/mult/ip_cmult_rtl_3dsp.vhd'],'ip_xpm_mult_lib'); + this_block.addFileToLibrary([filepath '/../../ip_xpm/mult/ip_cmult_rtl_4dsp.vhd'],'ip_xpm_mult_lib'); + this_block.addFileToLibrary([filepath '/../../casper_correlator/cross_multiplier.vhd'],'casper_correlator_lib'); + return; +end +% ------------------------------------------------------------ + +function setup_as_single_rate(block,clkname,cename) + inputRates = block.inputRates; + uniqueInputRates = unique(inputRates); + if (length(uniqueInputRates)==1 & uniqueInputRates(1)==Inf) + block.addError('The inputs to this block cannot all be constant.'); + return; + end + if (uniqueInputRates(end) == Inf) + hasConstantInput = true; + uniqueInputRates = uniqueInputRates(1:end-1); + end + if (length(uniqueInputRates) ~= 1) + block.addError('The inputs to this block must run at a single rate.'); + return; + end + theInputRate = uniqueInputRates(1); + for i = 1:block.numSimulinkOutports + block.outport(i).setRate(theInputRate); + end + block.addClkCEPair(clkname,cename,theInputRate); + return; +end +% ------------------------------------------------------------ + diff --git a/wrappers/simulink/cross_multiplier_blk.slx b/wrappers/simulink/cross_multiplier_blk.slx new file mode 100644 index 00000000..c1b1f296 Binary files /dev/null and b/wrappers/simulink/cross_multiplier_blk.slx differ diff --git a/wrappers/simulink/cross_multiplier_code_gen.m b/wrappers/simulink/cross_multiplier_code_gen.m new file mode 100644 index 00000000..25f81a1a --- /dev/null +++ b/wrappers/simulink/cross_multiplier_code_gen.m @@ -0,0 +1,153 @@ +function vhdlfile = cross_multiplier_code_gen(number_of_streams, number_of_aggregations, in_bit_w, out_bit_w) + %gather all the string arrays required to write full file: + filepathscript = fileparts(which('cross_multiplier_code_gen')); %get the filepath of this script (and thereby all scripts needed) + %where the top vhdl file will be generated + vhdlfilefolder = [fileparts(which(bdroot)) '/tmp_dspdevel']; + if ~exist(vhdlfilefolder, 'dir') + mkdir(vhdlfilefolder) + end + %and what it will be named + vhdlfile = fullfile(vhdlfilefolder, [bdroot '_cross_mult_top.vhd']); %filename for vhd file + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%upperdec%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + upperlines = [ + "library ieee, common_pkg_lib, casper_multiplier_lib, cross_multiplier_lib;" + "use ieee.std_logic_1164.all;" + "use ieee.numeric_std.all;" + "use common_pkg_lib.common_pkg.all;" + "use work.correlator_pkg.all;" + "" + "entity cross_multiplier_top is" + " generic(" + " g_use_gauss : BOOLEAN := FALSE;" + " g_use_dsp : BOOLEAN := TRUE;" + " g_pipeline_input : NATURAL := 1; --! 0 or 1" + " g_pipeline_product : NATURAL := 1; --! 0 or 1" + " g_pipeline_adder : NATURAL := 1; --! 0 or 1" + " g_pipeline_round : NATURAL := 1; --! 0 or 1" + " g_pipeline_output : NATURAL := 0; --! >= 0" + " ovflw_behav : BOOLEAN := FALSE;" + " quant_behav : NATURAL := 0" + " );" + " port(" + " clk : in std_logic;" + " ce : in std_logic;" + " sync_in : in std_logic;" + " sync_out : out std_logic;" + ]; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%lowerdec%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + lowerlines = [ + ");" + "architecture RTL of cross_multiplier_top is" + "SIGNAL s_din : s_cross_mult_din;" + "SIGNAL s_dout : s_cross_mult_out;" + "begin" + " u_cross_mult : entity cross_multiplier_lib.cross_multiplier" + " generic map (" + " g_use_gauss => g_use_gauss" + " g_use_dsp => g_use_dsp" + " g_pipeline_input => g_pipeline_input" + " g_pipeline_product => g_pipeline_product" + " g_pipeline_adder => g_pipeline_adder" + " g_pipeline_round => g_pipeline_round" + " g_pipeline_output => g_pipeline_output" + " ovflw_behav => ovflw_behav" + " quant_behav => quant_behav" + " )" + " port map (" + " clk => clk," + " ce => ce," + " sync_in => sync_in," + " sync_out => sync_out," + " din => din," + " dout => dout" + " );" + ]; + + portdec = join(mknprts(number_of_streams),'\n'); + archdec = join(mknarch(number_of_streams),'\n'); + + Vfile = fopen(vhdlfile,'w'); + if(Vfile == -1) + error("Cannot open vhdl file"); + end + + fprintf(Vfile,'%s\n',upperlines{:}); + fprintf(Vfile,portdec{:}); + fprintf(Vfile,'%s\n',lowerlines{:}); + fprintf(Vfile,archdec{:}); + fprintf(Vfile,"\nend architecture RTL;"); + fclose(Vfile); + + %update generics package + updatepkgs(filepathscript, vhdlfilefolder, number_of_streams, number_of_aggregations,in_bit_w, out_bit_w); +end + +function chararr = mknprts(number_of_streams) + nof_outputs = ((number_of_streams + 1) * number_of_streams)/ 2; + chararr = strings(number_of_streams + nof_outputs,0); + din = "din_%d : in STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_input_cbit_width) - 1 downto 0);"; + dout = "dout_%d : out STD_LOGIC_VECTOR((c_cross_mult_aggregation_per_stream * c_cross_mult_output_cbit_width) - 1 downto 0);"; + i=1; + for s = 0:1:number_of_streams-1 + chararr(i,1) = sprintf(din,s,s); + i=i+1; + end + for o = 0:1:nof_outputs-1 + if o ~= nof_outputs-1 + chararr(i,1) = sprintf(dout,o,o); + else + chararr(i,1) = sprintf(strip(dout,';'),o,o); + end + i=i+1; + end +end + +function chararr = mknarch(number_of_streams) + nof_outputs = ((number_of_streams + 1) * number_of_streams)/ 2; + chararr = strings(number_of_streams + nof_outputs,0); + din = "s_din(%d) <= din_%d;"; + dout = "dout_%d <= s_dout(%d);"; + i=1; + for s = 0:1:number_of_streams-1 + chararr(i,1) = sprintf(din,s,s); + i=i+1; + end + for o = 0:1:nof_outputs-1 + chararr(i,1) = sprintf(dout,o,o); + i=i+1; + end +end + +function updatepkgs(filepathscript, vhdlfilefolder, nof_streams, nof_aggregations, in_dat_w, out_dat_w) + insertloc = 5; + pkgsource = [filepathscript '/../../casper_correlator/correlator_pkg.vhd']; + pkgdest = [vhdlfilefolder '/correlator_pkg.vhd']; + lineone = sprintf( "CONSTANT c_cross_mult_nof_input_streams : NATURAL := %d;",nof_streams); + linetwo = sprintf( "CONSTANT c_cross_mult_aggregation_per_stream : NATURAL := %d;",nof_aggregations); + linethree = sprintf("CONSTANT c_cross_mult_input_bit_width : NATURAL := %d;",in_dat_w); + linefour = sprintf("CONSTANT c_cross_mult_output_bit_width : NATURAL := %d;",out_dat_w); + fid = fopen(pkgsource,'r'); + if(fid == -1) + error("Cannot open vhdl file: %s",pkgsource); + end + lines = textscan(fid, '%s', 'Delimiter', '\n', 'CollectOutput',true); + lines = lines{1}; + fclose(fid); + fid = fopen(pkgdest, 'w'); + if(fid == -1) + error("Cannot open vhdl file: %s",pkgdest); + end + for jj = 1: insertloc + fprintf(fid,'%s\n',lines{jj}); + end + fprintf(fid,'%s\n', lineone); + fprintf(fid,'%s\n',linetwo); + fprintf(fid,'%s\n',linethree); + fprintf(fid,'%s\n',linefour); + for jj = insertloc+5 : length(lines) + fprintf( fid, '%s\n', lines{jj} ); + end + fclose(fid); +end \ No newline at end of file diff --git a/wrappers/simulink/cross_multiplier_subsys2bb_knit.m b/wrappers/simulink/cross_multiplier_subsys2bb_knit.m new file mode 100644 index 00000000..43e637af --- /dev/null +++ b/wrappers/simulink/cross_multiplier_subsys2bb_knit.m @@ -0,0 +1,79 @@ +function cross_multiplier_subsys2bb_knit() + subsysblk = gcb; + cross_multiplier_bb = [subsysblk '/cross_multiplier']; + nof_streams = str2double(get_param(subsysblk,'nof_streams')); + + global inprts; + global outprts; + global din; + global dout; + global bbports; + + % Unlink block, otherwise we're not allowed to modify it + set_param(subsysblk, 'LinkStatus', 'inactive'); + + function updatedataprts(subsysblk, cross_multiplier_bb) + bbports=get_param(cross_multiplier_bb,'PortHandles'); + inprts = find_system(subsysblk,'LookUnderMasks','on','BlockType','Inport'); + outprts = find_system(subsysblk,'LookUnderMasks','on','BlockType','Outport'); + + %Get index of all re, im in/outs: + din = find(contains(inprts,'din')); + dout = find(contains(outprts,'x')); + end + + function map = gen_map(nof_streams) + nof_outputs = ((nof_streams + 1) * nof_streams)/2; + map = zeros(nof_outputs,2); + i = 1; + for s=0:1:nof_streams-1 + for ss=s:1:nof_streams-1 + map(i,:) = [s,ss]; + i = i+1; + end + end + end + + function delete_all_data_ports() + %delete input ports + for k=0:length(din)-1 + din_prt = inprts(din(end-k)); + din_lh = get_param(din_prt{1},'LineHandles'); + delete_line(din_lh.Outport(1)); + delete_block(din_prt{1}); + end + %delete output ports + for j=0:length(dout)-1 + dout_prt = outprts(dout(end-j)); + dout_lh = get_param(dout_prt{1},'LineHandles'); + delete_line(dout_lh.Inport(1)); + delete_block(dout_prt{1}); + end + end + + function add_data_ports(subsysblk, nof_streams) + map = gen_map(nof_streams); + nof_outputs = ((nof_streams + 1) * nof_streams)/2; + + for i=0:1:nof_streams-1 + din_str_n = sprintf([subsysblk '/din_%d'],i); + add_block('simulink/Commonly Used Blocks/In1',din_str_n); + din_ph = get_param(din_str_n,'PortHandles'); + add_line(subsysblk,din_ph.Outport(1),bbports.Inport(i+2)); + end + + for o=0:1:nof_outputs-1 + a = map(o+1,1); + b = map(o+1,2); + dout_str_n = sprintf([subsysblk '/din%d_x_din%d'],a,b); + add_block('simulink/Commonly Used Blocks/Out1',dout_str_n); + dout_ph = get_param(dout_str_n,'PortHandles'); + add_line(subsysblk,bbports.Outport(o+2),dout_ph.Inport(1)); + end + end + + updatedataprts(subsysblk, cross_multiplier_bb); + delete_all_data_ports(); + updatedataprts(subsysblk, cross_multiplier_bb); + add_data_ports(subsysblk, nof_streams); +end \ No newline at end of file diff --git a/wrappers/simulink/hdl_library.slx b/wrappers/simulink/hdl_library.slx index 28427928..d1683820 100644 Binary files a/wrappers/simulink/hdl_library.slx and b/wrappers/simulink/hdl_library.slx differ