----------------------------------------------
-- MSS copyright 1999-2001
-- Filename: SIGNED_MULT12X9_10_S.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: new
-- Edit date: 1-29-01
-- Revision: 0.011
--
-- VHDL code for signed multiplier
-- Signed multiplier. The two inputs are expressed in 2's complement
-- with 12 bit and 9 bit respectively.
-- The output also expressed in 2's complement with 10 bits.  
-- This version is clocked. The latency between input and output is 5 CLK periods.
-- (1 clk for input register, 4 clocks for unsigned multiplication, 0 clock for output)
--
-- Rev 0.012 az
-- Switched from active low to active high reset
-- Removed references to inc_slv functions
----------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


entity SIGNED_MULT12X9_10_S is
	port (
		CLK: in std_logic;
		ASYNC_RESET: in std_logic;
		A: in std_logic_vector(11 downto 0);
			-- input A, 12-bit wide, signed (2's complement)
		B: in std_logic_vector(8 downto 0);
			-- input B, 9-bit wide, signed (2's complement)
		M: out std_logic_vector(9 downto 0)
			-- output, 10-bit wide, signed (2's complement)
	);
end SIGNED_MULT12X9_10_S;
architecture BEHAVIOR of SIGNED_MULT12X9_10_S is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
component M11x8U_A is
	port(
		A: in std_logic_vector(10 downto 0);
			-- input A, 11-bit wide, unsigned
		B: in std_logic_vector(7 downto 0);
			-- input B, 8-bit wide, unsigned
		CLK: in std_logic;
			-- reference clock
		ASYNC_RESET: in std_logic;
			-- reset, active low
		M: out std_logic_vector(8 downto 0)
			-- output, 9-bit wide, unsigned
);
end component;   

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
-- _D denotes one delay, _D2 denotes two CLK delays
signal zero: std_logic;
signal one: std_logic;   
signal A_D: std_logic_vector(11 downto 0);
signal B_D: std_logic_vector(8 downto 0);
signal UNSIGNED_A: std_logic_vector(10 downto 0);
signal UNSIGNED_B: std_logic_vector(7 downto 0);
signal UNSIGNED_M: std_logic_vector(8 downto 0);    
signal UNSIGNED_M_N: std_logic_vector(8 downto 0);    
signal A_N: std_logic_vector(10 downto 0);
signal NEG_A: std_logic_vector(10 downto 0);
signal B_N: std_logic_vector(7 downto 0);   
signal NEG_B: std_logic_vector(7 downto 0);   
signal NEG_M: std_logic_vector(8 downto 0);    
signal SIGN_M_D: std_logic;	-- 0 for positive, 1 for negative
signal SIGN_M_D2: std_logic;
signal SIGN_M_D3: std_logic;
signal SIGN_M_D4: std_logic;

-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin

zero <= '0';
one <= '1';   

-- reclock inputs
RECLOCK_001: process(ASYNC_RESET, CLK, A, B)
begin
	if(ASYNC_RESET = '1') then
		A_D <= "000000000000";
		B_D <= "000000000";
	elsif rising_edge(CLK) then
		A_D <= A;
		B_D <= B;
	end if;
end process;

-- compute the 2's complement (invert all bits, then add one)
B_N <= not (B_D(7 downto 0));
NEG_B <= B_N + 1;

A_N <= not (A_D(10 downto 0));
NEG_A <= A_N + 1;
		

-- take absolute values of operands A and B
ABS_A: process(A_D, NEG_A)
begin
	if(A_D(11) = '0') then   	-- A is positive. just drop the sign bit
		UNSIGNED_A(10 downto 0) <= A_D(10 downto 0);
	elsif (A_D = "100000000000") then 
		-- Exception to the rule: the inverse of -2048 is not 0, approximate with +2047
	    UNSIGNED_A <= "11111111111";
	else                    -- A is negative, 2's complement
		UNSIGNED_A(10 downto 0) <= NEG_A(10 downto 0);
	end if;
end process;  

ABS_B: process(B_D, NEG_B)
begin
	if(B_D(8) = '0') then   	-- B is positive. just drop the sign bit
		UNSIGNED_B(7 downto 0) <= B_D(7 downto 0);
	elsif (B_D = "100000000") then 
		-- Exception to the rule: the inverse of -256 is not 0, approximate with +255
	    UNSIGNED_B <= "11111111";
	else                    -- B is negative, 2's complement
		UNSIGNED_B(7 downto 0) <= NEG_B(7 downto 0);
	end if;
end process;  

-- delay the sign for four clocks to compensate for latency in unsigned multiplier
DELAY_SIGN_001: process(ASYNC_RESET, CLK, A_D, B_D, SIGN_M_D,
			SIGN_M_D2, SIGN_M_D3)
begin
	if(ASYNC_RESET = '1') then
		SIGN_M_D <= '0';
		SIGN_M_D2 <= '0';
		SIGN_M_D3 <= '0';
		SIGN_M_D4 <= '0';
	elsif rising_edge(CLK) then   
		SIGN_M_D <= A_D(11) xor B_D(8);	-- product sign
		SIGN_M_D2 <= SIGN_M_D;
		SIGN_M_D3 <= SIGN_M_D2;
		SIGN_M_D4 <= SIGN_M_D3;
	end if;
end process;

-- latency is 4 CLKs, input signs must be delayed accordingly 
UNSIGNED_MULT: M11x8U_A port map(A => UNSIGNED_A, 
			B => UNSIGNED_B, CLK => CLK, ASYNC_RESET => ASYNC_RESET, M => UNSIGNED_M);     

-- compute negative products, if needed
UNSIGNED_M_N <= not UNSIGNED_M;
NEG_M <= UNSIGNED_M_N + 1;

-- restore the sign of the output M based on the operand signs   
SIGN: process(UNSIGNED_M, NEG_M, SIGN_M_D4) 
begin       
	if(SIGN_M_D4 = '0') then -- output of multiplier is positive
		M(9) <= '0';
    	M(8 downto 0) <= UNSIGNED_M;
	elsif(UNSIGNED_M = "000000000") then
		-- special case 0. negative is also zero
		M(8 downto 0) <= UNSIGNED_M;
		M(9) <= '0';
	else	
		 -- negative product
		 M(9) <= '1';
	    M(8 downto 0) <= NEG_M;     
	end if;
end process;

end BEHAVIOR;

