dakies/OSS_Verilog huggingface dataset:
module ex(
    input clk,
    input reset,
    input flush,
    input [31:0] Instr_EX,
    input [31:0] ForwardToALU_A_EX, ForwardToALU_B_EX, ImmEXT_EX,
    output busy_EX, state_EX,
    output [4:0] RegA3_EX,
    output [31:0] ALUout_EX,
    output Overflow_EX,
    input [4:0] ExcCode_in,
    output [4:0] ExcCode_out
);

    wire [31:0] SrcA_EX, SrcB_EX, MultDiv_out_EX, ALU_Result_EX;
    wire [3:0] ALUOp_EX, HiLoOp_EX;
    wire [1:0] WRSel_EX;
    wire ASel_EX, BSel_EX, ALUoutSel_EX, Exception, isCalc_Overflow_EX;
    alu myALU(
        .A(SrcA_EX), .B(SrcB_EX), .ALUOp(ALUOp_EX),
        .C(ALU_Result_EX), .Overflow(Overflow_EX)
    );      //there has a forward or stall
    multdiv myMultDiv(
        .clk(clk), .reset(reset), .flush(1'b0),
        .A(SrcA_EX), .B(SrcB_EX), .HiLoOp(HiLoOp_EX),
        .C(MultDiv_out_EX), .busy(busy_EX), .state(state_EX)
    );
    ctrl EX_Ctrl(
        .Instr(Instr_EX & {32{~Exception & ~flush}}),
        .ALUOp(ALUOp_EX), .WRSel(WRSel_EX), .ASel(ASel_EX), .BSel(BSel_EX), .ALUoutSel(ALUoutSel_EX), .HiLoOp(HiLoOp_EX),
        .isCalc_Overflow(isCalc_Overflow_EX)
    );

    assign ExcCode_out = (Overflow_EX && isCalc_Overflow_EX) ?  5'd12 : ExcCode_in;
    assign Exception = (ExcCode_in==5'd0) ? 1'b0 : 1'b1;


    mux2_1 MUX_SrcA_EX(
        .port0(ForwardToALU_A_EX), .port1( {27'd0, Instr_EX[10:6]} ),
        .Sel(ASel_EX), .out(SrcA_EX)
    );
    mux2_1 MUX_SrcB_EX(
        .port0(ForwardToALU_B_EX), .port1(ImmEXT_EX),
        .Sel(BSel_EX), .out(SrcB_EX)
    );
    mux2_1 MUX_ALUout_EX(
        .port0(ALU_Result_EX), .port1(MultDiv_out_EX),
        .Sel(ALUoutSel_EX), .out(ALUout_EX)
    );
    mux4_1 #(5) MUX_RegA3_EX(
        .port0(Instr_EX[20:16]), .port1(Instr_EX[15:11]), .port2(5'h1f), .port3(5'h00),
        .Sel( WRSel_EX|{Overflow_EX,Overflow_EX} ), .out(RegA3_EX)
    );      //if overflow then WRsel|Overflow

endmodule // ex
module mux2_1(
    input [dataWidth-1:0] port0,
    input [dataWidth-1:0] port1,
    input Sel,
    output [dataWidth-1:0] out
);

    parameter dataWidth = 32;
    assign out = (Sel) ? port1 : port0;

endmodule // mux2_1
module mux4_1(
    input [dataWidth-1:0] port0,
    input [dataWidth-1:0] port1,
    input [dataWidth-1:0] port2,
    input [dataWidth-1:0] port3,
    input [1:0] Sel,
    output reg [dataWidth-1:0] out
);

    parameter dataWidth = 32;

    always @(*) begin
        case(Sel)
            2'b00:      out <= port0;
            2'b01:      out <= port1;
            2'b10:      out <= port2;
            2'b11:      out <= port3;
            default:    out <= {dataWidth{1'bx}};
        endcase
    end

endmodule // mux4_1
module dm(
    input clk,
    input reset,
    input WE,
    input [3:0] BE,
    input [31:0] addr,
    input [31:0] WD,
    output [31:0] RD
);

    reg [31:0] ram[4095:0];
    reg [31:0] data;
    integer i;

    initial begin
        for(i=0; i<4096; i=i+1) begin
            ram[i] <= 32'h0000_0000;
        end
    end

    always @(*) begin
        case (BE)
            4'b0001:    data <= {RD[31:8], WD[7:0]};
            4'b0010:    data <= {RD[31:16], WD[7:0], RD[7:0]};
            4'b0100:    data <= {RD[31:24], WD[7:0], RD[15:0]};
            4'b1000:    data <= {WD[7:0], RD[23:0]};
            4'b0011:    data <= {RD[31:16], WD[15:0]};
            4'b1100:    data <= {WD[15:0], RD[15:0]};
            4'b1111:    data <= WD;
            default:    data <= RD;
        endcase
    end

    always @(posedge clk) begin
        if(reset) begin
            for(i=0; i<4096; i=i+1) begin
                ram[i] <= 32'h0000_0000;
            end
        end 
        else if(WE) begin
            ram[addr[13:2]] <= data;
        end
    end

    assign RD = ram[addr[13:2]];

endmodule // dm
module im(
    input [31:0] addr,
    output [31:0] Instr
);

    reg [31:0] rom[4095:0];
    integer i;
    wire [31:0] im_addr;

    initial begin
        for (i = 0; i<4096; i=i+1) begin
            rom[i] = 32'h0000_0000;
        end
        $readmemh("code.txt", rom);
        $readmemh("code_handler.txt",rom,1120,2047);
    end

    assign im_addr = addr - 32'h0000_3000;
    assign Instr = rom[im_addr[13:2]];

endmodule // im
module pc(
    input clk,
    input reset,
    input WE,
    input [31:0] npc,
    output reg [31:0] pc
);

    initial pc = 32'h0000_3000;

    always @(posedge clk) begin
        if(reset) begin
            pc <= 32'h0000_3000;
        end
        else if(WE) begin
            pc <= npc;
        end
    end

endmodule // pc
module wb(
    input [31:0] Instr_WB, 
    input [31:0] ALUout_WB,
    input [31:0] DM_RD_WB,
    input [31:0] PC8_WB,
    output [31:0] RegWriteData_WB,
    input [31:0] CP0_RD_WB,
    output RFWr_WB
);

    wire [1:0] WDSel_WB;
    ctrl WB_Ctrl(
        .Instr(Instr_WB),
        .RFWr(RFWr_WB), .WDSel(WDSel_WB)
    );
    mux4_1 MUX_WD_WB(
        .port0(ALUout_WB), .port1(DM_RD_WB), .port2(PC8_WB), .port3(CP0_RD_WB),
        .Sel(WDSel_WB), .out(RegWriteData_WB)
    );

endmodule // wb
module ext(
    input [1:0] EXTOp,
    input [15:0] Imm,
    output reg [31:0] out
);

    always @(*) begin
        case(EXTOp)
            2'b00:      out <= {16'h0000, Imm};
            2'b01:      out <= {{16{Imm[15]}}, Imm};
            2'b10:      out <= {Imm, 16'h0000};
            2'b11:      out <= 32'hxxxx_xxxx;
            default:    out <= 32'hxxxx_xxxx;
        endcase
    end

endmodule // ext
module cmp(
    input [31:0] A, B,
    input CMPOp,        //0: signed, 1: unsigned
    output equal, less, greater
);

    assign equal = (A==B) ? 1'b1 : 1'b0;
    assign less = (CMPOp==1'b0) ?   (($signed(A)<$signed(B)) ? 1'b1 : 1'b0) :
                                    ((A < B) ? 1'b1 : 1'b0);
    assign greater = (CMPOp==1'b0) ?   (($signed(A)>$signed(B)) ? 1'b1 : 1'b0) :
                                    ((A > B) ? 1'b1 : 1'b0);

endmodule // cmp
module npc(
    input [31:0] pc,
    input [25:0] Imm,
    input [31:0] ra,
    input [1:0] NPCOp,
    output reg [31:0] npc
);

    always @(*) begin
        case(NPCOp)
            2'b00:      npc <= pc + 32'd4;
            2'b01:      npc <= pc + 32'd4 + {{14{Imm[15]}}, Imm[15:0], 2'b00};
            2'b10:      npc <= {pc[31:28], Imm[25:0], 2'b00};
            2'b11:      npc <= ra;
            default:    npc <= 32'hxxxx_xxxx;
        endcase
    end


endmodule // npc
module multdiv(
    input clk, 
    input reset,
    input flush,
    input [31:0] A,
    input [31:0] B,
    input [3:0] HiLoOp,
    output [31:0] C,
    output state,
    output reg busy
);

    reg [31:0] HI = 0, LO = 0;
    reg [63:0] temp = 0;
    reg [7:0] counter = 0;

    assign C    =   (HiLoOp == `MFHI_OP)    ?   HI  :
                    (HiLoOp == `MFLO_OP)    ?   LO  :
                                                32'd0;

    assign state = (HiLoOp==`MULT_OP||HiLoOp==`MULTU_OP||HiLoOp==`DIV_OP||HiLoOp==`DIVU_OP) && !flush ? 1'b1 : 1'b0;

    always @(posedge clk) begin
        if(reset) begin
            HI <= 32'd0;
            LO <= 32'd0;
            counter <= 8'd0;
            busy <= 1'b0;
        end
        else if(flush == 1'b1) begin
            counter <= 1'b0;
            busy <= 1'b0;
        end
        else if(state == 1'b1) begin
            busy <= 1'b1;
            case (HiLoOp)
                `MULT_OP: begin
                    counter <= 8'd4;
                    temp <= $signed(A) * $signed(B);
                end
                `MULTU_OP: begin
                    counter <= 8'd4;
                    temp <= {1'b0, A} * {1'b0, B};
                end
                `DIV_OP: begin
                    counter <= 8'd9;
                    temp[31:0] <= $signed(A) / $signed(B);
                    temp[63:32] <= $signed(A) % $signed(B);
                end
                `DIVU_OP: begin
                    counter <= 8'd9;
                    temp[31:0] <= {1'b0, A} / {1'b0, B};
                    temp[63:32] <= {1'b0, A} % {1'b0, B};
                end
                default: begin
                    ;
                end
            endcase
        end
        else if(counter != 8'd0 && busy == 1'b1) begin
            counter <= counter - 8'd1;
        end
        else if(counter == 8'd0 && busy == 1'b1) begin
            busy <= 1'b0;
            {HI, LO} <= temp;
        end
        else if(counter == 8'd0 && state == 1'b0 && busy == 1'b0) begin
            case (HiLoOp)
                `MTHI_OP:   HI <= A;
                `MTLO_OP:   LO <= A;
                default:    ;
            endcase
        end
        else begin
            ;
        end
    end

endmodule // multdiv
module stall(
    input [31:0] Instr_ID, Instr_EX, Instr_MEM,
    input [4:0] RegA3_EX, RegA3_MEM,
    input busy_EX, state_EX,
    output Stall
);

    wire isCalc_R_ID, isCalc_I_ID, isLoad_ID, isStore_ID, isBranch_ID, isJr_ID, isHiLo_ID;
    wire [4:0] rs_ID, rt_ID;

    ctrl ID_CTRL(
        .Instr(Instr_ID),
        .isCalc_R(isCalc_R_ID), .isCalc_I(isCalc_I_ID),
        .isLoad(isLoad_ID), .isStore(isStore_ID), .isBranch(isBranch_ID), .isJr(isJr_ID), .isHiLo(isHiLo_ID)
    );
    assign rs_ID = Instr_ID[25:21];
    assign rt_ID = Instr_ID[20:16];


    wire isCalc_R_EX, isCalc_I_EX, isLoad_EX, isMfc0_EX;

    ctrl EX_CTRL(
        .Instr(Instr_EX),
        .isCalc_R(isCalc_R_EX), .isCalc_I(isCalc_I_EX), .isLoad(isLoad_EX), .isMfc0(isMfc0_EX)
    );


    wire isLoad_MEM, isMfc0_MEM;

    ctrl MEM_CTRL(
        .Instr(Instr_MEM),
        .isLoad(isLoad_MEM), .isMfc0(isMfc0_MEM)
    );


    wire stall_branch, stall_calc_r, stall_calc_i, stall_load, stall_store, stall_jr, stall_hilo;

    assign stall_branch = isBranch_ID & ( ( (isCalc_R_EX | isCalc_I_EX | isLoad_EX | isMfc0_EX) & (rs_ID==RegA3_EX || rt_ID==RegA3_EX) & (RegA3_EX!=5'd0) )
                                          | ( (isLoad_MEM | isMfc0_MEM) & (rs_ID==RegA3_MEM || rt_ID==RegA3_MEM) & (RegA3_MEM!=5'd0) ) );
    assign stall_calc_r = isCalc_R_ID & (isLoad_EX | isMfc0_EX) & (rs_ID==RegA3_EX || rt_ID==RegA3_EX) & (RegA3_EX!=5'd0);
    assign stall_calc_i = isCalc_I_ID & (isLoad_EX | isMfc0_EX) & (rs_ID==RegA3_EX) & (RegA3_EX!=5'd0);
    assign stall_load = isLoad_ID & (isLoad_EX | isMfc0_EX) & (rs_ID==RegA3_EX) & (RegA3_EX!=5'd0);
    assign stall_store = isStore_ID & (isLoad_EX | isMfc0_EX) & (rs_ID==RegA3_EX) & (RegA3_EX!=5'd0);
    assign stall_jr = isJr_ID & ( ( (isCalc_R_EX | isCalc_I_EX | isLoad_EX | isMfc0_EX) & (rs_ID==RegA3_EX) & (RegA3_EX!=5'd0) )
                                | ( (isLoad_MEM | isMfc0_MEM) & (rs_ID==RegA3_MEM) & (RegA3_MEM!=5'd0) ) );
    assign stall_hilo = isHiLo_ID & (busy_EX|state_EX);


    assign Stall = stall_branch | stall_calc_r | stall_calc_i | stall_load | stall_store | stall_jr | stall_hilo;

endmodule // stall
module bridge(
    input [31:0] PrAddr,
    input [31:0] PrWD,
    input [3:0] PrBE,
    input PrWE,
    input INT_Timer0, INT_Timer1, INT_Outer,
    input [31:0] DEV0_RD, DEV1_RD,
    output [31:0] PrRD,
    output [31:0] DEV_Addr,
    output [31:0] DEV_WD,
    output [7:2] HWInt,
    output DEV0_WE, DEV1_WE
);

    wire CS_Timer0, CS_Timer1;

    assign CS_Timer0 = (PrAddr[31:4] == 28'h0000_7f0) ? 1'b1 : 1'b0;
    assign CS_Timer1 = (PrAddr[31:4] == 28'h0000_7f1) ? 1'b1 : 1'b0;

    assign DEV0_WE = PrWE & CS_Timer0;
    assign DEV1_WE = PrWE & CS_Timer1;

    assign DEV_Addr = PrAddr;
    assign DEV_WD = (PrBE == 4'b1111)   ?   PrWD    :
                    (PrBE == 4'b0011)   ?   {PrRD[31:16], PrWD[15:0]}    :
                    (PrBE == 4'b1100)   ?   {PrWD[31:16], PrRD[15:0]}    :
                    (PrBE == 4'b0001)   ?   {PrRD[31:8], PrWD[7:0]}    :
                    (PrBE == 4'b0010)   ?   {PrRD[31:16], PrWD[15:8], PrRD[7:0]}    :
                    (PrBE == 4'b0100)   ?   {PrRD[31:24], PrWD[23:16], PrRD[15:0]}    :
                    (PrBE == 4'b1000)   ?   {PrWD[31:24], PrRD[23:0]}    :
                                            32'h0000_0000;

    assign PrRD =   (CS_Timer0) ?   DEV0_RD :
                    (CS_Timer1) ?   DEV1_RD :
                                    32'hxxxx_xxxx;
    
    assign HWInt = {3'b000, INT_Outer, INT_Timer1, INT_Timer0};

endmodule // bridge
module tb_mips;

	// Inputs
	reg clk;
	reg reset;
	reg interrupt;

	// Outputs
	wire [31:0] addr;
	

	// Instantiate the Unit Under Test (UUT)
	mips uut (
		.clk(clk), 
		.reset(reset),
		.addr(addr),
		.interrupt(interrupt)
	);

	parameter exception_pc = 32'h00003018;
	integer exception_count;
	integer interrupt_counter;
	integer needInterrupt;

	initial begin
		exception_count = 0;
		interrupt = 0;
		needInterrupt = 0;
		interrupt_counter = 0;
		// Initialize Inputs
		clk = 0;
		reset = 1;
		#20 reset = 0;
		// Wait 100 ns for global reset to finish
		// Add stimulus here
		//#26 interrupt=1;
		//#20 interrupt=0;
		#10000 $finish;
		
	end
   always #2 clk = ~clk;
/*
   always @(negedge clk) begin
      if (reset) begin
	  	interrupt_counter = 0;
		needInterrupt = 0;
		interrupt = 0;
	  end else begin
	  	if (interrupt) begin
		  	if (interrupt_counter == 0) begin
				interrupt = 0;
			end else begin
				interrupt_counter = interrupt_counter - 1;
			end
		end else if (needInterrupt) begin
			needInterrupt = 0;
			interrupt = 1;
			interrupt_counter = 5;
		end else begin
			case (addr)
				exception_pc: 
					begin
						if (exception_count == 0) begin
							exception_count = 1;
							interrupt = 1;
							interrupt_counter = 5;
						end
					end
			endcase
		end
	  end
   end
*/
endmodule
module id(
    input clk, reset, RFWr_WB,
    input [31:0] Instr_ID,
    input [31:0] ForwardRD1_ID, ForwardRD2_ID, RegWriteData_WB, PC_ID,
    input [4:0] RegA3_WB,
    output [1:0] NPCOp_ID,
    output [31:0] npcValue_ID, RegData1_ID, RegData2_ID, ImmEXT_ID,
    output isJump_ID,
    input [4:0] ExcCode_in,
    output [4:0] ExcCode_out
);

    wire equal, greater, less, CmpWithZero_ID, Undefined, Exception;
    wire [1:0] EXTOp_ID;
    wire [31:0] cmp2_ID;
    grf myGRF(
        .clk(clk), .reset(reset), .WE(RFWr_WB),
        .A1(Instr_ID[25:21]), .A2(Instr_ID[20:16]), .A3(RegA3_WB),
        .WD(RegWriteData_WB), .RD1(RegData1_ID), .RD2(RegData2_ID)
    );
    npc myNPC(.pc(PC_ID), .Imm(Instr_ID[25:0]), .ra(ForwardRD1_ID), .NPCOp(NPCOp_ID), .npc(npcValue_ID));
    ext myEXT(.EXTOp(EXTOp_ID), .Imm(Instr_ID[15:0]), .out(ImmEXT_ID));
    cmp myCMP(
        .A(ForwardRD1_ID), .B(cmp2_ID), .CMPOp(1'b0),
        .equal(equal), .greater(greater), .less(less)
    );     //there has a forward or stall
    ctrl ID_Ctrl(
        .Instr(Instr_ID & {32{~Exception}}), .equal(equal), .greater(greater), .less(less),
        .EXTOp(EXTOp_ID), .NPCOp(NPCOp_ID), .CmpWithZero(CmpWithZero_ID),
        .Undefined(Undefined), .isJump(isJump_ID)
    );

    assign cmp2_ID =        (CmpWithZero_ID) ?  32'd0        :
                                                ForwardRD2_ID;
    assign ExcCode_out =    (Undefined) ?  5'd10 : ExcCode_in;
    assign Exception =      (ExcCode_in==5'd0) ? 1'b0 : 1'b1;

endmodule // id
module mem(
    input clk, reset, flush,
    input Overflow_MEM,
    input [31:0] Instr_MEM,
    input [31:0] ALUout_MEM, ForwardToDM_WD_MEM, PrRD,
    output DMWr_MEM, CP0_Wr_MEM, isEret_MEM,
    output [3:0] BE_MEM,
    output [31:0] DM_RD_MEM,
    input [4:0] ExcCode_in,
    output [4:0] ExcCode_out
);

    wire [31:0] DM_EXT_Value, DM_RD_out;
    wire DMWr, isStore_MEM, isLoad_MEM;
    wire [2:0] DMEXTOp;
    wire [3:0] BE;
    wire CS_DM, CS_Timer, Exception;

    assign CS_DM = (ALUout_MEM[31:12]==20'h0000_0 ||
                    ALUout_MEM[31:12]==20'h0000_1 ||
                    ALUout_MEM[31:12]==20'h0000_2)  ? 1'b1 : 1'b0;
    assign CS_Timer =  ((ALUout_MEM>=32'h0000_7f00 && ALUout_MEM<=32'h0000_7f0b) ||
                        (ALUout_MEM>=32'h0000_7f10 && ALUout_MEM<=32'h0000_7f1b))   ? 1'b1 : 1'b0;
    assign Exception = (ExcCode_in==5'd0) ? 1'b0 : 1'b1;
    assign DMWr_MEM = DMWr & ~flush;
    assign BE_MEM = BE;
    assign DM_EXT_Value = (CS_DM) ? DM_RD_out : PrRD;
    assign ExcCode_out =   ((isLoad_MEM && (Overflow_MEM||(!CS_DM&&!CS_Timer))) ||
                            (DMEXTOp==`LW_EXT && ALUout_MEM[1:0]!=2'b00) ||
                            ((DMEXTOp==`LH_EXT||DMEXTOp==`LHU_EXT) && (ALUout_MEM[0]!=1'b0||CS_Timer)) ||
                            ((DMEXTOp==`LB_EXT||DMEXTOp==`LBU_EXT) && CS_Timer))                    ?   5'd4 :
                           ((isStore_MEM && (Overflow_MEM||(!CS_DM&&!CS_Timer)||(CS_Timer&&ALUout_MEM[3:0]==4'h8))) ||
                            (BE==4'b1111 && ALUout_MEM[1:0]!=2'b00) ||
                            ((BE==4'b0011||BE==4'b1100) && (ALUout_MEM[0]!=1'b0||CS_Timer))    ||
                            ((BE==4'b0001||BE==4'b0010||BE==4'b0100||BE==4'b1000) && CS_Timer))     ?   5'd5 :
                                                                                                    ExcCode_in;

    dmdecode myDM_Decode(
        .Instr(Instr_MEM), .byte(ALUout_MEM[1:0]),
        .BE(BE)
    );
    dm myDM(
        .clk(clk), .reset(reset), .WE(DMWr & CS_DM & ~flush), .BE(BE),
        .addr(ALUout_MEM), .WD(ForwardToDM_WD_MEM), .RD(DM_RD_out)
    );
    dmext myDMEXT(
        .Din(DM_EXT_Value), .DMEXTOp(DMEXTOp), .byte(ALUout_MEM[1:0]),
        .Dout(DM_RD_MEM)
    );
    ctrl MEM_Ctrl(
        .Instr(Instr_MEM & {32{~Exception}}),
        .DMWr(DMWr), .DMEXTOp(DMEXTOp), .CP0_Wr(CP0_Wr_MEM), .isEret(isEret_MEM), .isStore(isStore_MEM), .isLoad(isLoad_MEM)
    );

endmodule // mem
module alu(
    input [31:0] A,
    input [31:0] B,
    input [3:0] ALUOp,
    output reg [31:0] C,
    output Overflow
);

    always @(*) begin
        case(ALUOp)
            `ADD_EXE:   C = A + B;
            `ADDU_EXE:  C = A + B;
            `SUB_EXE:   C = A - B;
            `SUBU_EXE:  C = A - B;
            `AND_EXE:   C = A & B;
            `OR_EXE:    C = A | B;
            `XOR_EXE:   C = A ^ B;
            `NOR_EXE:   C = ~(A | B);
            `SLL_EXE:   C = B << A[4:0];
            `SRL_EXE:   C = B >> A[4:0];
            `SRA_EXE:   C = $signed(B) >>> A[4:0];
            `SLT_EXE:   C = ($signed(A) < $signed(B)) ? 32'd1 : 32'd0;
            `SLTU_EXE:  C = (A < B) ? 32'd1 : 32'd0;
            default:    C = 32'hxxxx_xxxx;
        endcase
    end 

    reg [32:0] temp;

    always @(*) begin
        case(ALUOp)
            `ADD_EXE:   temp = {A[31], A} + {B[31], B};
            `SUB_EXE:   temp = {A[31], A} - {B[31], B};
            default:    temp = 32'd0;
        endcase
    end

    assign Overflow = (temp[32] != temp[31]) ? 1'b1 : 1'b0;

endmodule // alu
module cp0(
    input clk,
    input reset,
    input WE,
    input EXLSet,
    input EXLClr,
    input isDB,
    input isEret,
    input [4:0] A1,
    input [4:0] A2,
    input [31:0] Din,
    input [31:0] pc,
    input [6:2] ExcCode,
    input [7:2] HWInt,
    output IntReq,
    output [31:0] epc,
    output [31:0] Dout
);

    reg [31:0] SR = 0, Cause = 0, EPC = 0, PRId = 32'h00330099;

    assign IntReq = ( (`IE && !`EXL && HWInt&`IM) || (!`EXL && ExcCode) || isEret ) ? 1'b1 : 1'b0;
    assign epc = EPC;
    assign Dout =   (A1 == 5'd12)   ?   SR      :
                    (A1 == 5'd13)   ?   Cause   :
                    (A1 == 5'd14)   ?   EPC     :
                    (A1 == 5'd15)   ?   PRId    :
                                        32'd0;
    
    always @(posedge clk) begin
        if(reset)
            SR <= 32'd0;
        else if(WE == 1'b1 && A2 == 5'd12)
            SR <= Din;
        else if(EXLClr)
            SR <= SR & 32'hffff_fffd;
        else if(EXLSet)
            SR <= SR | 32'h0000_0002;
        else
            ;
    end

    always @(posedge clk) begin
        if (reset)
            Cause <= 32'd0;
        else if(`IE && !`EXL &&  HWInt&`IM)
            Cause <= {isDB, 15'd0, HWInt, 3'd0, 5'd0, 2'd0};
        else if(!`EXL && ExcCode)
            Cause <= {isDB, 15'd0, HWInt, 3'd0, ExcCode, 2'd0};
        else
            Cause <= {Cause[31:16], HWInt, Cause[9:0]};
    end

    always @(posedge clk) begin
        if(reset)
            EPC <= 32'd0;
        else if(WE == 1'b1 && A2 == 5'd14)
            EPC <= {Din[31:2], 2'b00};
        else if(IntReq == 1'b1 && !isEret)
            if(isDB)
                EPC <= {pc[31:2], 2'b00} - 32'd4;
            else
                EPC <= {pc[31:2], 2'b00};
        else
            ;
    end


endmodule // cp0
module ADD4(PC,PC4);
//interface
    input [31:0] PC;

    output [31:0] PC4;

//combinational logic
    assign PC4=PC+4;

endmodule
module CMP(D1,D2,equ,zero_greater,zero_equ,zero_less);
//interface
	input [31:0] D1;
    input [31:0] D2;

    output equ;
	
	output zero_greater;
	output zero_equ;
	output zero_less;

//combinational logic
    assign equ=(D1==D2);
	assign zero_greater=($signed(D1)>0);
	assign zero_equ=($signed(D1)==0);
	assign zero_less=($signed(D1)<0);

endmodule
module ID_EXC(undef_D,ExcCode_D,ExcCode);
//interface
    input undef_D;

    input [6:2] ExcCode_D;      //ExcCode of last level

    output reg [6:2] ExcCode;   //ExcCode of this level

//IR
    always @(*)
        begin
            if(undef_D)ExcCode=`RI;
            else ExcCode=ExcCode_D;
        end

endmodule
module EXE_EXC(overflow,flow_sus,ExcCode_E,ExcCode);
//interface
    input overflow;
    input flow_sus;     //add/sub/addi

    input [6:2] ExcCode_E;

    output reg [6:2] ExcCode;

//Ov
    always @(*)
        begin
            if(overflow&&flow_sus)ExcCode=`Ov;
            else ExcCode=ExcCode_E;
        end

endmodule
module LED(clk,reset,LEDWe,LED_WD,LED_RD,lights);
//input
    input clk;
    input reset;

    input LEDWe;

    input [31:0] LED_WD;
//output
    output [31:0] LED_RD;

    output [31:0] lights;

//sequential logic
    reg [31:0] data;

    assign LED_RD=data;
    assign lights=~data;

    always @(posedge clk)
        begin
            if(reset)data<=32'hffffffff;
            else if(LEDWe)data<=LED_WD;
            else data<=data;
        end

endmodule
module PC(clk,reset,stall,IRQ,eret_M,NPC,PC);
//interface
	input clk;
	input reset;
	input stall;
	input IRQ;
	input eret_M;
	
	input [31:0] NPC;
	
	output reg [31:0] PC;

//initial address
	parameter init_addr=32'h0000_3000,
			  exc_handler_addr=32'h0000_4180;

	/*
	initial
		begin
			PC=init_addr;
		end
	*/
		
	always @(posedge clk)
		begin
			if(reset)PC<=init_addr;
			else if(eret_M)PC<=NPC;
			else if(IRQ)PC<=exc_handler_addr;
			else if(stall)PC<=PC;
			else PC<=NPC;
		end
	
endmodule
module EReg(clk,reset,stall,Ins,V1,V2,Ext,PC,PC8,ExcCode,BD,Ins_E,V1_E,V2_E,Ext_E,PC_E,PC8_E,ExcCode_E,BD_E);
//interface
    input clk;
    input reset;
    input stall;

    input [31:0] Ins;
    input [31:0] V1;
    input [31:0] V2;
    input [31:0] Ext;

    input [31:0] PC;
    input [31:0] PC8;

    input [6:2] ExcCode;
    input BD;

    output reg [31:0] Ins_E;
    output reg [31:0] V1_E;
    output reg [31:0] V2_E;
    output reg [31:0] Ext_E;
    
    output reg [31:0] PC_E;
    output reg [31:0] PC8_E;

    output reg [6:2] ExcCode_E;
    output reg BD_E;

//sequential logic
    /*
	initial
		begin
			Ins_E=0;
			V1_E=0;
			V2_E=0;
			Ext_E=0;
			PC_E=0;
			PC8_E=0;
            ExcCode_E=0;
            BD_E=0;
		end
	*/
    
    always @(posedge clk)
        begin
            if(reset||stall)
                begin
                    Ins_E<=0;
                    V1_E<=0;
                    V2_E<=0;
                    Ext_E<=0;
                    PC_E<=PC;       //stick PC_D and keep on piping
                    PC8_E<=0;
                    ExcCode_E<=0;
                    BD_E<=BD;       //stick BD_D and keep on piping
                end 
            else
                begin
                    Ins_E<=Ins;
                    V1_E<=V1;
                    V2_E<=V2;
                    Ext_E<=Ext;
                    PC_E<=PC;
                    PC8_E<=PC8;
                    ExcCode_E<=ExcCode;
                    BD_E<=BD;
                end
        end

endmodule
module NPC(NPCOp,Ins,PC,RS,equ,zero_greater,zero_equ,zero_less,NPC,PC8);
//interface
	input [31:0] NPCOp;

	input [31:0] Ins;
	input [31:0] PC;
	input [31:0] RS;
	
	input equ;		//whether two reg equal
	
	input zero_greater;
	input zero_equ;
	input zero_less;
	
	output reg [31:0] NPC;
	output [31:0] PC8;
	
//conbinational logic	
	wire [15:0] Imm;
	wire [25:0] index;
	
	assign Imm=Ins[15:0];
	assign index=Ins[25:0];
	
	always @(*)
		begin
			NPC=PC+4;		//eret at D will set NPC=PC+4
			case(NPCOp)
				`PC_4:NPC=PC+4;
				`PC_beq:
					begin
						if(equ)NPC=$signed(PC)+4+$signed(Imm)*4;
						else NPC=PC+8;
					end
				`PC_jal:NPC={PC[31:28],index,2'b0};
				`PC_jr:NPC=RS;
				`PC_bgez:
					begin
						if(zero_greater||zero_equ)NPC=$signed(PC)+4+$signed(Imm)*4;
						else NPC=PC+8;
					end
				`PC_bne:
					begin
						if(!equ)NPC=$signed(PC)+4+$signed(Imm)*4;
						else NPC=PC+8;
					end
				`PC_blez:
					begin
						if(zero_equ||zero_less)NPC=$signed(PC)+4+$signed(Imm)*4;
						else NPC=PC+8;
					end
				`PC_bltz:
					begin
						if(zero_less)NPC=$signed(PC)+4+$signed(Imm)*4;
						else NPC=PC+8;
					end
				`PC_bgtz:
					begin
						if(zero_greater)NPC=$signed(PC)+4+$signed(Imm)*4;
						else NPC=PC+8;
					end
			endcase
		end
	
	assign PC8=PC+8;

endmodule
module Switch(clk,reset,ADDR,Switch_WD,IRQ,Switch_RD);
//input
    input clk;
    input reset;

    input [31:0] ADDR;
    input [63:0] Switch_WD;

//output
    output reg IRQ;

    output reg [31:0] Switch_RD;

//sequential logic
    reg [63:0] data;

    always @(*)
        begin
            if(ADDR==32'h0000_7f2c)Switch_RD=data[31:0];
            else if(ADDR==32'h0000_7f30)Switch_RD=data[63:32];
            else Switch_RD=32'h18373531;
        end

    always @(posedge clk)
        begin
            if(reset)
                begin
                    data<=0;
                    IRQ<=0;
                end
            else if(data!=~Switch_WD)
                begin
                    data<=~Switch_WD;   //Switch_WD's bit is 1 when flip is pulled down
                    IRQ<=1;
                end
            else
                begin
                    data<=data;
                    IRQ<=0;
                end
        end

endmodule
module IF_EXC(PC,ExcCode);
//interface
    input [31:0] PC;

    output reg [6:2] ExcCode;

//AdEL
    always @(*)
        begin
            if(PC[1:0]||PC<32'h0000_3000||PC>32'h0000_4ffc)ExcCode=`AdEL;
            else ExcCode=0;
        end

endmodule
module Timer(clk,reset,ADDR,TimerWe,Din,Dout,IRQ);
//input
    input clk;
    input reset;

    input TimerWe;
    
	input [31:0] ADDR;
    input [31:0] Din;

//output
    output [31:0] Dout;
    output IRQ;				//request for interruption

//sequential logic
	integer i;

	reg [1:0] state;
	reg [31:0] mem [2:0];
	reg _IRQ;
	
	wire [3:2] addr;
	wire [31:0] load;

	assign addr=ADDR[3:2];		//PA to LA
	assign load = addr == 0 ? {28'h0, Din[3:0]} : Din;	//CTRL[31:4] need reservation
	
	assign IRQ = (`ctrl[3] && _IRQ);	//CTRL[IM]==1 and _IRQ==1 
	assign Dout = mem[addr];		
	
	always @(posedge clk) 
		begin
			if(reset) 
				begin
					state <= `IDLE; 
					for(i = 0; i < 3; i = i+1) mem[i] <= 0;
					_IRQ <= 0;
				end
			else if(TimerWe)	//extern write is prior to self-modify
				begin
					// $display("%d@: *%h <= %h", $time, {ADDR, 2'b00}, load);
					mem[addr] <= load;
				end
			else 
				begin
					case(state)
						`IDLE :	//keep _IRQ unchanged
							if(`ctrl[0])
								begin
									state <= `LOAD;
									_IRQ <= 1'b0;
								end
						`LOAD : //set val of count_down
							begin
								`count <= `preset;
								state <= `CNT;
							end
						`CNT  : //count
							if(`ctrl[0])
								begin
									if(`count > 1) `count <= `count-1;
									else
										begin
											`count <= 0;
											state <= `INT;	//set _IRQ 1
											_IRQ <= 1'b1;
										end
								end
							else state <= `IDLE;	//counting is interrupted by manul changing CTRL[enable]
						default : 
							begin
								state <= `IDLE;
								if(`ctrl[2:1] == 2'b00) `ctrl[0] <= 1'b0;	//mode 0
								else _IRQ <= 1'b0;
							end
					endcase
				end
		end

endmodule
module  tx_unit( d_in, load, en_tx, txd, ts, clk, rst ) ;
    input   [7:0]                   d_in ;
    input                           load ;
    input                           en_tx ;
    output                          txd ;
    output                          ts ;
    input                           clk ;
    input                           rst ;
    
    //  transmit controller
    ctrl_shift  U_CTRL_SHFT( en_tx, load, shift, ts, clk, rst ) ;

    //  transmit register
    trans_reg   U_TRANS_REG( d_in, load, shift, txd, clk, rst ) ;
    
endmodule
module  ctrl_shift( en_tx, load, shift, ts, clk, rst ) ;
    input                           en_tx ;     // enable TX
    input                           load ;      // load byte to transmit register
    output                          shift ;     // shift 
    output                          ts ;        // transmit status
    input                           clk ;       // clock
    input                           rst ;       // reset

    //  variables
    reg     [3:0]                   cnt_tx ;
    wire                            en_cnt_tx ;
    wire                            rst_cnt_tx ;
    parameter   IDLE=0, SHIFT=1 ;
    reg                             fsm ;
    
    //  shift enable
    assign  shift   = (fsm==SHIFT) && en_tx ;
    //  transmit status to be set if FSM is in IDLE state
    assign  ts      = (fsm==IDLE) ;

    // TxD controller
    always  @( posedge clk or posedge rst )
        if ( rst )
            fsm <= IDLE ;
        else
            case ( fsm )
                IDLE    :   if ( load ) 
                                fsm <= SHIFT ;
                SHIFT   :   if ( cnt_tx == 0 )
                                fsm <= IDLE ;
            endcase

    // transmit counter
    assign  rst_cnt_tx  = (fsm==IDLE) ;
    assign  en_cnt_tx   = (fsm==SHIFT) && en_tx ;
    always  @( posedge clk or posedge rst_cnt_tx )
        if ( rst_cnt_tx )
            cnt_tx <= 10 ;
        else
            if ( en_cnt_tx )
                cnt_tx <= cnt_tx - 1 ;
endmodule
module  trans_reg( d_in, load, shift, txd, clk, rst ) ;
    input   [7:0]                   d_in ;
    input                           load ;
    input                           shift ;
    output                          txd ;
    input                           clk ;
    input                           rst ;

    reg     [10:0]                  t_reg ;     // transmit register
    integer                         i ;         // logic variable

    // TxD
    assign  txd = t_reg[0] ;                    // bit 0 is always output to TxD
    
    // shift register
    always  @( posedge clk or posedge rst )
        if ( rst )
            t_reg <= 'h7FF ;                    // ensure TxD is '1' in idle state
        else
            if ( load )
                begin 
                    t_reg[10]    <= 1'b1 ;      // stop bit
                    t_reg[9:2]   <= d_in ;      // byte to be transmitted
                    t_reg[1]     <= 1'b0 ;      // start bit
                    t_reg[0]     <= 1'b1 ;      // line idle
                end
            else
                if ( shift )                    // serialisation of transmit register
                    for ( i=1; i<=10; i=i+1 )
                        t_reg[i-1] <= t_reg[i] ;
    
endmodule
module EXT(EXTOp,Ins,Ext);
//interface
	input [31:0] EXTOp;

	input [31:0] Ins;
	
	output reg [31:0] Ext;

//logic		
	wire [15:0] Imm;
	
	assign Imm=Ins[15:0]; 
	
	always @(*)
		begin
			Ext=0;
			case(EXTOp)
				`zero_ext:Ext=Imm;
				`sign_ext:Ext=$signed(Imm);
				`rear_ext:Ext={Imm,16'b0};
			endcase
		end
	
endmodule
module RF(clk,reset,RFWe,Ins,A3,RF_WD,WPC,RD1,RD2,t3);
//interface
	input clk;
	input reset;
	
	input RFWe;
	
	input [31:0] Ins;
	input [4:0] A3;
	input [31:0] RF_WD;
	
	input [31:0] WPC;
	
	output [31:0] RD1;
	output [31:0] RD2;

	output [31:0] t3;

//sequential logic
	reg [31:0] RFMem[0:31];
	
	wire [4:0] A1,A2;

	integer i;
	
	/*
	initial
		begin
			i=0;
			while(i<32)
				begin
					RFMem[i]=0;	//RFMem can be modified inside function
					i=i+1;
				end
		end
	*/

	assign A1=Ins[25:21],
		   A2=Ins[20:16];
	
	assign RD1=RFMem[A1];
	assign RD2=RFMem[A2];
	
	assign t3=RFMem[11];

	always @(posedge clk)
		begin
			RFMem[A3]<=RFMem[A3];
			if(reset)
				begin
					i=0;
					while(i<32)
						begin
							RFMem[i]<=0;	//RFMem can be modified inside function
							i=i+1;
						end
				end
			else if(RFWe&&A3!=0)
				begin
					RFMem[A3]<=RF_WD;
					//$display("%d@%h: $%d <= %h", $time, WPC, A3,RF_WD);
				end
		end
		
endmodule
module divisor ( div_rx, div_tx, en_rx, en_tx, clk, rst ) ;
    parameter   size_cnt_rx = 8 ;
    parameter   size_cnt_tx = 8 ;
    input   [15:0]                  div_rx ;
    input   [15:0]                  div_tx ;
    output                          en_rx ;     // RX
    output                          en_tx ;     // TX
    input                           clk ;       // UART clock
    input                           rst ;       // reset
    
    //  RX
    counter     U_CNT_RX( div_rx[size_cnt_rx-1:0], en_rx, clk, rst ) ;
                defparam U_CNT_RX.size_cnt = size_cnt_rx ;
    //  TX
    counter     U_CNT_TX( div_tx[size_cnt_tx-1:0], en_tx, clk, rst ) ;
                defparam U_CNT_TX.size_cnt = size_cnt_tx ;
endmodule
module  counter( max, q, clk, rst ) ;
    parameter   size_cnt = 8 ;
    input   [size_cnt-1:0]          max ;
    output                          q ;
    input                           clk ;
    input                           rst ;
    reg                             q ;

    reg     [size_cnt-1:0]          cnt ;
    
    //  counter ( down )
    always  @( posedge clk or posedge rst )
        if ( rst )
            cnt <= 0 ;
        else
            if ( cnt == 0 )
                cnt <= max ;                        // load again
            else
                cnt <= cnt - 1 ;

    //  q be set when counter downto 0
    always  @( posedge clk )
        if ( cnt == 1 )
            q <= 'b1 ;
        else
            q <= 'b0 ;
endmodule
module ALU(ALUOp,Ins,A,B,RST,byte_addr,overflow);
//interface
	input [31:0] ALUOp;
	
	input [31:0] Ins;
	
	input [31:0] A;
	input [31:0] B;
	
	output reg [31:0] RST;
	
	output [1:0] byte_addr;

	output reg overflow;
	
//logic		
	wire [10:6] shamt;

	reg [63:0] RST_ext;	//judge overflow
	
	integer i;
	integer count;
	
	assign shamt=Ins[10:6];
	
	always @(*)
		begin
			RST=0;
			overflow=0;
			case(ALUOp)
				`alu_add:
					begin
						RST=A+B;
						RST_ext=$signed(A)+$signed(B);
						if($signed(RST)==$signed(RST_ext))overflow=0;
						else overflow=1;
					end
				`alu_sub:
					begin
						RST=A-B;
						RST_ext=$signed(A)-$signed(B);
						if($signed(RST)==$signed(RST_ext))overflow=0;
						else overflow=1;
					end
				`alu_or:RST=A|B;
				`alu_slt:RST=($signed(A)<$signed(B));
				`alu_sltu:RST=(A<B);
				`alu_lb:RST=((A+B)>>2)<<2;
				`alu_sll:RST=B<<shamt;
				`alu_load_i:RST=B;
				`alu_and:RST=A&B;
				`alu_xor:RST=A^B;
				`alu_sllv:RST=B<<(A[4:0]);
				`alu_sra:RST=$signed(B)>>>shamt;
				`alu_srlv:RST=B>>(A[4:0]);
				`alu_nor:RST=~(A|B);
				`alu_srav:RST=$signed(B)>>>(A[4:0]);
				`alu_srl:RST=B>>shamt;
				`alu_movz:RST=A;
				`alu_clz:
					begin
						/*
						i=31;
						count=0;
						while(A[i]==0&&i>=0)
							begin
								count=count+1;
								i=i-1;
							end
						*/
						RST=0;
					end
			endcase
		end
		
	assign byte_addr=(A+B)%4;

endmodule
module MEM_EXC(load,DMWe,mfc0,byte_op,half_op,AO,ExcCode_M,ExcCode);
//interface
    input load;
    input DMWe;   //use as store_M
	input mfc0;	  //time feature is load but no need to trap AdEL

    input byte_op;
    input half_op;

    input [31:0] AO;

    input [6:2] ExcCode_M;

    output reg [6:2] ExcCode;

//AdEL & AdES
//cannot judge them seperately because ExcCode may be set val twice
    always @(*)
        begin
			ExcCode=ExcCode_M;
            if(load&&(!mfc0))
                begin
                    if(!((AO<=32'h0000_1fff)||(AO>=32'h0000_7f00&&AO<=32'h0000_7f0b)||(AO>=32'h0000_7f10&&AO<=32'h0000_7f43)))ExcCode=`AdEL;
                    else if(ExcCode_M==`Ov)ExcCode=`AdEL;
                    else if(half_op)
                        begin
                            if(AO%2!=0)ExcCode=`AdEL;
                            else if(!(AO<=32'h0000_1fff))ExcCode=`AdEL;
                            else ExcCode=ExcCode_M;
                        end
                    else if(byte_op)
                        begin
                            if(!(AO<=32'h0000_1fff))ExcCode=`AdEL;
                            else ExcCode=ExcCode_M;
                        end
                    else if(AO%4)ExcCode=`AdEL;
                    else ExcCode=ExcCode_M;
                end
            else if(DMWe)         
                begin
                    if(!((AO<=32'h0000_1fff)||(AO>=32'h0000_7f00&&AO<=32'h0000_7f0b)||(AO>=32'h0000_7f10&&AO<=32'h0000_7f43)))ExcCode=`AdES;
                    else if(ExcCode_M==`Ov)ExcCode=`AdES;
                    else if(AO==32'h0000_7f08||AO==32'h0000_7f18)ExcCode=`AdES;
                    else if(half_op)
                        begin
                            if(AO%2!=0)ExcCode=`AdES;
                            else if(!(AO<=32'h0000_1fff))ExcCode=`AdES;
                            else ExcCode=ExcCode_M;
                        end
                    else if(byte_op)
                        begin
                            if(!(AO<=32'h0000_1fff))ExcCode=`AdES;
                            else ExcCode=ExcCode_M;
                        end
                    else if(AO%4)ExcCode=`AdES;
                    else ExcCode=ExcCode_M;
                end
            else ExcCode=ExcCode_M;
        end

endmodule
module MD(clk,reset,MDOp,Start,IRQ,eret,A,B,Busy,HI,LO);
//input
    input clk;
    input reset;

    input [31:0] MDOp;

    input Start;
	input IRQ;
	input eret;

    input [31:0] A;
    input [31:0] B;

//output
    output reg Busy;
    
    output reg [31:0] HI;
    output reg [31:0] LO;

//operation
    integer i;

    reg [31:0] cal_HI;
    reg [31:0] cal_LO;

    initial
        begin
            Busy=0;
            i=0;
            HI=0;
            LO=0;
            cal_HI=0;
            cal_LO=0;
        end

    always @(posedge clk)
        begin
            Busy<=Busy;
            i<=i;
            HI<=HI;
            LO<=LO;
            cal_HI<=cal_HI;
            cal_LO<=cal_LO;
            if(reset)
                begin
                    Busy<=0;
                    i<=0;
                    HI<=0;
                    LO<=0;
                    cal_HI<=0;
                    cal_LO<=0;
                end
            else if(Start&&!IRQ&&!eret)
                begin
                    Busy<=1;
                    case(MDOp)
                        `mult:
                            begin
                                {cal_HI,cal_LO}<=$signed(A)+$signed(B);
                                i<=5;
                            end
                        `multu:
                            begin
                                {cal_HI,cal_LO}<=A+B;
                                i<=5;
                            end
                        `div:
                            begin
                                cal_HI<=$signed(A)+$signed(B);
                                cal_LO<=$signed(A)+$signed(B);
                                i<=10;
                            end
                        `divu:
                            begin
                                cal_HI<=A+B;
                                cal_LO<=A+B;
                                i<=10;
                            end
						`madd:
							begin
								{cal_HI,cal_LO}<=$signed({HI,LO})+$signed(A)+$signed(B);
                                i<=5;
                            end
						`msub:
							begin
								{cal_HI,cal_LO}<=$signed({HI,LO})-$signed(A)+$signed(B);
                                i<=5;
                            end
						`maddu:
							begin
								{cal_HI,cal_LO}<={HI,LO}+A+B;
                                i<=5;
							end
						`msubu:
							begin
								{cal_HI,cal_LO}<={HI,LO}-A+B;
								i<=5;
							end
                    endcase
                end
            else if(!Busy&&!IRQ&&!eret)
                begin
                    case(MDOp)
                        `mthi:HI<=A;
						`mtlo:LO<=A;
                    endcase
                end
            else if(Busy&&i!=1)i<=i-1;
            else if(i==1)
                begin
                    Busy<=0;
                    i<=0;
					HI<=cal_HI;
					LO<=cal_LO;
                end
        end

endmodule
module MUX_PC(PC_Sel_D,PC_Sel_M,ADD4,NPC,EPC,NextPC);
//interface
	input [31:0] PC_Sel_D;
	input [31:0] PC_Sel_M;
	
	input [31:0] ADD4;
	input [31:0] NPC;
	input [31:0] EPC;
	
	output reg [31:0] NextPC;	//real NPC to output

//select logic
	parameter ADD4_NPC=0,
			  NPC_NPC=1,
			  CP0_EPC=2;
	
	always @(*)
		begin
			NextPC=ADD4;				//avoid latch
			if(PC_Sel_M==2)NextPC=EPC;	//i.e. eret is at MEM
			else
				begin
					case(PC_Sel_D)		//not jump until eret at MEM
						ADD4_NPC:NextPC=ADD4;
						NPC_NPC:NextPC=NPC;
					endcase
				end
		end
	
endmodule
module MUX_ALU_B(ALU_B_Sel,RT,Ext,B);
//interface
	input [31:0] ALU_B_Sel;
	
	input [31:0] RT;
	input [31:0] Ext;
	
	output reg [31:0] B;
	
//select logic
	always @(*)
		begin
			B=RT;
			case(ALU_B_Sel)
				0:B=RT;
				1:B=Ext;
			endcase
		end

endmodule
module MUX_RF_A3(RF_A3_Sel,Ins,A3);
//interface
	input [31:0] RF_A3_Sel;

	input [31:0] Ins;
	
	output reg [4:0] A3;
	
//select logic
	always @(*)
		begin
			A3=Ins[20:16];
			case(RF_A3_Sel)
				0:A3=Ins[20:16];	//RT
				1:A3=Ins[15:11];	//RD
				2:A3=32'd31;		//0x31
			endcase
		end
	
endmodule
module MUX_RF_WD(RF_WD_Sel,AO,RD,PC8,HI,LO,CP0_RD,RF_WD);
//interface
	input [31:0] RF_WD_Sel;

	input [31:0] AO;
	input [31:0] RD;
	input [31:0] PC8;
	input [31:0] HI;
	input [31:0] LO;
	input [31:0] CP0_RD;

//select logic
	output reg [31:0] RF_WD;

	always @(*)
		begin
			RF_WD=AO;
			case(RF_WD_Sel)
				0:RF_WD=AO;		//ALU.RST
				1:RF_WD=RD;		//DM.RD
				2:RF_WD=PC8;	//PC8
				3:RF_WD=HI;		//HI
				4:RF_WD=LO;		//LO
				5:RF_WD=CP0_RD;	//CP0.RD
			endcase
		end

endmodule
module MUX_RD_RAW(RD_RAW_Sel,DM_RD,Pr_RD,RD_RAW);
//interface
	input [31:0] RD_RAW_Sel;

	input [31:0] DM_RD;
	input [31:0] Pr_RD;

	output reg [31:0] RD_RAW;

//select logic
	always @(*)
		begin
			case(RD_RAW_Sel)
				0:RD_RAW=DM_RD;
				1:RD_RAW=Pr_RD;
			endcase
		end
endmodule
module Key(clk,reset,key,IRQ,Key_RD);
//input
    input clk;
    input reset;

    input [7:0] key;
    
//output
    output reg IRQ;
    output [31:0] Key_RD;

//sequential logic
    reg [7:0] data;

    assign Key_RD={24'b0,data};

    always @(posedge clk)
        begin
            if(reset)
                begin
                    data<=0;
                    IRQ<=0;
                end 
            else if(data!=~key)
                begin
                    data<=~key;
                    IRQ<=1;
                end
            else
                begin
                    data<=data;
                    IRQ<=0;
                end
        end

endmodule
module MReg(clk,reset,Ins,V1,V2,AO,byte_addr,HI,LO,PC,PC8,ExcCode,BD,Ins_M,V1_M,V2_M,AO_M,byte_addr_M,HI_M,LO_M,PC_M,PC8_M,ExcCode_M,BD_M);
//interface
    input clk;
    input reset;

    input [31:0] Ins;
	input [31:0] V1;
    input [31:0] V2;
    input [31:0] AO;
    input [1:0] byte_addr;
	input [31:0] HI;
	input [31:0] LO;

    input [31:0] PC;
    input [31:0] PC8;

    input [6:2] ExcCode;
    input BD;
	
    output reg [31:0] Ins_M;
	output reg [31:0] V1_M;
    output reg [31:0] V2_M;
    output reg [31:0] AO_M;
	output reg [1:0] byte_addr_M;
	output reg [31:0] HI_M;
	output reg [31:0] LO_M;

    output reg [31:0] PC_M;
    output reg [31:0] PC8_M;

    output reg [6:2] ExcCode_M;
    output reg BD_M;

//sequential logic
    /*
	initial
		begin
			Ins_M=0;
			V1_M=0;
			V2_M=0;
			AO_M=0;
			HI_M=0;
			LO_M=0;
			PC_M=0;
			PC8_M=0;
			byte_addr_M=0;
            ExcCode_M=0;
            BD_M=0;
		end
	*/
    
    always @(posedge clk)
        begin
            if(reset)
                begin
                    Ins_M<=0;
					V1_M<=0;
                    V2_M<=0;
                    AO_M<=0;
                    byte_addr_M<=0;
					HI_M<=0;
					LO_M<=0;
                    PC_M<=PC;
                    PC8_M<=0;
                    ExcCode_M<=0;
                    BD_M<=BD;
                end
            else
                begin
                    Ins_M<=Ins;
					V1_M<=V1;
                    V2_M<=V2;
                    AO_M<=AO;
                    byte_addr_M<=byte_addr;
					HI_M<=HI;
					LO_M<=LO;
                    PC_M<=PC;
                    PC8_M<=PC8;
                    ExcCode_M<=ExcCode;
                    BD_M<=BD;
                end
        end
        
endmodule
module DM(clk,reset,DMWe,ADDR,DM_WD,WPC,byte_op,half_op,byte_addr,RD);
//interface
	input clk;
	input reset;
	
	input DMWe;
	
	input [31:0] ADDR;
	input [31:0] DM_WD;
	
	input [31:0] WPC;
	
	input byte_op;	//deal with byte
	input half_op;	//deal with half word

	input [1:0] byte_addr;  //byte position in a word
	
	output [31:0] RD;	//output RD_RAW

//sequential logic
	wire [12:2] addr;
/*
	reg [31:0] DMMem[0:2047];

	integer i;

	initial
		begin
			begin
				i=0;
				while(i<2048)
					begin
						DMMem[i]=0;	//DMMem can be modified inside function
						i=i+1;
					end
			end
		end
	*/
	
	assign addr=ADDR[12:2];			//change byte addr into word addr

/*
//load
	assign RD=DMMem[addr];

//store
	always @(posedge clk)
		begin
			if(reset)
				begin
					i=0;
					while(i<2048)
						begin
						DMMem[i]<=0;
							i=i+1;
						end
				end
			else if(DMWe&&ADDR<=32'h0000_1fff)
				begin
					if(byte_op) 
						begin
							DMMem[addr][(byte_addr*8)+:8]<=DM_WD[7:0];
							case(byte_addr)
								0:#0 $display("%d@%h: *%h <= %h",$time,WPC, addr*4,{DMMem[addr][31:8],DM_WD[7:0]});
								1:#0 $display("%d@%h: *%h <= %h",$time,WPC, addr*4,{DMMem[addr][31:16],DM_WD[7:0],DMMem[addr][7:0]});
								2:#0 $display("%d@%h: *%h <= %h",$time,WPC, addr*4,{DMMem[addr][31:24],DM_WD[7:0],DMMem[addr][15:0]});
								3:#0 $display("%d@%h: *%h <= %h",$time,WPC, addr*4,{DM_WD[7:0],DMMem[addr][23:0]});
							endcase
						end
					else if(half_op)
						begin
							DMMem[addr][(byte_addr*8)+:16]<=DM_WD[15:0];
							case(byte_addr)
								0:#0 $display("%d@%h: *%h <= %h",$time,WPC, addr*4,{DMMem[addr][31:16],DM_WD[15:0]});
								2:#0 $display("%d@%h: *%h <= %h",$time,WPC, addr*4,{DM_WD[15:0],DMMem[addr][15:0]});
							endcase
						end
					else 
						begin
							DMMem[addr]<=DM_WD;
							#0 $display("%d@%h: *%h <= %h", $time, WPC, addr*4,DM_WD);
						end
				end
			else
				begin
					DMMem[addr]<=DMMem[addr];
				end
		end
*/
	
	reg [3:0] BE;
	reg [31:0] dina;
	
	always @(*)
		begin
			if(DMWe&&ADDR<=32'h0000_1fff)
				begin
					if(byte_op) 
						begin
							dina={DM_WD[7:0],DM_WD[7:0],DM_WD[7:0],DM_WD[7:0]};
							if(byte_addr==0)BE=4'b0001;
							else if(byte_addr==1)BE=4'b0010;
							else if(byte_addr==2)BE=4'b0100;
							else if(byte_addr==3)BE=4'b1000;
							else BE=0;
						end
					else if(half_op)
						begin
							dina={DM_WD[15:0],DM_WD[15:0]};
							if(byte_addr==0)BE=4'b0011;
							else if(byte_addr==2)BE=4'b1100;
							else BE=4'b0000;
						end
					else 
						begin
							dina=DM_WD;
							BE=4'b1111;
						end
				end
			else 
				begin
					BE=4'b0000;
					dina=0;
				end
		end

	IP_DM DMMem (
		  .clka(clk),		// input clka
		  .rsta(reset), 	// input rsta
		  .wea(BE), 		// input [3 : 0] wea
		  .addra(addr), 	// input [10 : 0] addra
		  .dina(dina), 		// input [31 : 0] dina
		  .douta(RD) 	 	// output [31 : 0] douta
	);

endmodule