`include "common.v" module ifetch; wire [31:0] pc, pc4; wire [31:0] ir; cpa cpa_0( pc, 32'b100, pc4); imem imem_0( pc, ir); endmodule module id; wire [31:0] pc4; wire [31:0] ir; reg [31:0] irx; wire [5:0] Op = irx[31:26], Funct = irx[5:0]; wire [4:0] Rs = irx[25:21], Rt = irx[20:16], Rd = irx[15:11]; wire [15:0] Immed = irx[15:0]; wire [25:0] Ja = irx[25:0]; wire [4:0] windex; wire [31:0] rdata1, rdata2, wdata; wire [31:0] offset = {{16{Immed[15]}},Immed}; reg RegDst, MemRead, MemWrite, Branch, ALUSrc, MemToReg, RegWrite; reg [1:0] ALUOp; wire IFflush; reg flush; reg Stall; wire notStartup; // This small trick is to handle the startup. enreg #(1) start_reg(mips.clk, 1'b1, 1'b1, notStartup); always @(ir or notStartup) begin if (notStartup) irx = ir; else irx = {6'b111111, 26'b0}; // some invalid opcode end always @(mips.mem.Branch or mips.mem.Zero) flush = mips.mem.Branch & mips.mem.Zero; always @(mips.ex.RegWrite or mips.ex.RegDst or mips.ex.Rt or mips.ex.Rd or Op or Rs or Rt or mips.mem.RegWrite or mips.mem.windex) begin Stall = mips.ex.MemRead & ((mips.ex.Rt == mips.id.Rs) | (mips.ex.Rt == mips.id.Rt)); end always @(Op) begin RegDst = (Op == 6'b000000); MemRead = (Op == 6'b100011); MemWrite = (Op == 6'b101011); Branch = (Op == 6'b000100); ALUSrc = MemRead | MemWrite; MemToReg = MemRead; RegWrite = RegDst | MemRead; ALUOp = {RegDst,Branch}; end regfile rf( mips.clk, mips.mem.RegWrite, Rs, Rt, mips.mem.windex, rdata1, rdata2, mips.mem.final); endmodule module ex; wire [31:0] pc4, offset, branchpc, rdata1, rdata2, rdataB, result, rdataFA, rdataFB; wire [4:0] Rt, Rd, Rs, windex; wire Zero, Overflow; wire [2:0] ALUControl; wire [1:0] ALUOp; wire RegDst, ALUSrc; wire Branch, MemRead, MemWrite; wire MemToReg, RegWrite; reg [1:0] ForwardA, ForwardB; mux #(5) wmux_0( RegDst, Rt, Rd, windex); cpa cpa_0( pc4, {offset[29:0],2'b00}, branchpc); mux #(32) wmux_1( ALUSrc, rdataFB, offset, rdataB); mux4 #(32) wmux_2( ForwardA, rdata1, mips.wb.final, mips.mem.result, 32'bx, rdataFA); mux4 #(32) wmux_3( ForwardB, rdata2, mips.wb.final, mips.mem.result, 32'bx, rdataFB); always @(mem.RegWrite or mem.windex or ex.Rs or ex.Rt or wb.RegWrite or wb.windex) begin ForwardA = 0; ForwardB = 0; if (mips.mem.RegWrite & (mips.mem.windex != 0) & (mips.mem.windex == ex.Rs)) ForwardA = 2'b10; if (mips.mem.RegWrite & (mips.mem.windex != 0) & (mips.mem.windex == mips.ex.Rt)) ForwardB = 2'b10; if (mips.wb.RegWrite & (mips.wb.windex != 0) & (mips.wb.windex == mips.ex.Rs) & (mips.mem.windex != mips.ex.Rs)) ForwardA = 2'b01; if (mips.wb.RegWrite & (mips.wb.windex != 0) & (mips.wb.windex == mips.ex.Rt) & (mips.mem.windex != mips.ex.Rt)) ForwardB = 2'b01; end alu alu_0( rdataFA, rdataB, result, Zero, Overflow, ALUControl); alucontrol alucontrol_0( offset[5:0], ALUOp, ALUControl); endmodule module mem; wire [31:0] result, wdata, rdata, final, branchpc, nextpc; wire [4:0] windex; wire Zero; wire Branch, MemRead, MemWrite; wire MemToReg, RegWrite; dmem dmem_0( mips.clk, MemRead, MemWrite, result, result, rdata, wdata); mux #(32) mux_0( Branch & Zero, mips.ifetch.pc4, branchpc, nextpc); mux #(32) wmux_0( MemToReg, result, rdata, final); endmodule module wb; wire [31:0] final; wire [4:0] windex; wire RegWrite; endmodule module mips; wire clk; clock clock_0( clk); defparam clock_0.period=11; defparam clock_0.cycles=1300; ifetch ifetch(); id id(); ex ex(); mem mem(); wb wb(); // // Don't increment the pc if there is a data hazard being resolved by a stall // unless a branch needs to be taken. In this case, a stall is not // necessary because the conflict is between the ID and EX stages and // the instructions in those stages will be flushed. // enreg #(32) wreg_0( clk, !id.Stall | mem.Branch & mem.Zero, mem.nextpc, ifetch.pc); enreg #(32) if_id_wreg_0( clk, !id.Stall, ifetch.pc4, id.pc4); enreg #(32) if_id_wreg_1( clk, !id.Stall, ifetch.ir, id.ir); // // id.IFflush means interpret id.ir as a nop // enreg #(1) if_id_wreg_2( clk, 1'b1, id.flush, id.IFflush); enreg #(32) id_ex_wreg_0( clk, 1'b1, id.pc4, ex.pc4); enreg #(32) id_ex_wreg_1( clk, 1'b1, id.rdata1, ex.rdata1); enreg #(32) id_ex_wreg_2( clk, 1'b1, id.rdata2, ex.rdata2); enreg #(32) id_ex_wreg_3( clk, 1'b1, id.offset, ex.offset); enreg_with_clear #(5) id_ex_wreg_4( clk, 1'b1, id.IFflush | id.flush | id.Stall, id.Rt, ex.Rt); enreg_with_clear #(5) id_ex_wreg_5( clk, 1'b1, id.IFflush | id.flush | id.Stall, id.Rd, ex.Rd); enreg_with_clear #(5) id_ex_wreg_6( clk, 1'b1, id.IFflush | id.flush | id.Stall, id.Rs, ex.Rs); // // On the next edge, insert a nop into the EX stage if there is a data hazard, // a mispredicted branch is in the MEM stage, or // a mispredicted branch was in the MEM stage in the last cycle // enreg_with_clear #(9) id_ex_wreg_7( clk, 1'b1, id.IFflush | id.flush | id.Stall, {id.ALUOp,id.RegDst,id.ALUSrc, id.Branch,id.MemRead,id.MemWrite, id.MemToReg,id.RegWrite}, {ex.ALUOp,ex.RegDst,ex.ALUSrc, ex.Branch,ex.MemRead,ex.MemWrite, ex.MemToReg,ex.RegWrite}); enreg #(32) ex_mem_wreg_0( clk, 1'b1, ex.branchpc, mem.branchpc); enreg #(32) ex_mem_wreg_1( clk, 1'b1, ex.result, mem.result); enreg #(32) ex_mem_wreg_2( clk, 1'b1, ex.rdataFB, mem.wdata); enreg #(1) ex_mem_wreg_3( clk, 1'b1, ex.Zero, mem.Zero); enreg_with_clear #(5) ex_mem_wreg_4( clk, 1'b1, id.flush, ex.windex, mem.windex); // // On the next edge, insert a nop into the MEM stage if there is // a mispredicted branch is in the MEM stage. // enreg_with_clear #(5) ex_mem_wreg_5( clk, 1'b1, id.flush, {ex.Branch,ex.MemRead,ex.MemWrite, ex.MemToReg,ex.RegWrite}, {mem.Branch,mem.MemRead,mem.MemWrite, mem.MemToReg,mem.RegWrite}); enreg #(32) mem_wb_wreg_0( clk, 1'b1, mem.final, wb.final); enreg_with_clear #(5) ex_wb_wreg_1( clk, 1'b1, id.flush, mem.windex, wb.windex); enreg_with_clear #(1) mem_wb_wreg_2( clk, 1'b1, 1'b0, {mem.RegWrite}, {wb.RegWrite}); initial begin $monitor( "%0d pc = %h", $time, ifetch.pc); $readmemb("prog.bin", ifetch.imem_0.a); $readmemb("data.bin", mem.dmem_0.a); #20000 $writememb("data.out", mem.dmem_0.a); $finish; end endmodule