-------------------------------------------------------------
-- Filename:  POLAR3MAGPROM.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 POLAR3MAGPROM 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 POLAR3MAGPROM 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 := (
		-- Magnitude bits 7:0 (lower half)
      -- Address 0 to 511
      x"FCF4ECE4DCD4CCC4BCB4ACA49C948C847C746C645C544C443C342C241C140C04",
      x"FCF4ECE4DCD4CCC4BCB4ACA49C948C847C746C645C544C443C342C241C140D5F",
      x"FCF4ECE4DCD4CCC4BCB4ACA49C948C847C746C645C544C443D352D251D160E5C",
      x"FCF4ECE4DCD4CCC4BCB4ACA49C948D857D756D655D554D453D352E261E175359",
      x"FDF5EDE5DDD5CDC5BDB5ADA59D958D857D756D655D564E463E362F27201A5156",
      x"FDF5EDE5DDD5CDC5BDB5ADA59D958D867E766E665E564F473F38302922484E54",
      x"FDF5EDE5DDD5CDC5BEB6AEA69E968E867E766F675F5750484139322B25454B51",
      x"FEF6EEE6DED6CEC6BEB6AEA69E978F877F7770686059514A423B342E3D42484E",
      x"FEF6EEE6DED6CEC7BFB7AFA79F97908880787169615A524B443D36303A40464C",
      x"FFF7EFE7DFD7CFC7BFB8B0A8A09891898179726A635B544D463F3931373D4349",
      x"FFF7EFE7E0D8D0C8C0B8B1A9A199928A827B736C645D564F48423B2F343A4046",
      x"00F8F0E8E0D9D1C9C1B9B2AAA29A938B847C756D665F58514A44262C32383E44",
      x"01F9F1E9E1D9D2CAC2BAB3ABA39C948C857E766F68615A534D4723292F353B41",
      x"01F9F2EAE2DAD3CBC3BBB4ACA49D958E867F78716A635C564F1B21262C32383F",
      x"02FAF3EBE3DBD4CCC4BDB5ADA69E978F88817A736C655E5852181E242A30363C",
      x"03FBF4ECE4DCD5CDC5BEB6AFA7A098918A837C756E67615B10151B21272D333A",
      -- Address 512 to 1023
      x"04FCF5EDE5DDD6CEC7BFB8B0A9A19A938C847E77706A635D0D12181E242B3137",
      x"05FDF6EEE6DFD7CFC8C0B9B2AAA39C948D868079726C66040A10161C22282E35",
      x"06FEF7EFE7E0D8D1C9C2BAB3ACA59D968F89827B756F6901070D13191F262C32",
      x"0700F8F0E9E1DAD2CBC3BCB5AEA69F98918B847E7771F9FF040A10171D232A30",
      x"0801F9F2EAE3DBD4CCC5BEB6AFA8A19A948D86807A74F6FC02080E141A21272E",
      x"0A02FBF3EBE4DDD5CEC7BFB8B1AAA39C968F89837DEEF3F9FF050B12181E252C",
      x"0B03FCF4EDE6DED7D0C8C1BAB3ACA59F98928B857FEBF1F6FD03090F161C2329",
      x"0C05FDF6EEE7E0D9D1CAC3BCB5AEA8A19A948E88E2E8EEF4FA00060D131A2027",
      x"0E06FFF7F0E9E1DAD3CCC5BEB7B0AAA39D97908BDFE5EBF1F7FE040A11181E25",
      x"0F0800F9F2EAE3DCD5CEC7C0B9B3ACA69F9993D7DDE3E9EFF5FB02080F151C23",
      x"110902FBF3ECE5DED7D0C9C2BBB5AEA8A29C96D4DAE0E6ECF2F9FF060C131A21",
      x"120B04FCF5EEE7E0D9D2CBC4BEB7B1ABA49ECCD1D7DDE3EAF0F6FD040A11181F",
      x"140C05FEF7F0E9E2DBD4CDC7C0BAB3ADA7A1C9CFD5DBE1E7EEF4FB01080F161D",
      x"150E0700F9F2EBE4DDD6CFC9C2BCB6B0AAC0C6CCD2D8DEE5EBF2F9FF060D141B",
      x"17100902FBF4EDE6DFD8D2CBC5BFB8B2ADBEC3C9CFD6DCE2E9F0F6FD040B1219",
      x"19120B04FDF6EFE8E1DBD4CEC7C1BBB5B5BBC1C7CDD3DAE0E7EDF4FB02091017",
      -- Address 1024 to 1535
		-- Magnitude bits 7:0 (upper half)	  					
      x"1B140C05FFF8F1EAE3DDD6D0CAC4BEB8B2B8BEC4CAD1D7DEE5EBF2F900070E15",
      x"1C150E0701FAF3ECE6DFD9D3CCC6C0AAAFB5BBC2C8CED5DCE2E9F0F7FE050C14",
      x"1E17100903FCF5EFE8E2DBD5CFC9C3A7ADB3B9BFC6CCD3DAE0E7EEF5FC030B12",
      x"2019120C05FEF7F1EAE4DED8D2CC9EA4AAB0B6BDC3CAD1D7DEE5ECF3FA020910",
      x"221B140E0700FAF3EDE7E0DAD4CE9CA1A7AEB4BAC1C8CED5DCE3EAF1F900070E",
      x"241D17100903FCF6EFE9E3DDD793999FA5ABB2B8BFC6CCD3DAE1E9F0F7FE060D",
      x"261F19120B05FEF8F2ECE6E0DA90969CA2A9AFB6BDC4CAD1D8E0E7EEF5FD040B",
      x"28221B140E0701FBF4EEE8E2888E949AA0A6ADB4BBC1C8D0D7DEE5ECF4FB020A",
      x"2B241D17100A03FDF7F1EBE5858B91979EA4ABB2B9C0C7CED5DCE3EBF2FA0109",
      x"2D261F19120C0600F9F4EE7C82888E959BA2A9B0B7BEC5CCD3DBE2E9F1F80007",
      x"2F28221B150E0802FCF6F07A80868C9299A0A7AEB5BCC3CAD2D9E0E8EFF7FE06",
      x"312B241E17110B05FFF971777D838A90979EA5ACB3BAC1C9D0D7DFE6EEF5FD05",
      x"332D26201A140D0702FC6E747A81878E959CA3AAB1B8C0C7CED6DDE5EDF4FC03",
      x"362F29221C16100A04666C72787E858C939AA1A8AFB7BEC6CDD5DCE4EBF3FB02",
      x"38322B251F19130D0763696F767C838A91989FA7AEB5BDC4CCD3DBE2EAF2F901",
      x"3A342E28211B15105B60666D737A81888F969EA5ACB4BBC3CAD2DAE1E9F1F800",
		-- Address 1536 to 2047
      x"3D36302A241E1812585E646B71787F868D959CA3ABB2BAC2C9D1D8E0E8F0F7FF",
      x"3F39332D27211B4F555B62686F767D848C939BA2AAB1B9C0C8D0D7DFE7EFF6FE",
      x"423B352F29231E4C52595F666D747B838A9299A1A8B0B8BFC7CFD6DEE6EEF6FD",
      x"443E38322C26444A50575D646B737A818990989FA7AFB6BEC6CED5DDE5EDF5FD",
      x"47413B352F2941474E545B626A717880878F979EA6AEB5BDC5CDD5DCE4ECF4FC",
      x"49433D3732393E454B525961686F777E868E959DA5ADB4BCC4CCD4DCE4EBF3FB",
      x"4C46403A34363C434950585F676E767D858D949CA4ACB4BBC3CBD3DBE3EBF3FB",
      x"4F49433D2D333A40484F565E656D747C848C949BA3ABB3BBC3CBD2DAE2EAF2FA",
      x"514B45402B31383F464D555C646C737B838B939BA2AAB2BAC2CAD2DAE2EAF2FA",
      x"544E4822282F363D444C545B636B737A828A929AA2AAB2BAC1C9D1D9E1E9F1F9",
      x"57514B1F262D343B434B525A626A727A82899199A1A9B1B9C1C9D1D9E1E9F1F9",
      x"5953171D242B333A424A52596169717981899199A1A9B1B9C1C9D1D9E1E9F1F9",
      x"5C56141B222A3139414951596169717981899098A0A8B0B8C0C8D0D8E0E8F0F8",
      x"5F0B121921293139404850586068707880889098A0A8B0B8C0C8D0D8E0E8F0F8",
      x"6209101820283038404850586068707880889098A0A8B0B8C0C8D0D8E0E8F0F8",
      x"6508101820283038404850586068707880889098A0A8B0B8C0C8D0D8E0E8F0F8"
);
-- 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 := (
		-- Magnitude bit 8 (lower half)
      x"0000000F00000007000000070000000300000003000000010000000100000000",
      x"800000FF8000007F8000007F8000003F8000003F0000001F0000001F0000000F",
      x"C000007FC000007FC000007FC00000FFC00000FF800001FF800001FF800000FF",
      x"F000000FF000000FF000000FE000001FE000001FE000001FE000003FC000003F",
		-- Magnitude bit 8 (upper half)
      x"FE000003FC000003FC000003FC000007F8000007F8000007F8000007F000000F",
      x"FF000001FF800001FF800001FF800001FF000001FF000001FF000003FE000003",
      x"F0000000F8000000F8000000FC000000FC000000FE000000FE000000FF000000",
      x"800000008000000080000000C0000000C0000000E0000000E0000000F0000000"
);


--------------------------------------------------------
--      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;
