/** * Assignment 3 part 2 * CL Leavitt, x2p4 * modified pipe cpu.java */package Arch.Y86.Machine.Pipe.Student;import Arch.Y86.Machine.AbstractY86CPU;import Arch.Y86.Machine.MainMemory;import Machine.AbstractMainMemory;import Machine.RegisterSet;import Machine.AbstractCPU.MachineHaltException;public class CPU extends AbstractY86CPU { public CPU(MainMemory aMem) { super("Pipe", aMem, true); } /** * Execute one clock cycle with all stages executing in parallel. * * @throws InvalidInstructionException * if instruction is invalid (including invalid register number) * @throws AbstractMainMemory.InvalidAddressException * if instruction attempts an invalid memory access (either * instruction or data) * @throws MachineHaltException * if instruction halts the CPU */ @Override protected void cycle() throws InvalidInstructionException, AbstractMainMemory.InvalidAddressException, MachineHaltException { cyclePipe(); } /** * Pipeline Hazard Control Logic STUDENT IMPLEMENTS THIS METHOD */ @Override protected void pipelineHazardControl() { /**CONTROL HAZARD: Conditional Jump * * IF JUMP SHOULD NOT HAVE BEEN TAKEN: Shoot down the 2 bad instructions * */ if ((m.iCd.read () == I_JXX && m.iFn.read() != J_NC && (m.bch.read()) == 0)) { e.clockTransition = BUBBLE; m.clockTransition = BUBBLE; } /** CONTROL HAZARD: Return */ if (d.iCd.read() == I_RET || e.iCd.read() == I_RET || m.iCd.read() == I_RET) { f.clockTransition = STALL; d.clockTransition = BUBBLE; } /** DATA HAZARDS - LOAD USE * we don't stall on register-register (write-reads) hazard with this * pipe implementation, data forwarding prevents needing it. Only Load use * required. * * if I'm in e, and he is in d, stall him iff he needs me **/ if (d.rA.read() == e.dstM.read() || d.rB.read() == e.dstM.read()) { //3rd - add one more stall f.clockTransition = STALL; // 2nd - stall instruction at decode until memory results are in d.clockTransition = STALL; // 1st Stall - fill stall vacuum at execute with NOP e.clockTransition = BUBBLE; } } /** * The part of the FETCH that selects the PC of the instruction to fetch * Writes the selected PC into the f.pc register. STUDENT CHANGES THIS * METHOD TO CORRECT MIS-PREDICTED PCS */ @Override protected void fetch_SelectPC() { // pc MUX int pc; pc = f.prPC.read(); /** RETURN CONTROL: Forward the correct PC to Fetch*/ if(w.iCd.read() == I_RET) { pc = w.valM.read(); } /** INSERT LOGIC TO CORRECT MIS-PREDICTED PC FROM JUMPS * * if I'm a jump, but not an unconditional one, check bch * and set the proper PC * */ if (m.iCd.read() == I_JXX) { if (m.iFn.read() != J_NC) { if (m.bch.read() == 0) { pc = m.valP.read(); } } } f.pc.write(pc); } /** * The part of the FETCH that predicts PC value of instruction to enter * fetch on next clock cycle. Writes the predicted PC into the f.prPC * register. STUDENT CHANGES THIS METHOD TO PREDICT THE PC */ private void fetch_PredictPC() { // prPC MUX switch (d.iCd.readInput()) { case I_JXX: /** * We always want to jump. * So I could also just let this fall into case I_CALL * if (d.iFn.readInput () == J_NC) * */ f.prPC.write(d.valC.readInput()); break; case I_CALL: f.prPC.write(d.valC.readInput()); break; default: f.prPC.write(d.valP.readInput()); } /*//********** RETURN PREDICT ****** if (m.iCd.read() == I_RET) { *//** find out what's going into valM *//* f.prPC.write(w.valM.readInput()); }*/ } /** * The FETCH stage of CPU Reads the PC from the input port of the f.pc * register. * * @throws InvalidInstructionException * if instruction opcode or format is invalid * @throws AbstractMainMemory.InvalidAddressException * if current pc is invalid */ @Override protected void fetch() throws InvalidInstructionException, AbstractMainMemory.InvalidAddressException { // determine PC fetch_SelectPC(); int pc = f.pc.readInput(); // get opcode d.iCd.write(mem.read(pc, 1)[0].value() >>> 4); d.iFn.write(mem.read(pc, 1)[0].value() & 0xf); // rA MUX switch (d.iCd.readInput()) { case I_RRMOVL: case I_RMMOVL: case I_MRMOVL: case I_OPL: case I_PUSHL: case I_POPL: d.rA.write(mem.read(pc + 1, 1)[0].value() >>> 4); break; default: d.rA.write(R_NONE); } // rB MUX switch (d.iCd.readInput()) { case I_RRMOVL: case I_IRMOVL: case I_RMMOVL: case I_MRMOVL: case I_OPL: d.rB.write(mem.read(pc + 1, 1)[0].value() & 0xf); break; default: d.rB.write(R_NONE); } // valC MUX switch (d.iCd.readInput()) { case I_IRMOVL: case I_RMMOVL: case I_MRMOVL: d.valC.write(mem.readIntegerUnaligned(pc + 2)); break; case I_JXX: case I_CALL: d.valC.write(mem.readIntegerUnaligned(pc + 1)); break; default: d.valC.write(0); } // valP MUX switch (d.iCd.readInput()) { case I_NOP: case I_HALT: case I_RET: d.valP.write(pc + 1); break; case I_RRMOVL: case I_OPL: case I_PUSHL: case I_POPL: d.valP.write(pc + 2); break; case I_JXX: case I_CALL: d.valP.write(pc + 5); break; case I_IRMOVL: case I_RMMOVL: case I_MRMOVL: d.valP.write(pc + 6); break; default: } // predict next PC fetch_PredictPC(); } /** * Determine current value of specified register by employing data * forwarding, where necessary. STUDENT CHANGES THIS METHOD TO IMPLEMENT * DATA FORWARDING * * @param regNum * number of register being read * @return value of register * @throws Machine.RegisterSet.InvalidRegisterNumberException * if register number is invalid */ private int decode_ReadRegisterWithForwarding(int regNum) throws Machine.RegisterSet.InvalidRegisterNumberException { if (regNum == R_NONE) { return 0; } /********************** EXECUTE STAGE CHECK **********************************/ /** is e writing **/ switch (e.iCd.read()) { case I_OPL: case I_IRMOVL: case I_RRMOVL: // valE = 0 + valA case I_PUSHL: // valE = valB + -4 written back to %esp case I_RET: // write to %esp valE = valB + 4 and dstE is %esp case I_CALL: // same as the above ones. if (e.dstE.read() == regNum /* dstE is this register */) { // read vale.readinput and give it to the register. //reg.write(regNum, m.valE.readInput()); return m.valE.readInput(); } break; /** * * case I_POPL: not needed here, it will get one bubble cause the * soonest we know is memory and no way to forward from here. case * I_MRMOVL: not needed here, since at execute there is no way to * forward from here. * **/ } /********************** MEMORY STAGE CHECK **********************************/ switch (m.iCd.read()) { case I_OPL: case I_IRMOVL: case I_RRMOVL: // valE = 0 + valA case I_PUSHL: // valE = valB + -4 written back to %esp case I_RET: // write to %esp valE = valB + 4 and dstE is %esp case I_CALL: // same as the above ones. if (m.dstE.read() == regNum /* dstE is this register */) { // read vale.readinput and give it to the register. return m.valE.read(); } break; /** SPECIAL CASE POPL WRITE-READ and DATA LOAD HAZARD **/ case I_POPL: // write to %esp valE = valB + 4 and dstE is %esp // num of reg that gets arithmetic op reg if (m.dstE.read() == regNum /* dste is this register */) { // read vale.readinput and give it to the register. reg.write(regNum, m.valE.read()); } else if (m.dstM.read() == regNum) { return w.valM.readInput(); // what will this be next turn } break; case I_MRMOVL: /** LOAD USE HAZARD **/ if (m.dstM.read() == regNum) { return w.valM.readInput(); } break; } /********************** WRITE BACK CHECK **********************************/ switch (w.iCd.read()) { case I_OPL: case I_IRMOVL: case I_RRMOVL: // valE = 0 + valA case I_PUSHL: // valE = valB + -4 written back to %esp case I_RET: // write to %esp valE = valB + 4 and dstE is %esp case I_CALL: // same as the above ones. if (w.dstE.read() == regNum /* dstE is this register */) { // read vale.readinput and give it to the register. return w.valE.read(); } break; case I_POPL: // write to %esp valE = valB + 4 and dstE is %esp /** SPECIAL CASE POPL WRITE-READ and DATA LOAD HAZARD **/ if (w.dstE.read() == regNum /* dstE is this register */) { // read vale.readinput and give it to the register. return w.valE.read(); } else if (w.dstM.read() == regNum) { return w.valM.read(); // read result of what write will be } break; case I_MRMOVL: /** LOAD USE HAZARD **/ if (w.dstM.read() == regNum) { return w.valM.read(); } break; } return reg.read(regNum); // this is outside so we actually use it. } /** * The DECODE stage of CPU * * @throws RegisterSet.InvalidRegisterNumberException * if instruction attempts to access register number > 7 */ @Override protected void decode() throws RegisterSet.InvalidRegisterNumberException { // pass on from previous stages e.iCd.write(d.iCd.read()); e.iFn.write(d.iFn.read()); e.valC.write(d.valC.read()); e.valP.write(d.valP.read()); // srcA MUX switch (d.iCd.read()) { case I_RRMOVL: case I_RMMOVL: case I_OPL: case I_PUSHL: e.srcA.write(d.rA.read()); break; case I_RET: case I_POPL: e.srcA.write(R_ESP); break; default: e.srcA.write(R_NONE); } // srcB MUX switch (d.iCd.read()) { case I_RRMOVL: case I_RMMOVL: case I_MRMOVL: case I_OPL: e.srcB.write(d.rB.read()); break; case I_CALL: case I_RET: case I_PUSHL: case I_POPL: e.srcB.write(R_ESP); break; default: e.srcB.write(R_NONE); } // dstE MUX switch (d.iCd.read()) { case I_RRMOVL: case I_IRMOVL: case I_OPL: e.dstE.write(d.rB.read()); break; case I_CALL: case I_RET: case I_PUSHL: case I_POPL: e.dstE.write(R_ESP); break; default: e.dstE.write(R_NONE); } // dstM MUX switch (d.iCd.read()) { case I_MRMOVL: case I_POPL: e.dstM.write(d.rA.read()); break; default: e.dstM.write(R_NONE); } // read valA and valB from register file (with data forwarding) e.valA.write(decode_ReadRegisterWithForwarding(e.srcA.readInput())); e.valB.write(decode_ReadRegisterWithForwarding(e.srcB.readInput())); } /** * The EXECUTE stage of CPU */ @Override protected void execute() { // pass on from previous stages m.iCd.write(e.iCd.read()); m.iFn.write(e.iFn.read()); m.valA.write(e.valA.read()); m.valP.write(e.valP.read()); m.dstE.write(e.dstE.read()); m.dstM.write(e.dstM.read()); m.valC.write(e.valC.read()); // aluA MUX int aluA; switch (e.iCd.read()) { case I_RRMOVL: case I_OPL: aluA = e.valA.read(); break; case I_IRMOVL: case I_MRMOVL: case I_RMMOVL: aluA = e.valC.read(); break; case I_RET: case I_POPL: aluA = 4; break; case I_CALL: case I_PUSHL: aluA = -4; break; default: aluA = 0; } // aluB MUX int aluB; switch (e.iCd.read()) { case I_RRMOVL: case I_IRMOVL: aluB = 0; break; case I_RMMOVL: case I_MRMOVL: case I_OPL: case I_CALL: case I_RET: case I_PUSHL: case I_POPL: aluB = e.valB.read(); break; default: aluB = 0; } // aluFun and setCC muxes MUX int aluFun; boolean setCC; switch (e.iCd.read()) { case I_RRMOVL: case I_IRMOVL: case I_RMMOVL: case I_MRMOVL: case I_CALL: case I_RET: case I_PUSHL: case I_POPL: aluFun = A_ADDL; setCC = false; break; case I_OPL: aluFun = e.iFn.read(); setCC = true; break; default: aluFun = 0; setCC = false; } // the ALU boolean overflow; switch (aluFun) { case A_ADDL: m.valE.write(aluB + aluA); overflow = ((aluB < 0) == (aluA < 0)) && ((m.valE.readInput() < 0) != (aluB < 0)); break; case A_SUBL: m.valE.write(aluB - aluA); overflow = ((aluB < 0) != (aluA < 0)) && ((m.valE.readInput() < 0) != (aluB < 0)); break; case A_ANDL: m.valE.write(aluB & aluA); overflow = false; break; case A_XORL: m.valE.write(aluB ^ aluA); overflow = false; break; default: overflow = false; } // CC MUX if (setCC) p.cc.write(((m.valE.readInput() == 0) ? 0x100 : 0) | ((m.valE.readInput() < 0) ? 0x10 : 0) | (overflow ? 0x1 : 0)); else p.cc.write(p.cc.read()); // bch MUX boolean bch; if (e.iCd.read() == I_JXX || e.iCd.read() == I_CMOV) { boolean zf = (p.cc.read() & 0x100) != 0; boolean sf = (p.cc.read() & 0x010) != 0; boolean of = (p.cc.read() & 0x001) != 0; switch (e.iFn.read()) { case J_NC: bch = true; break; case J_LE: bch = (sf ^ of) | zf; break; case J_L: bch = sf ^ of; break; case J_E: bch = zf; break; case J_NE: bch = !zf; break; case J_GE: bch = !(sf ^ of); break; case J_G: bch = !(sf ^ of) & !zf; break; default: throw new AssertionError(); } } else bch = true; m.bch.write(bch ? 1 : 0); } /** * The MEMORY stage of CPU * * @throws AbstractMainMemory.InvalidAddressException * if instruction attempts to access an invalid memory address */ @Override protected void memory() throws AbstractMainMemory.InvalidAddressException { // pass on from previous stages w.iCd.write(m.iCd.read()); w.valE.write(m.valE.read()); w.valP.write(m.valP.read()); w.dstE.write(m.dstE.read()); w.dstM.write(m.dstM.read()); w.valC.write(m.valC.read()); w.bch.write(m.bch.readInput()); // write Main Memory switch (m.iCd.read()) { case I_RMMOVL: case I_PUSHL: mem.writeInteger(m.valE.read(), m.valA.read()); break; case I_CALL: mem.writeInteger(m.valE.read(), m.valP.read()); break; default: } // valM MUX (read main memory) switch (m.iCd.read()) { case I_MRMOVL: w.valM.write(mem.readInteger(m.valE.read())); break; case I_RET: case I_POPL: w.valM.write(mem.readInteger(m.valA.read())); break; default: } } /** * The WRITE BACK stage of CPU * * @throws MachineHaltException * if instruction halts the CPU (e.g., halt instruction) * @throws RegisterSet.InvalidRegisterNumberException * if instruction attempts to access register number > 7 */ @Override protected void writeBack() throws MachineHaltException, RegisterSet.InvalidRegisterNumberException { // write valE to register file if (w.dstE.read () != R_NONE && w.bch.readInput () == 1) reg.write (w.dstE.read (), w.valE.read ()); // write valM to register file if (w.dstM.read () != R_NONE) reg.write (w.dstM.read (), w.valM.read ()); // the HALT instruction if (w.iCd.read () == I_HALT) throw new MachineHaltException (); }}