----------------------------------------------------------------------------------
-- Project Name: RPU
-- Description: RPU core glue entity
--
--  Brings all core components together with a little logic.
--  This is the CPU interface required.
--
----------------------------------------------------------------------------------
-- Copyright 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;

library work;
use work.constants.all;

entity core is
    port (
        I_clk : in STD_LOGIC;
        I_reset : in STD_LOGIC;
        I_halt : in STD_LOGIC;

        -- External Interrupt interface

        -- Will be fixed when memory swizzling is brought core-size
        MEM_O_byteEnable : out std_logic_vector(1 downto 0);
        MEM_O_addr : out std_logic_vector(XLEN32M1 downto 0);
        MEM_O_data : out std_logic_vector(XLEN32M1 downto 0);
        MEM_I_data : in std_logic_vector(XLEN32M1 downto 0);
        MEM_I_dataReady : in std_logic

        ; -- This debug output contains some internal state for debugging
        O_halted : out std_logic;
        O_DBG : out std_logic_vector(63 downto 0)
    );
end core;

architecture Behavioral of core is
    component pc_unit
        port (
            I_clk : in std_logic;
            I_nPC : in std_logic_vector(XLENM1 downto 0);
            I_nPCop : in std_logic_vector(1 downto 0);
            I_intVec : in std_logic;
            O_PC : out std_logic_vector(XLENM1 downto 0)
        );
    end component;

    component control_unit
        port (
            I_clk : in STD_LOGIC;
            I_halt : in STD_LOGIC;
            I_reset : in STD_LOGIC;
            I_aluop : in STD_LOGIC_VECTOR (6 downto 0);
            O_state : out STD_LOGIC_VECTOR (6 downto 0);

            I_int : in STD_LOGIC;
            O_int_ack : out STD_LOGIC;

            I_int_enabled : in STD_LOGIC;
            I_int_mem_data : in STD_LOGIC_VECTOR(XLENM1 downto 0);
            O_idata : out STD_LOGIC_VECTOR(XLENM1 downto 0);
            O_set_idata : out STD_LOGIC;
            O_set_ipc : out STD_LOGIC;
            O_set_irpc : out STD_LOGIC;
            O_instTick : out STD_LOGIC;

            I_misalignment : in STD_LOGIC;
            I_ready : in STD_LOGIC;
            O_execute : out STD_LOGIC;
            I_dataReady : in STD_LOGIC;
            I_aluMultiCy : in STD_LOGIC;
            I_aluWait : in STD_LOGIC
        );
    end component;
    component decoder_RV32
        port (
            I_clk : in std_logic;
            I_en : in std_logic;
            I_dataInst : in std_logic_vector(31 downto 0);
            O_selRS1 : out std_logic_vector(4 downto 0);
            O_selRS2 : out std_logic_vector(4 downto 0);
            O_selD : out std_logic_vector(4 downto 0);
            O_dataIMM : out std_logic_vector(31 downto 0);
            O_regDwe : out std_logic;
            O_aluOp : out std_logic_vector(6 downto 0);
            O_aluFunc : out std_logic_vector(15 downto 0);
            O_memOp : out STD_LOGIC_VECTOR(4 downto 0);
            O_csrOP : out STD_LOGIC_VECTOR(4 downto 0);
            O_csrAddr : out STD_LOGIC_VECTOR(11 downto 0);
            O_trapExit : out STD_LOGIC;
            O_multycyAlu : out STD_LOGIC;
            O_int : out STD_LOGIC;
            O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
            I_int_ack : in STD_LOGIC
        );
    end component;
    component alu_RV32I is
        port (
            I_clk : in STD_LOGIC;
            I_en : in STD_LOGIC;
            I_dataA : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            I_dataB : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            I_dataDwe : in STD_LOGIC;
            I_aluop : in STD_LOGIC_VECTOR (4 downto 0);
            I_aluFunc : in STD_LOGIC_VECTOR (15 downto 0);
            I_PC : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            I_epc : in STD_LOGIC_VECTOR (XLENM1 downto 0);
            I_dataIMM : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            I_clear : in STD_LOGIC;
            O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            O_branchTarget : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            O_dataWriteReg : out STD_LOGIC;
            O_lastPC : out STD_LOGIC_VECTOR(XLEN32M1 downto 0);
            O_shouldBranch : out std_logic;
            O_wait : out std_logic

        );
    end component;

    component register_set
        port (
            I_clk : in std_logic;
            I_en : in std_logic;
            I_dataD : in std_logic_vector(31 downto 0);
            I_selRS1 : in std_logic_vector(4 downto 0);
            I_selRS2 : in std_logic_vector(4 downto 0);
            I_selD : in std_logic_vector(4 downto 0);
            I_we : in std_logic;
            O_dataA : out std_logic_vector(31 downto 0);
            O_dataB : out std_logic_vector(31 downto 0)
        );
    end component;

    component csr_unit
        port (
            I_clk : in STD_LOGIC;
            I_en : in STD_LOGIC;

            I_dataIn : in STD_LOGIC_VECTOR(XLENM1 downto 0);
            O_dataOut : out STD_LOGIC_VECTOR(XLENM1 downto 0);

            I_csrOp : in STD_LOGIC_VECTOR (4 downto 0);
            I_csrAddr : in STD_LOGIC_VECTOR (11 downto 0);

            -- This unit can raise exceptions
            O_int : out STD_LOGIC;
            O_int_data : out STD_LOGIC_VECTOR (31 downto 0);

            I_instRetTick : in STD_LOGIC;
            -- interrupt handling causes many data dependencies
            -- mcause has a fast path in from other units
            I_int_cause : in STD_LOGIC_VECTOR (XLENM1 downto 0);
            I_int_pc : in STD_LOGIC_VECTOR (XLENM1 downto 0);
            I_int_mtval : in STD_LOGIC_VECTOR (XLENM1 downto 0);
            -- We need to know when an interrupt occurs as to perform the
            -- relevant csr modifications. Same with exit.
            I_int_entry : in STD_LOGIC;
            I_int_exit : in STD_LOGIC;

            -- Currently just feeds machine level CSR values
            O_csr_status : out STD_LOGIC_VECTOR (XLENM1 downto 0);
            O_csr_cause : out STD_LOGIC_VECTOR (XLENM1 downto 0);
            O_csr_ie : out STD_LOGIC_VECTOR (XLENM1 downto 0);
            O_csr_tvec : out STD_LOGIC_VECTOR (XLENM1 downto 0);
            O_csr_epc : out STD_LOGIC_VECTOR (XLENM1 downto 0)
        );
    end component;

    component lint_unit
        port (
            I_clk : in STD_LOGIC;
            I_reset : in STD_LOGIC;
            I_nextPc : in STD_LOGIC_VECTOR (31 downto 0);
            I_enMask : in STD_LOGIC_VECTOR (3 downto 0);
            I_pc : in STD_LOGIC_VECTOR (31 downto 0);
            I_int0 : in STD_LOGIC;
            I_int_data0 : in STD_LOGIC_VECTOR (31 downto 0);
            O_int0_ack : out STD_LOGIC;
            I_int1 : in STD_LOGIC;
            I_int_data1 : in STD_LOGIC_VECTOR (31 downto 0);
            O_int1_ack : out STD_LOGIC;
            I_int2 : in STD_LOGIC;
            I_int_data2 : in STD_LOGIC_VECTOR (31 downto 0);
            O_int2_ack : out STD_LOGIC;
            I_int3 : in STD_LOGIC;
            I_int_data3 : in STD_LOGIC_VECTOR (31 downto 0);
            O_int3_ack : out STD_LOGIC;
            O_int : out STD_LOGIC;
            O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
            O_int_epc : out STD_LOGIC_VECTOR (31 downto 0)
        );
    end component;

    component mem_controller
        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 component;
    signal state : std_logic_vector(6 downto 0) := (others => '0');
    signal pcop : std_logic_vector(1 downto 0);
    signal in_pc : std_logic_vector(XLENM1 downto 0);

    signal aluFunc : std_logic_vector(15 downto 0);
    signal memOp : std_logic_vector(4 downto 0);
    signal branchTarget : std_logic_vector(XLENM1 downto 0) := (others => '0');

    signal instruction : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal dataA : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal dataB : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal dataDwe : std_logic := '0';
    signal aluop : std_logic_vector(6 downto 0) := (others => '0');
    signal dataIMM : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal selRS1 : std_logic_vector(4 downto 0) := (others => '0');
    signal selRS2 : std_logic_vector(4 downto 0) := (others => '0');
    signal selD : std_logic_vector(4 downto 0) := (others => '0');
    signal dataregWrite : std_logic := '0';
    signal dataResult : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal latchedDataResult : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal dataWriteReg : std_logic := '0';
    signal shouldBranch : std_logic := '0';
    signal memMode : std_logic := '0';
    signal ram_req_size : std_logic := '0';
    signal alu_wait : std_logic := '0';
    signal alutobemulticycle : std_logic := '0';

    signal decoder_int : STD_LOGIC;
    signal decoder_int_data : STD_LOGIC_VECTOR(XLENM1 downto 0);
    signal decoder_int_ack : STD_LOGIC := '0';
    signal decoder_trap_exit : STD_LOGIC := '0';
    signal reg_en : std_logic := '0';
    signal reg_we : std_logic := '0';

    signal registerWriteData : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal alu_or_csr_output : std_logic_vector(XLENM1 downto 0) := (others => '0');

    signal en_fetch : std_logic := '0';
    signal en_decode : std_logic := '0';
    signal en_alu : std_logic := '0';
    signal en_csru : std_logic := '0';
    signal en_memory : std_logic := '0';
    signal en_regwrite : std_logic := '0';
    signal en_stall : std_logic := '0';

    signal PC : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal PC_at_int : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal lastPC_dec : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal lastPC_alu : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal nextPC_stall : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal mtval : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal memctl_ready : std_logic;
    signal memctl_execute : std_logic := '0';
    signal memctl_dataWe : std_logic;
    signal memctl_address : std_logic_vector(XLENM1 downto 0);
    signal memctl_in_data : std_logic_vector(XLENM1 downto 0);
    signal memctl_dataByteEn : std_logic_vector(1 downto 0);
    signal memctl_out_data : std_logic_vector(XLENM1 downto 0) := (others => '0');
    signal memctl_dataReady : std_logic := '0';
    signal memctl_size : std_logic_vector(1 downto 0);
    signal memctl_signExtend : std_logic := '0';

    signal PCintVec : STD_LOGIC := '0';

    signal int_idata : STD_LOGIC_VECTOR(XLENM1 downto 0);
    signal int_set_idata : STD_LOGIC;
    signal int_enabled : std_logic := '1';
    signal int_set_irpc : STD_LOGIC;

    signal I_int_entry : STD_LOGIC := '0';
    signal I_int_exit : STD_LOGIC := '0';

    signal csru_int : STD_LOGIC;
    signal csru_int_data : STD_LOGIC_VECTOR(XLENM1 downto 0);
    signal csru_int_ack : STD_LOGIC := '0';

    signal csru_dataIn : STD_LOGIC_VECTOR(XLENM1 downto 0);
    signal csru_dataOut : STD_LOGIC_VECTOR(XLENM1 downto 0);

    signal csru_csrOp : STD_LOGIC_VECTOR (4 downto 0);
    signal csru_csrAddr : STD_LOGIC_VECTOR (11 downto 0);

    signal csru_instRetTick : STD_LOGIC;

    -- Some CSRs are needed in various places easily, so they are distributed
    signal csr_status : STD_LOGIC_VECTOR(XLENM1 downto 0);
    signal csr_tvec : STD_LOGIC_VECTOR(XLENM1 downto 0);
    signal csr_cause : STD_LOGIC_VECTOR (XLENM1 downto 0);
    signal csr_ie : STD_LOGIC_VECTOR (XLENM1 downto 0);
    signal csr_epc : STD_LOGIC_VECTOR (XLENM1 downto 0);
    signal core_clock : STD_LOGIC := '0';

    signal lint_reset : STD_LOGIC := '0';

    signal misalign_hint : STD_LOGIC := '0'; -- a signal that is early for use by the control unit to stop the next fetch
    signal misalign_branch_hint : STD_LOGIC := '0'; -- a signal that is early for use by the control unit to stop the next fetch
    signal misalign_mem_hint : STD_LOGIC := '0'; -- a signal that is early for use by the control unit

    signal misalign_int : STD_LOGIC := '0';
    signal misalign_int_data : STD_LOGIC_VECTOR(XLENM1 downto 0) := (others => '0');
    signal misalign_int_ack : STD_LOGIC := '0';
    signal lint_int : STD_LOGIC;
    signal lint_int_data : STD_LOGIC_VECTOR(XLENM1 downto 0);

    signal lint_enable_mask : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');

    signal external_int_ack : STD_LOGIC := '0';

    signal dbg_data_line : STD_LOGIC_VECTOR(XLENM1 downto 0);

    signal is_illegal : std_logic := '0';

    signal should_halt : STD_LOGIC := '0';
begin

    should_halt <= I_halt;
    O_halted <= should_halt;
    core_clock <= I_clk;

    memctl : mem_controller port map(
        I_clk => I_clk,
        I_reset => I_reset,

        O_ready => memctl_ready,
        I_execute => memctl_execute,
        I_dataWe => memctl_dataWe,
        I_address => memctl_address,
        I_data => memctl_in_data,
        I_dataByteEn => memctl_dataByteEn,
        I_signExtend => memctl_signExtend,
        O_data => memctl_out_data,
        O_dataReady => memctl_dataReady,

        MEM_I_ready => MEM_I_ready,
        MEM_O_cmd => MEM_O_cmd,
        MEM_O_we => MEM_O_we,
        MEM_O_byteEnable => MEM_O_byteEnable,
        MEM_O_addr => MEM_O_addr,
        MEM_O_data => MEM_O_data,
        MEM_I_data => MEM_I_data,
        MEM_I_dataReady => MEM_I_dataReady
    );

    pcunit : pc_unit port map(
        I_clk => core_clock,
        I_nPC => in_pc,
        I_nPCop => pcop,
        I_intVec => PCintVec,
        O_PC => PC
    );

    control : control_unit port map(
        I_clk => core_clock,
        I_reset => I_reset,
        I_halt => should_halt,
        I_aluop => aluop,

        I_int => lint_int,
        O_int_ack => lint_reset,
        I_int_enabled => int_enabled,
        I_int_mem_data => lint_int_data,
        O_idata => int_idata,
        O_set_idata => int_set_idata,
        O_set_ipc => PCintVec,
        O_set_irpc => int_set_irpc,
        O_instTick => csru_instRetTick,
        I_misalignment => misalign_hint,
        I_ready => memctl_ready,
        O_execute => memctl_execute,
        I_dataReady => memctl_dataReady,
        I_aluWait => alu_wait,
        I_aluMultiCy => alutobemulticycle,
        O_state => state

    );

    decoder : decoder_RV32 port map(
        I_clk => core_clock,
        I_en => en_decode,
        I_dataInst => instruction,
        O_selRS1 => selRS1,
        O_selRS2 => selRS2,
        O_selD => selD,
        O_dataIMM => dataIMM,
        O_regDwe => dataDwe,
        O_aluOp => aluOp,
        O_aluFunc => aluFunc,
        O_memOp => memOp,
        O_csrOp => csru_csrOp,
        O_csrAddr => csru_csrAddr,
        O_trapExit => decoder_trap_exit,
        O_multycyAlu => alutobemulticycle,
        -- This unit can raise exceptions
        O_int => decoder_int,
        O_int_data => decoder_int_data,
        I_int_ack => decoder_int_ack
    );

    alu : alu_RV32I port map(
        I_clk => core_clock,
        I_en => en_alu,
        I_dataA => dataA,
        I_dataB => dataB,
        I_dataDwe => dataDwe,
        I_aluop => aluop(6 downto 2),
        I_aluFunc => aluFunc,
        I_PC => PC,
        I_epc => csr_epc,
        I_dataIMM => dataIMM,
        I_clear => misalign_int,
        O_dataResult => dataResult,
        O_branchTarget => branchTarget,
        O_dataWriteReg => dataWriteReg,
        O_lastPC => lastPC_alu,
        O_shouldBranch => shouldBranch,
        O_wait => alu_wait
    );

    reg : register_set port map(
        I_clk => core_clock,
        I_en => reg_en,
        I_dataD => registerWriteData,
        O_dataA => dataA,
        O_dataB => dataB,
        I_selRS1 => selRS1,
        I_selRS2 => selRS2,
        I_selD => selD,
        I_we => reg_we
    );

    csru : csr_unit port map(

        I_clk => core_clock,
        I_en => en_csru,

        I_dataIn => csru_dataIn,
        O_dataOut => csru_dataOut,

        I_csrOp => csru_csrOp,
        I_csrAddr => csru_csrAddr,

        -- This unit can raise exceptions
        O_int => csru_int,
        O_int_data => csru_int_data,
        --I_int_ack => csru_int_ack,

        I_instRetTick => csru_instRetTick,

        I_int_cause => lint_int_data,
        I_int_pc => PC_at_int,
        I_int_mtval => mtval,

        I_int_entry => I_int_entry,
        I_int_exit => I_int_exit,

        O_csr_status => csr_status,
        O_csr_tvec => csr_tvec,
        O_csr_cause => csr_cause,
        O_csr_ie => csr_ie,
        O_csr_epc => csr_epc
    );

    lint : lint_unit port map(
        I_clk => core_clock,
        I_reset => lint_reset,
        I_nextPc => nextPC_stall,

        I_enMask => lint_enable_mask,
        I_pc => lastPC_dec,

        I_int0 => decoder_int,
        I_int_data0 => decoder_int_data,
        O_int0_ack => decoder_int_ack,

        I_int1 => csru_int,
        I_int_data1 => csru_int_data,
        O_int1_ack => csru_int_ack,

        I_int2 => I_int,
        I_int_data2 => I_int_data,
        O_int2_ack => external_int_ack,
        I_int3 => misalign_int, -- this should be used for misaligned jump and misaligned memory op
        I_int_data3 => misalign_int_data,
        O_int3_ack => misalign_int_ack,

        O_int => lint_int,
        O_int_data => lint_int_data--,
        --     O_int_epc => PC_at_int
    );

    O_int_ack <= external_int_ack;

    state_latcher : process (core_clock)
    begin
        if rising_edge(core_clock) then
            if en_decode = '1' then
                lastPC_dec <= PC;
            end if;
            if state(6) = '1' then
                nextPC_stall <= PC;
            end if;
            if state(0) = '1' then
                instruction <= memctl_out_data;
            end if;
        end if;
    end process;

    -- Register file controls
    reg_en <= en_decode or en_regwrite;
    reg_we <= dataWriteReg and en_regwrite;-- and not misalign_mem_hint;

    -- These are the pipeline stage enable bits
    en_fetch <= state(0);
    en_decode <= state(1);
    en_alu <= state(3);
    en_csru <= state(3) when (aluop(6 downto 2) = OPCODE_SYSTEM and aluFunc(2 downto 0) /= "000") else '0';
    en_memory <= state(4);
    en_regwrite <= state(5);
    en_stall <= state(6);

    -- This decides what the next PC should be
    pcop <= PCU_OP_RESET when I_reset = '1' else
        PCU_OP_ASSIGN when shouldBranch = '1' and state(5) = '1' else
        PCU_OP_INC when shouldBranch = '0' and state(5) = '1' else
        PCU_OP_ASSIGN when PCintvec = '1' else
        PCU_OP_NOP;

    -- this is lint interrupt enable for consuming the interrupt
    -- misalignment/external/crsu/decoder
    -- Only accept external on ALU stage to prevent issues with externals taking decode int's in fetch cycles
    -- externals are also programmable via csr register bit
    lint_enable_mask <= '1' & (csr_status(3)and state(3)) & '1' & '1';
    -- interrupts are controlled by mstatus.mie - this is proper control unit acceptance
    int_enabled <= '1' when (lint_int_data(31) = '0' and lint_int = '1') else csr_status(3);

    PC_at_int <= branchTarget when (shouldBranch = '1' and lint_int_data(31) = '1' and state(6) = '1' and lint_int = '1') else PC when (lint_int_data(31) = '1' and lint_int = '1') else lastPC_dec;

    -- This tries to find misaligned access issues and forward data to the LINT
    -- theres a hacky thing here in that we ignore misaligned memory ops if the
    -- address has first 4 bits set; as this is the mmio space, and I've got some
    -- misaligned legacy devices/code in various places
    -- additionally, misaligned traps can't handle the latency that the LINT incurs whilst
    -- dealing with priorities, so we have hint signals to insert dummy "int stalls" into the pipeline.
    misalign_branch_hint <= lint_enable_mask(3) when (I_reset = '0' and misalign_int = '0' and en_regwrite = '1' and shouldBranch = '1' and branchTarget(1 downto 0) /= "00") else '0';
    misalign_mem_hint <= lint_enable_mask(3) when (I_reset = '0' and en_memory = '1' and memctl_address(31 downto 28) /= X"F" and ((memctl_dataByteEn = F2_MEM_LS_SIZE_H and memctl_address(0) = '1') or (memctl_dataByteEn = F2_MEM_LS_SIZE_W and memctl_address(1 downto 0) /= "00"))) else '0';
    misalign_hint <= misalign_branch_hint or misalign_mem_hint;

    misalign_int_finder : process (core_clock)
    begin
        if rising_edge(core_clock) then
            if I_reset = '0' and misalign_int = '0' and en_regwrite = '1' and shouldBranch = '1' and branchTarget(1 downto 0) /= "00" then
                -- jump misalign
                misalign_int <= lint_enable_mask(3);
                misalign_int_data <= EXCEPTION_INSTRUCTION_ADDR_MISALIGNED;
                mtval <= branchTarget;

            elsif I_reset = '0' and misalign_int = '0' and en_memory = '1' and memctl_dataByteEn = F2_MEM_LS_SIZE_H and memctl_address(0) = '1' and memctl_address(31 downto 28) /= X"F" then -- dont misalign trap on MMIO (Fxxxxxx addr)
                -- half load misalign
                misalign_int <= lint_enable_mask(3);
                if memctl_dataWe = '0' then
                    misalign_int_data <= EXCEPTION_LOAD_ADDRESS_MISALIGNED;
                else
                    misalign_int_data <= EXCEPTION_STORE_AMO_ADDRESS_MISALIGNED;
                end if;
                mtval <= memctl_address;

            elsif I_reset = '0' and misalign_int = '0' and en_memory = '1' and memctl_dataByteEn = F2_MEM_LS_SIZE_W and memctl_address(1 downto 0) /= "00" and memctl_address(31 downto 28) /= X"F" then -- dont misalign trap on MMIO (Fxxxxxx addr)
                -- word load misalign
                misalign_int <= lint_enable_mask(3);
                if memctl_dataWe = '0' then
                    misalign_int_data <= EXCEPTION_LOAD_ADDRESS_MISALIGNED;
                else
                    misalign_int_data <= EXCEPTION_STORE_AMO_ADDRESS_MISALIGNED;
                end if;
                mtval <= memctl_address;

            elsif misalign_int = '1' and misalign_int_ack = '1' then
                misalign_int <= '0';
            end if;
        end if;
    end process;

    -- On Interrupt service entry, CSRs need some maintenance.
    -- We need to strobe the CSR unit on this event.
    I_int_entry <= PCintvec;
    -- To detect exit, we strobe using the ALU enable with the decoder trap request bit
    I_int_exit <= decoder_trap_exit and en_alu;

    -- The input PC is just always the branch target output from ALU
    -- todo: tvec needs modified for vectored exceptions
    in_pc <= csr_tvec when PCintvec = '1' else branchTarget;

    -- input data from the register file, or use immediate if the OP specifies it
    csru_dataIn <= dataIMM when csru_csrOp(CSR_OP_BITS_IMM) = '1' else dataA;

    --dbg_data_line can be used to aid debugging cpu issues using trace dumps.
    --dbg_data_line <= csr_tvec when memctl_execute = '1' else csru_dataIn when en_csru = '1' else registerWriteData when state(5) = '1' else X"000000" & "000" & selD when state(3) = '1' else instruction when state(1)='1' else memctl_address;
    --dbg_data_line <= memctl_address when memctl_execute = '1' else MEM_I_data;
    dbg_data_line <= X"ABCDEF01" when (decoder_int_data = EXCEPTION_INSTRUCTION_ILLEGAL and X"00000010" = csr_epc) else csru_dataIn when en_csru = '1' else registerWriteData when state(5) = '1' else X"000000" & "000" & selD when state(3) = '1' else instruction when state(1) = '1' else memctl_address;
    --dbg_data_line <= PC_at_int;--registerWriteData when state(5) = '1' else X"000000" & "000" & selD when state(3) = '1' else instruction when state(1)='1' else csr_epc when ( lint_reset  = '1') else memctl_address;

    is_illegal <= '1' when decoder_int_data = EXCEPTION_INSTRUCTION_ILLEGAL else '0';

    -- The debug output just allows some internal state to be visible outside the core black box
    -- byte 1 - memctrl&dataready
    -- byte 2 - dataWriteReg, int_en, lint_reset, lint_int, interrupt_type_ decoder and csru_int
    -- byte 3 - aluop
    -- byte 4 - state
    -- uint32 - data
    O_DBG <= "0000" & "0" & memctl_execute & memctl_ready & memctl_dataReady & --alutobemulticycle & alu_wait & --
        -- dataWriteReg & int_enabled & lint_reset  & lint_int & lint_int_data(31) & PCintvec & decoder_int & decoder_int_ack &--&csru_int & --I_int & --
        dataWriteReg & int_enabled & lint_reset & lint_int & I_int & external_int_ack & decoder_int & decoder_int_ack & --&csru_int & --I_int & --
        is_illegal & "00" & aluop(6 downto 2) &
        "0" & state &
        dbg_data_line;

    -- Below statements are for memory interface use.
    memctl_address <= dataResult when en_memory = '1' else PC;
    ram_req_size <= memMode when en_memory = '1' else '0';
    memctl_dataByteEn <= memctl_size when en_memory = '1' else F2_MEM_LS_SIZE_W;
    memctl_in_data <= dataB;
    memctl_dataWe <= '1' when en_memory = '1' and memOp(4 downto 3) = "11" else '0';
    memctl_size <= memOp(1 downto 0);
    memctl_signExtend <= not memOp(2);

    -- This chooses to write registers with memory data or ALU/csr data
    registerWriteData <= memctl_out_data when memOp(4 downto 3) = "10" else dataB when (aluop(6 downto 2) = OPCODE_STORE) else csru_dataOut when (aluop(6 downto 2) = OPCODE_SYSTEM and aluFunc(2 downto 0) /= "000") else dataResult;
end Behavioral;

        I_int_data : in STD_LOGIC_VECTOR(31 downto 0);
        I_int : in STD_LOGIC;
        O_int_ack : out STD_LOGIC;

        -- memory interface


----------------------------------------------------------------------------------
-- Project Name:  RISC-V CPU
-- Description: CSR unit RV32I
--
----------------------------------------------------------------------------------
-- Copyright 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 csr_unit is
    port (
        I_clk : in STD_LOGIC;
        I_en : in STD_LOGIC;
        I_dataIn : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        O_dataOut : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_csrOp : in STD_LOGIC_VECTOR (4 downto 0);
        I_csrAddr : in STD_LOGIC_VECTOR (11 downto 0);
        O_int : out STD_LOGIC;
        O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
        I_instRetTick : in STD_LOGIC;

        -- interrupt handling causes many data dependencies
        -- mcause has a fast path in from other units
        I_int_cause : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_int_pc : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_int_mtval : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        -- We need to know when an interrupt occurs as to perform the
        -- relevant csr modifications. Same with exit.
        I_int_entry : in STD_LOGIC;
        I_int_exit : in STD_LOGIC;

        -- Currently just feeds machine level CSR values
        O_csr_status : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        O_csr_cause : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        O_csr_ie : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        O_csr_tvec : out STD_LOGIC_VECTOR (XLENM1 downto 0);
        O_csr_epc : out STD_LOGIC_VECTOR (XLENM1 downto 0)
    );
end csr_unit;

architecture Behavioral of csr_unit is

    constant CSR_ADDR_USTATUS : STD_LOGIC_VECTOR (11 downto 0) := X"000";
    constant CSR_ADDR_UIE : STD_LOGIC_VECTOR (11 downto 0) := X"004";
    constant CSR_ADDR_UTVEC : STD_LOGIC_VECTOR (11 downto 0) := X"005";

    constant CSR_ADDR_USCRATCH : STD_LOGIC_VECTOR (11 downto 0) := X"040";
    constant CSR_ADDR_UEPC : STD_LOGIC_VECTOR (11 downto 0) := X"041";
    constant CSR_ADDR_UCAUSE : STD_LOGIC_VECTOR (11 downto 0) := X"042";
    constant CSR_ADDR_UTVAL : STD_LOGIC_VECTOR (11 downto 0) := X"043";
    constant CSR_ADDR_UIP : STD_LOGIC_VECTOR (11 downto 0) := X"044";

    constant CSR_ADDR_CYCLE : STD_LOGIC_VECTOR (11 downto 0) := X"C00";
    constant CSR_ADDR_TIME : STD_LOGIC_VECTOR (11 downto 0) := X"C01";
    constant CSR_ADDR_INSTRET : STD_LOGIC_VECTOR (11 downto 0) := X"C02";

    constant CSR_ADDR_CYCLEH : STD_LOGIC_VECTOR (11 downto 0) := X"C80";
    constant CSR_ADDR_TIMEH : STD_LOGIC_VECTOR (11 downto 0) := X"C81";
    constant CSR_ADDR_INSTRETH : STD_LOGIC_VECTOR (11 downto 0) := X"C82";
    constant CSR_ADDR_TEST_400 : STD_LOGIC_VECTOR (11 downto 0) := X"400";
    constant CSR_ADDR_TEST_401 : STD_LOGIC_VECTOR (11 downto 0) := X"401";

    constant CSR_ADDR_MSTATUS : STD_LOGIC_VECTOR (11 downto 0) := X"300";
    constant CSR_ADDR_MISA : STD_LOGIC_VECTOR (11 downto 0) := X"301";
    constant CSR_ADDR_MEDELEG : STD_LOGIC_VECTOR (11 downto 0) := X"302";
    constant CSR_ADDR_MIDELEG : STD_LOGIC_VECTOR (11 downto 0) := X"303";
    constant CSR_ADDR_MIE : STD_LOGIC_VECTOR (11 downto 0) := X"304";
    constant CSR_ADDR_MTVEC : STD_LOGIC_VECTOR (11 downto 0) := X"305";
    constant CSR_ADDR_MCOUNTEREN : STD_LOGIC_VECTOR (11 downto 0) := X"306";

    constant CSR_ADDR_MSCRATCH : STD_LOGIC_VECTOR (11 downto 0) := X"340";
    constant CSR_ADDR_MEPC : STD_LOGIC_VECTOR (11 downto 0) := X"341";
    constant CSR_ADDR_MCAUSE : STD_LOGIC_VECTOR (11 downto 0) := X"342";
    constant CSR_ADDR_MTVAL : STD_LOGIC_VECTOR (11 downto 0) := X"343";
    constant CSR_ADDR_MIP : STD_LOGIC_VECTOR (11 downto 0) := X"344";

    constant CSR_ADDR_MCYCLE : STD_LOGIC_VECTOR (11 downto 0) := X"B00";
    constant CSR_ADDR_MINSTRET : STD_LOGIC_VECTOR (11 downto 0) := X"B02";

    constant CSR_ADDR_MCYCLEH : STD_LOGIC_VECTOR (11 downto 0) := X"B80";
    constant CSR_ADDR_MINSTRETH : STD_LOGIC_VECTOR (11 downto 0) := X"B82";

    constant CSR_ADDR_MVENDORID : STD_LOGIC_VECTOR (11 downto 0) := X"F11";
    constant CSR_ADDR_MARCHID : STD_LOGIC_VECTOR (11 downto 0) := X"F12";
    constant CSR_ADDR_MIMPID : STD_LOGIC_VECTOR (11 downto 0) := X"F13";
    constant CSR_ADDR_MHARDID : STD_LOGIC_VECTOR (11 downto 0) := X"F14";
    signal csr_cycles : STD_LOGIC_VECTOR(63 downto 0) := (others => '0');
    signal csr_instret : STD_LOGIC_VECTOR(63 downto 0) := (others => '0');

    signal csr_mstatus : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000000";
    signal csr_mie : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
    signal csr_mtvec : STD_LOGIC_VECTOR (XLENM1 downto 0) := X"00000004";
    signal csr_mscratch : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
    signal csr_mepc : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
    signal csr_mcause : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
    signal csr_mtval : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
    signal csr_mip : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');

    signal csr_vexrisc_irq_mask : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');
    signal csr_vexrisc_irq_pending : STD_LOGIC_VECTOR (XLENM1 downto 0) := (others => '0');

    signal curr_csr_value : STD_LOGIC_VECTOR(XLENM1 downto 0) := (others => '0');
    signal next_csr_value : STD_LOGIC_VECTOR(XLENM1 downto 0) := (others => '0');

    signal test0_CSR : STD_LOGIC_VECTOR(XLENM1 downto 0) := X"FEFbbEF0";
    signal test1_CSR : STD_LOGIC_VECTOR(XLENM1 downto 0) := X"FEFbbEF1";

    signal csr_op : STD_LOGIC_VECTOR(4 downto 0) := (others => '0');
    signal opState : integer := 0;

    signal raise_int : std_logic := '0';

    constant STEP_READ_OR_IDLE : integer := 0;
    constant STEP_MODIFY : integer := 1;
    constant STEP_WRITE : integer := 2;
begin

    O_int <= raise_int;
    O_int_data <= X"00000000";
    O_csr_status <= csr_mstatus;
    O_csr_tvec <= csr_mtvec;
    O_csr_cause <= csr_mcause;
    O_csr_ie <= csr_mie;
    O_csr_epc <= csr_mepc;

    O_dataOut <= curr_csr_value;

    cycles : process (I_clk)
    begin
        if rising_edge(I_clk) then
            csr_cycles <= std_logic_vector(unsigned(csr_cycles) + 1);
        end if;
    end process;

    instret : process (I_clk)
    begin
        if rising_edge(I_clk) and I_instRetTick = '1' then

    protection : process (I_clk, I_en)
    begin
        if rising_edge(I_clk) then
            if (I_csrAddr(CSR_ADDR_ACCESS_BIT_START downto CSR_ADDR_ACCESS_BIT_END) = CSR_ADDR_ACCESS_READONLY) and
                (I_csrOp(CSR_OP_BITS_WRITTEN) = '1') then
                --todo: raise exception
                raise_int <= '1';
            else
                raise_int <= '0';
            end if;
        end if;
    end process;

    -- Read data is available next cycle, with an additional cycle before another op can be processed
    -- Write to CSR occurs 3 cycles later.
    -- cycle 1: read of existing csr available
    -- cycle 2: update value calculates (whole write, set/clear bit read-modify-write)
    -- cycle 3: actual write to csr occurs.
    datamain : process (I_clk, I_en)
    begin
        if rising_edge(I_clk) then

            if I_int_entry = '1' then
                -- on entry:
                -- mstatus.mpie = mstatus.mie
                csr_mstatus(7) <= csr_mstatus(3);
                -- mstatus.mie = 0
                csr_mstatus(3) <= '0';
                -- mstatus.mpp = current privilege mode
                csr_mstatus(12 downto 11) <= "11";

                csr_mcause <= I_int_cause;
                csr_mepc <= I_int_pc;
                csr_mtval <= I_int_mtval;

            elsif I_int_exit = '1' then
                -- privilege set to mstatus.mpp
                -- mstatus.mie = mstatus.mpie
                csr_mstatus(3) <= csr_mstatus(7);
                csr_mstatus(7) <= '1';
                csr_mstatus(12 downto 11) <= "00";

                -- interrupt data changes take all priority

            elsif I_en = '1' and opState = STEP_READ_OR_IDLE then
                csr_op <= I_csrOp;
                case I_csrAddr is
                    when CSR_ADDR_MVENDORID =>
                        curr_csr_value <= X"00000000"; -- JEDEC non-commercial
                    when CSR_ADDR_MARCHID =>
                        curr_csr_value <= X"00000000";
                    when CSR_ADDR_MIMPID =>
                        curr_csr_value <= X"52505531"; -- "RPU1"
                    when CSR_ADDR_MHARDID =>
                        curr_csr_value <= X"00000000";
                    when CSR_ADDR_MISA =>
                        curr_csr_value <= X"40001100"; -- XLEN 32, RV32IM

                    when CSR_ADDR_MSTATUS =>
                        curr_csr_value <= csr_mstatus;
                    when CSR_ADDR_MTVEC =>
                        curr_csr_value <= csr_mtvec;
                    when CSR_ADDR_MIE =>
                        curr_csr_value <= csr_mie;
                    when CSR_ADDR_MIP =>
                        curr_csr_value <= csr_mip;
                    when CSR_ADDR_MCAUSE =>
                        curr_csr_value <= csr_mcause;
                    when CSR_ADDR_MEPC =>
                        curr_csr_value <= csr_mepc;
                    when CSR_ADDR_MTVAL =>
                        curr_csr_value <= csr_mtval;
                    when CSR_ADDR_MSCRATCH =>
                        curr_csr_value <= csr_mscratch;

                    when CSR_ADDR_CYCLE =>
                        curr_csr_value <= csr_cycles(31 downto 0);
                    when CSR_ADDR_CYCLEH =>
                        curr_csr_value <= csr_cycles(63 downto 32);

                    when CSR_ADDR_INSTRET =>
                        curr_csr_value <= csr_instret(31 downto 0);
                    when CSR_ADDR_INSTRETH =>
                        curr_csr_value <= csr_instret(63 downto 32);

                    when CSR_ADDR_MCYCLE =>
                        curr_csr_value <= csr_cycles(31 downto 0);
                    when CSR_ADDR_MCYCLEH =>
                        curr_csr_value <= csr_cycles(63 downto 32);

                    when CSR_ADDR_MINSTRET =>
                        curr_csr_value <= csr_instret(31 downto 0);
                    when CSR_ADDR_MINSTRETH =>
                        curr_csr_value <= csr_instret(63 downto 32);

                    when others =>
                        -- raise exception for unsupported CSR
                end case;
                opState <= STEP_MODIFY;

            elsif opState = STEP_MODIFY then
                -- update stage for sets, clears and writes
                case csr_op(3 downto 2) is
                    when CSR_MAINOP_WR =>
                        next_csr_value <= I_dataIn;
                    when CSR_MAINOP_SET =>
                        next_csr_value <= curr_csr_value or I_dataIn;
                    when CSR_MAINOP_CLEAR =>
                        next_csr_value <= curr_csr_value and (not I_dataIn);
                    when others =>
                end case;

                if I_csrOp(CSR_OP_BITS_WRITTEN) = '1' then
                    opState <= STEP_WRITE;
                else
                    opState <= STEP_READ_OR_IDLE;
                end if;

            elsif opState = STEP_WRITE then
                -- write stage
                opState <= STEP_READ_OR_IDLE;
                case I_csrAddr is

                    when CSR_ADDR_MSTATUS =>
                        csr_mstatus <= next_csr_value;
                    when CSR_ADDR_MTVEC =>
                        csr_mtvec <= next_csr_value;
                    when CSR_ADDR_MIE =>
                        csr_mie <= next_csr_value;
                    when CSR_ADDR_MIP =>
                        csr_mip <= next_csr_value;
                    when CSR_ADDR_MEPC =>
                        csr_mepc <= next_csr_value;
                    when CSR_ADDR_MSCRATCH =>
                        csr_mscratch <= next_csr_value;
                    when others =>
                end case;
            end if;
        end if;

    end process;

end Behavioral;

            csr_instret <= std_logic_vector(unsigned(csr_instret) + 1);
        end if;


----------------------------------------------------------------------------------
-- Project Name:  RISC-V CPU
-- Description: Local Interrupt unit
--
----------------------------------------------------------------------------------
-- Copyright 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 lint_unit is
    port (
        I_clk : in STD_LOGIC;
        I_reset : in STD_LOGIC;
        I_nextPc : in STD_LOGIC_VECTOR (31 downto 0);
        I_pc : in STD_LOGIC_VECTOR (31 downto 0);
        I_enMask : in STD_LOGIC_VECTOR (3 downto 0);
        I_int0 : in STD_LOGIC;
        I_int_data0 : in STD_LOGIC_VECTOR (31 downto 0);
        O_int0_ack : out STD_LOGIC;
        I_int1 : in STD_LOGIC;
        I_int_data1 : in STD_LOGIC_VECTOR (31 downto 0);
        O_int1_ack : out STD_LOGIC;
        I_int2 : in STD_LOGIC;
        I_int_data2 : in STD_LOGIC_VECTOR (31 downto 0);
        O_int2_ack : out STD_LOGIC;
        I_int3 : in STD_LOGIC;
        I_int_data3 : in STD_LOGIC_VECTOR (31 downto 0);
        O_int3_ack : out STD_LOGIC;
        O_int : out STD_LOGIC;
        O_int_data : out STD_LOGIC_VECTOR (31 downto 0);
        O_int_epc : out STD_LOGIC_VECTOR (31 downto 0)
    );
end lint_unit;

architecture Behavioral of lint_unit is

    signal actual_int : std_logic := '0';
    signal actual_int_data : std_logic_vector (31 downto 0) := X"00000000";
    signal actual_int_epc : std_logic_vector (31 downto 0) := X"00000000";

    signal int0_ack : std_logic := '0';
    signal int1_ack : std_logic := '0';
    signal int2_ack : std_logic := '0';
    signal int3_ack : std_logic := '0';

    signal reset_counter : integer := 0;

begin

    O_int <= actual_int;
    O_int_data <= actual_int_data;
    O_int_epc <= actual_int_epc;

    O_int0_ack <= int0_ack;
    O_int1_ack <= int1_ack;
    O_int2_ack <= int2_ack;
    O_int3_ack <= int3_ack;

    -- This simply filters one of the 4 int sources to a single one in
    -- decreasing priority, latching the data until a reset.
    arb : process (I_clk)
    begin
        if rising_edge(I_clk) then
            if I_reset = '1' then
                reset_counter <= 1;
                int0_ack <= '0';
                int1_ack <= '0';
                int2_ack <= '0';
                int3_ack <= '0';
            elsif reset_counter = 1 then
                reset_counter <= 2;
            elsif reset_counter = 2 then
                reset_counter <= 3;
            elsif reset_counter = 3 then
                actual_int <= '0';
                reset_counter <= 0;
            elsif reset_counter = 0 and actual_int = '0' then

                if I_enMask(0) = '1' and I_int0 = '1' and int0_ack = '0' then
                    actual_int <= '1';
                    actual_int_data <= I_int_data0;
                    int0_ack <= '1';
                elsif I_enMask(1) = '1' and I_int1 = '1' and int1_ack = '0'then

                    actual_int_data <= I_int_data3;
                    int3_ack <= '1';
                end if;
            end if;
        end if;
    end process;
end Behavioral;

                    actual_int <= '1';
                    actual_int_data <= I_int_data1;
                    int1_ack <= '1';
                elsif I_enMask(2) = '1' and I_int2 = '1' and int2_ack = '0' then
                    actual_int <= '1';


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

                end if;
            elsif state = 2 then
                cmd <= '0';
                state <= 0;


----------------------------------------------------------------------------------
-- Project Name: RPU
-- Description: Program Counter unit of RPU
--
-- Simple black box for holding and manipulating the PC
--
----------------------------------------------------------------------------------
-- 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 pc_unit is
    Port (
        I_clk : in  STD_LOGIC;
        I_nPC : in  STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_nPCop : in  STD_LOGIC_VECTOR (1 downto 0);
        I_intVec: in STD_LOGIC;
        O_PC : out  STD_LOGIC_VECTOR (XLENM1 downto 0)
    );
end pc_unit;

architecture Behavioral of pc_unit is
    signal current_pc: std_logic_vector( XLENM1 downto 0) := ADDR_RESET;
begin

	process (I_clk)

					current_pc <= std_logic_vector(unsigned(current_pc) + 4); -- 32bit byte addressing
				when PCU_OP_ASSIGN => 	-- set from external input
					current_pc <= I_nPC;
				when PCU_OP_RESET => 	-- Reset
					current_pc <= ADDR_RESET;
				when others =>
			end case;
		end if;
	end process;

	O_PC <= current_pc;

end Behavioral;

	begin
		if rising_edge(I_clk) then
			case I_nPCop is


----------------------------------------------------------------------------------
-- Project Name:  RISC-V CPU
-- Description: Register file unit
--
----------------------------------------------------------------------------------
-- Copyright 2016 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 register_set is
    Port (
        I_clk    : in STD_LOGIC;
        I_en     : in STD_LOGIC;
        I_dataD  : in STD_LOGIC_VECTOR (XLENM1 downto 0); -- Data to write to regD
        I_selRS1 : in STD_LOGIC_VECTOR (4 downto 0);      -- Select line for regRS1
        I_selRS2 : in STD_LOGIC_VECTOR (4 downto 0);      -- Select line for regRS2
        I_selD   : in STD_LOGIC_VECTOR (4 downto 0);      -- Select line for regD
        I_we     : in STD_LOGIC;                          -- Write enable for regD

end register_set;

architecture Behavioral of register_set is
    type store_t is array (0 to 31) of std_logic_vector(XLENM1 downto 0);
    signal regsA: store_t := (others => X"00000000");
    signal regsB: store_t := (others => X"00000000");
    signal dataAout: STD_LOGIC_VECTOR (XLENM1 downto 0) := (others=>'0');
    signal dataBout: STD_LOGIC_VECTOR (XLENM1 downto 0) := (others=>'0');
begin

	process(I_clk, I_en)
	begin
		if rising_edge(I_clk) and I_en='1' then
			dataAout <= regsA(to_integer(unsigned(I_selRS1)));
			dataBout <= regsB(to_integer(unsigned(I_selRS2)));
			if (I_we = '1') then
				regsA(to_integer(unsigned(I_selD))) <= I_dataD;
                regsB(to_integer(unsigned(I_selD))) <= I_dataD;
			end if;
		end if;
	end process;

	O_dataA <= dataAout when I_selRS1 /= "00000" else X"00000000";
	O_dataB <= dataBout when I_selRS2 /= "00000" else X"00000000";

end Behavioral;

        O_dataA  : out STD_LOGIC_VECTOR (XLENM1 downto 0);-- regRS1 data out
        O_dataB  : out STD_LOGIC_VECTOR (XLENM1 downto 0) -- regRS2 data out


----------------------------------------------------------------------------------
-- Project Name: RISC-V CPU
-- Description: ALU unit suitable for RV32I operational use
--
----------------------------------------------------------------------------------
-- 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 alu_RV32I is
    port (
        I_clk : in STD_LOGIC;
        I_en : in STD_LOGIC;
        I_dataA : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
        I_dataB : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
        I_dataDwe : in STD_LOGIC;
        I_aluop : in STD_LOGIC_VECTOR (4 downto 0);
        I_aluFunc : in STD_LOGIC_VECTOR (15 downto 0);
        I_PC : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
        I_epc : in STD_LOGIC_VECTOR (XLENM1 downto 0);
        I_dataIMM : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
        I_clear : in STD_LOGIC;
        O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
        O_branchTarget : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
        O_dataWriteReg : out STD_LOGIC;
        O_lastPC : out STD_LOGIC_VECTOR(XLEN32M1 downto 0);
        O_shouldBranch : out std_logic;
        O_wait : out std_logic
    );
end alu_RV32I;

architecture Behavioral of alu_RV32I is
    -- The internal register for results of operations.
    -- 32 bit + carry/overflow
    signal s_aluFunc : STD_LOGIC_VECTOR (15 downto 0) := (others => '0');
    signal s_branchTarget : STD_LOGIC_VECTOR (XLEN32M1 downto 0) := (others => '0');

    signal s_result : STD_LOGIC_VECTOR(63 downto 0) := (others => '0');
    signal s_resultms : STD_LOGIC_VECTOR(63 downto 0) := (others => '0');
    signal s_resultmu : STD_LOGIC_VECTOR(63 downto 0) := (others => '0');
    signal s_resultmsu : STD_LOGIC_VECTOR(65 downto 0) := (others => '0'); -- result has 66 bits to accomodate mulhsu with it's additional-bit-fakery
    signal s_shouldBranch : STD_LOGIC := '0';
    signal s_lastPC : STD_LOGIC_VECTOR(XLEN32M1 downto 0) := (others => '0');
    signal s_wait : std_logic := '0';
    component alu_int32_div is
        port (
            I_clk : in STD_LOGIC;
            I_exec : in STD_LOGIC;
            I_dividend : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            I_divisor : in STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            I_op : in STD_LOGIC_VECTOR (1 downto 0);
            O_dataResult : out STD_LOGIC_VECTOR (XLEN32M1 downto 0);
            O_done : out STD_LOGIC;
            O_int : out std_logic
        );
    end component;
    signal s_div_exec : std_logic := '0';
    signal s_div_dividend : std_logic_vector(31 downto 0) := (others => '0');
    signal s_div_divisor : std_logic_vector(31 downto 0) := (others => '0');
    signal s_div_op : std_logic_vector(1 downto 0) := (others => '0');
    signal s_div_dataResult : std_logic_vector(31 downto 0) := (others => '0');
    signal s_div_done : std_logic := '0';
    signal s_div_int : std_logic := '0';
    constant DIVUNIT_STATE_IDLE : integer := 0;
    constant DIVUNIT_STATE_INFLIGHT : integer := 1;
    constant DIVUNIT_STATE_COMPLETE : integer := 2;

    constant MUL_STATE_IDLE : integer := 0;
    constant MUL_STATE_COMPLETE : integer := 2;

    signal s_mul_state : integer := 0;
    signal s_divunit_state : integer := 0;

begin
    div_rem_unit : alu_int32_div port map(
        I_clk => I_clk,
        I_exec => s_div_exec,
        I_dividend => s_div_dividend,
        I_divisor => s_div_divisor,
        I_op => s_div_op,
        O_dataResult => s_div_dataResult,
        O_done => s_div_done,
        O_int => s_div_int
    );

    s_div_dividend <= I_dataA;
    s_div_divisor <= I_dataB;

    process (I_clk, I_en)
    begin
        if rising_edge(I_clk) then
            if I_clear = '1' and I_en = '0' then
                s_branchTarget <= X"00000000";
                s_result <= X"0000000000000000";

            elsif I_en = '1' then
                s_lastPC <= I_PC;
                O_dataWriteReg <= I_dataDwe;
                s_aluFunc <= I_aluFunc;
                case I_aluop is
                    when OPCODE_OPIMM =>
                        s_wait <= '0';
                        s_shouldBranch <= '0';
                        case I_aluFunc(2 downto 0) is
                            when F3_OPIMM_ADDI =>
                                s_result(31 downto 0) <= std_logic_vector(signed(I_dataA) + signed(I_dataIMM));

                            when F3_OPIMM_XORI =>
                                s_result(31 downto 0) <= I_dataA xor I_dataIMM;

                            when F3_OPIMM_ORI =>
                                s_result(31 downto 0) <= I_dataA or I_dataIMM;

                            when F3_OPIMM_ANDI =>
                                s_result(31 downto 0) <= I_dataA and I_dataIMM;

                            when F3_OPIMM_SLTI =>
                                if signed(I_dataA) < signed(I_dataIMM) then
                                    s_result(31 downto 0) <= X"00000001";
                                else
                                    s_result(31 downto 0) <= X"00000000";
                                end if;

                                if unsigned(I_dataA) < unsigned(I_dataIMM) then
                                    s_result(31 downto 0) <= X"00000001";
                                else
                                    s_result(31 downto 0) <= X"00000000";
                                end if;

                            when F3_OPIMM_SLLI =>
                                s_result(31 downto 0) <= std_logic_vector(shift_left(unsigned(I_dataA), to_integer(unsigned(I_dataIMM(4 downto 0)))));

                            when F3_OPIMM_SRLI =>
                                case I_aluFunc(9 downto 3) is
                                    when F7_OPIMM_SRLI =>
                                        s_result(31 downto 0) <= std_logic_vector(shift_right(unsigned(I_dataA), to_integer(unsigned(I_dataIMM(4 downto 0)))));
                                    when F7_OPIMM_SRAI =>
                                        s_result(31 downto 0) <= std_logic_vector(shift_right(signed(I_dataA), to_integer(unsigned(I_dataIMM(4 downto 0)))));
                                    when others =>
                                end case;
                            when others =>
                        end case;

                    when OPCODE_OP =>

                        if I_aluFunc(9 downto 3) = F7_OP_M_EXT then

                            if I_aluFunc(2) = '0' then -- mul ops
                                if s_mul_state = MUL_STATE_IDLE then
                                    s_resultms(63 downto 0) <= std_logic_vector(signed(I_dataA) * signed(I_dataB));
                                    s_resultmu(63 downto 0) <= std_logic_vector(unsigned(I_dataA) * unsigned(I_dataB));
                                    s_resultmsu(65 downto 0) <= std_logic_vector(signed(I_dataA(31) & I_dataA) * signed('0' & I_dataB));

                                    s_wait <= '0'; -- there is _always_ a 1 cycle additional wait for a multicycle alu, so immediately flag complete
                                    s_mul_state <= MUL_STATE_COMPLETE;

                                elsif s_mul_state = MUL_STATE_COMPLETE then

                                    if I_aluFunc(2 downto 0) = F3_OP_M_MUL then
                                        s_result(31 downto 0) <= s_resultms(31 downto 0);

                                    elsif I_aluFunc(2 downto 0) = F3_OP_M_MULH then
                                        s_result(31 downto 0) <= s_resultms(63 downto 32);

                                    elsif I_aluFunc(2 downto 0) = F3_OP_M_MULHU then
                                        s_result(31 downto 0) <= s_resultmu(63 downto 32);

                                    elsif I_aluFunc(2 downto 0) = F3_OP_M_MULHSU then
                                        s_result(31 downto 0) <= s_resultmsu(63 downto 32);
                                    end if;

                                    s_mul_state <= MUL_STATE_IDLE;

                                end if;
                            else
                                -- div & rem
                                if s_divunit_state = DIVUNIT_STATE_IDLE then
                                    s_div_exec <= '1';
                                    s_div_op <= I_aluFunc(1 downto 0);
                                    s_divunit_state <= DIVUNIT_STATE_INFLIGHT;

                                    s_wait <= '1'; -- stall the cpu until done
                                elsif s_divunit_state = DIVUNIT_STATE_INFLIGHT then
                                    s_div_exec <= '0';

                                    if s_div_done = '1' then
                                        s_divunit_state <= DIVUNIT_STATE_COMPLETE;
                                        s_wait <= '0';
                                    end if;
                                elsif s_divunit_state = DIVUNIT_STATE_COMPLETE then

                                    s_divunit_state <= DIVUNIT_STATE_IDLE;
                                    s_result(31 downto 0) <= s_div_dataResult;
                                end if;

                            end if;
                        else
                            s_wait <= '0';
                            case I_aluFunc(9 downto 0) is
                                when F7_OP_ADD & F3_OP_ADD =>
                                    s_result(31 downto 0) <= std_logic_vector(signed(I_dataA) + signed(I_dataB));

                                when F7_OP_SUB & F3_OP_SUB =>
                                    s_result(31 downto 0) <= std_logic_vector(signed(I_dataA) - signed(I_dataB));

                                when F7_OP_SLT & F3_OP_SLT =>
                                    if signed(I_dataA) < signed(I_dataB) then
                                        s_result(31 downto 0) <= X"00000001";
                                    else
                                        s_result(31 downto 0) <= X"00000000";
                                    end if;

                                when F7_OP_SLTU & F3_OP_SLTU =>
                                    if unsigned(I_dataA) < unsigned(I_dataB) then
                                        s_result(31 downto 0) <= X"00000001";
                                    else
                                        s_result(31 downto 0) <= X"00000000";
                                    end if;

                                when F7_OP_XOR & F3_OP_XOR =>
                                    s_result(31 downto 0) <= I_dataA xor I_dataB;

                                when F7_OP_OR & F3_OP_OR =>
                                    s_result(31 downto 0) <= I_dataA or I_dataB;

                                when F7_OP_AND & F3_OP_AND =>
                                    s_result(31 downto 0) <= I_dataA and I_dataB;

                                when F7_OP_SLL & F3_OP_SLL =>
                                    s_result(31 downto 0) <= std_logic_vector(shift_left(unsigned(I_dataA), to_integer(unsigned(I_dataB(4 downto 0)))));

                                when F7_OP_SRL & F3_OP_SRL =>
                                    s_result(31 downto 0) <= std_logic_vector(shift_right(unsigned(I_dataA), to_integer(unsigned(I_dataB(4 downto 0)))));

                                when F7_OP_SRA & F3_OP_SRA =>
                                    s_result(31 downto 0) <= std_logic_vector(shift_right(signed(I_dataA), to_integer(unsigned(I_dataB(4 downto 0)))));

                                when others =>
                                    s_result <= X"00000000" & X"CDC1FEF1";
                            end case;
                        end if;
                        s_shouldBranch <= '0';

                    when OPCODE_LOAD | OPCODE_STORE =>
                        s_wait <= '0';
                        s_shouldBranch <= '0';
                        s_result(31 downto 0) <= std_logic_vector(signed(I_dataA) + signed(I_dataIMM));

                    when OPCODE_JALR =>
                        s_wait <= '0';
                        s_branchTarget <= std_logic_vector(signed(I_dataA) + signed(I_dataIMM)) and X"FFFFFFFE"; -- jalr clears the lowest bit
                        s_shouldBranch <= '1';
                        s_result(31 downto 0) <= std_logic_vector(signed(I_PC) + 4);

                    when OPCODE_JAL =>
                        s_wait <= '0';
                        s_branchTarget <= std_logic_vector(signed(I_PC) + signed(I_dataIMM));
                        s_shouldBranch <= '1';
                        s_result(31 downto 0) <= std_logic_vector(signed(I_PC) + 4);

                    when OPCODE_SYSTEM =>
                        s_wait <= '0';
                        if I_aluFunc(9 downto 0) = F7_PRIVOP_MRET & F3_PRIVOP then
                            s_branchTarget <= I_epc;
                            s_shouldBranch <= '1';
                            s_result(31 downto 0) <= std_logic_vector(signed(I_PC) + 4);
                        elsif I_aluFunc(2 downto 0) /= F3_PRIVOP then
                            -- do not branch on CSR unit work
                            s_shouldBranch <= '0';
                        end if;
                    when OPCODE_LUI =>
                        s_wait <= '0';
                        s_shouldBranch <= '0';
                        s_result(31 downto 0) <= I_dataIMM;

                    when OPCODE_AUIPC =>
                        s_wait <= '0';
                        s_shouldBranch <= '0';
                        s_result(31 downto 0) <= std_logic_vector(signed(I_PC) + signed(I_dataIMM));

                    when OPCODE_BRANCH =>
                        s_wait <= '0';
                        s_branchTarget <= std_logic_vector(signed(I_PC) + signed(I_dataIMM));
                        case I_aluFunc(2 downto 0) is
                            when F3_BRANCH_BEQ =>
                                if I_dataA = I_dataB then
                                    s_shouldBranch <= '1';
                                else
                                    s_shouldBranch <= '0';
                                end if;

                            when F3_BRANCH_BNE =>
                                if I_dataA /= I_dataB then
                                    s_shouldBranch <= '1';
                                else
                                    s_shouldBranch <= '0';
                                end if;

                            when F3_BRANCH_BLT =>
                                if signed(I_dataA) < signed(I_dataB) then
                                    s_shouldBranch <= '1';
                                else
                                    s_shouldBranch <= '0';
                                end if;

                            when F3_BRANCH_BGE =>
                                if signed(I_dataA) >= signed(I_dataB) then
                                    s_shouldBranch <= '1';
                                else
                                    s_shouldBranch <= '0';
                                end if;

                            when F3_BRANCH_BLTU =>
                                if unsigned(I_dataA) < unsigned(I_dataB) then
                                    s_shouldBranch <= '1';
                                else
                                    s_shouldBranch <= '0';
                                end if;

                            when F3_BRANCH_BGEU =>
                                if unsigned(I_dataA) >= unsigned(I_dataB) then
                                    s_shouldBranch <= '1';
                                else
                                    s_shouldBranch <= '0';
                                end if;

                            when others =>
                        end case;

                    when others =>
                        s_result <= X"00000000" & X"CDCDFEFE";
                end case;
            end if;
        end if;
    end process;

    O_wait <= s_wait;

    O_dataResult <= s_result(XLEN32M1 downto 0);
    O_shouldBranch <= s_shouldBranch;
    O_branchTarget <= s_branchTarget;
    O_lastPC <= s_lastPC;

end Behavioral;


----------------------------------------------------------------------------------
-- Project Name:  RISC-V CPU
-- Description: decoder unit RV32I
--
----------------------------------------------------------------------------------
-- 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 decoder_RV32 is
    port (
        I_clk : in STD_LOGIC;
        I_en : in STD_LOGIC;
        I_dataInst : in STD_LOGIC_VECTOR (31 downto 0); -- Instruction to be decoded
        O_selRS1 : out STD_LOGIC_VECTOR (4 downto 0);   -- Selection out for regrs1
        O_selRS2 : out STD_LOGIC_VECTOR (4 downto 0);   -- Selection out for regrs2
        O_selD : out STD_LOGIC_VECTOR (4 downto 0);     -- Selection out for regD
        O_dataIMM : out STD_LOGIC_VECTOR (31 downto 0); -- Immediate value out
        O_regDwe : out STD_LOGIC;                       -- RegD wrtite enable
        O_aluOp : out STD_LOGIC_VECTOR (6 downto 0);    -- ALU opcode
        O_aluFunc : out STD_LOGIC_VECTOR (15 downto 0); -- ALU function

        I_int_ack : in STD_LOGIC                        -- our int is now being serviced
    );
end decoder_RV32;

architecture Behavioral of decoder_RV32 is
    signal s_trapExit : STD_LOGIC := '0';
    signal s_csrOP : STD_LOGIC_VECTOR(4 downto 0) := (others => '0');
    signal s_csrAddr : STD_LOGIC_VECTOR(11 downto 0) := (others => '0');
    signal s_int : STD_LOGIC := '0';
    signal s_intdata : STD_LOGIC_VECTOR(31 downto 0) := (others => '0');
    signal s_multicy : std_logic := '0';
begin
    O_multycyAlu <= s_multicy;
    O_int <= s_int;
    O_int_data <= s_intdata;
    O_csrOP <= s_csrOP;
    O_csrAddr <= s_csrAddr;
    O_trapExit <= s_trapExit;

    -- Register selects for reads are async
    O_selRS1 <= I_dataInst(R1_START downto R1_END);
    O_selRS2 <= I_dataInst(R2_START downto R2_END);

    process (I_clk, I_en)
    begin

        if rising_edge(I_clk) then
            if I_en = '1' then

                O_selD <= I_dataInst(RD_START downto RD_END);

                O_aluOp <= I_dataInst(OPCODE_START downto OPCODE_END);

                O_aluFunc <= "000000" & I_dataInst(FUNCT7_START downto FUNCT7_END)
                              & I_dataInst(FUNCT3_START downto FUNCT3_END);

                case I_dataInst(OPCODE_START downto OPCODE_END_2) is
                    when OPCODE_LUI =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        O_regDwe <= '1';
                        O_memOp <= "00000";
                        O_dataIMM <= I_dataInst(IMM_U_START downto IMM_U_END)
                            & "000000000000";

                    when OPCODE_AUIPC =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        O_regDwe <= '1';
                        O_memOp <= "00000";
                        O_dataIMM <= I_dataInst(IMM_U_START downto IMM_U_END)
                            & "000000000000";

                    when OPCODE_JAL =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        if I_dataInst(RD_START downto RD_END) = "00000" then
                            O_regDwe <= '0';
                        else
                            O_regDwe <= '1';
                        end if;
                        O_memOp <= "00000";
                        if I_dataInst(IMM_U_START) = '1' then
                            O_dataIMM <= "111111111111" & I_dataInst(19 downto 12) & I_dataInst(20) & I_dataInst(30 downto 21) & '0';
                        else
                            O_dataIMM <= "000000000000" & I_dataInst(19 downto 12) & I_dataInst(20) & I_dataInst(30 downto 21) & '0';
                        end if;

                    when OPCODE_JALR =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        if I_dataInst(RD_START downto RD_END) = "00000" then
                            O_regDwe <= '0';
                        else
                            O_regDwe <= '1';
                        end if;
                        O_memOp <= "00000";
                        if I_dataInst(IMM_U_START) = '1' then
                            O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END);
                        else
                            O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END);
                        end if;

                    when OPCODE_OPIMM =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        O_regDwe <= '1';
                        O_memOp <= "00000";
                        if I_dataInst(IMM_U_START) = '1' then
                            O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END);
                        else
                            O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END);
                        end if;

                    when OPCODE_OP =>
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        O_memOp <= "00000";

                        -- M based extension ops are multicycle, otherwise they are single-cycle
                        if (I_dataInst(FUNCT7_START downto FUNCT7_END) = F7_OP_M_EXT) then
                            s_multicy <= '1';
                        else
                            s_multicy <= '0';
                        end if;

                        s_int <= '0';
                        O_regDwe <= '1';

                    when OPCODE_LOAD =>
                        s_multicy <= '0';
                        -- Load's opcode is all 0s - but the first two bits of the word should be '11'
                        -- we check this here, because if we do not, null instructions will be treated as loads...
                        if I_dataInst(1 downto 0) = "11" then
                            s_trapExit <= '0';
                            s_csrOP <= "00000";
                            s_int <= '0';
                            O_regDwe <= '1';
                            O_memOp <= "10" & I_dataInst(FUNCT3_START downto FUNCT3_END);
                            if I_dataInst(IMM_U_START) = '1' then
                                O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_I_START downto IMM_I_END);
                            else
                                O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_I_START downto IMM_I_END);
                            end if;
                        else
                            -- likely a null instruction - fault!
                            s_trapExit <= '0';
                            s_csrOP <= "00000";
                            s_int <= '1'; ---------------
                            s_intdata <= EXCEPTION_INSTRUCTION_ILLEGAL;
                            O_memOp <= "00000";
                            O_regDwe <= '0';
                            O_dataIMM <= I_dataInst(IMM_I_START downto IMM_S_B_END)
                                & "0000000";
                        end if;

                    when OPCODE_STORE =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        O_regDwe <= '0';
                        O_memOp <= "11" & I_dataInst(FUNCT3_START downto FUNCT3_END);
                        if I_dataInst(IMM_U_START) = '1' then
                            O_dataIMM <= X"FFFF" & "1111" & I_dataInst(IMM_S_A_START downto IMM_S_A_END) & I_dataInst(IMM_S_B_START downto IMM_S_B_END);
                        else
                            O_dataIMM <= X"0000" & "0000" & I_dataInst(IMM_S_A_START downto IMM_S_A_END) & I_dataInst(IMM_S_B_START downto IMM_S_B_END);
                        end if;

                    when OPCODE_BRANCH =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        O_regDwe <= '0';
                        O_memOp <= "00000";
                        if I_dataInst(IMM_U_START) = '1' then
                            O_dataIMM <= X"FFFF" & "1111" & I_dataInst(7) & I_dataInst(30 downto 25) & I_dataInst(11 downto 8) & '0';
                        else
                            O_dataIMM <= X"0000" & "0000" & I_dataInst(7) & I_dataInst(30 downto 25) & I_dataInst(11 downto 8) & '0';
                        end if;

                    when OPCODE_MISCMEM =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '0';
                        O_regDwe <= '0';
                        O_memOp <= "01000";
                        O_dataIMM <= I_dataInst;

                    when OPCODE_SYSTEM =>
                        s_multicy <= '0';
                        O_memOp <= "00000";
                        if I_dataInst(FUNCT3_START downto FUNCT3_END) = F3_PRIVOP then
                            -- ECALL or EBREAK
                            case I_dataInst(IMM_I_START downto IMM_I_END) is
                                when IMM_I_SYSTEM_ECALL =>
                                    -- raise trap, save pc, perform requiredCSR operations
                                    s_trapExit <= '0';
                                    s_csrOP <= "00000";
                                    s_int <= '1';
                                    O_regDwe <= '0';
                                    s_intdata <= EXCEPTION_ENVIRONMENT_CALL_FROM_MMODE;
                                    --todo:  Priv level needs checked as to mask this to user/supervisor/machine level
                                when IMM_I_SYSTEM_EBREAK =>
                                    s_trapExit <= '0';
                                    s_csrOP <= "00000";
                                    s_int <= '1';
                                    s_intdata <= EXCEPTION_BREAKPOINT;
                                    O_regDwe <= '0';
                                when F7_PRIVOP_MRET & R2_PRIV_RET =>
                                    s_trapExit <= '1';
                                    s_csrOP <= "00000";
                                    s_int <= '0';
                                    O_regDwe <= '0';
                                    -- return from interrupt. implement as a branch - alu will branch to epc.
                                when others =>
                            end case;
                        else
                            s_trapExit <= '0';
                            s_int <= '0';
                            -- CSR
                            -- The immediate output is the zero-extended R1 value for Imm-form CSR ops
                            O_dataIMM <= X"000000" & "000" & I_dataInst(R1_START downto R1_END);

                            -- The 12bit immediate in the instruction forms the csr address.
                            s_csrAddr <= I_dataInst(IMM_I_START downto IMM_I_END);

                            -- is there a destination? if not, CSR is not read
                            if I_dataInst(RD_START downto RD_END) = "00000" then
                                s_csrOP(0) <= '0';
                                O_regDwe <= '0';
                            else
                                O_regDwe <= '1';
                                s_csrOP(0) <= '1';
                            end if;

                            -- is there source data? if not, CSR value is not written
                            -- is it's CSRRS/CSRRC/CSRRSI/CSRRCI ONLY! I.E (Func3 and 010) != 0
                            if (I_dataInst(FUNCT3_END + 1) = '1') and I_dataInst(R1_START downto R1_END) = "00000" then
                                s_csrOP(1) <= '0';
                            else
                                s_csrOP(1) <= '1';
                            end if;

                            s_csrOp(4 downto 2) <= I_dataInst(FUNCT3_START downto FUNCT3_END);

                        end if;
                    when others =>
                        s_multicy <= '0';
                        s_trapExit <= '0';
                        s_csrOP <= "00000";
                        s_int <= '1'; ---------------
                        s_intdata <= EXCEPTION_INSTRUCTION_ILLEGAL;
                        O_memOp <= "00000";
                        O_regDwe <= '0';
                        O_dataIMM <= I_dataInst(IMM_I_START downto IMM_S_B_END)
                            & "0000000";
                end case;
            elsif I_int_ack = '1' then
                s_int <= '0';
            end if;
        end if;
    end process;

end Behavioral;

        O_memOp : out STD_LOGIC_VECTOR(4 downto 0);     -- Memory operation
        O_csrOP : out STD_LOGIC_VECTOR(4 downto 0);     -- CSR operations
        O_csrAddr : out STD_LOGIC_VECTOR(11 downto 0);  -- CSR address
        O_trapExit : out STD_LOGIC;                     -- request to exit trap handler