MonkeyHeartImpl.extras
Last Edited by: Ross February 3, 1987 3:48:57 pm PST
Last Edited by: Ross February 3, 1987 10:18:02 am PST
InstructionExecute: PUBLIC PROC [processor: Processor, mi: MicroInstruction] = {
p: Processor = processor; -- for a short name
SELECT control FROM
nextInst => {};
Continue with the new PC. This will be the case even if we are branching. A mispredicted jump will have cleared the instruction buffer in MonkeyLiver.Execute.
doSwitch =>
Continue with the new PC, but also flush the instruction buffer. This is the case for mispredicted jumps, or for stack jumps.
ForceBufferEmpty[];
doCall, doAbort, doReturn => {
lastCallDelta: INT ← p.stats.instructions - p.lastCallRtn;
ForceBufferEmpty[];
IF control = doReturn
THEN {
currentSize: NAT ← p.stackEntries;
stackedStatus: TamarinOps.StackedStatusWord ← p.ifuStack[youngest].status;
SELECT currentSize FROM
= 0 => {
SIGNAL OutsideEnvelope["IFU control stack empty on return!"];
thisPC ← TrapPC[StackUnderflowTrap];
GO TO badGnus;
};
> IFUStackSize => {
SIGNAL OutsideEnvelope["IFU control stack is too full during return!"];
GO TO badGnus;
};
> IFUOverflow =>
IF stackedStatus.trapsEnabled THEN {
SIGNAL OutsideEnvelope["IFU control stack is too full during return!"];
GO TO badGnus;
};
ENDCASE;
p.trapsEnabled ← stackedStatus.trapsEnabled;
p.userMode ← stackedStatus.userMode;
p.stackEntries ← currentSize - 1;
thisPC ← p.ifuStack[youngest].pc;
p.regs[ifuL] ← regL ← RegToWord[VAL[stackedStatus.lBase]];
p.youngest ← youngest ←
(youngest + (MonkeyIFUStackSize - 1)) MOD MonkeyIFUStackSize;
EXITS badGnus => {};
}
ELSE {
For doCall & doAbort, just push the PC, the overflow checking was done by the instruction execution.
newSize: NAT ← p.stackEntries+1;
stackedStatus: TamarinOps.StackedStatusWord ← [
version: CurrentProcVersion,
userMode: p.userMode,
trapsEnabled: p.trapsEnabled,
lBase: ORD[WordToReg[regL]]
];
IF newSize > IFUStackSize
THEN SIGNAL OutsideEnvelope["IFU control stack too full!"]
ELSE {
p.stackEntries ← newSize;
p.youngest ← youngest ← (youngest + 1) MOD MonkeyIFUStackSize;
};
p.ifuStack[youngest] ← [rtnPC, stackedStatus];
IF control = doAbort THEN
We force kernel with traps disabled.
p.userMode ← p.trapsEnabled ← FALSE;
IF tx # NoFault THEN
We get here due to an early fault (like an IFUPageFault). The idea is to log the completion of the instruction, although no instruction was really started! This aids "shadow" simulations like McCreight's.
IF p.logger # NIL AND p.logger.instDone # NIL THEN
p.logger.instDone[p.logger.data, p, newPC, rtnPC, doAbort, cycles];
};
SELECT lastCallDelta FROM
>= 3, < 0 => {};
ENDCASE => {
We must delay to allow changes in the IFU stack from the last call/return to become stable. This test is somewhat conservative, since multi-cycle instructions will allow the ifuStack to be stable sooner than this. However, this is a cheap way to estimate this extra delay.
cycles ← cycles + (3-lastCallDelta);
p.stats.returnInterlockCycles ← p.stats.returnInterlockCycles + lastCallDelta;
};
p.lastCallRtn ← p.stats.instructions;
};
ENDCASE => ERROR;
p.regs[ifuPC] ← thisPC;
p.stats.cycles ← p.stats.cycles+cycles;
p.stats.instBytes ← p.stats.instBytes+nBytes;
};
MemoryCycle: PROC [processor: Processor] ~ {
Try to fetch ahead if the bus is not busy, and we need more bytes in the instruction buffer. Note that this may cause more contention for the bus than we might like, but that the bytes should be ready at the proper time in straight-line code.
IF instBuffer.busyUntil <= initCycles+cycles THEN {
The instruction buffer is not trying to fetch anything from the cache, so we can use the cache to try to get the next instruction.
IF valid <= max-bytesPerWord THEN {
There is room to get more words into the buffer
fetchPC: Word ← IntToWord[WordToInt[newPC] + valid];
IF instBuffer.validWords = instBuffer.max THEN FlushInstWord[];
We have completely filled the inst buffer, but still need more, so shift out the lowest address word, and update the base address and # of bytes valid.
[wordAddr, rbi] ← BytePCToWordAddress[[fetchPC]];
IF rbi # 0 THEN ERROR; -- rats! we blew it!
[word, tx, rCycles] ←
p.ifuCache.fetch[p.ifuCache, wordAddr, initCycles+cycles, p.userMode];
p.stats.lookaheadProbes ← p.stats.lookaheadProbes + 1;
IF rCycles # 0 THEN {
The reject cycles occur in the background, and are not counted towards elapsed cycles. But we do keep track of the next time that the ifu cache will be available.
instBuffer.busyUntil ← p.ifuCache.sharedBase.busyUntil;
p.stats.lookaheadRejects ← p.stats.lookaheadRejects + rCycles;
};
IF tx = NoFault THEN {
The fetch was successful, so we can put more bytes into the inst buffer. Note that we restrict the max bytes in the buffer to be a multiple of bytesPerWord. There will be no wraparound in the buffer for this word fetch.
instBuffer[instBuffer.validWords] ← word;
instBuffer.validWords ← instBuffer.validWords + 1;
};
tx ← NoFault;
Make sure that the readahead fault does not show up later.
};
};
};