`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 11/17/2018 05:27:41 PM
// Design Name: 
// Module Name: CSRRegFile
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module CSRRegFile(
    input    clk,
    input    clk_slow,
    input    rst,
    input [31:0] pc,
    input    nmi,
    input    ecall,
    input    ebreak,
    input    int,
    input    en_inter, 
    input    en_ecall, 
    input    en_int, 
    input    en_tmr,
    input [11:0] addr,
    input    CSR_Write,
    input [31:0] CSR_Write_Data,
    input    Set,
    input    Clear,
    input    JumpOrBranch,
    output reg [31:0] CSR_Read_Data,
    output   interF,
    output [31:0] interAddr
);

`define MEPC 3'b000
`define MCYCLE 3'b001
`define MTIME 3'b010
`define MINSTRET 3'b011
`define MTIMECMP 3'b100
`define MIE 3'b101
`define MIP 3'b110

/**
 CRS Registers
 0. 0x341 mepc 
 1. 0xB00 mcycle 
 2. 0xB01 mtime 
 3. 0xB02 minstret 
 4. 0xB03 mtimecmp 
 
 5. 0x304 mie [0] 0 => 1 To Enable TMR, [1] 0 => 1 To Enable  EX_INTERRUPT, [2] 0 => 1 TO Enable ECALL, [3] TO GLOBALLY DISABLE/ENABLE
 6. 0x344 mip 0 => 1 iff TMR IS HIGH, 0 => 1 iff NMI || EX_INTERRUPT IS HIGH, 0 => 1 iff EBREAK OR ECALL IS HIGH WITH EACH CLOCK CYCLE
*/

/**
  CSR Registers Classifications
  
  Standard Read-Write
  MEPC //STORES ADDRESS OF PC AS IS WITHOUT INCREMENT
  MIE
  MIP
  Read-Write Shadows
  MCYCLE
  MTIME
  MINSTRET
  MTIMECMP
*/

reg [31:0] CSR [6:0];
reg [2:0] Selected_Addr;
reg [31:0] count;

reg tmr;
wire [2:0] interSel, int_num;
wire load;


//Convert Incoming RISC V standard addr to selected address
always @(*) begin
    Selected_Addr = (addr == 12'h341) ? `MEPC :
                   (addr == 12'hb00) ? `MCYCLE :
                   (addr == 12'hb01) ? `MTIME :
                   (addr == 12'hb02) ? `MINSTRET :
                   (addr == 12'hb03) ? `MTIMECMP :
                   (addr == 12'h304) ? `MIE :
                   `MIP ;
end


//rst or write with posedge of clk
always @(posedge clk) begin
    if(rst) begin
        CSR[`MTIME] = 32'd22000;
        CSR[`MTIMECMP] = 32'd22010;
        CSR[`MEPC] = 32'b0;
        CSR[`MINSTRET] = 32'b0;
        CSR[`MCYCLE] = 32'b0;
        CSR[`MIP] = 32'b0;
        CSR[`MIE] = 32'b01111;
        count = 0;
    end
end

//Read with negedge of clk
always @(negedge clk) begin
    CSR_Read_Data = CSR[Selected_Addr] ;
end

always @(posedge clk_slow) begin
    // MCYCLES Reg Handling
    CSR[`MCYCLE] = CSR[`MCYCLE] + 1;
    
    // MTIME && MTIMECMP Regs Handling
    if(count == 10) begin
        CSR[`MTIME] = CSR[`MTIME] + 1;
        count = count + 1;
    end
    else if(count == 10 + 1) begin
        count = 0;
    end
    else
        count = count + 1;
        
    if(CSR[`MTIME] == CSR[`MTIMECMP]) begin
        CSR[`MTIMECMP] = CSR[`MTIMECMP] + 10;
        tmr = 1'b1;
    end
    else begin
        CSR[`MTIMECMP] = CSR[`MTIMECMP];
        tmr = 1'b0;
    end
    
    // MinStret Reg Handling
    if(!JumpOrBranch)
        CSR[`MINSTRET] = CSR[`MINSTRET] + 1;
end

assign load = clk ^ clk_slow;

always @(load) begin
// Writing
    if(CSR_Write) begin
        if(Set) // CSRS
            CSR[Selected_Addr] = CSR[Selected_Addr] | CSR_Write_Data;
        else if(Clear)  // CSRC
            CSR[Selected_Addr] = CSR[Selected_Addr] & ~CSR_Write_Data;
        else    // CSRW
            CSR[Selected_Addr] = CSR_Write_Data;
    end
    else
        CSR[Selected_Addr] = CSR[Selected_Addr];
end

always @ (posedge clk) begin
    CSR[`MIE] = {28'b0, en_inter, en_ecall, en_int, en_tmr};
    CSR[`MIP] = {29'b0, (ecall || ebreak), int, tmr};
    
    
    if(interF && pc >= `CODE_SEGMENT)
        CSR[`MEPC] = pc;
end


Interrupt_Detector interUnit (
                .nmi(nmi),
                .ecall(ecall),
                .ebreak(ebreak),
                .int(int),
                .tmr(tmr),
                
                .en_inter(en_inter),
                
                .en_ecall(en_ecall),
                .en_int(en_int),
                .en_tmr(en_tmr),
                
                .interFlag(interF),
                .interSel(interSel) 
);

InterruptAddressGenerator intrAdrrGen(
                .interruptF(interF),
                .interSel(interSel),
                .intNum(int_num),
                .addr(interAddr)
);


endmodule