/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the Archimulator multicore architectural simulator.
* <p>
* Archimulator 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 3 of the License, or
* (at your option) any later version.
* <p>
* Archimulator 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.
* <p>
* You should have received a copy of the GNU General Public License
* along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.core;
import archimulator.core.bpred.BranchPredictorUpdate;
import archimulator.core.bpred.DynamicBranchPredictor;
import archimulator.core.event.DynamicInstructionCommittedEvent;
import archimulator.core.event.DynamicInstructionDecodedEvent;
import archimulator.core.event.StaticInstructionFetchBeginEvent;
import archimulator.core.event.StaticInstructionFetchEndEvent;
import archimulator.isa.RegisterDependencyType;
import archimulator.isa.StaticInstruction;
import archimulator.isa.StaticInstructionType;
import archimulator.os.ContextState;
import archimulator.util.Reference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Basic thread.
*
* @author Min Cai
*/
public class BasicThread extends AbstractBasicThread {
private int lineSizeOfICache;
private int fetchNpc;
private int fetchNnpc;
private boolean fetchStalled;
private int lastFetchedCacheLine;
private DynamicInstruction lastDecodedDynamicInstruction;
private boolean lastDecodedDynamicInstructionCommitted;
private long lastCommitCycle;
private int noDynamicInstructionCommittedCounterThreshold;
private DynamicInstruction nextDynamicInstructionInWarmupPhase;
/**
* Create a basic thread.
*
* @param core the core
* @param num the num of the thread
*/
public BasicThread(Core core, int num) {
super(core, num);
this.lineSizeOfICache = this.core.getL1IController().getCache().getGeometry().getLineSize();
}
@Override
public void fastForwardOneCycle() {
if (this.context != null && this.context.getState() == ContextState.RUNNING) {
StaticInstruction staticInstruction;
do {
staticInstruction = this.context.decodeNextInstruction();
StaticInstruction.execute(staticInstruction, this.context);
if (!this.context.isPseudoCallEncounteredInLastInstructionExecution() && staticInstruction.getMnemonic().getType() != StaticInstructionType.NOP) {
this.numInstructions++;
}
}
while (this.context != null && this.context.getState() == ContextState.RUNNING && (this.context.isPseudoCallEncounteredInLastInstructionExecution() || staticInstruction.getMnemonic().getType() == StaticInstructionType.NOP));
}
}
@Override
public void warmupOneCycle() {
if (this.context != null && this.context.getState() == ContextState.RUNNING && !this.fetchStalled) {
if (this.nextDynamicInstructionInWarmupPhase == null) {
StaticInstruction staticInstruction;
do {
staticInstruction = this.context.decodeNextInstruction();
this.nextDynamicInstructionInWarmupPhase = new DynamicInstruction(this, this.context.getRegisterFile().getPc(), staticInstruction);
StaticInstruction.execute(staticInstruction, this.context);
if (!this.context.isPseudoCallEncounteredInLastInstructionExecution() && staticInstruction.getMnemonic().getType() != StaticInstructionType.NOP) {
this.numInstructions++;
}
}
while (this.context != null && this.context.getState() == ContextState.RUNNING && !this.fetchStalled && (this.context.isPseudoCallEncounteredInLastInstructionExecution() || staticInstruction.getMnemonic().getType() == StaticInstructionType.NOP));
}
int pc = this.nextDynamicInstructionInWarmupPhase.getPc();
int cacheLineToFetch = aligned(pc, this.lineSizeOfICache);
if (cacheLineToFetch != this.lastFetchedCacheLine) {
if (this.core.canIfetch(this, pc)) {
this.core.ifetch(this, pc, pc, () -> fetchStalled = false);
this.fetchStalled = true;
this.lastFetchedCacheLine = cacheLineToFetch;
} else {
return;
}
}
int effectiveAddress = this.nextDynamicInstructionInWarmupPhase.getEffectiveAddress();
if (this.nextDynamicInstructionInWarmupPhase.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.LOAD) {
if (this.core.canLoad(this, effectiveAddress)) {
this.core.load(this.nextDynamicInstructionInWarmupPhase, effectiveAddress, pc, () -> {
});
this.nextDynamicInstructionInWarmupPhase = null;
}
} else if (this.nextDynamicInstructionInWarmupPhase.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE) {
if (this.core.canStore(this, effectiveAddress)) {
this.core.store(this.nextDynamicInstructionInWarmupPhase, effectiveAddress, pc, () -> {
});
this.nextDynamicInstructionInWarmupPhase = null;
}
} else {
this.nextDynamicInstructionInWarmupPhase = null;
}
}
}
@Override
public void updateFetchNpcAndNnpcFromRegs() {
this.fetchNpc = this.context.getRegisterFile().getNpc();
this.fetchNnpc = this.context.getRegisterFile().getNnpc();
this.lastCommitCycle = this.getCycleAccurateEventQueue().getCurrentCycle();
}
/**
* Get a value indicating whether the thread can fetch instructions at the moment or not.
*
* @return a value indicating whether the thread can fetch instructions at the moment or not
*/
private boolean canFetch() {
if (!this.context.useICache()) {
this.lastFetchedCacheLine = aligned(this.fetchNpc, this.lineSizeOfICache);
return true;
} else {
if (this.fetchStalled) {
return false;
}
int cacheLineToFetch = aligned(this.fetchNpc, this.lineSizeOfICache);
if (cacheLineToFetch != this.lastFetchedCacheLine) {
if (!this.core.canIfetch(this, this.fetchNpc)) {
return false;
} else {
this.core.ifetch(this, this.fetchNpc, this.fetchNpc, () -> {
fetchStalled = false;
this.getBlockingEventDispatcher().dispatch(new StaticInstructionFetchEndEvent(this, this.getCycleAccurateEventQueue().getCurrentCycle(), this.id, this.fetchNpc));
});
this.getBlockingEventDispatcher().dispatch(new StaticInstructionFetchBeginEvent(this, this.getCycleAccurateEventQueue().getCurrentCycle(), this.id, this.fetchNpc));
this.fetchStalled = true;
this.lastFetchedCacheLine = cacheLineToFetch;
return false;
}
}
return true;
}
}
@Override
public void fetch() {
if (!this.canFetch()) {
return;
}
boolean hasDone = false;
while (!hasDone) {
if (this.context.getState() != ContextState.RUNNING) {
break;
}
if (this.decodeBuffer.isFull()) {
this.numFetchStallsOnDecodeBufferIsFull++;
break;
}
if (this.context.getRegisterFile().getNpc() != this.fetchNpc) {
if (this.context.isSpeculative()) {
this.context.getRegisterFile().setNpc(this.fetchNpc);
} else {
this.context.enterSpeculativeState();
}
}
DynamicInstruction dynamicInstruction;
do {
StaticInstruction staticInstruction = this.context.decodeNextInstruction();
dynamicInstruction = new DynamicInstruction(this, this.context.getRegisterFile().getPc(), staticInstruction);
StaticInstruction.execute(staticInstruction, this.context);
if (this.context.isPseudoCallEncounteredInLastInstructionExecution() || dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.NOP) {
this.updateFetchNpcAndNnpcFromRegs();
}
}
while (this.context.isPseudoCallEncounteredInLastInstructionExecution() || dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.NOP);
this.fetchNpc = this.fetchNnpc;
if (!this.context.isSpeculative() && this.context.getState() != ContextState.RUNNING) {
this.lastDecodedDynamicInstruction = dynamicInstruction;
this.lastDecodedDynamicInstructionCommitted = false;
}
this.getBlockingEventDispatcher().dispatch(new DynamicInstructionDecodedEvent(dynamicInstruction));
if ((this.fetchNpc + 4) % this.lineSizeOfICache == 0) {
hasDone = true;
}
BranchPredictorUpdate branchPredictorUpdate = new BranchPredictorUpdate();
Reference<Integer> returnAddressStackRecoverIndexRef = new Reference<>(0);
int destination = dynamicInstruction.getStaticInstruction().getMnemonic().isControl() ? this.branchPredictor.predict(this.fetchNpc, 0, dynamicInstruction.getStaticInstruction().getMnemonic(), branchPredictorUpdate, returnAddressStackRecoverIndexRef) : this.fetchNpc + 4;
this.fetchNnpc = destination <= 1 ? this.fetchNpc + 4 : destination;
if (this.fetchNnpc != this.fetchNpc + 4) {
hasDone = true;
}
this.decodeBuffer.getEntries().add(new DecodeBufferEntry(dynamicInstruction, this.context.getRegisterFile().getNpc(), this.context.getRegisterFile().getNnpc(), this.fetchNnpc, returnAddressStackRecoverIndexRef.get(), branchPredictorUpdate, this.context.isSpeculative()));
}
}
@Override
public boolean registerRenameOne() {
DecodeBufferEntry decodeBufferEntry = this.decodeBuffer.getEntries().get(0);
DynamicInstruction dynamicInstruction = decodeBufferEntry.getDynamicInstruction();
for (Map.Entry<RegisterDependencyType, Integer> entry : dynamicInstruction.getStaticInstruction().getNumFreePhysicalRegistersToAllocate().entrySet()) {
if (this.getPhysicalRegisterFile(entry.getKey()).getNumFreePhysicalRegisters() < entry.getValue()) {
return false;
}
}
if ((dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.LOAD || dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE) && this.getLoadStoreQueue().isFull()) {
this.numRegisterRenameStallsOnLoadStoreQueueFull++;
return false;
}
ReorderBufferEntry reorderBufferEntry = new ReorderBufferEntry(this, dynamicInstruction, decodeBufferEntry.getNpc(), decodeBufferEntry.getNnpc(), decodeBufferEntry.getPredictedNnpc(), decodeBufferEntry.getReturnAddressStackRecoverIndex(), decodeBufferEntry.getBranchPredictorUpdate(), decodeBufferEntry.isSpeculative());
reorderBufferEntry.setEffectiveAddressComputation(dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.LOAD || dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE);
for (int inputDependency : reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getInputDependencies()) {
reorderBufferEntry.getSourcePhysicalRegisters().put(inputDependency, this.renameTable.get(inputDependency));
}
reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getOutputDependencies().stream().filter(outputDependency -> outputDependency != 0).forEach(outputDependency -> {
reorderBufferEntry.getOldPhysicalRegisters().put(outputDependency, this.renameTable.get(outputDependency));
PhysicalRegister physReg = this.getPhysicalRegisterFile(RegisterDependencyType.getType(outputDependency)).allocate(outputDependency);
this.renameTable.put(outputDependency, physReg);
reorderBufferEntry.getTargetPhysicalRegisters().put(outputDependency, physReg);
});
reorderBufferEntry.getSourcePhysicalRegisters().values().stream().filter(physicalRegister -> !physicalRegister.isReady()).forEach(physicalRegister -> {
reorderBufferEntry.setNumNotReadyOperands(reorderBufferEntry.getNumNotReadyOperands() + 1);
physicalRegister.getDependents().add(reorderBufferEntry);
});
if (reorderBufferEntry.isEffectiveAddressComputation()) {
PhysicalRegister physicalRegister = reorderBufferEntry.getSourcePhysicalRegisters().get(reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getInputDependencies().get(0));
if (!physicalRegister.isReady()) {
physicalRegister.getEffectiveAddressComputationOperandDependents().add(reorderBufferEntry);
} else {
reorderBufferEntry.setEffectiveAddressComputationOperandReady(true);
}
}
if (dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.LOAD || dynamicInstruction.getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE) {
LoadStoreQueueEntry loadStoreQueueEntry = new LoadStoreQueueEntry(this, dynamicInstruction, decodeBufferEntry.getNpc(), decodeBufferEntry.getNnpc(), decodeBufferEntry.getPredictedNnpc(), 0, null, false);
loadStoreQueueEntry.setEffectiveAddress(dynamicInstruction.getEffectiveAddress());
loadStoreQueueEntry.setSourcePhysicalRegisters(reorderBufferEntry.getSourcePhysicalRegisters());
loadStoreQueueEntry.setTargetPhysicalRegisters(reorderBufferEntry.getTargetPhysicalRegisters());
loadStoreQueueEntry.getSourcePhysicalRegisters().values().stream().filter(physicalRegister -> !physicalRegister.isReady()).forEach(physicalRegister -> {
physicalRegister.getDependents().add(loadStoreQueueEntry);
});
loadStoreQueueEntry.setNumNotReadyOperands(reorderBufferEntry.getNumNotReadyOperands());
PhysicalRegister storeAddressPhysicalRegister = loadStoreQueueEntry.getSourcePhysicalRegisters().get(loadStoreQueueEntry.getDynamicInstruction().getStaticInstruction().getInputDependencies().get(0));
if (!storeAddressPhysicalRegister.isReady()) {
storeAddressPhysicalRegister.getStoreAddressDependents().add(loadStoreQueueEntry);
} else {
loadStoreQueueEntry.setStoreAddressReady(true);
}
this.loadStoreQueue.getEntries().add(loadStoreQueueEntry);
reorderBufferEntry.setLoadStoreQueueEntry(loadStoreQueueEntry);
}
this.reorderBuffer.getEntries().add(reorderBufferEntry);
this.decodeBuffer.getEntries().remove(0);
return true;
}
@Override
public boolean dispatchOne() {
for (ReorderBufferEntry reorderBufferEntry : this.reorderBuffer.getEntries()) {
if (!reorderBufferEntry.isDispatched()) {
if (reorderBufferEntry.isAllOperandReady()) {//TODO: is it correct or efficient?
this.core.getReadyInstructionQueue().add(reorderBufferEntry);
} else {
this.core.getWaitingInstructionQueue().add(reorderBufferEntry);
}
reorderBufferEntry.setDispatched();
if (reorderBufferEntry.getLoadStoreQueueEntry() != null) {
LoadStoreQueueEntry loadStoreQueueEntry = reorderBufferEntry.getLoadStoreQueueEntry();
if (loadStoreQueueEntry.getDynamicInstruction().getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE) {
if (loadStoreQueueEntry.isAllOperandReady()) {//TODO: is it correct or efficient?
this.core.getReadyStoreQueue().add(loadStoreQueueEntry);
} else {
this.core.getWaitingStoreQueue().add(loadStoreQueueEntry);
}
}
loadStoreQueueEntry.setDispatched();
}
return true;
}
}
return false;
}
@Override
public void refreshLoadStoreQueue() { //TODO: to be clarified
List<Integer> stdUnknowns = new ArrayList<>();
for (LoadStoreQueueEntry loadStoreQueueEntry : this.loadStoreQueue.getEntries()) {
if (loadStoreQueueEntry.getDynamicInstruction().getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE) {
if (loadStoreQueueEntry.isStoreAddressReady()) {
break;
} else if (!loadStoreQueueEntry.isAllOperandReady()) {
stdUnknowns.add(loadStoreQueueEntry.getEffectiveAddress());
} else {
for (int i = 0; i < stdUnknowns.size(); i++) {
if (stdUnknowns.get(i) == loadStoreQueueEntry.getEffectiveAddress()) {
stdUnknowns.set(i, 0);
}
}
}
}
if (loadStoreQueueEntry.getDynamicInstruction().getStaticInstruction().getMnemonic().getType() == StaticInstructionType.LOAD && loadStoreQueueEntry.isDispatched() && !this.core.getReadyLoadQueue().contains(loadStoreQueueEntry) && !loadStoreQueueEntry.isIssued() && !loadStoreQueueEntry.isCompleted() && loadStoreQueueEntry.isAllOperandReady()) {
if (!stdUnknowns.contains(loadStoreQueueEntry.getEffectiveAddress())) {
this.core.getReadyLoadQueue().add(loadStoreQueueEntry);
}
}
}
}
@Override
public void commit() {
int COMMIT_TIMEOUT = 1000000;
if (this.getCycleAccurateEventQueue().getCurrentCycle() - this.lastCommitCycle > COMMIT_TIMEOUT) {
if (noDynamicInstructionCommittedCounterThreshold > 5) {
getSimulation().dumpPendingFlowTree();
// Logger.fatalf(Logger.THREAD, "%s: No instruction committed for %d cycles, %d committed.", this.getCycleAccurateEventQueue().getCurrentCycle(), this.getName(), COMMIT_TIMEOUT, this.numInstructions);
} else {
this.lastCommitCycle = this.getCycleAccurateEventQueue().getCurrentCycle();
this.noDynamicInstructionCommittedCounterThreshold++;
}
}
int numCommitted = 0;
while (!this.reorderBuffer.isEmpty() && numCommitted < getExperiment().getConfig().getCommitWidth()) {
ReorderBufferEntry reorderBufferEntry = this.reorderBuffer.getEntries().get(0);
if (!reorderBufferEntry.isCompleted()) {
reorderBufferEntry.getDynamicInstruction().setNumCyclesSpentAtHeadOfReorderBuffer(reorderBufferEntry.getDynamicInstruction().getNumCyclesSpentAtHeadOfReorderBuffer() + 1);
break;
}
if (reorderBufferEntry.isSpeculative()) {
if (this.branchPredictor.isDynamic()) {
((DynamicBranchPredictor) this.branchPredictor).getReturnAddressStack().recover(reorderBufferEntry.getReturnAddressStackRecoverIndex());
}
this.context.exitSpeculativeState();
this.fetchNpc = this.context.getRegisterFile().getNpc();
this.fetchNnpc = this.context.getRegisterFile().getNnpc();
this.squash();
break;
}
if (reorderBufferEntry.isEffectiveAddressComputation()) {
LoadStoreQueueEntry loadStoreQueueEntry = reorderBufferEntry.getLoadStoreQueueEntry();
if (!loadStoreQueueEntry.isCompleted()) {
break;
}
this.core.removeFromQueues(loadStoreQueueEntry);
this.loadStoreQueue.getEntries().remove(loadStoreQueueEntry);
}
reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getOutputDependencies().stream().filter(outputDependency -> outputDependency != 0).forEach(outputDependency -> {
reorderBufferEntry.getOldPhysicalRegisters().get(outputDependency).reclaim();
reorderBufferEntry.getTargetPhysicalRegisters().get(outputDependency).commit();
});
if (reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getMnemonic().isControl()) {
this.branchPredictor.update(
reorderBufferEntry.getDynamicInstruction().getPc(),
reorderBufferEntry.getNnpc(),
reorderBufferEntry.getNnpc() != (reorderBufferEntry.getNpc() + 4),
reorderBufferEntry.getPredictedNnpc() != (reorderBufferEntry.getNpc() + 4),
reorderBufferEntry.getPredictedNnpc() == reorderBufferEntry.getNnpc(),
reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getMnemonic(),
reorderBufferEntry.getBranchPredictorUpdate()
);
}
this.core.removeFromQueues(reorderBufferEntry);
this.getBlockingEventDispatcher().dispatch(new DynamicInstructionCommittedEvent(reorderBufferEntry.getDynamicInstruction()));
if (this.context.getState() == ContextState.FINISHED && reorderBufferEntry.getDynamicInstruction() == this.lastDecodedDynamicInstruction) {
this.lastDecodedDynamicInstructionCommitted = true;
}
this.reorderBuffer.getEntries().remove(0);
this.numInstructions++;
this.lastCommitCycle = this.getCycleAccurateEventQueue().getCurrentCycle();
numCommitted++;
// Logger.infof(Logger.DEBUG, "%s: instruction committed: reorderBufferEntry.id=%d", this.getName(), reorderBufferEntry.getId());
}
}
@Override
public void squash() {
// Logger.infof(Logger.THREAD, "%s: squash", this.getName());
while (!this.reorderBuffer.isEmpty()) {
ReorderBufferEntry reorderBufferEntry = this.reorderBuffer.getEntries().get(this.reorderBuffer.getEntries().size() - 1);
if (reorderBufferEntry.isEffectiveAddressComputation()) {
LoadStoreQueueEntry loadStoreQueueEntry = reorderBufferEntry.getLoadStoreQueueEntry();
this.core.removeFromQueues(loadStoreQueueEntry);
this.loadStoreQueue.getEntries().remove(loadStoreQueueEntry);
}
this.core.removeFromQueues(reorderBufferEntry);
reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getOutputDependencies().stream().filter(outputDependency -> outputDependency != 0).forEach(outputDependency -> {
reorderBufferEntry.getTargetPhysicalRegisters().get(outputDependency).recover();
this.renameTable.put(outputDependency, reorderBufferEntry.getOldPhysicalRegisters().get(outputDependency));
});
reorderBufferEntry.getTargetPhysicalRegisters().clear();
this.reorderBuffer.getEntries().remove(reorderBufferEntry);
}
if (!this.reorderBuffer.getEntries().isEmpty() || !this.loadStoreQueue.getEntries().isEmpty()) {
throw new IllegalArgumentException();
}
this.core.getFunctionalUnitPool().releaseAll(); //TODO: is it correct or just release those FUs that this thread uses?
this.decodeBuffer.getEntries().clear();
}
@Override
public boolean isLastDecodedDynamicInstructionCommitted() {
return this.lastDecodedDynamicInstruction == null || lastDecodedDynamicInstructionCommitted;
}
/**
* Get the physical register file based on the specified register dependency type.
*
* @param type the register dependency type
* @return the physical register file based on the specified register dependency type
*/
private PhysicalRegisterFile getPhysicalRegisterFile(RegisterDependencyType type) {
switch (type) {
case INTEGER:
return this.intPhysicalRegisterFile;
case FLOAT:
return this.fpPhysicalRegisterFile;
default:
return this.miscPhysicalRegisterFile;
}
}
/**
* Get the aligned value for the specified number and alignment.
*
* @param n the number
* @param alignment the alignment
* @return the aligned value for the specified number and alignment
*/
private static int aligned(int n, int alignment) {
return n & ~(alignment - 1);
}
}