/*!\file RS5.sv
 * RS5 VERSION - 1.1.0 - Pipeline Simplified and Core Renamed
 *
 * Distribution:  July 2023
 *
 * Willian Nunes    <willian.nunes@edu.pucrs.br>
 * Angelo Dal Zotto <angelo.dalzotto@edu.pucrs.br>
 * Marcos Sartori   <marcos.sartori@acad.pucrs.br>
 * Ney Calazans     <ney.calazans@ufsc.br>
 * Fernando Moraes  <fernando.moraes@pucrs.br>
 * GAPH - Hardware Design Support Group
 * PUCRS - Pontifical Catholic University of Rio Grande do Sul <https://pucrs.br/>
 *
 * \brief
 * Is the top Module of RS5 processor core.
 *
 * \detailed
 * This is the top Module of the RS5 processor core
 * and is responsible for the instantiation of the lower level modules
 * and also defines the interface ports (inputs and outputs) os the processor.
 */

`include "RS5_pkg.sv"

module RS5
    import RS5_pkg::*;
#(
`ifndef SYNTH
    parameter bit           DEBUG          = 1'b0,
    parameter string        DBG_REG_FILE   = "./debug/regBank.txt",
    parameter bit           PROFILING      = 1'b0,
    parameter string        PROFILING_FILE = "./debug/Report.txt",
`endif
    parameter environment_e Environment      = ASIC,
    parameter mul_e         MULEXT           = MUL_M,
    parameter atomic_e      AMOEXT           = AMO_A,
    parameter logic [31:0]  START_ADDR       = '0,
    parameter bit           COMPRESSED       = 1'b0,
    parameter bit           VEnable          = 1'b0,
    parameter int           VLEN             = 256,
    parameter int           LLEN             = 32,
    parameter int           BUS_WIDTH        = 32,
    parameter bit           XOSVMEnable      = 1'b0,
    parameter bit           ZKNEEnable       = 1'b0,
    parameter bit           ZBKBEnable       = 1'b0,
    parameter bit           ZKNHEnable       = 1'b0,
    parameter bit           XKYBEREnable     = 1'b0,
    parameter bit           ZICONDEnable     = 1'b0,
    parameter bit           ZCBEnable        = 1'b0,
    parameter bit           HPMCOUNTEREnable = 1'b0,
    parameter int           IQUEUE_SIZE      = 2,
    parameter bit           BRANCHPRED       = 1'b1,
    parameter bit           FORWARDING       = 1'b1
)
(
    input  logic                    clk,
    input  logic                    reset_n,
    input  logic                    sys_reset_i,
    input  logic                    stall,
    input  logic                    busy_i,

    input  logic                    tip_i,
    input  logic                    eip_i,
    output logic                    interrupt_ack_o,

    input  logic [63:0]             mtime_i,

    output logic                    imem_operation_enable_o,
    output logic [31:0]             instruction_address_o,
    input  logic [31:0]             instruction_i,

    output logic                    dmem_operation_enable_o,
    output logic [BUS_WIDTH/8-1:0]  mem_write_enable_o,
    output logic [31:0]             mem_address_o,
    output logic [BUS_WIDTH-1:0]    mem_data_o,
    input  logic [BUS_WIDTH-1:0]    mem_data_i
);

//////////////////////////////////////////////////////////////////////////////
// Global signals
//////////////////////////////////////////////////////////////////////////////

    logic            jump;
    logic            hazard;
    logic            fetch_hazard;
    logic            hold;

    logic            mmu_inst_fault;
    logic            mmu_data_fault;

    privilegeLevel_e privilege;
    logic   [31:0]   jump_target;

    logic                   mem_enable;
    logic [31:0]            mem_address_exec;
    logic [31:0]            instruction_address;

    /* We always mask the two lsbs */
    /* verilator lint_off UNUSEDSIGNAL */
    logic   [31:0]   mem_address;
    /* verilator lint_on UNUSEDSIGNAL */

//////////////////////////////////////////////////////////////////////////////
// Fetch signals
//////////////////////////////////////////////////////////////////////////////

    logic           enable_fetch;

//////////////////////////////////////////////////////////////////////////////
// Decoder signals
//////////////////////////////////////////////////////////////////////////////

    logic   [31:0]  pc_decode;
    logic           enable_decode;
    logic           jump_misaligned;

//////////////////////////////////////////////////////////////////////////////
// RegBank signals
//////////////////////////////////////////////////////////////////////////////

    logic    [4:0]  rs1, rs2;
    logic   [31:0]  regbank_data1, regbank_data2;
    logic    [4:0]  rd_retire;
    logic   [31:0]  regbank_data_writeback;
    logic           regbank_write_enable;

//////////////////////////////////////////////////////////////////////////////
// Execute signals
//////////////////////////////////////////////////////////////////////////////

    iType_e         instruction_operation_execute;
    iTypeAtomic_e   atomic_operation_execute;
    iTypeVector_e   vector_operation_execute;
    logic   [31:0]  rs1_data_execute, rs2_data_execute, second_operand_execute;
    logic   [31:0]  instruction_execute;
    logic   [31:0]  pc_execute;
    logic    [4:0]  rd_execute;
    logic    [4:0]  rs1_execute;
    logic           exc_ilegal_inst_execute;
    logic           exc_inst_access_fault_execute;
    logic           instruction_compressed_execute;
    logic   [31:0]  vtype, vlen;

//////////////////////////////////////////////////////////////////////////////
// Mem Access Signals
//////////////////////////////////////////////////////////////////////////////

    iType_e         instruction_operation_mem_access;
    logic   [31:0]  result_mem_access;
    logic    [4:0]  rd_mem_access;
    logic           write_enable_mem_access;


//////////////////////////////////////////////////////////////////////////////
// Retire signals
//////////////////////////////////////////////////////////////////////////////

    iType_e         instruction_operation_retire;
    logic   [31:0]  result_retire;
    logic           killed;

//////////////////////////////////////////////////////////////////////////////
// CSR Bank signals
//////////////////////////////////////////////////////////////////////////////

    logic           csr_read_enable, csr_write_enable;
    csrOperation_e  csr_operation;
    logic   [11:0]  csr_addr;
    logic   [31:0]  csr_data_to_write, csr_data_read;
    logic   [31:0]  mepc, mtvec;
    logic           RAISE_EXCEPTION, MACHINE_RETURN;
    logic           interrupt_pending;
    exceptionCode_e Exception_Code;

    /* Signals enabled with XOSVM */
    /* verilator lint_off UNUSEDSIGNAL */
    logic   [31:0]  mvmdo, mvmio, mvmds, mvmis, mvmdm, mvmim;
    logic           mvmctl;
    logic           mmu_en;
    /* verilator lint_on UNUSEDSIGNAL */

//////////////////////////////////////////////////////////////////////////////
// Assigns
//////////////////////////////////////////////////////////////////////////////

    if (XOSVMEnable == 1'b1) begin : gen_xosvm_mmu_on
        assign mmu_en = privilege != privilegeLevel_e'(2'b11) && mvmctl == 1'b1;
    end
    else begin : gen_xosvm_mmu_off
        assign mmu_en = 1'b0;
    end

    assign enable_fetch  = !(stall || hold || fetch_hazard);
    assign enable_decode = !(stall || hold);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////// FETCH //////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    logic        bp_ack;
    logic        valid_fetch;
    logic        should_jump;
    logic        jump_rollback;
    logic        jumping;
    logic        ctx_switch;
    logic        bp_take_fetch;
    logic        compressed_decode;
    logic [31:0] bp_target;
    logic [31:0] ctx_switch_target;
    logic [31:0] instruction_decode;

    fetch #(
        .start_address(START_ADDR),
        .IQUEUE_SIZE  (IQUEUE_SIZE),
        .MULEXT       (MULEXT),
        .ZCBEnable    (ZCBEnable),
        .COMPRESSED   (COMPRESSED),
        .BRANCHPRED   (BRANCHPRED)
    ) fetch1 (
        .clk                  (clk                    ),
        .reset_n              (reset_n                ),
        .sys_reset            (sys_reset_i            ),
        .enable_i             (enable_fetch           ),
        .busy_i               (busy_i                 ),
        .valid_o              (valid_fetch            ),
        .bp_ack_o             (bp_ack                 ),
        .jump_i               (jump                   ),
        .should_jump_i        (should_jump            ),
        .jump_rollback_i      (jump_rollback          ),
        .jump_target_i        (jump_target            ),
        .ctx_switch_i         (ctx_switch             ),
        .ctx_switch_target_i  (ctx_switch_target      ),
        .bp_take_i            (bp_take_fetch          ),
        .bp_target_i          (bp_target              ),
        .jumping_o            (jumping                ),
        .jump_misaligned_o    (jump_misaligned        ),
        .compressed_o         (compressed_decode      ),
        .mem_operation_en_o   (imem_operation_enable_o),
        .instruction_address_o(instruction_address    ),
        .instruction_data_i   (instruction_i          ),
        .instruction_o        (instruction_decode     ),
        .pc_o                 (pc_decode              )
    );

    if (XOSVMEnable == 1'b1) begin : gen_xosvm_i_mmu_on
        mmu i_mmu (
            .en_i           (mmu_en               ),
            .mask_i         (mvmim                ),
            .offset_i       (mvmio                ),
            .size_i         (mvmis                ),
            .address_i      (instruction_address  ),
            .exception_o    (mmu_inst_fault       ),
            .address_o      (instruction_address_o)
        );
    end
    else begin : gen_xosvm_i_mmu_off
        assign mmu_inst_fault        = 1'b0;
        assign instruction_address_o = instruction_address;
    end

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////// DECODER /////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    logic        bp_taken_exec;
    logic        write_enable_exec;
    logic [31:0] result_exec;
    logic [31:0] jump_imm_target_exec;

    decode # (
        .MULEXT       (MULEXT      ),
        .AMOEXT       (AMOEXT      ),
        .COMPRESSED   (COMPRESSED  ),
        .ZKNEEnable   (ZKNEEnable  ),
        .ZBKBEnable   (ZBKBEnable  ),
        .ZKNHEnable   (ZKNHEnable  ),
        .XKYBEREnable (XKYBEREnable),
        .ZICONDEnable (ZICONDEnable),
        .VEnable      (VEnable     ),
        .BRANCHPRED   (BRANCHPRED  ),
        .FORWARDING   (FORWARDING  )
    ) decoder1 (
        .clk                        (clk),
        .reset_n                    (reset_n),
        .enable                     (enable_decode),
        .sys_reset                  (sys_reset_i),
        .valid_i                    (valid_fetch),
        .instruction_i              (instruction_decode),
        .pc_i                       (pc_decode),
        .rs1_data_read_i            (regbank_data1),
        .rs2_data_read_i            (regbank_data2),
        .rd_mem_access_i            (rd_mem_access),
        .rd_retire_i                (rd_retire),
        .mem_access_result_i        (result_mem_access),
        .writeback_i                (regbank_data_writeback),
        .result_i                   (result_exec),
        .mem_access_we_i            (write_enable_mem_access),
        .regbank_we_i               (regbank_write_enable),
        .execute_we_i               (write_enable_exec),
        .rs1_o                      (rs1),
        .rs2_o                      (rs2),
        .rd_o                       (rd_execute),
        .instr_rs1_o                (rs1_execute),
        .csr_address_o              (csr_addr),
        .rs1_data_o                 (rs1_data_execute),
        .rs2_data_o                 (rs2_data_execute),
        .second_operand_o           (second_operand_execute),
        .pc_o                       (pc_execute),
        .instruction_o              (instruction_execute),
        .jump_imm_target_o          (jump_imm_target_exec),
        .compressed_o               (instruction_compressed_execute),
        .instruction_operation_o    (instruction_operation_execute),
        .atomic_operation_o         (atomic_operation_execute),
        .vector_operation_o         (vector_operation_execute),
        .dec_hazard_o               (fetch_hazard),
        .hazard_o                   (hazard),
        .killed_o                   (killed),
        .should_jump_i              (should_jump),
        .jumping_i                  (jumping),
        .jump_rollback_i            (jump_rollback),
        .compressed_i               (compressed_decode),
        .jump_misaligned_i          (jump_misaligned),
        .bp_take_o                  (bp_take_fetch),
        .bp_taken_o                 (bp_taken_exec),
        .bp_target_o                (bp_target),
        .exc_inst_access_fault_i    (mmu_inst_fault),
        .exc_inst_access_fault_o    (exc_inst_access_fault_execute),
        .exc_ilegal_inst_o          (exc_ilegal_inst_execute)
    );

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////// REGISTER BANK ///////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    if (Environment == FPGA) begin :RegFileFPGA_blk
        DRAM_RegBank RegBankA (
            .clk        (clk),
            .we         (regbank_write_enable),
            .a          (rd_retire),
            .d          (regbank_data_writeback),
            .dpra       (rs1),
            .dpo        (regbank_data1)
        );

        DRAM_RegBank RegBankB (
            .clk        (clk),
            .we         (regbank_write_enable),
            .a          (rd_retire),
            .d          (regbank_data_writeback),
            .dpra       (rs2),
            .dpo        (regbank_data2)
        );
    end
    else begin : RegFileFF_blk
        regbank #(
        `ifndef SYNTH
            .DEBUG      (DEBUG       ),
            .DBG_FILE   (DBG_REG_FILE)
        `endif
        ) regbankff (
            .clk        (clk),
            .reset_n    (reset_n),
            .rs1        (rs1),
            .rs2        (rs2),
            .rd         (rd_retire),
            .enable     (regbank_write_enable),
            .data_i     (regbank_data_writeback),
            .data1_o    (regbank_data1),
            .data2_o    (regbank_data2)
        );
    end

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////// EXECUTE /////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    logic [31:0] reservation_data;
    logic [31:0] pc_irq;
    logic [31:0] pc_exc;
    logic        exec_valid;
    logic        load_access_fault;
    assign exec_valid = !(killed || hazard);

    execute #(
        .Environment  (Environment ),
        .MULEXT       (MULEXT      ),
        .AMOEXT       (AMOEXT      ),
        .COMPRESSED   (COMPRESSED  ),
        .ZKNEEnable   (ZKNEEnable  ),
        .ZBKBEnable   (ZBKBEnable  ),
        .ZKNHEnable   (ZKNHEnable  ),
        .XKYBEREnable (XKYBEREnable),
        .ZICONDEnable (ZICONDEnable),
        .VEnable      (VEnable     ),
        .VLEN         (VLEN        ),
        .LLEN         (LLEN        ),
        .BUS_WIDTH    (BUS_WIDTH   ),
        .BRANCHPRED   (BRANCHPRED  )
    ) execute1 (
        .clk                     (clk),
        .reset_n                 (reset_n),
        .stall                   (stall),
        .bp_ack_i                (bp_ack),
        .instruction_i           (instruction_execute),
        .rs1_data_i              (rs1_data_execute),
        .rs2_data_i              (rs2_data_execute),
        .second_operand_i        (second_operand_execute),
        .rd_i                    (rd_execute),
        .rs1_i                   (rs1_execute),
        .instruction_operation_i (instruction_operation_execute),
        .atomic_operation_i      (atomic_operation_execute),
        .vector_operation_i      (vector_operation_execute),
        .privilege_i             (privilege),
        .valid_i                 (exec_valid),
        .exc_ilegal_inst_i       (exc_ilegal_inst_execute),
        .exc_inst_access_fault_i (exc_inst_access_fault_execute),
        .exc_load_access_fault_i (load_access_fault),
        .hold_o                  (hold),
        .write_enable_o          (write_enable_mem_access),
        .write_enable_fwd_o      (write_enable_exec),
        .instruction_operation_o (instruction_operation_mem_access),
        .result_o                (result_mem_access),
        .result_fwd_o            (result_exec),
        .rd_o                    (rd_mem_access),
        .mem_address_exec_o      (mem_address_exec),
        .mem_address_o           (mem_address),
        .mem_enable_o            (mem_enable),
        .mem_write_enable_o      (mem_write_enable_o),
        .mem_write_data_o        (mem_data_o),
        .mem_read_data_i         (mem_data_i),
        .csr_address_i           (csr_addr),
        .csr_read_enable_o       (csr_read_enable),
        .csr_data_read_i         (csr_data_read),
        .csr_write_enable_o      (csr_write_enable),
        .csr_operation_o         (csr_operation),
        .csr_data_o              (csr_data_to_write),
        .vtype_o                 (vtype),
        .vlen_o                  (vlen),
        .bp_taken_i              (bp_taken_exec),
        .ctx_switch_o            (ctx_switch),
        .jump_rollback_o         (jump_rollback),
        .ctx_switch_target_o     (ctx_switch_target),
        .jump_imm_target_i       (jump_imm_target_exec),
        .reservation_data_i      (reservation_data),
        .jump_target_o           (jump_target),
        .compressed_i            (instruction_compressed_execute),
        .pc_i                    (pc_execute),
        .interrupt_pending_i     (interrupt_pending),
        .mtvec_i                 (mtvec),
        .mepc_i                  (mepc),
        .pc_irq_o                (pc_irq),
        .pc_exc_o                (pc_exc),
        .jump_o                  (jump),
        .should_jump_o           (should_jump),
        .interrupt_ack_o         (interrupt_ack_o),
        .machine_return_o        (MACHINE_RETURN),
        .raise_exception_o       (RAISE_EXCEPTION),
        .exception_code_o        (Exception_Code)
    );

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////// MEMORY ACCESS ///////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            instruction_operation_retire <= NOP;
            result_retire        <= '0;
            rd_retire            <= '0;
        end
        else if (!stall) begin
            instruction_operation_retire <= instruction_operation_mem_access;
            result_retire                <= result_mem_access;
            rd_retire                    <= rd_mem_access;
        end
    end

    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n)
            regbank_write_enable <= '0;
        else
            regbank_write_enable <= (
                write_enable_mem_access 
                && !load_access_fault /* Disable write on load mmu exception     */
                && !stall             /* Disable writing immediately after stall */
            ); 
    end

    if (XOSVMEnable == 1'b1) begin : gen_d_mmu_on
        logic mmu_en_r;
        always_ff @(posedge clk or negedge reset_n) begin
            if (!reset_n) begin
                mmu_en_r <= 1'b0;
            end
            else if (!stall) begin
                mmu_en_r <= mmu_en;
            end
        end

        mmu d_mmu (
            .en_i           (mmu_en_r                  ),
            .mask_i         (mvmdm                     ),
            .offset_i       (mvmdo                     ),
            .size_i         (mvmds                     ),
            .address_i      ({mem_address[31:2], 2'b00}),
            .exception_o    (mmu_data_fault            ),
            .address_o      (mem_address_o             )
        );
        assign load_access_fault = mem_enable && mmu_data_fault;
    end
    else begin : gen_d_mmu_off
        assign load_access_fault = 1'b0;
        assign mmu_data_fault    = 1'b0;
        assign mem_address_o     = {mem_address[31:2], 2'b00};
    end

    assign dmem_operation_enable_o = mem_enable && !mmu_data_fault;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////// RETIRE //////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    retire #(
        .AMOEXT      (AMOEXT)
    ) retire1 (
        .clk                    (clk),
        .reset_n                (reset_n),
        .regbank_we_i           (regbank_write_enable),
        .instruction_operation_i(instruction_operation_retire),
        .result_i               (result_retire),
        .mem_data_i             (mem_data_i[31:0]),
        .reservation_data_o     (reservation_data),
        .regbank_data_o         (regbank_data_writeback)
    );

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////// CSRs BANK ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    CSRBank #(
    `ifndef SYNTH
        .PROFILING     (PROFILING     ),
        .PROFILING_FILE(PROFILING_FILE),
    `endif
        .HPMCOUNTEREnable(HPMCOUNTEREnable),
        .XOSVMEnable     (XOSVMEnable     ),
        .XKYBEREnable    (XKYBEREnable    ),
        .COMPRESSED      (COMPRESSED      ),
        .MULEXT          (MULEXT          ),
        .VEnable         (VEnable         ),
        .VLEN            (VLEN            )
    ) CSRBank1 (
        .clk                        (clk),
        .reset_n                    (reset_n),
        .sys_reset                  (sys_reset_i),
        .read_enable_i              (csr_read_enable),
        .write_enable_i             (csr_write_enable),
        .operation_i                (csr_operation),
        .address_i                  (CSRs'(csr_addr)),
        .data_i                     (csr_data_to_write),
        .killed                     (killed),
        .data_o                     (csr_data_read),
        .instruction_operation_i    (instruction_operation_execute),
        .vector_operation_i         (vector_operation_execute),
        .hazard                     (hazard),
        .stall                      (stall),
        .hold                       (hold),
        .vtype_i                    (vtype),
        .vlen_i                     (vlen),
        .raise_exception_i          (RAISE_EXCEPTION),
        .machine_return_i           (MACHINE_RETURN),
        .exception_code_i           (Exception_Code),
        .pc_i                       (pc_execute),
        .mem_address_exec_i         (mem_address_exec),
        .mem_address_i              (mem_address),
        .pc_irq_i                   (pc_irq),
        .pc_exc_i                   (pc_exc),
        .instruction_i              (instruction_execute),
        .instruction_compressed_i   (instruction_compressed_execute),
        .jump_misaligned_i          (jump_misaligned),
        .jump_target_i              (jump_target),
        .mtime_i                    (mtime_i),
        .tip_i                      (tip_i),
        .eip_i                      (eip_i),
        .interrupt_ack_i            (interrupt_ack_o),
        .interrupt_pending_o        (interrupt_pending),
        .privilege_o                (privilege),
        .mepc_o                     (mepc),
        .mtvec_o                    (mtvec),
    // XOSVM Signals
        .mvmctl_o                   (mvmctl),
        .mvmdo_o                    (mvmdo),
        .mvmds_o                    (mvmds),
        .mvmdm_o                    (mvmdm),
        .mvmio_o                    (mvmio),
        .mvmis_o                    (mvmis),
        .mvmim_o                    (mvmim)
    );

endmodule
