/* CycleBuilder.java * * This class builds the temporal diagram of the pipeline that is then * represented by GUICycles. * (c) 2006-2013 Filippo Mondello, Trubia Massimo (FPU modifications), Andrea * Spadaccini * * 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.utils; import org.edumips64.core.CPU; import org.edumips64.core.is.Instruction; import java.util.*; import java.util.logging.Logger; public class CycleBuilder { private CPU cpu; private int curTime, oldTime; private int instructionsCount; private static final Logger logger = Logger.getLogger(CycleBuilder.class.getName()); // Data structure that contains the actual time diagram of the pipeline. private List<CycleElement> elementsList; // Counter of how many times a given instruction, represented by its serial number, has // been processed in a given step of the CycleBuilder. The map is reset at every step. // // This is needed to handle corner cases related to the same instruction being present // multiple times in the pipeline. By marking an instruction as processed, it's possible // to get the right CycleElement corresponding to the proper instruction instance being // processed at a given time. private Map<Integer, Integer> processedCountMap; // Stalls counters. // These are updated at the end of the step() cycle because they hold the value of the stalls for the *last* cycle, // so when step() is called again the new stall values can be compared with them to detect stalls. private int oldRAWStalls, oldWAWStalls, oldStructStallsEX, oldStructStallsDivider, oldStructStallsFuncUnit; // Used to understand if the EX instruction is in structural stall (memory). private int oldMemoryStalls; // Groups five stalls (EXNotAvailable, FuncUnitNotAvailable, // DividerNotAvailable, RAW, WAW), in order to understand if a new // instruction has to be added to "elementsList" private int oldInputStructuralStalls; private List<CycleElement> lastElements; public CycleBuilder(CPU cpu) { this.cpu = cpu; elementsList = new ArrayList<>(); lastElements = new ArrayList<>(); updateStalls(); } public List<CycleElement> getElementsList() { return elementsList; } public int getInstructionsCount() { return instructionsCount; } public int getTime() { return curTime; } public void reset() { elementsList.clear(); lastElements.clear(); oldTime = 0; instructionsCount = 0; } // This method assumes that the CPU has just finished one cycle and that it is necessary to update the // CycleBuilder's internal representation to match the state of the CPU. public void step() { Map<CPU.PipeStage, Instruction> pipeline = cpu.getPipeline(); curTime = cpu.getCycles(); int instrInPipelineCount = cpu.getInstructionCount(); // View into the last N elements of the list. This list is used to search for CycleElements // corresponding to a given instruction in the getElementToUpdate() method. // // The max() statement is needed to avoid negative indexing (and therefore errors), in the case when the // size of the list of elements is smaller than the number of stages in the pipeline, which happens in the // first cycles of a program. lastElements = new ArrayList<>(elementsList.subList(Math.max(elementsList.size() - instrInPipelineCount, 0), elementsList.size())); // Reverse the list since the elements are updated from IF to WB, and the elementsList is sorted in // chronological order. Collections.reverse(lastElements); logger.info("Got " + lastElements.size() + " CycleElements. " + instrInPipelineCount + " instructions in the pipeline."); // The map is reset at every cycle on purpose. processedCountMap = new HashMap<>(); if (oldTime != curTime) { CycleElement el; // Pre-compute EX stage stalls boolean RAWStallOccurred = (oldRAWStalls != cpu.getRAWStalls()); boolean WAWStallOccurred = (oldWAWStalls != cpu.getWAWStalls()); boolean structStallEXOccurred = (oldStructStallsEX != cpu.getStructuralStallsEX()); boolean structStallDividerOccurred = (oldStructStallsDivider != cpu.getStructuralStallsDivider()); boolean structStallsFuncUnitOccurred = (oldStructStallsFuncUnit != cpu.getStructuralStallsFuncUnit()); boolean inputStallOccurred = (oldInputStructuralStalls != cpu.getStructuralStallsDivider() + cpu.getStructuralStallsEX() + cpu.getStructuralStallsFuncUnit() + cpu.getRAWStalls() + cpu.getWAWStalls()); // Check if something fishy is going on. if (inputStallOccurred && !(RAWStallOccurred || WAWStallOccurred || structStallDividerOccurred || structStallEXOccurred || structStallsFuncUnitOccurred)) { logger.severe("Something fishy going on with the instruction that has to go into ID"); } // IF if (pipeline.get(CPU.PipeStage.IF) != null) { // We must instantiate a new CycleElement only if the CPU is running or there was a JumpException and the the // IF instruction was changed (i.e., if no input stalls occurred). if (!inputStallOccurred) { synchronized(elementsList) { logger.info("Adding a new element to the list of elements"); CycleElement newElement = new CycleElement(pipeline.get(CPU.PipeStage.IF), curTime); elementsList.add(newElement); } instructionsCount++; } else { el = getElementToUpdate(pipeline.get(CPU.PipeStage.IF)); if (el != null) { el.addState(" "); } } } // If there were stalls, such as RAW, WAW, EXNotAvailable, // DividerNotAvailable, FuncUnitNotAvailable, we cannot add a new // CycleElement in "elementsList" and we must add tags as RAW, WAW, // StEx, StDiv, StFun into the right instruction's state list // ID el = getElementToUpdate(pipeline.get(CPU.PipeStage.ID)); if (el != null) { if (!inputStallOccurred) { el.addState("ID"); } if (RAWStallOccurred) { el.addState("RAW"); } if (WAWStallOccurred) { el.addState("WAW"); } if (structStallDividerOccurred) { el.addState("StDiv"); } if (structStallEXOccurred) { el.addState("StEx"); } if (structStallsFuncUnitOccurred) { el.addState("StFun"); } } // EX el = getElementToUpdate(pipeline.get(CPU.PipeStage.EX)); if (el != null) { // If a structural stall(memory) occurs, the instruction in EX has to be tagged first with "EX" and then with "StEx" boolean exTagged = false; if (el.getLastState().equals("ID") || el.getLastState().equals("RAW") || el.getLastState().equals("WAW") || el.getLastState().equals("StEx")) { el.addState("EX"); exTagged = true; } //we check if a structural hazard occurred if there's a difference between the previous value of memoryStall counter and the current one if (oldMemoryStalls != cpu.getMemoryStalls() && !exTagged) { el.addState("Str"); } } // MEM el = getElementToUpdate(pipeline.get(CPU.PipeStage.MEM)); if (el != null) { el.addState("MEM"); } // WB el = getElementToUpdate(pipeline.get(CPU.PipeStage.WB)); if (el != null) { el.addState("WB"); } //we have to check instructions in the FP pipeline //ADDER ------------------------------------------------- String stage; // Handle non-terminal adder stages (1-3) with a cycle. for (int i = 1; i <= 3; ++i) { el = getElementToUpdate(cpu.getInstructionByFuncUnit("ADDER", i)); if (el != null) { el.addState("A" + i); } } el = getElementToUpdate(cpu.getInstructionByFuncUnit("ADDER", 4)); if (el != null) { boolean A4tagged = false; if (el.getLastState().equals("A3")) { el.addState("A4"); A4tagged = true; } //we have to check if a structural hazard occurred and it involved the divider or the multiplier (it is sufficient to control if the "A4" o "StAdd" tag was added to the instruction if (!A4tagged && (el.getLastState().equals("A4") || el.getLastState().equals("StAdd"))) { el.addState("StAdd"); } } //MULTIPLIER ---------------------------------------------------------------- // Handle non-terminal multiplier stages (1-6) with a cycle. for (int i = 1; i <= 6; ++i) { el = getElementToUpdate(cpu.getInstructionByFuncUnit("MULTIPLIER", i)); if (el != null) { el.addState("M" + i); } } el = getElementToUpdate(cpu.getInstructionByFuncUnit("MULTIPLIER", 7)); if (el != null) { boolean M7tagged = false; if (el.getLastState().equals("M6")) { el.addState("M7"); M7tagged = true; } //we check if a structural hazard occurred and involved the divider if (!M7tagged && (el.getLastState().equals("M7") || el.getLastState().equals("StMul"))) { el.addState("StMul"); } } //DIVIDER ------------------------------------------------------ el = getElementToUpdate(cpu.getInstructionByFuncUnit("DIVIDER", 0)); if (el != null) { boolean DIVtagged = false; stage = el.getLastState(); if (!stage.equals("DIV") && !stage.matches("D[0-2][0-9]")) { el.addState("DIV"); DIVtagged = true; } if (!DIVtagged) { int divCount = cpu.getDividerCounter(); String divCountStr = String.valueOf(divCount); //divCount in the format DXX (XX belongs to [00 24]) el.addState((divCount < 10) ? "D0" + divCountStr : "D" + divCountStr); } } oldTime = curTime; } updateStalls(); } // Returns the CycleElement corresponding to the given Instruction. Returns null if the Instruction is null, // is a BUBBLE or if it's not in the map. private CycleElement getElementToUpdate(Instruction instruction) { // Early exit in case of bogus instructions. if (instruction == null || instruction.isBubble()) { return null; } int serial = instruction.getSerialNumber(); if (!processedCountMap.containsKey(serial)) { processedCountMap.put(serial, 0); } int instructionProcessedCount = processedCountMap.get(serial); // The CycleElement to be returned. CycleElement element = null; // How many times the a CycleElement corresponding to the given instruction was seen in the list of CycleElements // being analyzed. Let N be the number of times the instruction was already processed by getElementToUpdate(). The // cycle terminates when the CycleElement N (corresponding to the given instruction) is found in the list of // CycleElements, or when the list is exhausted. (N == instructionProcessedCount). int instructionSeenCount = 0; for (CycleElement el : lastElements) { if (el.getSerialNumber() == serial) { if (instructionSeenCount == instructionProcessedCount) { element = el; break; } instructionSeenCount++; } } // Increment the counter to indicate that, in this step() cycle, the given instruction was processed one more time. processedCountMap.put(serial, instructionProcessedCount + 1); return element; } private void updateStalls() { oldMemoryStalls = cpu.getMemoryStalls(); oldRAWStalls = cpu.getRAWStalls(); oldWAWStalls = cpu.getWAWStalls(); oldStructStallsEX = cpu.getStructuralStallsEX(); oldStructStallsDivider = cpu.getStructuralStallsDivider(); oldStructStallsFuncUnit = cpu.getStructuralStallsFuncUnit(); oldInputStructuralStalls = oldStructStallsDivider + oldStructStallsEX + oldStructStallsFuncUnit + oldRAWStalls + oldWAWStalls; } }