diff --git a/libraries/technology/ip_agi027_xxxx/mult_add2/ip_agi027_xxxx_mult_add2_rtl.vhd b/libraries/technology/ip_agi027_xxxx/mult_add2/ip_agi027_xxxx_mult_add2_rtl.vhd new file mode 100644 index 0000000000000000000000000000000000000000..a79530ba358f68568e21856f6916dedc868ddfbe --- /dev/null +++ b/libraries/technology/ip_agi027_xxxx/mult_add2/ip_agi027_xxxx_mult_add2_rtl.vhd @@ -0,0 +1,202 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2023 +-- 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 : D.F. Brouwer +-- Purpose: +-- RadioHDL wrapper +-- Reference: +-- Copied from */technology/ip_arria10_e2sg/mult_add2/ip_arria10_e2sg_mult_add2.vhd, +-- that is based on ip_stratixiv_mult_add2_rtl + +library IEEE, common_lib; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use common_lib.common_pkg.all; + +------------------------------------------------------------------------------ +-- Function: +-- . res = a0 * b0 + a1 * b1 +-- . res = a0 * b0 - a1 * b1 +------------------------------------------------------------------------------ + +entity ip_agi027_xxxx_mult_add2_rtl is + generic ( + g_in_a_w : positive; + g_in_b_w : positive; + g_res_w : positive; -- g_in_a_w + g_in_b_w + log2(2) + g_force_dsp : boolean := true; -- when TRUE resize input width to >= 18 + g_add_sub : string := "ADD"; -- or "SUB" + g_nof_mult : integer := 2; -- fixed + 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_output : natural := 1 -- >= 0 + ); + port ( + rst : in std_logic := '0'; + clk : in std_logic; + clken : in std_logic := '1'; + in_a : in std_logic_vector(g_nof_mult * g_in_a_w - 1 downto 0); + in_b : in std_logic_vector(g_nof_mult * g_in_b_w - 1 downto 0); + res : out std_logic_vector(g_res_w - 1 downto 0) + ); +end ip_agi027_xxxx_mult_add2_rtl; + +architecture str of ip_agi027_xxxx_mult_add2_rtl is + -- Extra output pipelining is only needed when g_pipeline_output > 1 + constant c_pipeline_output : natural := sel_a_b(g_pipeline_output > 0, g_pipeline_output - 1, 0); + + constant c_prod_w : natural := g_in_a_w + g_in_b_w; + constant c_sum_w : natural := c_prod_w + 1; + + -- registers + signal reg_a0 : signed(g_in_a_w - 1 downto 0); + signal reg_b0 : signed(g_in_b_w - 1 downto 0); + signal reg_a1 : signed(g_in_a_w - 1 downto 0); + signal reg_b1 : signed(g_in_b_w - 1 downto 0); + signal reg_prod0 : signed(c_prod_w - 1 downto 0); + signal reg_prod1 : signed(c_prod_w - 1 downto 0); + signal reg_sum : signed(c_sum_w - 1 downto 0); + signal reg_result : signed(g_res_w - 1 downto 0); + + -- combinatorial + signal nxt_a0 : signed(g_in_a_w - 1 downto 0); + signal nxt_b0 : signed(g_in_b_w - 1 downto 0); + signal nxt_a1 : signed(g_in_a_w - 1 downto 0); + signal nxt_b1 : signed(g_in_b_w - 1 downto 0); + signal nxt_prod0 : signed(c_prod_w - 1 downto 0); + signal nxt_prod1 : signed(c_prod_w - 1 downto 0); + signal nxt_sum : signed(c_sum_w - 1 downto 0); + signal nxt_result : signed(g_res_w - 1 downto 0); + + -- the active signals + signal a0 : signed(g_in_a_w - 1 downto 0); + signal b0 : signed(g_in_b_w - 1 downto 0); + signal a1 : signed(g_in_a_w - 1 downto 0); + signal b1 : signed(g_in_b_w - 1 downto 0); + signal prod0 : signed(c_prod_w - 1 downto 0); + signal prod1 : signed(c_prod_w - 1 downto 0); + signal sum : signed(c_sum_w - 1 downto 0); + signal result : signed(g_res_w - 1 downto 0); +begin + ------------------------------------------------------------------------------ + -- Registers + ------------------------------------------------------------------------------ + + -- Put all potential registers in a single process for optimal DSP inferrence + -- Use rst only if it is supported by the DSP primitive, else leave it at '0' + p_reg : process (rst, clk) + begin + if rising_edge(clk) then + if rst = '1' then + reg_a0 <= (others => '0'); + reg_b0 <= (others => '0'); + reg_a1 <= (others => '0'); + reg_b1 <= (others => '0'); + reg_prod0 <= (others => '0'); + reg_prod1 <= (others => '0'); + reg_sum <= (others => '0'); + reg_result <= (others => '0'); + elsif clken = '1' then + reg_a0 <= nxt_a0; -- inputs + reg_b0 <= nxt_b0; + reg_a1 <= nxt_a1; + reg_b1 <= nxt_b1; + reg_prod0 <= nxt_prod0; -- products + reg_prod1 <= nxt_prod1; + reg_sum <= nxt_sum; -- sum + reg_result <= nxt_result; -- result sum after optional rounding + end if; + end if; + end process; + + ------------------------------------------------------------------------------ + -- Inputs + ------------------------------------------------------------------------------ + + nxt_a0 <= signed(in_a( g_in_a_w - 1 downto 0)); + nxt_b0 <= signed(in_b( g_in_b_w - 1 downto 0)); + nxt_a1 <= signed(in_a(2 * g_in_a_w - 1 downto g_in_a_w)); + nxt_b1 <= signed(in_b(2 * g_in_b_w - 1 downto g_in_b_w)); + + no_input_reg : if g_pipeline_input = 0 generate -- wired + a0 <= nxt_a0; + b0 <= nxt_b0; + a1 <= nxt_a1; + b1 <= nxt_b1; + end generate; + + gen_input_reg : if g_pipeline_input > 0 generate -- register input + a0 <= reg_a0; + b0 <= reg_b0; + a1 <= reg_a1; + b1 <= reg_b1; + end generate; + + ------------------------------------------------------------------------------ + -- Products + ------------------------------------------------------------------------------ + + nxt_prod0 <= a0 * b0; + nxt_prod1 <= a1 * b1; + + no_product_reg : if g_pipeline_product = 0 generate -- wired + prod0 <= nxt_prod0; + prod1 <= nxt_prod1; + end generate; + gen_product_reg : if g_pipeline_product > 0 generate -- register + prod0 <= reg_prod0; + prod1 <= reg_prod1; + end generate; + + ------------------------------------------------------------------------------ + -- Sum + ------------------------------------------------------------------------------ + gen_add : if g_add_sub = "ADD" generate + nxt_sum <= RESIZE_NUM(prod0, c_sum_w) + prod1; + end generate; + + gen_sub : if g_add_sub = "SUB" generate + nxt_sum <= RESIZE_NUM(prod0, c_sum_w) - prod1; + end generate; + + no_adder_reg : if g_pipeline_adder = 0 generate -- wired + sum <= nxt_sum; + end generate; + gen_adder_reg : if g_pipeline_adder > 0 generate -- register + sum <= reg_sum; + end generate; + + ------------------------------------------------------------------------------ + -- Result sum after optional rounding + ------------------------------------------------------------------------------ + + nxt_result <= RESIZE_NUM(sum, g_res_w); + + no_result_reg : if g_pipeline_output = 0 generate -- wired + result <= nxt_result; + end generate; + gen_result_reg : if g_pipeline_output > 0 generate -- register + result <= reg_result; + end generate; + + res <= std_logic_vector(result); +end str;