----------------------------------------------
-- MSS copyright 1999-2017
-- Filename: POLAR3_DUALMAG.VHD
-- Authors: 	Alain Zarembowitch / MSS
-- 		Bengt Bengtsson / MSS
--		Adam Kwiatkowski / MSS
--		Poornima Sarvadevabahatla / MSS
-- Edit date:  8/22/17
-- Revision: 5
-- Inheritance: POLAR3.VHD Rev5 4/29/17
--
-- Returns two magnitudes from two cartesian coordinates.
-- Latency is 5 CLKs.  Uses one 18Kbit dual-port block ROM
--
-- Minimum period: 4.671ns (Maximum Frequency: 214.087MHz) Artix-7 -1 speed grade
-- FFs: 300
-- lut: 1227
-- 18Kb block ram: 1
----------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;

entity POLAR3_DUALMAG is port (  
		--// CLOCK, RESET
		CLK: in std_logic;
			-- Reference clock, synchronous design.

		--// INPUTS 
		XA_IN: in std_logic_vector(11 downto 0);
		YA_IN: in std_logic_vector(11 downto 0);
		SAMPLEA_CLK_IN: in std_logic;
			-- read input complex sample at rising_edge of CLK when SAMPLE_CLK_IN = '1'
		XB_IN: in std_logic_vector(11 downto 0);
		YB_IN: in std_logic_vector(11 downto 0);
		SAMPLEB_CLK_IN: in std_logic;

		--// OUTPUTS
		MAGNITUDEA_OUT: out std_logic_vector(13 downto 0) := "00000000000000";
		MAGNITUDEB_OUT: out std_logic_vector(13 downto 0) := "00000000000000";
			-- 4 x the true magnitude in unsigned representation (if you want
			-- signed simply add a zero before the MSb because a magnitude
			-- is always positive). To get the true scale the divide this
			-- number by four (or simply chop off the two least significant
			-- bits). 
		SAMPLEA_CLK_OUT: out std_logic := '0';
		SAMPLEB_CLK_OUT: out std_logic := '0'
			-- read output at rising_edge of CLK when SAMPLE_CLK_OUT = '1'
			-- Latency is 5 CLKs.
 );
end entity;  

architecture Behavior of POLAR3_DUALMAG is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
	COMPONENT POLAR3MAGPROM
	PORT(
		CLKA : IN std_logic;
		CSA : IN std_logic;
		OEA : IN std_logic;
		ADDRA : IN std_logic_vector(10 downto 0);
		CLKB : IN std_logic;
		CSB : IN std_logic;
		OEB : IN std_logic;
		ADDRB : IN std_logic_vector(10 downto 0);          
		DOA : OUT std_logic_vector(8 downto 0);
		DOB : OUT std_logic_vector(8 downto 0)
		);
	END COMPONENT;
	
-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
-- Suffix _E is for precision extended
-- Suffix _D for delay
signal XA_ABS: std_logic_vector(10  downto 0) := (others => '0');
signal XB_ABS: std_logic_vector(10  downto 0) := (others => '0');
signal YA_ABS: std_logic_vector(10  downto 0) := (others => '0');
signal YB_ABS: std_logic_vector(10  downto 0) := (others => '0');
signal XA_ABS_Q: std_logic_vector(5  downto 0) := (others => '0');
signal XB_ABS_Q: std_logic_vector(5  downto 0) := (others => '0');
signal YA_ABS_Q: std_logic_vector(5  downto 0) := (others => '0');
signal YB_ABS_Q: std_logic_vector(5  downto 0) := (others => '0');
signal ADDRA: std_logic_vector(10  downto 0) := (others => '0');
signal ADDRB: std_logic_vector(10  downto 0) := (others => '0');

signal MAGA_DIFFER: std_logic_vector(6  downto 0) := (others => '0');
signal MAGB_DIFFER: std_logic_vector(6  downto 0) := (others => '0');
				   
signal SAMPLEA_CLK_IN_D: std_logic := '0';      
signal SAMPLEA_CLK_IN_D2: std_logic := '0';      
signal SAMPLEA_CLK_IN_D3: std_logic := '0';      
signal SAMPLEA_CLK_IN_D4: std_logic := '0';      
signal SAMPLEB_CLK_IN_D: std_logic := '0';      
signal SAMPLEB_CLK_IN_D2: std_logic := '0';      
signal SAMPLEB_CLK_IN_D3: std_logic := '0';      
signal SAMPLEB_CLK_IN_D4: std_logic := '0';      

signal MAGNITUDEA_RANGE_0_175: std_logic_vector(8  downto 0) := (others => '0');
signal MAGNITUDEB_RANGE_0_175: std_logic_vector(8  downto 0) := (others => '0');

signal NO_OF_SHIFTSA: std_logic_vector(2  downto 0) := (others => '0');
signal NO_OF_SHIFTSA_D: std_logic_vector(2  downto 0) := (others => '0');
signal NO_OF_SHIFTSA_D2: std_logic_vector(2  downto 0) := (others => '0');
signal NO_OF_SHIFTSB: std_logic_vector(2  downto 0) := (others => '0');
signal NO_OF_SHIFTSB_D: std_logic_vector(2  downto 0) := (others => '0');
signal NO_OF_SHIFTSB_D2: std_logic_vector(2  downto 0) := (others => '0');

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

-- separate sign and magnitude. 
-- Compute the absolute value. Beware of the special case of 1000000000 in 2's complement.
ABS_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(XA_IN(11) = '0') then
			-- positive
			XA_ABS <= XA_IN(10 downto 0);
	  	elsif (XA_IN /= "100000000000") then
			-- negative, but not the most negative (special case)
			XA_ABS <= not(XA_IN(10 downto 0)) + 1;
		else
			XA_ABS <= "11111111111";
		end if;
	end if;
end process;
ABS_GEN_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(XB_IN(11) = '0') then
			-- positive
			XB_ABS <= XB_IN(10 downto 0);
	  	elsif (XB_IN /= "100000000000") then
			-- negative, but not the most negative (special case)
			XB_ABS <= not(XB_IN(10 downto 0)) + 1;
		else
			XB_ABS <= "11111111111";
		end if;
	end if;
end process;

ABS_GEN_003: process(CLK)
begin
	if rising_edge(CLK) then
		if(YA_IN(11) = '0') then
			-- positive
			YA_ABS <= YA_IN(10 downto 0);
	  	elsif (YA_IN /= "100000000000") then
			-- negative, but not the most negative (special case)
			YA_ABS <= not(YA_IN(10 downto 0)) + 1;
		else
			YA_ABS <= "11111111111";
		end if;
	end if;
end process;
ABS_GEN_004: process(CLK)
begin
	if rising_edge(CLK) then
		if(YB_IN(11) = '0') then
			-- positive
			YB_ABS <= YB_IN(10 downto 0);
	  	elsif (YB_IN /= "100000000000") then
			-- negative, but not the most negative (special case)
			YB_ABS <= not(YB_IN(10 downto 0)) + 1;
		else
			YB_ABS <= "11111111111";
		end if;
	end if;
end process;


-- size the input 'usable' magnitude (keep the 6 most significant bits of the magnitude)
-- This limits the size of the look-up table to 11 (5/6 real + 5/6 imaginary) input bits.
MAGNITUDE_ADJUSTMENT: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLEA_CLK_IN_D <= SAMPLEA_CLK_IN;
		SAMPLEA_CLK_IN_D2 <= SAMPLEA_CLK_IN_D;
		SAMPLEA_CLK_IN_D3 <= SAMPLEA_CLK_IN_D2;
		SAMPLEA_CLK_IN_D4 <= SAMPLEA_CLK_IN_D3;
		SAMPLEA_CLK_OUT <= SAMPLEA_CLK_IN_D4;
		SAMPLEB_CLK_IN_D <= SAMPLEB_CLK_IN;
		SAMPLEB_CLK_IN_D2 <= SAMPLEB_CLK_IN_D;
		SAMPLEB_CLK_IN_D3 <= SAMPLEB_CLK_IN_D2;
		SAMPLEB_CLK_IN_D4 <= SAMPLEB_CLK_IN_D3;
		SAMPLEB_CLK_OUT <= SAMPLEB_CLK_IN_D4;

		NO_OF_SHIFTSA_D <= NO_OF_SHIFTSA;
		NO_OF_SHIFTSA_D2 <= NO_OF_SHIFTSA_D;
		NO_OF_SHIFTSB_D <= NO_OF_SHIFTSB;
		NO_OF_SHIFTSB_D2 <= NO_OF_SHIFTSB_D;

		if(SAMPLEA_CLK_IN_D = '1') then
			-- go down the concentric circle from the maximum amplitude downwards.
			if(XA_ABS(10) = '1') or (YA_ABS(10) = '1') then
				XA_ABS_Q <= XA_ABS(10 downto 5);
				YA_ABS_Q <= YA_ABS(10 downto 5);
				NO_OF_SHIFTSA <= "110";
			elsif(XA_ABS(9) = '1') or (YA_ABS(9) = '1') then
				XA_ABS_Q <= XA_ABS(9 downto 4);
				YA_ABS_Q <= YA_ABS(9 downto 4);
				NO_OF_SHIFTSA <= "101";
			elsif(XA_ABS(8) = '1') or (YA_ABS(8) = '1') then
				XA_ABS_Q <= XA_ABS(8 downto 3);
				YA_ABS_Q <= YA_ABS(8 downto 3);
				NO_OF_SHIFTSA <= "100";
			elsif(XA_ABS(7) = '1') or (YA_ABS(7) = '1') then
				XA_ABS_Q <= XA_ABS(7 downto 2);
				YA_ABS_Q <= YA_ABS(7 downto 2);
				NO_OF_SHIFTSA <= "011";
			elsif(XA_ABS(6) = '1') or (YA_ABS(6) = '1') then
				XA_ABS_Q <= XA_ABS(6 downto 1);
				YA_ABS_Q <= YA_ABS(6 downto 1);
				NO_OF_SHIFTSA <= "010";
			elsif(XA_ABS(5) = '1') or (YA_ABS(5) = '1') then
				XA_ABS_Q <= XA_ABS(5 downto 0);
				YA_ABS_Q <= YA_ABS(5 downto 0);
				NO_OF_SHIFTSA <= "001";
			else
				XA_ABS_Q <= XA_ABS(4 downto 0) & '1';
				YA_ABS_Q <= YA_ABS(4 downto 0) & '1';
				NO_OF_SHIFTSA <= "000";
			end if;
	  	end if;
		if(SAMPLEB_CLK_IN_D = '1') then
			-- go down the concentric circle from the maximum amplitude downwards.
			if(XB_ABS(10) = '1') or (YB_ABS(10) = '1') then
				XB_ABS_Q <= XB_ABS(10 downto 5);
				YB_ABS_Q <= YB_ABS(10 downto 5);
				NO_OF_SHIFTSB <= "110";
			elsif(XB_ABS(9) = '1') or (YB_ABS(9) = '1') then
				XB_ABS_Q <= XB_ABS(9 downto 4);
				YB_ABS_Q <= YB_ABS(9 downto 4);
				NO_OF_SHIFTSB <= "101";
			elsif(XB_ABS(8) = '1') or (YB_ABS(8) = '1') then
				XB_ABS_Q <= XB_ABS(8 downto 3);
				YB_ABS_Q <= YB_ABS(8 downto 3);
				NO_OF_SHIFTSB <= "100";
			elsif(XB_ABS(7) = '1') or (YB_ABS(7) = '1') then
				XB_ABS_Q <= XB_ABS(7 downto 2);
				YB_ABS_Q <= YB_ABS(7 downto 2);
				NO_OF_SHIFTSB <= "011";
			elsif(XB_ABS(6) = '1') or (YB_ABS(6) = '1') then
				XB_ABS_Q <= XB_ABS(6 downto 1);
				YB_ABS_Q <= YB_ABS(6 downto 1);
				NO_OF_SHIFTSB <= "010";
			elsif(XB_ABS(5) = '1') or (YB_ABS(5) = '1') then
				XB_ABS_Q <= XB_ABS(5 downto 0);
				YB_ABS_Q <= YB_ABS(5 downto 0);
				NO_OF_SHIFTSB <= "001";
			else
				XB_ABS_Q <= XB_ABS(4 downto 0) & '1';
				YB_ABS_Q <= YB_ABS(4 downto 0) & '1';
				NO_OF_SHIFTSB <= "000";
			end if;
	  	end if;
		
	end if;
end process;		    

MAGA_DIFFER <= ('0' & YA_ABS_Q) - ('0' & XA_ABS_Q);
MAGB_DIFFER <= ('0' & YB_ABS_Q) - ('0' & XB_ABS_Q);

-- Generate address for LUTs
LUT_ADDR_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then

		if(SAMPLEA_CLK_IN_D2 = '1') then
			if(MAGA_DIFFER > 0) then
				if(YA_ABS_Q(0) = '1') then
					ADDRA <= not(XA_ABS_Q & YA_ABS_Q(5 downto 1));
				else
					ADDRA <= (XA_ABS_Q & YA_ABS_Q(5 downto 1));
				end if;
 			elsif(MAGA_DIFFER = 0) then
				if(XA_ABS_Q(0) = '1') then
					ADDRA <= not(YA_ABS_Q & XA_ABS_Q(5 downto 1)) - 1; -- minor adjustment at 45deg indicies
				else
					ADDRA <= YA_ABS_Q & XA_ABS_Q(5 downto 1);
				end if;
			else
				if(XA_ABS_Q(0) = '1') then
					ADDRA <= not(YA_ABS_Q & XA_ABS_Q(5 downto 1));
				else
					ADDRA <= YA_ABS_Q & XA_ABS_Q(5 downto 1);
				end if;
			end if;
	  	end if;
		if(SAMPLEB_CLK_IN_D2 = '1') then
			if(MAGB_DIFFER > 0) then
				if(YB_ABS_Q(0) = '1') then
					ADDRB <= not(XB_ABS_Q & YB_ABS_Q(5 downto 1));
				else
					ADDRB <= (XB_ABS_Q & YB_ABS_Q(5 downto 1));
				end if;
 			elsif(MAGB_DIFFER = 0) then
				if(XB_ABS_Q(0) = '1') then
					ADDRB <= not(YB_ABS_Q & XB_ABS_Q(5 downto 1)) - 1; -- minor adjustment at 45deg indicies
				else
					ADDRB <= YB_ABS_Q & XB_ABS_Q(5 downto 1);
				end if;
			else
				if(XB_ABS_Q(0) = '1') then
					ADDRB <= not(YB_ABS_Q & XB_ABS_Q(5 downto 1));
				else
					ADDRB <= YB_ABS_Q & XB_ABS_Q(5 downto 1);
				end if;
			end if;
	  	end if;
	end if;
end process;

POLAR3MAGPROM_001: POLAR3MAGPROM PORT MAP(
	CLKA => CLK,
	CSA => '1',
	OEA => '1',
	ADDRA => ADDRA,
	DOA => MAGNITUDEA_RANGE_0_175(8 downto 0),
	CLKB => CLK,
	CSB => '1',
	OEB => '1',
	ADDRB => ADDRB,
	DOB => MAGNITUDEB_RANGE_0_175(8 downto 0)
);


--------------------------------------------------------------------------
-- MAGNITUDE LOOK-UP CODE STARTS HERE
--------------------------------------------------------------------------
-- Undo the shifts that were performed earlier (see the generation of
-- the signals X_ABS_Q and Y_ABS_Q above). Set the lower bits in the 
-- middle i.e. to round((2^noOfBits)/2). This gives a better average SNR 
-- than simply zero-padding.
UNSHIFT_001: process(CLK)
begin
	if rising_edge(CLK) then
		 
		if(SAMPLEA_CLK_IN_D4 = '1') then
			case NO_OF_SHIFTSA_D2 is
				when "000" => 
					MAGNITUDEA_OUT(13 downto 8) <= (others => '0');
					MAGNITUDEA_OUT(7 downto 0) <= MAGNITUDEA_RANGE_0_175(8 downto 1);
		
				when "001" => 
					MAGNITUDEA_OUT(13 downto 9) <= (others => '0');
					MAGNITUDEA_OUT(8 downto 0) <= MAGNITUDEA_RANGE_0_175;
		
				when "010" => 
					MAGNITUDEA_OUT(13 downto 10) <= (others => '0');
					MAGNITUDEA_OUT(9 downto 1) <= MAGNITUDEA_RANGE_0_175;
					MAGNITUDEA_OUT(0 downto 0) <= "0";
		
				when "011" => 
					MAGNITUDEA_OUT(13 downto 11) <= (others => '0');
					MAGNITUDEA_OUT(10 downto 2) <= MAGNITUDEA_RANGE_0_175;
					MAGNITUDEA_OUT(1 downto 0) <= "01";
		
				when "100" => 
					MAGNITUDEA_OUT(13 downto 12) <= (others => '0');
					MAGNITUDEA_OUT(11 downto 3) <= MAGNITUDEA_RANGE_0_175;
					MAGNITUDEA_OUT(2 downto 0) <= "011";
		
				when "101" => 
					MAGNITUDEA_OUT(13) <= '0';
					MAGNITUDEA_OUT(12 downto 4) <= MAGNITUDEA_RANGE_0_175;
					MAGNITUDEA_OUT(3 downto 0) <= "0111";
		
				when others => -- "110"
					MAGNITUDEA_OUT(13 downto 5) <= MAGNITUDEA_RANGE_0_175;
					MAGNITUDEA_OUT(4 downto 0) <= "01111";
		
			end case;
		end if;
		if(SAMPLEB_CLK_IN_D4 = '1') then
			case NO_OF_SHIFTSB_D2 is
				when "000" => 
					MAGNITUDEB_OUT(13 downto 8) <= (others => '0');
					MAGNITUDEB_OUT(7 downto 0) <= MAGNITUDEB_RANGE_0_175(8 downto 1);
		
				when "001" => 
					MAGNITUDEB_OUT(13 downto 9) <= (others => '0');
					MAGNITUDEB_OUT(8 downto 0) <= MAGNITUDEB_RANGE_0_175;
		
				when "010" => 
					MAGNITUDEB_OUT(13 downto 10) <= (others => '0');
					MAGNITUDEB_OUT(9 downto 1) <= MAGNITUDEB_RANGE_0_175;
					MAGNITUDEB_OUT(0 downto 0) <= "0";
		
				when "011" => 
					MAGNITUDEB_OUT(13 downto 11) <= (others => '0');
					MAGNITUDEB_OUT(10 downto 2) <= MAGNITUDEB_RANGE_0_175;
					MAGNITUDEB_OUT(1 downto 0) <= "01";
		
				when "100" => 
					MAGNITUDEB_OUT(13 downto 12) <= (others => '0');
					MAGNITUDEB_OUT(11 downto 3) <= MAGNITUDEB_RANGE_0_175;
					MAGNITUDEB_OUT(2 downto 0) <= "011";
		
				when "101" => 
					MAGNITUDEB_OUT(13) <= '0';
					MAGNITUDEB_OUT(12 downto 4) <= MAGNITUDEB_RANGE_0_175;
					MAGNITUDEB_OUT(3 downto 0) <= "0111";
		
				when others => -- "110"
					MAGNITUDEB_OUT(13 downto 5) <= MAGNITUDEB_RANGE_0_175;
					MAGNITUDEB_OUT(4 downto 0) <= "01111";
		
			end case;
		end if;
	end if;
end process;

end Behavior;
