----------------------------------------------------------------------------------
-- Project Name: RPU
-- Description: Memory controller unit of RPU
--
-- Very simple. Allows for delays in reads, whilsts writes go through immediately.
-- MEM_ signals are expected to be exposted to any SoC fabric.
-- 
----------------------------------------------------------------------------------
-- Copyright 2016,2018,2019,2020 Colin Riley
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;

use ieee.numeric_std.all;

library work;
use work.constants.all;
entity mem_controller is
    port (
        I_clk : in STD_LOGIC;
        I_reset : in STD_LOGIC;

        O_ready : out STD_LOGIC;
        I_execute : in STD_LOGIC;
        I_dataWe : in STD_LOGIC;
        I_address : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_data : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_dataByteEn : in STD_LOGIC_VECTOR(1 downto 0);
        I_signExtend : in STD_LOGIC;
        O_data : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        O_dataReady : out STD_LOGIC;

        MEM_I_ready : in STD_LOGIC;
        MEM_O_cmd : out STD_LOGIC;
        MEM_O_we : out STD_LOGIC;
        MEM_O_byteEnable : out STD_LOGIC_VECTOR (1 downto 0);
        MEM_O_addr : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        MEM_O_data : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        MEM_I_data : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        MEM_I_dataReady : in STD_LOGIC
    );
end mem_controller;

architecture Behavioral of mem_controller is

    signal we : std_logic := '0';
    signal addr : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";
    signal indata : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";
    signal outdata : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";

    signal byteEnable : STD_LOGIC_VECTOR (1 downto 0) := "11";
    signal cmd : STD_LOGIC := '0';
    signal state : integer := 0;

    signal ready : STD_LOGIC := '0';

begin

    process (I_clk, I_execute)
    begin
        if rising_edge(I_clk) then
            if I_reset = '1' then
                we <= '0';
                cmd <= '0';
                state <= 0;
                O_dataReady <= '0';
            elsif state = 0 and I_execute = '1' and MEM_I_ready = '1' then
                we <= I_dataWe;
                addr <= I_address;
                indata <= I_data;
                byteEnable <= I_dataByteEn;
                cmd <= '1';
                O_dataReady <= '0';
                outdata <= X"ABCDEFEE";
                if I_dataWe = '0' then
                    state <= 1;-- read
                else
                    state <= 2;-- write
                end if;
            elsif state = 1 then
                cmd <= '0';
                if MEM_I_dataReady = '1' then
                    O_dataReady <= '1';
                    -- sign extend, if required
                    if I_signExtend = '1' then
                        if I_dataByteEn = F2_MEM_LS_SIZE_W then
                            outdata <= MEM_I_data;
                        elsif I_dataByteEn = F2_MEM_LS_SIZE_H then
                            outdata <= std_logic_vector(resize(signed(MEM_I_data(15 downto 0)), XLEN));
                        elsif I_dataByteEn = F2_MEM_LS_SIZE_B then
                            outdata <= std_logic_vector(resize(signed(MEM_I_data(7 downto 0)), XLEN));
                        end if;
                    else
                        outdata <= MEM_I_data;
                    end if;
                    state <= 2;
                end if;
            elsif state = 2 then
                cmd <= '0';
                state <= 0;
                O_dataReady <= '0';
            end if;
        end if;
    end process;

    O_data <= outdata;
    O_ready <= (MEM_I_ready and not I_execute) when state = 0 else '0';

    MEM_O_cmd <= cmd;
    MEM_O_byteEnable <= byteEnable;
    MEM_O_data <= indata;
    MEM_O_addr <= addr;
    MEM_O_we <= we;

end Behavioral;