-- altera vhdl_input_version vhdl_2008
-- XXX: QSYS Fix (https://www.intel.com/content/www/us/en/support/programmable/articles/000079458.html)

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity true_dual_port_ram is
        generic (
            ADDR_WIDTH      : natural := 8;
            DATA_WIDTH      : natural := 12;
            MEMORY_DEPTH    : natural := 256
        );
        port (
            clk         : in std_logic;
            addr_a      : in std_logic_vector(ADDR_WIDTH-1 downto 0);
            addr_b      : in std_logic_vector(ADDR_WIDTH-1 downto 0);
            wen_a       : in std_logic;
            wen_b       : in std_logic;
            ren_a       : in std_logic;
            ren_b       : in std_logic;
            wr_data_a   : in std_logic_vector(DATA_WIDTH-1 downto 0);
            wr_data_b   : in std_logic_vector(DATA_WIDTH-1 downto 0);
            rd_data_a   : out std_logic_vector(DATA_WIDTH-1 downto 0);
            rd_data_b   : out std_logic_vector(DATA_WIDTH-1 downto 0)
        );
end entity;

architecture arch of true_dual_port_ram is
    
    type RAM_TYPE is array (0 to MEMORY_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
    
    signal mem : RAM_TYPE := (others => (others => '0'));
    
begin

    ram_A_prc : process(all)
    begin
        if rising_edge(clk) then
            -- Mixed Port Read-During-Write not supported
            assert (not (addr_a = addr_b and ((ren_a = '1' and wen_b = '1') or (wen_b = '1' and ren_a = '1')))) severity FAILURE;
            
            -- Port A
            rd_data_a   <= (others => '0');
            
            if (wen_a = '1') then
                mem(to_integer(unsigned(addr_a)))   <= wr_data_a;
            end if;
            
            if (ren_a = '1') then
                -- Same Port Read-During-Write returns New Data
                if (wen_a = '1') then
                    rd_data_a   <= wr_data_a;
                else
                    rd_data_a   <= mem(to_integer(unsigned(addr_a)));
                end if;
            end if;
            
            -- Port B
            rd_data_b   <= (others => '0');
            
            if (wen_b = '1') then
                mem(to_integer(unsigned(addr_b)))   <= wr_data_b;
            end if;
            
            if (ren_b = '1') then
                -- Same Port Read-During-Write returns New Data
                if (wen_b = '1') then
                    rd_data_b   <= wr_data_b;
                else
                    rd_data_b   <= mem(to_integer(unsigned(addr_b)));
                end if;
            end if;
        end if;
    end process;
end architecture;
