// /////////////////////////////////////////////////////////// // C O M M O N . V ///////////////////////////////////////////////////////////// // Gary Look and Gene Ma // CSE 471 // 2.18.2000 module clock( clk); parameter period = 11; parameter cycles = 45; output clk; reg clk; initial begin clk = 0; #(period) repeat (cycles) begin clk = 1; #(period/2) clk = 0; #(period-period/2) ; end #(period) $stop; end endmodule module enreg( clk, en, d, q); parameter width=1; input clk, en; input [width-1:0] d; output [width-1:0] q; reg [width-1:0] q; initial q = 0; always @(posedge clk) if ( en) q = d; endmodule module enreg_with_clear( clk, en, clr, d, q); parameter width=1; input clk, en, clr; input [width-1:0] d; output [width-1:0] q; reg [width-1:0] q; initial q = 0; always @(posedge clk) if ( en) if ( clr) q = 0; else q = d; endmodule module mux( cntl, in0, in1, out); parameter width=1; input cntl; input [width-1:0] in0, in1; output [width-1:0] out; reg [width-1:0] out; always @(cntl or in0 or in1) if ( cntl == 1) out = in1; else if ( cntl == 0) out = in0; else out = 'bx; endmodule // Addition: new to the code, a 4:1 mux module mux4( cntl, in0, in1, in2, in3, out); parameter width=1; input [1:0] cntl; input [width-1:0] in0, in1, in2, in3; output [width-1:0] out; reg [width-1:0] out; always @(cntl or in0 or in1 or in2 or in3) begin case(cntl) 2'b00: out = in0; 2'b01: out = in1; 2'b10: out = in2; 2'b11: out = in3; default: out = 2'bxx; endcase end endmodule module cpa( a, b, r); input [31:0] a, b; output [31:0] r; reg [31:0] r; always @(a or b) r <= #10 a + b; endmodule module imem( raddress, rdata); input [31:0] raddress; output [31:0] rdata; reg [31:0] rdata; reg [31:0] a [0:63]; always @(raddress) rdata <= #10 a[raddress>>2]; endmodule module regfile( clk, we2, index0, index1, index2, value0, value1, value2); input clk, we2; input [4:0] index0, index1, index2; output [31:0] value0, value1; reg [31:0] value0, value1; input [31:0] value2; reg [31:0] rf [0:31]; initial rf[0] = 0; always @(posedge clk) if ( we2 && index2 != 0) rf[index2] <= #5 value2; always @(index0 or rf[index0]) value0 <= #5 rf[index0]; always @(index1 or rf[index1]) value1 <= #5 rf[index1]; endmodule module alu( a, b, r, z, v, aluop); input [31:0] a, b; output [31:0] r; reg [31:0] r; output z, v; reg z, v, CarryOut; input [2:0] aluop; always @(aluop or a or b) begin case (aluop) 3'b000: {CarryOut,r} <= #10 {1'b0,a&b}; 3'b001: {CarryOut,r} <= #10 {1'b0,a|b}; 3'b010: {CarryOut,r} <= #10 a+b; 3'b110: {CarryOut,r} <= #10 a-b; 3'b111: begin :slt integer ia, ib; ia = a; ib = b; {CarryOut,r} <= #10 {32'b0,ia>2]) rdata <= #10 a[raddress>>2]; always @(wdata) wdata_delayed <= #10 wdata; always @(waddress) waddress_delayed <= #10 waddress; always @(posedge clk) if ( MemWrite ) a[waddress_delayed>>2] = wdata_delayed; endmodule ///////////////////////////////////////////////////////////////// // P I P E L I N E . 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; wire [5:0] Op = ir[31:26], Funct = ir[5:0]; wire [4:0] Rs = ir[25:21], Rt = ir[20:16], Rd = ir[15:11]; wire [15:0] Immed = ir[15:0]; wire [25:0] Ja = ir[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; // Add: this wire holds the result of a branch target computation wire [31:0] branchpc; reg flush; reg [4:0] tmpindex; reg Stall; // For Debug and testing purposes // reg [1:0] ALUSrcA, ALUSrcB; wire NotMemWrite; assign NotMemWrite = ~MemWrite; initial begin // ALUSrcA = 2'b00; // always choose rdata1 // ALUSrcB = 2'b00; // always choose rdata2 end // Change: originally, the branch address computation was done in the // exe stage; we have moved it back to the id stage cpa cpa_0( pc4, {offset[29:0],2'b00}, branchpc); // Change: originally, this always block triggered on the Branch or Zero // signals from mips.mem, not mips.ex always @(mips.ex.Branch or mips.ex.Zero) flush = mips.ex.Branch & mips.ex.Zero; always @(mips.ex.RegWrite or mips.ex.MemRead 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 tmpindex = (mips.ex.RegDst == 0) ? mips.ex.Rt : mips.ex.Rd; // Stall = mips.ex.RegWrite & tmpindex != 0 & // ( tmpindex == Rs | tmpindex == Rt & Op != 6'b100011 ) // | mips.mem.RegWrite & mips.mem.windex != 0 & // ( mips.mem.windex == Rs | mips.mem.windex == Rt // & Op != 6'b100011 ); //$display("NotMemWrite = %d, MemWrite = %d", NotMemWrite, MemWrite); // Change: replaced stall logic, with forwarding added // Stall if the instruction ahead is a load and we are using // the result of that load Stall = (mips.ex.MemRead & ((mips.ex.Rt == Rs) | (mips.ex.Rt == Rt)) ); // If decoding a lw or a sw then stall only if we use result of load in // computing mem address of the store if ( (Op == 6'b101011) | (Op == 6'b100011) ) begin Stall = Stall & (mips.ex.Rt == Rs); end 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; wire [4:0] Rt, Rd, windex; wire Zero, Overflow; wire [2:0] ALUControl; wire [1:0] ALUOp; wire RegDst, ALUSrc; wire Branch, MemRead, MemWrite; wire MemToReg, RegWrite; // Add: wire to hold name of Rs wire [4:0] Rs; // Add: this wire holds result of branch computation wire [31:0] nextpc; // Add: dummy wires used to pass into the muxes, but never selected wire [31:0] dummyA, dummyB; // Add: wire holds one input into the ALU wire [31:0] rdataA; // Add: wire to hold temporary value of output from the 4:1 mux used as // input to the 2:1 mux wire [31:0] tempdataB; // Add: control lines for the 2 4:1 muxes wire [1:0] RsMuxCtl; wire [1:0] RtMuxCtl; mux #(5) wmux_0( RegDst, Rt, Rd, windex); // cpa cpa_0( pc4, {offset[29:0],2'b00}, branchpc); // Add: a 4:1 mux for selecting rdataA, one input into the ALU mux4 #(32) fmux_0(RsMuxCtl, rdata1, mips.wb_result, mips.mem.result, dummyA, rdataA); // Add: a 4:1 mux for selecting which to go into the wmux_1 mux4 #(32) fmux_1(RtMuxCtl, rdata2, mips.wb_result, mips.mem.result, dummyB, tempdataB); // modified to chose between data or offset mux #(32) wmux_1( ALUSrc, tempdataB, offset, rdataB); // mux #(32) wmux_1( ALUSrc, rdata2, offset, rdataB); // alu alu_0( rdata1, rdataB, result, Zero, Overflow, ALUControl); alu alu_1( rdataA, rdataB, result, Zero, Overflow, ALUControl); // Change: this line moved up from the mem module mux #(32) mux_0( Branch & Zero, mips.ifetch.pc4, branchpc, nextpc); 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; // Add: wire for mux control line wire MemMuxCtl; // Add: wire to hold ouput of mux wire [31:0] temp_wdata; // Add: new mux to select which data goes into memory in a store mux #(32) d_mem_mux( MemMuxCtl, wdata, mips.wb_result, temp_wdata ); dmem dmem_0( mips.clk, MemRead, MemWrite, result, result, rdata, temp_wdata); // mux #(32) mux_0( Branch & Zero, mips.ifetch.pc4, branchpc, nextpc); mux #(32) wmux_0( MemToReg, result, rdata, final); endmodule module mips; wire clk; clock clock_0( clk); defparam clock_0.period=11; defparam clock_0.cycles=100; ifetch ifetch(); id id(); ex ex(); mem mem(); // Add: wires to hold values in mem/wb pipeline register wire [4:0] wb_windex; wire wb_RegWrite; wire [31:0] wb_result; wire wb_MemRead; // Add: wire to hold the 1-bit input to the Memory mux // wire MemMuxCtl; // Add: our forwarding unit forward forward(ex.Rs, ex.Rt, mem.windex, wb_windex, mem.RegWrite, wb_RegWrite, wb_MemRead, mem.MemWrite, ex.RsMuxCtl, ex.RtMuxCtl, mem.MemMuxCtl); // // 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. // // Change: Originally, we looked at the Branch and Zero signals from // the mem stage. Now, we look from the ex stage enreg #(32) wreg_0( clk, !id.Stall | ex.Branch & ex.Zero, ex.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 #(5) id_ex_wreg_4( clk, 1'b1, id.Rt, ex.Rt); enreg #(5) id_ex_wreg_5( clk, 1'b1, id.Rd, ex.Rd); // // 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_6( 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}); // Add: new id/ex register to hold value of a branch PC enreg #(32) id_ex_wreg_7( clk, 1'b1, id.branchpc, ex.branchpc); // For Debug purposes //enreg #(2) id_ex_wreg_20( clk, 1'b1, id.ALUSrcA, ex.ALUSrcA ); //enreg #(2) id_ex_wreg_21( clk, 1'b1, id.ALUSrcB, ex.ALUSrcB ); // Add: new id/ex register to hold name of Rs enreg #(5) id_ex_wreg_8( clk, 1'b1, id.Rs, ex.Rs); // 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.rdata2, mem.wdata); // enreg #(1) ex_mem_wreg_3( clk, 1'b1, ex.Zero, mem.Zero); enreg #(5) ex_mem_wreg_4( clk, 1'b1, 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}); // Change: Since the branch is resolved one cylce sooner, we don't // need to carry the Branch signal into the mem stage enreg_with_clear #(4) ex_mem_wreg_5( clk, 1'b1, id.flush, {ex.MemRead,ex.MemWrite, ex.MemToReg,ex.RegWrite}, {mem.MemRead,mem.MemWrite, mem.MemToReg,mem.RegWrite}); // Add: the Mem/Wb Registers enreg #(5) mem_wb_wreg_0( clk, 1'b1, mem.windex, wb_windex ); enreg #(1) mem_wb_wreg_1( clk, 1'b1, mem.RegWrite, wb_RegWrite); enreg #(32) mem_wb_wreg_2( clk, 1'b1, mem.final, wb_result ); enreg #(1) mem_wb_wreg_3( clk, 1'b1, mem.MemRead, wb_MemRead); initial begin // $monitor( "%0d pc = %h Stall = %b Wb to register #%0d w/value = %0d", // $time, ifetch.pc, id.Stall, mem.windex,mem.final); // $monitor( "%0d pc = %h Stall = %b Wb to register #%0d w/value = %0d\n\tR2=%0d R3=%0d R8=%0d R7=%0d R5=%0d R6=%0d R4=%0d R9=%0d\n\tmips.ex.MemRead = %0d, mips.ex.Rt = %0d, mips.id.Rs = %0d, mips.id.Rt = %0d\n\t mips.id.MemWrite = %0d, mips.id.NotMemWrite = %0d\n\tMemMuxCtl = %0d, mem.windex = %0d, wb_windex = %0d,\n\twb_MemRead = %0d, mem.MemWrite = %0d, \n\tmem.temp_wdata = %0d, mem.wdata = %0d, wb_result = %0d\n", // $time, ifetch.pc, id.Stall, mem.windex,mem.final, id.rf.rf[2], // id.rf.rf[3], id.rf.rf[8], id.rf.rf[7], id.rf.rf[5], // id.rf.rf[6], id.rf.rf[4], id.rf.rf[9], // mips.ex.MemRead, mips.ex.Rt, mips.id.Rs, mips.id.Rt, // mips.id.MemWrite, mips.id.NotMemWrite, // mips.mem.MemMuxCtl, mips.mem.windex, mips.wb_windex, // mips.wb_MemRead, mips.mem.MemWrite, mips.mem.temp_wdata, // mips.mem.wdata, mips.wb_result ); $readmemb("prog.bin", ifetch.imem_0.a); $readmemb("data.bin", mem.dmem_0.a); end endmodule // Add: our forwarding unit module forward(Rs, Rt, MemReg, WbReg, MemRegWr, WbRegWr, WbMemRead, MemMemWr, RsMuxCtl, RtMuxCtl, MemMuxCtl); // the names of the Rs and Rt registers currently being // used in the Ex stage input [4:0] Rs; input [4:0] Rt; // the names of the logical registers being written to by // the instructions in the Mem and Wb stages input [4:0] MemReg; input [4:0] WbReg; // the control signals indicating whether or not the register file // is being written to by the instruction in the Mem or Wb stage input MemRegWr; input WbRegWr; input MemMemWr; // the control signal wether or not the instruction in the wb // stage read from memory input WbMemRead; // the control lines for the muxes that determine which value // gets forwarded to the ALU in the Ex stage output [1:0] RsMuxCtl; output [1:0] RtMuxCtl; reg [1:0] RsMuxCtl; reg [1:0] RtMuxCtl; // a control line to determine which value gets forwarded to // the Mem stage to be written to data memory output MemMuxCtl; reg MemMuxCtl; // Should I clock this?? always @ (Rs or Rt or MemReg or WbReg or MemRegWr or WbRegWr) begin // determine value of RsMuxCtl begin if( MemRegWr & (MemReg != 5'b00000) & (MemReg == Rs) ) RsMuxCtl = 2'b10; // forward from the Mem stage else if ( WbRegWr & (WbReg != 5'b00000) & (WbReg == Rs) ) RsMuxCtl = 2'b01; // forward from the Wb stage else RsMuxCtl = 2'b00; end // determine value of RtMuxCtl begin if( MemRegWr & (MemReg != 5'b00000) & (MemReg == Rt) ) RtMuxCtl = 2'b10; // forward from the Mem stage else if ( WbRegWr & (WbReg != 5'b00000) & (WbReg == Rt) ) RtMuxCtl = 2'b01; // forward from the Wb stage else RtMuxCtl = 2'b00; end //MemMuxCtl = 1'b0; // Haven't implemented the lw-sw forwarding yet MemMuxCtl = ( (MemReg == WbReg) & WbMemRead & MemMemWr ); end endmodule