--Based on https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/hb/qts/qts_qii51007.pdf
--VHDL Single-Clock Simple Dual-Port Synchronous RAM with New Data Read-During-Write Behavior

LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.mem_pkg.all;

entity single_clock_rw_ram is
	generic (
		ADDR_WIDTH : integer;
		DATA_WIDTH : integer
	);
	port (
		clk           : in std_logic;
		data_in       : in std_logic_vector(DATA_WIDTH-1 downto 0);
		write_address : in std_logic_vector(ADDR_WIDTH-1 downto 0);
		read_address  : in std_logic_vector(ADDR_WIDTH-1 downto 0);
		we            : in std_logic;
		data_out      : out std_logic_vector(DATA_WIDTH-1 downto 0);
		byteena       : in mem_byteena_type
	);
end entity;

architecture rtl of single_clock_rw_ram is
	type MEM is array(0 TO (2**ADDR_WIDTH)-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
	signal ram : MEM := (others => (others => '0'));
	signal read_address_reg : std_logic_vector(ADDR_WIDTH-1 downto 0) := (others =>'0');
	signal big_byteena : std_logic_vector(8*mem_byteena_type'length-1 downto 0);
	constant BYTE_WIDTH : integer := 8;
begin
	
	
	-- synchronous
	process(clk)
	begin
		if rising_edge(clk) then
			if (we = '1') then
				ram(to_integer(unsigned(write_address))) <= (data_in and big_byteena) or (ram(to_integer(unsigned(write_address))) and (not big_byteena));
			end if;
			read_address_reg <= read_address;
		end if;
	end process;
	data_out <= ram(to_integer(unsigned(read_address_reg)));
	
	
	set_big_byteena : process(all)
	begin
		for i in byteena'range loop
			big_byteena((i+1) * BYTE_WIDTH - 1 downto i * BYTE_WIDTH) <= (others => byteena(i));
		end loop;
	end process;
end architecture;
