library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library work;
use work.common.all;

library unisim;
use unisim.vcomponents.all;

-- Signed 33b x 33b multiplier giving 64-bit product, with no addend.

entity multiply_32s is
    port (
        clk   : in std_logic;
        stall : in std_ulogic;

        m_in  : in MultiplyInputType;
        m_out : out MultiplyOutputType
        );
end entity multiply_32s;

architecture behaviour of multiply_32s is
    signal clocken : std_ulogic;
    signal data1 : std_ulogic_vector(52 downto 0);
    signal data2 : std_ulogic_vector(34 downto 0);
    signal m00_p, m01_p : std_ulogic_vector(47 downto 0);
    signal m00_pc : std_ulogic_vector(47 downto 0);
    signal m10_p, m11_p : std_ulogic_vector(47 downto 0);
    signal m10_pc : std_ulogic_vector(47 downto 0);
    signal p0_pat, p0_patb : std_ulogic;
    signal p1_pat, p1_patb : std_ulogic;
    signal product_lo : std_ulogic_vector(22 downto 0);

begin
    -- sign extend if signed
    data1(31 downto 0)  <= m_in.data1(31 downto 0);
    data1(52 downto 32) <= (others => m_in.is_signed and m_in.data1(31));
    data2(31 downto 0)  <= m_in.data2(31 downto 0);
    data2(34 downto 32) <= (others => m_in.is_signed and m_in.data2(31));

    clocken <= m_in.valid and not stall;

    m00: DSP48E1
        generic map (
            ACASCREG => 0,
            ALUMODEREG => 0,
            AREG => 0,
            BCASCREG => 0,
            BREG => 0,
            CARRYINREG => 0,
            CARRYINSELREG => 0,
            CREG => 0,
            INMODEREG => 0,
            MREG => 0,
            OPMODEREG => 0,
            PREG => 0
            )
        port map (
            A => "0000000" & data1(22 downto 0),
            ACIN => (others => '0'),
            ALUMODE => "0000",
            B => '0' & data2(16 downto 0),
            BCIN => (others => '0'),
            C => (others => '0'),
            CARRYCASCIN => '0',
            CARRYIN => '0',
            CARRYINSEL => "000",
            CEA1 => '0',
            CEA2 => '0',
            CEAD => '0',
            CEALUMODE => '0',
            CEB1 => '0',
            CEB2 => '0',
            CEC => '0',
            CECARRYIN => '0',
            CECTRL => '0',
            CED => '0',
            CEINMODE => '0',
            CEM => '0',
            CEP => '0',
            CLK => clk,
            D => (others => '0'),
            INMODE => "00000",
            MULTSIGNIN => '0',
            OPMODE => "0110101",
            P => m00_p,
            PCIN => (others => '0'),
            PCOUT => m00_pc,
            RSTA => '0',
            RSTALLCARRYIN => '0',
            RSTALUMODE => '0',
            RSTB => '0',
            RSTC => '0',
            RSTCTRL => '0',
            RSTD => '0',
            RSTINMODE => '0',
            RSTM => '0',
            RSTP => '0'
            );

    m01: DSP48E1
        generic map (
            ACASCREG => 0,
            ALUMODEREG => 0,
            AREG => 0,
            BCASCREG => 0,
            BREG => 0,
            CARRYINREG => 0,
            CARRYINSELREG => 0,
            CREG => 0,
            INMODEREG => 0,
            MREG => 0,
            OPMODEREG => 0,
            PREG => 0
            )
        port map (
            A => "0000000" & data1(22 downto 0),
            ACIN => (others => '0'),
            ALUMODE => "0000",
            B => data2(34 downto 17),
            BCIN => (others => '0'),
            C => (others => '0'),
            CARRYCASCIN => '0',
            CARRYIN => '0',
            CARRYINSEL => "000",
            CEA1 => '0',
            CEA2 => '0',
            CEAD => '0',
            CEALUMODE => '0',
            CEB1 => '0',
            CEB2 => '0',
            CEC => '0',
            CECARRYIN => '0',
            CECTRL => '0',
            CED => '0',
            CEINMODE => '0',
            CEM => '0',
            CEP => '0',
            CLK => clk,
            D => (others => '0'),
            INMODE => "00000",
            MULTSIGNIN => '0',
            OPMODE => "1010101",
            P => m01_p,
            PCIN => m00_pc,
            RSTA => '0',
            RSTALLCARRYIN => '0',
            RSTALUMODE => '0',
            RSTB => '0',
            RSTC => '0',
            RSTCTRL => '0',
            RSTD => '0',
            RSTINMODE => '0',
            RSTM => '0',
            RSTP => '0'
            );

    m10: DSP48E1
        generic map (
            ACASCREG => 0,
            ALUMODEREG => 0,
            AREG => 0,
            BCASCREG => 0,
            BREG => 0,
            CARRYINREG => 0,
            CARRYINSELREG => 0,
            CREG => 1,
            INMODEREG => 0,
            MASK => x"fffffffe00ff",
            OPMODEREG => 0,
            PREG => 0,
            USE_PATTERN_DETECT => "PATDET"
            )
        port map (
            A => data1(52 downto 23),
            ACIN => (others => '0'),
            ALUMODE => "0000",
            B => '0' & data2(16 downto 0),
            BCIN => (others => '0'),
            C => std_ulogic_vector(resize(signed(m01_p(38 downto 6)), 48)),
            CARRYCASCIN => '0',
            CARRYIN => '0',
            CARRYINSEL => "000",
            CEA1 => '0',
            CEA2 => '0',
            CEAD => '0',
            CEALUMODE => '0',
            CEB1 => '0',
            CEB2 => '0',
            CEC => clocken,
            CECARRYIN => '0',
            CECTRL => '0',
            CED => '0',
            CEINMODE => '0',
            CEM => clocken,
            CEP => '0',
            CLK => clk,
            D => (others => '0'),
            INMODE => "00000",
            MULTSIGNIN => '0',
            OPMODE => "0110101",
            P => m10_p,
            PATTERNDETECT => p0_pat,
            PATTERNBDETECT => p0_patb,
            PCIN => (others => '0'),
            PCOUT => m10_pc,
            RSTA => '0',
            RSTALLCARRYIN => '0',
            RSTALUMODE => '0',
            RSTB => '0',
            RSTC => '0',
            RSTCTRL => '0',
            RSTD => '0',
            RSTINMODE => '0',
            RSTM => '0',
            RSTP => '0'
            );

    m11: DSP48E1
        generic map (
            ACASCREG => 0,
            ALUMODEREG => 0,
            AREG => 0,
            BCASCREG => 0,
            BREG => 0,
            CARRYINREG => 0,
            CARRYINSELREG => 0,
            CREG => 0,
            INMODEREG => 0,
            MASK => x"fffffc000000",
            OPMODEREG => 0,
            PREG => 0,
            USE_PATTERN_DETECT => "PATDET"
            )
        port map (
            A => data1(52 downto 23),
            ACIN => (others => '0'),
            ALUMODE => "0000",
            B => data2(34 downto 17),
            BCIN => (others => '0'),
            C => (others => '0'),
            CARRYCASCIN => '0',
            CARRYIN => '0',
            CARRYINSEL => "000",
            CEA1 => '0',
            CEA2 => '0',
            CEAD => '0',
            CEALUMODE => '0',
            CEB1 => '0',
            CEB2 => '0',
            CEC => '0',
            CECARRYIN => '0',
            CECTRL => '0',
            CED => '0',
            CEINMODE => '0',
            CEM => clocken,
            CEP => '0',
            CLK => clk,
            D => (others => '0'),
            INMODE => "00000",
            MULTSIGNIN => '0',
            OPMODE => "1010101",
            P => m11_p,
            PATTERNDETECT => p1_pat,
            PATTERNBDETECT => p1_patb,
            PCIN => m10_pc,
            RSTA => '0',
            RSTALLCARRYIN => '0',
            RSTALUMODE => '0',
            RSTB => '0',
            RSTC => '0',
            RSTCTRL => '0',
            RSTD => '0',
            RSTINMODE => '0',
            RSTM => '0',
            RSTP => '0'
            );

    m_out.result(127 downto 64) <= (others => '0');
    m_out.result(63 downto 40) <= m11_p(23 downto 0);
    m_out.result(39 downto 23) <= m10_p(16 downto 0);
    m_out.result(22 downto 0)  <= product_lo;

    m_out.overflow <= not ((p0_pat and p1_pat) or (p0_patb and p1_patb));

    process(clk)
    begin
        if rising_edge(clk) then
            if stall = '0' then
                m_out.valid <= m_in.valid;
                product_lo <= m01_p(5 downto 0) & m00_p(16 downto 0);
            end if;
        end if;
    end process;

end architecture behaviour;
