-- 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 dp_mem_ctrl is
    generic (
        ADDR_WIDTH          : natural;
        DATA_WIDTH          : natural;
        MEMORY_DEPTH        : natural;
        MAX_BURST_LENGTH    : natural
    );
    port (
        -- SYSTEM
        clk         : in std_logic;
        reset       : in std_logic;
        -- READ PORT
        raddr       : in std_logic_vector(ADDR_WIDTH-1 downto 0);
        rvalid_in   : in std_logic;
        rready_in   : out std_logic;
        rvalid_out  : out std_logic;
        rready_out  : in std_logic;
        rdata_out   : out std_logic_vector(DATA_WIDTH-1 downto 0);
        -- WRITE PORT
        waddr       : in std_logic_vector(ADDR_WIDTH-1 downto 0);
        wvalid_in   : in std_logic;
        wready_in   : out std_logic;
        wdata_in    : in std_logic_vector(DATA_WIDTH-1 downto 0)
    );
end entity;

architecture arch of dp_mem_ctrl is
    
    -- *CONSTANT DECLARATION*
    constant READ_LATENCY : natural := 1;
    
    -- *TYPE DECLARATION*
    
    -- *SIGNAL DECLARATION*
    signal mem_read_data : std_logic_vector(DATA_WIDTH-1 downto 0);
    signal delay_line : std_logic_vector(READ_LATENCY-1 downto 0);
    signal fifo_empty : std_logic;
    signal fifo_cnt : natural range 0 to MAX_BURST_LENGTH;
    signal delay_cnt : natural range 0 to READ_LATENCY;
    signal rready_in_sig, wready_in_sig, rvalid_out_sig : std_logic;
    
begin

    --*****COMPONENT INSTANTIATION*****
    ram_inst : configuration work.dual_port_ram_cfg
        generic map (
            ADDR_WIDTH      => ADDR_WIDTH,
            DATA_WIDTH      => DATA_WIDTH,
            MEMORY_DEPTH    => MEMORY_DEPTH
        )
        port map (
            clk         => clk,
            raddr       => raddr,
            waddr       => waddr,
            wen         => wready_in_sig and wvalid_in,
            ren         => rready_in_sig and rvalid_in,
            wr_data     => wdata_in,
            rd_data     => mem_read_data
        );
    
    wready_in_sig   <= '1'; -- We are always ready to receive writes
    wready_in       <= wready_in_sig;
    
    rready_in_sig   <= '0' when (fifo_cnt - delay_cnt = 0) else '1';
    rready_in       <= rready_in_sig;
    
    rvalid_out_sig  <= not fifo_empty;
    rvalid_out      <= rvalid_out_sig;
    
    delay_line_prc :  process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                delay_line      <= (others => '0');
                delay_cnt       <= 0;
            else
                -- Shift Right
                if (READ_LATENCY > 1) then
                    delay_line(READ_LATENCY-2 downto 0) <= delay_line(READ_LATENCY-1 downto 1);
                    delay_line(READ_LATENCY-1)          <= rready_in_sig and rvalid_in;
                    
                    if ((rready_in_sig and rvalid_in) = '1' and delay_line(1) = '0') then
                        delay_cnt   <= delay_cnt + 1;
                    elsif ((rready_in_sig and rvalid_in) = '0' and delay_line(1) = '1') then
                        delay_cnt   <= delay_cnt - 1;
                    end if;
                else -- READ_LATENCY = 1
                    delay_line(0)  <= rready_in_sig and rvalid_in;
                    
                    if ((rready_in_sig and rvalid_in) = '1' and delay_line(0) = '0') then
                        delay_cnt   <= delay_cnt + 1;
                    elsif ((rready_in_sig and rvalid_in) = '0' and delay_line(0) = '1') then
                        delay_cnt   <= delay_cnt - 1;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    burst_fifo_inst : configuration work.FWFT_FIFO_cfg
        generic map (
            FIFO_DEPTH  => MAX_BURST_LENGTH,
            DATA_WIDTH  => DATA_WIDTH
        )
        port map (
            clk         => clk,
            reset       => reset,
            data_in     => mem_read_data,
            write       => delay_line(0),
            read        => rready_out and rvalid_out_sig,
            data_out    => rdata_out,
            empty       => fifo_empty,
            full        => open,
            free        => fifo_cnt
        );
end architecture;
