`timescale 1ns / 1ps
/*
----------------------------------------------------------------------------------
-- Company: NUS	
-- Engineer: (c) Rajesh Panicker  
-- 
-- Create Date: 09/22/2020 06:49:10 PM
-- Module Name: RV
-- Project Name: CG3207 Project
-- Target Devices: Nexys 4 / Basys 3
-- Tool Versions: Vivado 2019.2
-- Description: RISC-V Processor Module
-- 
-- Dependencies: NIL
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments: The interface SHOULD NOT be modified (except making output reg) unless you modify Wrapper.v/vhd too. 
                        The implementation can be modified.
-- 
----------------------------------------------------------------------------------

----------------------------------------------------------------------------------
--	License terms :
--	You are free to use this code as long as you
--		(i) DO NOT post it on any public repository;
--		(ii) use it only for educational purposes;
--		(iii) accept the responsibility to ensure that your implementation does not violate anyone's intellectual property.
--		(iv) accept that the program is provided "as is" without warranty of any kind or assurance regarding its suitability for any particular purpose;
--		(v) send an email to rajesh<dot>panicker<at>ieee.org briefly mentioning its use (except when used for the course CG3207 at the National University of Singapore);
--		(vi) retain this notice in this file as well as any files derived from this.
----------------------------------------------------------------------------------
*/

// Change wire to reg if assigned inside a procedural (always) block. However, where it is easy enough, use assign instead of always.
// A 2-1 multiplexing can be done easily using an assign with a ternary operator
// For multiplexing with number of inputs > 2, a case construct within an always block is a natural fit. DO NOT to use nested ternary assignment operator as it hampers the readability of your code.


module RV #(
    parameter PC_INIT = 32'h00400000
)(
    input CLK,
    input RESET,
    //input Interrupt,      // for optional future use.
    input [31:0] Instr,     // give us the instruction at PC location
    input [31:0] ReadData_in,       // Name mangled to support lb/lbu/lh/lhu
    output MemRead,
    output [3:0] MemWrite_out,		// Column-wise write enable to support sb/sw. Each column is a byte.
    output [31:0] PC,               // we give location of instruction to get
    output [31:0] ALUResult,
    output [31:0] WriteData_out		// Name mangled to support sb/sw
);

    wire [2:0] SizeSel;     //to support lb/lbu/lh/lhu/sb/sh todo what is this

    //RV Signals
    assign MemRead = MemtoRegM; // This is needed for the proper functionality of some devices such as UART CONSOLE
    assign ReadDataM = ReadData_in;       // Change datapath as appropriate if supporting lb/lbu/lh/lhu
    assign WriteData_out = WriteData;    // Change datapath as appropriate if supporting sb/sh
    assign MemWrite_out = {4{MemWriteM}}; // Change datapath as appropriate if supporting sb/sh
    assign SizeSel = 3'b010;             // Change this to be generated by the Decoder (control) as appropriate if 
                                         // supporting lb/sb/lbu/lh/sh/lhu/lw/sw. Hint: funct3
    assign ALUResult = ALUResultM;
    assign InstrF = Instr; // the instruction coming in from Wrapper to RV
    assign PC = PCF; // the instruction going out to Wrapper from RV 
    
    //Branch Predictor Signals
    wire PredictedTakenF;
    wire [31:0] PredictedBTAF;
    wire IsBranchE;
    wire BranchMispredicted;

    wire PredictedTakenD;
    wire [31:0] PredictedBTAD;
    
    // to determine if instruction in execute stage is a branch instruction
    assign IsBranchE = (PCSE == 2'b01);
    
    // check is prediction was wrong
    // 1. predicted taken but actually not taken (PCSrcE == 0)
    // 2. predicted not taken but actually taken (PCSrcE != 0)
    // 3. predicted taken and actually taken, but wrong BTA
    wire ActualTakenE;
    assign ActualTakenE = (PCSrcE != 2'b00);
    assign BranchMispredicted = IsBranchE && (
        (PredictedTakenE != ActualTakenE) || 
        (PredictedTakenE && ActualTakenE && (PredictedBTAE != ActualBTAE))
    );
    
    // store predicted info in execute stage
    wire PredictedTakenE;
    wire [31:0] PredictedBTAE;
    
    wire [31:0] ActualBTAE;
    assign ActualBTAE = PCE + ExtImmE;
	
	BranchPredictorUnit #(.BHT_SIZE(5)) BranchPredictorUnit1 (
	   .CLK(CLK),
	   .RESET(RESET),
	   
	   // Fetch stage prediction
	   .PCF(PCF),
	   .PredictedTakenF(PredictedTakenF),
	   .PredictedBTAF(PredictedBTAF),
	   
	   // Execute stage
	   .PCE(PCE),
	   .IsBranchE(IsBranchE),
	   .ActualTakenE(ActualTakenE),
	   .ActualBTAE(ActualBTAE)
	);
	
	HazardUnit HazardUnit1(
//        MCycleBusy,
        PCSE,
        BranchMispredicted,
        // Input
        rs1D,
        rs2D,
        rs1E,
        rs2E,
        rdE,
        rs2M,
        rdM,
        rdW,
        // Output
        StallF,
        StallD,
        FlushD,
        Forward2D,
        Forward1D,
        FlushE,
        ForwardBE,
        ForwardAE,
        MemtoRegE,
        PCSrcE,
        ForwardM,
        RegWriteM,
        MemWriteM,
        RegWriteW,
        MemtoRegW
    );
	
	
    // ============================================= FETCH STAGE ============================================= //
    
    // ProgramCounter signals
    wire WE_PC ;    
    wire [31:0] PC_IN ;
   
	// Stage registers -> sequential done here
    wire [31:0] PCF;
    wire [31:0] InstrF;
    
	// Hazard control
	wire StallF;
	
	// modified PC calculation to include branch prediction
	// if branch is mispredicted in execute stage, use correct PC
	// else, use prediction in fetch stage
	wire [31:0] PredictedPC;
	wire [31:0] ActualPC;
	wire IsJumpE; 
	wire [31:0] JumpPC;
	
    assign PredictedPC = (PredictedTakenF ? PredictedBTAF : (PCF + 4));
	assign ActualPC = (PCSrcE[0] ? ExtImmE : 4) + (PCSrcE[0] ? (PCSrcE[1] ? RD1E : PCE) 
	                                                      : PCE);
	assign IsJumpE = (PCSE == 2'b11 || PCSE == 2'b10);
	assign JumpPC = (PCSE == 2'b11) ? RD1E + ExtImmE // jalr
	                :PCE + ExtImmE; // jal
	                                                    
    assign PC_IN = (IsJumpE) ? JumpPC : (BranchMispredicted ? ActualPC : PredictedPC);

    assign WE_PC = ~MCycleBusy && ~StallF;
    
    
    // todo remember to flip PC enable EN
    
    // Instantiate ProgramCounter    
    ProgramCounter #(.PC_INIT(PC_INIT)) ProgramCounter1(
                    CLK,
                    RESET,
                    WE_PC,    
                    PC_IN,
                    PCF  
                );
         
    
	
	// ============================================= DECODE STAGE ============================================= //
	
	// Stage registers
    wire [31:0] InstrD;
    wire [4:0] rdD;
    wire [31:0] PCD;
    wire [31:0] RD1D;
    wire [31:0] RD2D;
    assign RD1D = (Forward1D) ? ResultW : RD1;
    assign RD2D = (Forward2D) ? ResultW : RD2;
    
    // Extend Module signals
    wire [2:0] ImmSrc;
    wire [24:0] InstrImm;
    wire [31:0] ExtImmD;
    
    // Decoder registers
    wire [1:0] PCSD;
    wire RegWriteD;
    wire MemtoRegD;
    wire MemWriteD;
    wire [3:0] ALUControlD;
    wire [1:0] ALUSrcBD;
    // Decoder signals
    wire [1:0] ALUSrcAD;
    wire [1:0] MCycleOp; // todo
    wire IsMulOrDiv; // todo
    
    // Register File signals
    wire WE;
    wire [31:0] WD;
    wire [31:0] R15;
    wire [31:0] RD1;
    wire [31:0] RD2;
	assign WE = RegWriteW;
    assign WD = ResultW;
    
    // Instruction splitting
    // Decoder signals
    wire [6:0] OpcodeD;
    wire [2:0] Funct3D;
    wire [6:0] Funct7D;
    // Register File signals
    wire [4:0] rs1D;
    wire [4:0] rs2D;
    // Instruction
    assign OpcodeD = InstrD[6:0];
	assign rdD = InstrD[11:7];
	assign Funct3D = InstrD[14:12];
	assign rs1D = InstrD[19:15];
	assign rs2D = InstrD[24:20];
	assign Funct7D = InstrD[31:25];
	assign InstrImm = InstrD[31:7];
    
	// Hazard control
	wire StallD;
	wire FlushD;
	wire Forward2D;
	wire Forward1D;
	
	DecodePipelineRegisters DecodePipelineRegisters1(
	                CLK,
	                StallD,
	                FlushD | BranchMispredicted, // flush on misprediction,
	                MCycleBusy,
	                InstrF,
	                PCF,
	                InstrD,
	                PCD,

                    PredictedTakenF,
	                PredictedBTAF,

                    PredictedTakenD,
	                PredictedBTAD
	            ); 

    // Instantiate RegFile
    RegFile RegFile1( 
                    CLK,
                    WE,
                    rs1D,
                    rs2D,
                    rdW,
                    WD,
                    RD1,
                    RD2     
                );
                
    // Instantiate Extend Module
    Extend Extend1(
                    ImmSrc,
                    InstrImm,
                    ExtImmD
                );
    
    // Instantiate Decoder
    Decoder Decoder1(
                    OpcodeD,
                    Funct3D,
                    Funct7D,
                    PCSD,
                    RegWriteD,
                    MemWriteD,
                    MemtoRegD,
                    ALUSrcAD,
                    ALUSrcBD,
                    ImmSrc,
                    ALUControlD
//                    MCycleOp,
//                    IsMulOrDiv
                );
 
 
    // ============================================= EXECUTE STAGE ============================================= //
	
	wire [31:0] ALUInputA;
	wire [31:0] ALUInputB;
	
	// Stage registers
	wire [31:0] RD1E;
	wire [31:0] RD2E;
	wire [31:0] WriteDataE;
	wire [31:0] ExtImmE;
	wire [31:0] PCE;
	wire [4:0] rdE;
	wire [2:0] Funct3E;
	wire [6:0] OpcodeE;
	wire [6:0] Funct7E;
	
	// Decoder Signals
    wire RegWriteE;
    wire MemtoRegE;
    wire MemWriteE;
    wire [3:0] ALUControlE;
    wire [1:0] ALUSrcBE;
    wire [1:0] ALUSrcAE; // to put this in as well
	
    // PC_Logic signals
    wire [1:0] PCSrcE;
    wire [1:0] PCSE;
    
    // ALU Signals
    wire [31:0] Src_A ;
    wire [31:0] Src_B ;
    wire [2:0] ALUFlags ;
    wire [31:0] RawALUResult; // changed from temp alu result
    wire [31:0] ALUResultE;
    
    // Hazard control signals
    wire [1:0] ForwardAE;
    wire [1:0] ForwardBE;
    wire FlushE;
    
    // ForwardAE: 00: RD1E, 01: ResultW, 10: ALUResultM
    assign ALUInputA = (ForwardAE == 2'b00) ? RD1E 
                                            : (ForwardAE == 2'b01) ? ResultW
                                                                   : ALUResultM;
    // ForwardBE: 00: RD2E, 01: ResultW, 10: ALUResultM
    assign WriteDataE = ALUInputB;
    assign ALUInputB = (ForwardBE == 2'b00) ? RD2E 
                                            : (ForwardBE == 2'b01) ? ResultW
                                                                   : ALUResultM;                
//    assign Src_B = (ALUSrcBE) ? ExtImmE : WriteDataE;
    
    assign ALUResultE = ((OpcodeE == 7'b0110011) && (Funct7E == 2'h01)) ? ((Funct3E[1] == 1'b0 && Funct3E != 1) ? MCycleResult1 
                                                                               : MCycleResult2) 
                                         : RawALUResult;
    
    assign Src_A = (ALUSrcAE[0]) ? ((ALUSrcAE[1] == 0) ? 1'b0 
                                                       : PCE) 
                                 : ALUInputA;
	assign Src_B = (ALUSrcBE[0]) ? ((ALUSrcBE[1] == 0) ? 4 
	                                                   : ExtImmE) 
	                             : ALUInputB;
	                             
	assign MCycleOp = (Funct3E == 3'b001) ? 2'b00 : {Funct3E[2], Funct3E[0]};
	assign IsMulOrDiv = (OpcodeE == 7'b0110011) && (Funct7E == 2'h01);
	
	wire WE;
    wire [31:0] WD;
    wire [31:0] R15;
    wire [31:0] RD1;
    wire [31:0] RD2;
	assign WE = RegWriteW;
    assign WD = ResultW;
	
	ExecutePipelineRegisters ExecutePipelineRegisters1(
	                CLK,
	                FlushE | BranchMispredicted, // flush on misprediction,
	                MCycleBusy,
	                // Inputs
	                PCSD,
	                RegWriteD,
	                MemtoRegD,
	                MemWriteD,
	                ALUControlD,
	                ALUSrcAD,
	                ALUSrcBD,
	                RD1D,
	                RD2D,
	                ExtImmD,
	                rdD,
	                PCD,
	                rs1D,
	                rs2D,
	                
	                PredictedTakenD,
	                PredictedBTAD,
	                
	                Funct3D,
	                OpcodeD,
	                Funct7D,
	                // Outputs
	                PCSE,
	                RegWriteE,
	                MemtoRegE,
	                MemWriteE,
	                ALUControlE,
	                ALUSrcAE,
	                ALUSrcBE,
	                RD1E,
	                RD2E,
	                ExtImmE,
	                rdE,
	                PCE,
	                rs1E,
	                rs2E,
	                
	                PredictedTakenE,
	                PredictedBTAE,
	                Funct3E,
	                OpcodeE,
	                Funct7E
	            );
	            
    // Instantiate PC_Logic
	PC_Logic PC_Logic1(
                    PCSE,
                    Funct3E, // todo no more func3??
                    ALUFlags,
//                    BranchMispredicted,
                    PCSrcE
		);
    
    // Instantiate ALU        
    ALU ALU1(
                    Src_A,
                    Src_B,
                    ALUControlE,
                    RawALUResult,
                    ALUFlags
                );
                
    // ============================================= MEMORY STAGE ============================================= //
    
    wire [31:0] WriteData; // goes to wrapper from RV
    wire [31:0] ReadDataM;
     
    // Stage registers
    wire RegWriteM;
    wire MemtoRegM;
    wire MemWriteM;
    wire [31:0] ALUResultM;
    wire [4:0] rdM;
    wire [31:0] WriteDataM;
    
    // Hazard control signals
	wire ForwardM;

    wire [4:0] rs1E;
    wire [4:0] rs2E;
    wire [4:0] rs2M;

    assign WriteData = (ForwardM) ? ResultW : WriteDataM;
    
    MemoryPipelineRegisters MemoryPipelineRegisters1( 
        CLK,
        MCycleBusy,
        // Inputs
        RegWriteE,
        MemtoRegE,
        MemWriteE,
        ALUResultE,
        WriteDataE,
        rdE,
        rs2E,
        // Outputs
        RegWriteM,
        MemtoRegM,
        MemWriteM,
        ALUResultM,
        WriteDataM,
        rdM,
        rs2M
    );
    
    
    // ============================================= WRITEBACK STAGE ============================================= //
    
    
    // Stage registers
    wire RegWriteW;
    wire MemtoRegW;
    wire [31:0] ReadDataW;
    wire [31:0] ALUResultW;
	wire [4:0] rdW;
	wire [31:0] ResultW;

	assign ResultW = (MemtoRegW == 1) ? ReadDataW : ALUResultW;


    WritebackPipelineRegisters WritebackPipelineRegisters1( 
        CLK,
//        MCycleBusy,
        // Inputs
        RegWriteM,
        MemtoRegM,
        ReadDataM,
        ALUResultM,
        rdM,
        // Outputs
        RegWriteW,
        MemtoRegW,
        ReadDataW,
        ALUResultW,
        rdW
    );
    
    // ============================================= MULTI-CYCLE PROCESSES ============================================= //
    
    // MCycle initialisation. "00" for signed multiplication, "01" for unsigned multiplication, "10" for signed division, "11" for unsigned division. Generated by Control unit
    wire [31:0] MCycleResult1; // LSW of Product / Quotient
    wire [31:0] MCycleResult2; // MSW of Product / Remainder
    wire MCycleBusy;
    
    MCycle #(.width(32)) MCycle1(
        CLK,
        RESET, 
        IsMulOrDiv,
        MCycleOp, 
        Src_A, 
        Src_B, 
        MCycleResult1, 
        MCycleResult2, 
        MCycleBusy
    );
    
    
endmodule