/* * FPPipeline.java * * This class models a MIPS FPU pipeline that supports multiple outstanding FP operations * it is used only by the cpu class * (c) 2006 Massimo Trubia * * This file is part of the EduMIPS64 project, and is released under the GNU * General Public License. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.edumips64.core.fpu; import org.edumips64.core.*; import org.edumips64.core.is.*; import org.edumips64.utils.IrregularStringOfBitsException; import java.util.*; /** This class models a MIPS FPU pipeline that supports multiple outstanding FP operations * it is used only by the cpu class * @author Massimo Trubia */ public class FPPipeline { //FPU functional units static class Costanti { public enum FPAdderStatus {A1, A2, A3, A4}; public enum FPMultiplierStatus {M1, M2, M3, M4, M5, M6, M7}; public enum FPDividerStatus {DIVIDER}; } public static int STRUCT_HAZARD = 0; //status constant of pipeStatus[] private int pipeStatus[]; private Divider divider; private Multiplier multiplier; private Adder adder; private int nInstructions; //used for understanding if the fpPipe is empty or not private Queue<Instruction> entryQueue; //if an output structural hazard occurs instructions leave the //FPPipeline in the same order by which they has entered private int readyToExit; //number of instructions that hold the last position of the f.u. public FPPipeline() { //Instanciating functional units objects nInstructions = 0; divider = new Divider(); divider.reset(); multiplier = new Multiplier(); multiplier.reset(); adder = new Adder(); adder.reset(); entryQueue = new LinkedList<Instruction>(); pipeStatus = new int[1]; pipeStatus[STRUCT_HAZARD] = 0; // 0 means that any structural hazard at the last getCompletedInstruction() call // happened. 1 means the contrary. readyToExit = 0; } public int size() { return nInstructions; } public String toString() { String output = ""; output += adder.toString(); output += multiplier.toString(); output += divider.toString(); return output; } public int getNReadyToExitInstr() { return readyToExit; } /** Returns true if the specified functional unit is filled by an instruction, false when the contrary happens. * No controls are carried out on the legality of parameters, for mistaken parameters false is returned * @param funcUnit The functional unit to check. Legal values are "ADDER", "MULTIPLIER", "DIVIDER" * @param stage The integer that refers to the stage of the functional unit. * ADDER [1,4], MULTIPLIER [1,7], DIVIDER [any] */ public boolean isFuncUnitFilled(String funcUnit, int stage) { if (funcUnit.compareToIgnoreCase("ADDER") == 0) switch (stage) { case 1: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A1) != null); case 2: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A2) != null); case 3: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A3) != null); case 4: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A4) != null); } if (funcUnit.compareToIgnoreCase("MULTIPLIER") == 0) switch (stage) { case 1: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M1) != null); case 2: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M2) != null); case 3: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M3) != null); case 4: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M4) != null); case 5: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M5) != null); case 6: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M6) != null); case 7: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M7) != null); } return funcUnit.compareToIgnoreCase("DIVIDER") == 0 && (divider.getFuncUnit() != null); } /** Returns the instruction of the specified functional unit , null if it is empty. * No controls are carried out on the legality of parameters, for mistaken parameters null is returned * @param funcUnit The functional unit to check. Legal values are "ADDER", "MULTIPLIER", "DIVIDER" * @param stage The integer that refers to the stage of the functional unit. * ADDER [1,4], MULTIPLIER [1,7], DIVIDER [any] */ public Instruction getInstructionByFuncUnit(String funcUnit, int stage) { if (funcUnit.compareToIgnoreCase("ADDER") == 0) switch (stage) { case 1: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A1)); case 2: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A2)); case 3: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A3)); case 4: return (adder.getFuncUnit().get(Costanti.FPAdderStatus.A4)); } if (funcUnit.compareToIgnoreCase("MULTIPLIER") == 0) switch (stage) { case 1: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M1)); case 2: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M2)); case 3: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M3)); case 4: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M4)); case 5: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M5)); case 6: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M6)); case 7: return (multiplier.getFuncUnit().get(Costanti.FPMultiplierStatus.M7)); } if (funcUnit.compareToIgnoreCase("DIVIDER") == 0) { return (divider.getFuncUnit()); } return null; } /** Inserts the passed instruction into the right functional unit. If no errors occur * 0 is returned, else, if we want to insert an ADD.fmt, MUL.fmt, SUB.fmt and * the first place of the adder or multiplier is filled by other * instructions 1 is returned, else, if we want to insert a DIV.fmt and the * divider is full 2 is returned and the CPU raises a StructuralException. * If an integer instruction is passed at the method 3 is returned */ public int putInstruction(Instruction instr, boolean simulation) { //throws InputStructuralHazardException if (instr != null && CPU.knownFPInstructions.contains(instr.getName())) { String instrName = instr.getName(); if ((instrName.compareToIgnoreCase("ADD.D") == 0) || (instrName.compareToIgnoreCase("SUB.D") == 0)) if (adder.putInstruction(instr, simulation) == -1) { return 1; } if (instrName.compareToIgnoreCase("MUL.D") == 0) if (multiplier.putInstruction(instr, simulation) == -1) { return 1; } if (instrName.compareToIgnoreCase("DIV.D") == 0) if (divider.putInstruction(instr, simulation) == -1) { return 2; } if (!simulation) { nInstructions++; } return 0; } return 3; } // Returns the completed FP instruction. If more than one instruction completed, it will return them in the following // order: 1. divider; 2. multiplier; 3. adder. If no instruction is complete, it'll return null. // The returned instruction will be removed from the corresponding FPU unit. public Instruction getCompletedInstruction() { Instruction dividerInstruction = divider.getInstruction(); Instruction multiplierInstruction = multiplier.getInstruction(); Instruction adderInstruction = adder.getInstruction(); if (dividerInstruction != null) { divider.removeLast(); nInstructions--; return dividerInstruction; } if (multiplierInstruction != null) { multiplier.removeLast(); nInstructions--; return multiplierInstruction; } if (adderInstruction != null) { adder.removeLast(); nInstructions--; return adderInstruction; } return null; } /* Shifts instructions into the functional units and calls the EX() method for instructions in the first step * this method is called from getCompletedInstruction in order to prepare the pipeline for a new instruction entrance */ public void step() { //try catch is necessary for handling structural stalls and stalls coming from the EX() method //adder adder.step(); //multiplier multiplier.step(); //divider divider.step(); } /** This method is used in order to understand if the fpPipe is not empty and the all CPU halt are disabled*/ public boolean isEmpty() { return (nInstructions == 0) ? true : false; } /** Returns the FPPipeline status, index must be one between the status constants * @return the value of the status vector. 0 means the status is false, 1 the contrary . -1 if the status doesn't exist * **/ public int getStatus(int index) { if (index > -1 && index < pipeStatus.length) { return pipeStatus[index]; } return -1; } public int getDividerCounter() { return divider.getCounter(); } /* Resets the fp pipeline */ public void reset() { nInstructions = 0; entryQueue.clear(); multiplier.reset(); adder.reset(); divider.reset(); } //---------------------- FUNCTIONAL UNITS ---------------------------------------- interface FPFunctionalUnit { public Object getFuncUnit(); public int putInstruction(Instruction instr, boolean simulation); public Instruction getInstruction(); public void removeLast(); public void step(); } /** This class models the 7 steps floating point multiplier*/ private class Multiplier implements FPFunctionalUnit { private Map<Costanti.FPMultiplierStatus, Instruction> multiplier; Multiplier() { //Multiplier initialization multiplier = new HashMap<Costanti.FPMultiplierStatus, Instruction>(); this.reset(); } public Map<Costanti.FPMultiplierStatus, Instruction> getFuncUnit() { return multiplier; } public String toString() { String output = ""; Instruction instr; output += "MULTIPLIER\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M1)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M2)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M3)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M4)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M5)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M6)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M7)) != null) ? instr.getName() + "\n" : "EMPTY\n"; return output; } /** Resets the functional unit*/ public void reset() { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M1, null); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M2, null); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M3, null); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M4, null); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M5, null); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M6, null); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M7, null); } /** Inserts the passed instruction in the first position of the functional unit * if another instruction holds that position a negative number is returned*/ public int putInstruction(Instruction instr, boolean simulation_enabled) { if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M1) == null) { if (!simulation_enabled) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M1, instr); } return 0; } else { return -1; } } /** Returns the last instruction in the functional unit, if any instruction was found * null is returned, the instruction is not removed from the HashMap */ public Instruction getInstruction() { Instruction instr; if ((instr = multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M7)) == null) { return null; } return instr; } /** Remove the last instruction in the functional unit*/ public void removeLast() { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M7, null); } /* Shifts instructions into the functional unit and calls the EX() method for instructions in the secondary step * this method is called from getCompletedInstruction in order to prepare the pipeline for a new instruction entrance */ public void step() { if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M7) == null) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M7, multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M6)); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M6, null); } if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M6) == null) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M6, multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M5)); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M5, null); } if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M5) == null) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M5, multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M4)); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M4, null); } if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M4) == null) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M4, multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M3)); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M3, null); } if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M3) == null) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M3, multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M2)); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M2, null); } if (multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M2) == null) { multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M2, multiplier.get(FPPipeline.Costanti.FPMultiplierStatus.M1)); multiplier.put(FPPipeline.Costanti.FPMultiplierStatus.M1, null); } } } /** This class models the 4 steps floating point adder*/ private class Adder implements FPFunctionalUnit { public Map<Costanti.FPAdderStatus, Instruction> adder; Adder() { adder = new HashMap<Costanti.FPAdderStatus, Instruction>(); this.reset(); } public Map<Costanti.FPAdderStatus, Instruction> getFuncUnit() { return adder; } public String toString() { String output = ""; Instruction instr; output += "ADDER\n"; output += ((instr = adder.get(FPPipeline.Costanti.FPAdderStatus.A1)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = adder.get(FPPipeline.Costanti.FPAdderStatus.A2)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = adder.get(FPPipeline.Costanti.FPAdderStatus.A3)) != null) ? instr.getName() + "\n" : "EMPTY\n"; output += ((instr = adder.get(FPPipeline.Costanti.FPAdderStatus.A4)) != null) ? instr.getName() + "\n" : "EMPTY\n"; return output; } /** Resets the functional unit*/ public void reset() { adder.put(FPPipeline.Costanti.FPAdderStatus.A1, null); adder.put(FPPipeline.Costanti.FPAdderStatus.A2, null); adder.put(FPPipeline.Costanti.FPAdderStatus.A3, null); adder.put(FPPipeline.Costanti.FPAdderStatus.A4, null); } /** Inserts the passed instruction in the first position of the functional unit * if another instruction holds that position a negative number is returned*/ public int putInstruction(Instruction instr, boolean simulation) { if (adder.get(FPPipeline.Costanti.FPAdderStatus.A1) == null) { if (!simulation) { adder.put(FPPipeline.Costanti.FPAdderStatus.A1, instr); } return 0; } else { return -1; } } /** Returns the last instruction in the functional unit, if any instruction was found * null is returned, the instruction is not removed from the HashMap */ public Instruction getInstruction() { Instruction instr; if ((instr = adder.get(FPPipeline.Costanti.FPAdderStatus.A4)) == null) { return null; } return instr; } /** Remove the last instruction in the functional unit*/ public void removeLast() { adder.put(FPPipeline.Costanti.FPAdderStatus.A4, null); } /* Shifts instructions into the functional unit and calls the EX() method for the instruction in the secondary step * this method is called from getCompletedInstruction in order to prepare the pipeline for a new instruction entrance */ public void step() { if (adder.get(FPPipeline.Costanti.FPAdderStatus.A4) == null) { adder.put(FPPipeline.Costanti.FPAdderStatus.A4, adder.get(FPPipeline.Costanti.FPAdderStatus.A3)); adder.put(FPPipeline.Costanti.FPAdderStatus.A3, null); } if (adder.get(FPPipeline.Costanti.FPAdderStatus.A3) == null) { adder.put(FPPipeline.Costanti.FPAdderStatus.A3, adder.get(FPPipeline.Costanti.FPAdderStatus.A2)); adder.put(FPPipeline.Costanti.FPAdderStatus.A2, null); } if (adder.get(FPPipeline.Costanti.FPAdderStatus.A2) == null) { adder.put(FPPipeline.Costanti.FPAdderStatus.A2, adder.get(FPPipeline.Costanti.FPAdderStatus.A1)); adder.put(FPPipeline.Costanti.FPAdderStatus.A1, null); } } } /** This class models the 24 steps floating point divider, instructions are not pipelined * and for this reason a structural hazard happens when a DIV.fmt would to enter the FU when * another DIV.fmt is present */ private class Divider implements FPFunctionalUnit { public Instruction instr; public int counter; Divider() { this.reset(); } public Instruction getFuncUnit() { return instr; } public String toString() { if (instr != null) { return "DIVIDER \n " + instr.getName() + " " + counter; } else { return "DIVIDER \n " + "EMPTY " + counter; } } /** Inserts the passed instruction in the first position of the functional unit * if another instruction holds that position a negative number is returned*/ public int putInstruction(Instruction instr, boolean simulation) { if (this.instr == null) { if (!simulation) { this.instr = instr; this.counter = 24; } return 0; } return -1; } /** Returns the instruction if counter has reached 1 else * null is returned*/ public Instruction getInstruction() { if (counter == 1) { return this.instr; } return null; } /* Shifts instructions into the functional unit and calls the EX() method for the instruction in the secondary step * this method is called from getCompletedInstruction in order to prepare the pipeline for a new instruction entrance */ public void step() { //if counter has reached 0 the instruction was removed by the previous getCompletedInstruction invocation wich called removeLast() //if counter is a number between 0 and 24 it must be decremented by 1 if (this.instr != null && counter > 0 && counter < 25) { counter--; } //if the divider does not contain instructions anyone operation is carried out } /** Resets the functional unit*/ public void reset() { instr = null; counter = 0; } /** Return the counter of the divider*/ public int getCounter() { return counter; } /** Removes the instruction in the functional unit (improper name for to conform to the others f.u.*/ public void removeLast() { this.instr = null; this.counter = 0; } } }