-------------------------------------------------------------
-- Filename:  POLAR3PHASEPROM.VHD
-- Authors: 
-- 	from http://www.xilinx.com/support/documentation/sw_manuals/xilinx14_4/xst_v6s6.pdf  p262
--		Alain Zarembowitch / MSS
-- Version: Rev 0
-- Last modified: 4/29/17

-- Inheritance: 	LFSR11PROM.VHD, ROM1.VHD
--
-- description:  synthesizable generic dual port ROM. Customized for cartesian to polar coordinates look-up.
-- Warning: convoluted pointers to alleviate re-writing the ROM contents transferred from a Xilinx block ram.
-- Mimics RAMB16_S9_S9 Xilinx primitive, with 8 data bits + 1 parity bit
-- POLAR3PHASEPROM.vhd for phase output
-- POLAR3MAGPROM.vhd for magnitude output
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity POLAR3PHASEPROM is
	 Generic (
		ADDR_WIDTH: integer := 11
	);
    Port ( 
	    -- Port A
		CLKA   : in  std_logic;
		CSA: in std_logic;	-- chip select, active high
		OEA : in std_logic;	-- output enable, active high
		ADDRA  : in  std_logic_vector(ADDR_WIDTH-1 downto 0);
		DOA  : out std_logic_vector(8 downto 0);
			-- Stores 8 contiguous PRBS-11 sequence in DATA_OUT(7:0). Period is 8*2047 bits.	
			-- SOF is placed in DATA_OUT(8)

		-- Port B 
		CLKB   : in  std_logic;
		CSB: in std_logic;	-- chip select, active high
		OEB : in std_logic;	-- output enable, active high
		ADDRB  : in  std_logic_vector(ADDR_WIDTH-1 downto 0);
		DOB  : out std_logic_vector(8 downto 0)
		);
end entity;

architecture Behavioral of POLAR3PHASEPROM is
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- inferred rom
signal DOA_local: std_logic_vector(7 downto 0) := (others => '0');
signal DOB_local: std_logic_vector(7 downto 0) := (others => '0');
type ROM_TYPE is array ( (2**(ADDR_WIDTH-5))-1 downto 0 ) of std_logic_vector(255 downto 0);
constant ROM : ROM_TYPE := (
		-- Phase bits 7:0 (lower half)
      -- Address 0 to 511
      x"0000000000000000000000000000000000000000000000000000000000000000",
      x"0505060606060607070708080809090A0B0B0C0D0E10111316191E242E406900",
      x"0A0B0B0B0C0C0D0D0E0E0F10111213141516181A1C1F22262B323B475B7CC0FD",
      x"101011111212131415161718191A1C1E1F2224272A2E3339404A576984B000FB",
      x"1515161718191A1B1C1D1E20212325272A2D3034383D444B55617288A9DCFDF8",
      x"1A1B1C1D1E1F2021232426282A2C2E3134383C40464C545D69788BA5CA00FAF5",
      x"1F20212223252628292B2D2F3234373B3E42474D535B646F7C8DA3C0E7FDF8F2",
      x"24252628292B2C2E303235373A3D4044484D53596069737F8EA1B9D700FAF5EF",
      x"292B2C2D2F31333537393C3F4245494E52585E656D77828FA0B4CDEDFDF7F2ED",
      x"2E3031333537393B3E4043464A4E52575C6269717A84909FB0C5E000FAF4EFEA",
      x"333537393B3D3F4244474A4E52565B60666C747C86919EADC0D6F0FDF7F1ECE7",
      X"383A3C3E404345484B4E52555A5E63696F767E87919DABBBCEE500FAF4EEE9E3",
      X"3D3F414446494B4E5155595D61666C72788088929DA9B8C8DCF3FDF7F1EBE6E0",
      X"424447494C4E5155585C6064696E747A8189929CA8B5C4D5E900FAF3EEE8E2DD",
      X"474A4C4F5154575B5E62676B70767C838A939CA6B2C0CFE1F5FDF6F0EAE5DFDA",
      X"4C4F5154575A5D6165696D72787E848B939CA5B0BCCADAEC00F9F3EDE7E1DCD7",
      -- Address 512 to 1023
      X"515456595C6063676B6F74797F858C939BA4AEBAC6D4E4F6FDF6F0E9E4DED9D3",
      X"56595B5E6265696D71767B80868C939BA3ADB7C3CFDEEE00F9F2ECE6E0DBD5D0",
      X"5B5E6164676B6F73777C81878D949BA3ABB5C0CBD8E7F7FCF5EFE9E2DDD7D2CD",
      X"5F6266696C7074797D82888D949BA2AAB3BDC8D4E1F000F9F2EBE5DFD9D4CEC9",
      X"64676B6E72767A7E83888E949AA2A9B2BBC5D0DCE9F8FCF5EEE8E1DBD5D0CBC6",
      X"696C6F73777B7F84898E949AA1A8B0B9C2CCD7E4F100F8F1EAE4DDD7D2CCC7C2",
      X"6E7174787C80858A8F949AA1A7AFB7C0C9D4DFEBF9FCF5EDE6E0DAD4CEC9C3BE",
      X"7276797D81858A8F949AA0A7AEB5BEC6D0DBE6F200F8F0E9E2DCD6D0CAC5C0BB",
      X"777A7E82868B8F949AA0A6ADB4BCC4CDD7E1EDF9FCF4ECE5DED8D2CCC6C1BCB7",
      X"7B7F83878B90959A9FA5ACB2BAC2CAD3DDE8F300F8F0E8E1DAD4CEC8C2BDB8B3",
      X"8083878B90959A9FA5ABB1B8C0C8D0DAE3EEFAFCF3EBE4DDD6D0CAC4BEB9B4AF",
      X"84888C90959A9FA4AAB0B7BEC5CDD6E0EAF400F7EFE7E0D8D2CBC5C0BAB5B0AB",
      X"888C9095999EA4A9AFB5BCC3CBD3DCE5EFFAFBF3EAE2DBD4CDC7C1BBB6B1ACA8",
      X"8D9195999EA3A9AEB4BBC1C9D0D9E2EBF500F7EEE6DED6CFC9C3BDB7B2ADA8A3",
      X"9195999EA3A8ADB3B9C0C7CED6DEE7F0FBFBF2E9E1D9D2CBC4BEB8B3AEA9A49F",
      X"95999EA2A7ADB2B8BEC5CCD3DBE3ECF600F6EDE4DCD4CDC6C0BAB4AEA9A4A09B",
      -- Address 1024 to 1535
		-- Phase bits 7:0 (upper half)			    
      X"999DA2A7ACB1B7BDC3C9D1D8E0E8F1FBFBF1E8DFD7CFC8C1BBB5AFAAA5A09B97",
      X"9DA2A6ABB0B6BBC1C8CED5DDE5EDF600F5ECE2DAD2CAC3BCB6B0ABA5A09C9793",
      X"A1A6AAAFB4BAC0C6CCD3DAE2EAF2FBFAF0E6DDD4CCC5BEB7B1ABA6A19C97938F",
      X"A5AAAFB3B9BEC4CAD1D7DFE6EEF700F5EAE1D7CFC7C0B9B2ACA6A19C97938E8A",
      X"A9AEB3B8BDC2C8CFD5DCE3EBF3FCFAEFE4DBD2C9C2BAB3ADA7A19C97928E8A86",
      X"ADB2B7BCC1C7CDD3D9E0E8EFF700F4E9DED5CCC4BCB5AEA8A29C97928E898581",
      X"B1B6BBC0C5CBD1D7DEE5ECF4FCFAEEE2D8CFC6BEB6AFA8A29D97928D8985817D",
      X"B5B9BEC4C9CFD5DBE2E9F0F800F3E7DCD2C8C0B8B0A9A39D97928D8884807C78",
      X"B8BDC2C7CDD3D9DFE6EDF4FCF9ECE0D5CBC2B9B1AAA39D97928D88837F7B7774",
      X"BCC1C6CBD1D7DDE3EAF1F800F2E5D9CEC4BBB3ABA49D97918C87827E7A76736F",
      X"C0C5CACFD5DAE1E7EEF5FCF8EADDD2C7BDB4ACA59E97918C86827D7975716E6A",
      X"C3C8CDD3D8DEE4EBF2F900F0E2D6CAC0B6ADA59E97918B86817C7874706C6966",
      X"C7CCD1D6DCE2E8EEF5FCF8E8DACEC2B8AFA69E97918A85807B76726E6B676461",
      X"CACFD4DAE0E5ECF2F900EFE0D2C5BAB0A79F97908A847F7A75716D6965625F5C",
      X"CED3D8DDE3E9EFF6FCF7E6D6C9BDB2A89F979089837D78736F6B6763605D5A57",
      X"D1D6DBE1E6ECF3F900EDDCCDC0B4A9A0978F88827C77726D6965615E5B585552",
      -- Address 1536 to 2047
      X"D4D9DFE4EAF0F6FDF5E2D2C3B6ABA0978F87817B75706B67635F5C585552504D",
      X"D7DDE2E7EDF3F900EAD7C7B9ACA1978E867F79736E6964605D595653504D4B48",
      X"DBE0E5EBF0F7FDF4DECCBCAEA2978E857E77716B66625E5A5653504D4A484643",
      X"DEE3E8EEF4FA00E7D2C0B0A3978D847C756F69645F5B5753504D4A474542403E",
      X"E1E6EBF1F7FDF2D9C4B3A4978C827A736C66615C5753504D494744423F3D3B39",
      X"E4E9EFF4FA00E2CAB6A5978B81787069635D5854504C494643403E3C3A383634",
      X"E7ECF2F7FDEFD2BAA7978A7F756D655F5954504C4845423F3C3A38363432312F",
      X"EAEFF5FA00DCC0A997887C7269615B55504B4744403D3B38363432302E2D2B2A",
      X"EDF2F7FDEAC7AC9786796E645D56504B46423F3B383633312F2D2C2A29272625",
      X"F0F5FA00D2B0978475695F57504A45403C393633312E2C2A292725242322201F",
      X"F3F8FDE2B697817063585049433E3A3633302D2B2927252322201F1E1D1C1B1A",
      X"F5FB00C0977C695B5047403B36322E2B29262422201F1E1C1B1A191817161615",
      X"F8FDD297755F50453C36312C292523201E1D1B1A181716151414131211111010",
      X"FB0097695040362E2924201E1B1917161413121110100F0E0E0D0D0C0C0B0B0B",
      X"FD97503629201B171412100F0E0D0C0B0A0A0909080807070707060606060505",
      X"0000000000000000000000000000000000000000000000000000000000000000"
);
-- parity bits (Mimics RAMB16_S9_S9 Xilinx primitive)
signal DOPA_local: std_logic_vector(0 downto 0) := (others => '0');
signal DOPB_local: std_logic_vector(0 downto 0) := (others => '0');
type ROMP_TYPE is array ( (2**(ADDR_WIDTH-8))-1 downto 0 ) of std_logic_vector(255 downto 0);
constant ROMP : ROMP_TYPE := (
		-- Phase bit 8 (lower half)
      x"0000000800000000000000040000000000000002000000000000000100000000",
      x"0000008000000000000000400000000000000020000000000000001000000000",
      x"0000080000000000000004000000000000000200000000000000010000000000",
      x"0000800000000000000040000000000000002000000000000000100000000000",
		-- Phase bit 8 (upper half)  -- last entry in LUT adjusted to correct extreme case
      x"0008000000000000000400000000000000020000000000000001000000000000",
      x"0080000000000000004000000000000000200000000000000010000000000000",
      x"0800000000000000040000000000000002000000000000000100000000000000",
      x"8000000000000000400000000000000020000000000000001000000000000000"
);


--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin
-- Port A read
process(CLKA, ADDRA)
variable ROWA: integer range 0 to (2**(ADDR_WIDTH-5))-1 := 0;
variable COLA: integer range 0 to 255 := 0;
variable ADDRAL: std_logic_vector(7 downto 0) := (others => '0');
variable ROWPA: integer range 0 to (2**(ADDR_WIDTH-8))-1 := 0;
variable COLPA: integer range 0 to 255 := 0;
variable ADDRPAL: std_logic_vector(7 downto 0) := (others => '0');
begin
	ROWA := to_integer(unsigned(not ADDRA(ADDRA'left downto 5)));	-- 32 bytes per row, reading from top to bottom
	ADDRAL := ADDRA(4 downto 0) & "000";	-- selected byte in a row, reading from right to left
	COLA := to_integer(unsigned(ADDRAL));	
	ROWPA := to_integer(unsigned(not ADDRA(ADDRA'left downto 8)));	-- 256 bits per row, reading from top to bottom
	ADDRPAL := ADDRA(7 downto 0);	-- selected byte in a row, reading from right to left
	COLPA := to_integer(unsigned(ADDRPAL));	

	if rising_edge(CLKA) then
		if(CSA = '1') then
			DOA_local <= ROM(ROWA)(COLA+7 downto COLA);
			DOPA_local <= ROMP(ROWPA)(COLPA downto COLPA);
		else
			DOA_local <= (others => '0');
			DOPA_local <= (others => '0');
		end if;
	end if;
end process;

-- tri-state output
DOA <= (DOPA_local & DOA_local) when (CSA = '1') and (OEA = '1') else (others => 'Z');
 

-- Port B read
process(CLKB, ADDRB)
variable ROWB: integer range 0 to (2**(ADDR_WIDTH-5))-1 := 0;
variable COLB: integer range 0 to 255 := 0;
variable ADDRBL: std_logic_vector(7 downto 0) := (others => '0');
variable ROWPB: integer range 0 to (2**(ADDR_WIDTH-8))-1 := 0;
variable COLPB: integer range 0 to 255 := 0;
variable ADDRPBL: std_logic_vector(7 downto 0) := (others => '0');
begin
	ROWB := to_integer(unsigned(not ADDRB(ADDRB'left downto 5)));	-- 32 bytes per row, reading from top to bottom
	ADDRBL := ADDRB(4 downto 0) & "000";
	COLB := to_integer(unsigned(ADDRBL));	
	ROWPB := to_integer(unsigned(not ADDRB(ADDRB'left downto 8)));	-- 256 bits per row, reading from top to bottom
	ADDRPBL := ADDRB(7 downto 0);	-- selected byte in a row, reading from right to left
	COLPB := to_integer(unsigned(ADDRPBL));	


	if rising_edge(CLKB) then
		if(CSB = '1') then
			DOB_local <= ROM(ROWB)(COLB+7 downto COLB);
			DOPB_local <= ROMP(ROWPB)(COLPB downto COLPB);
		else
			DOB_local <= (others => '0');
			DOPB_local <= (others => '0');
		end if;
	end if;
end process;

-- tri-state output
DOB <= (DOPB_local & DOB_local) when (CSB = '1') and (OEB = '1') else (others => 'Z');

end Behavioral;
