Files
Toooba/src_Core/CPU/Core.bsv
James Clarke 60c1e31b01 Reduce needless fetch stalls in presence of compressed instructions
Currently, our next address prediction cannot distinguish between a
taken compressed branch to PC+4 and an uncompressed instruction that
falls through. We can instead make the NAP machinery much more robust by
keying it on the 16b parcels, with uncompressed branches having their
taken prediction on the second 16b parcel. This also removes the need
for the address prediction requests to be chained.

Moreover, if we decode more than 2 instructions in one cycle due to
decompression, we throw away any subsequent instructions and treat it
like a branch miss, redirecting and thus restarting the pipeline from
the first discarded PC. We should therefore instead save them for
issuing on the next cycle and avoid the redirects. To ensure we don't
needlessly reduce our IPC, if we have a partial issue's width of
instructions saved, we should also support issuing instructions from the
next ICache response if valid, which should be the case in hot
correctly-predicted code paths, especially tight loops. As part of this
change, we also keep the pending straddle state in Fetch3 rather than
sending it to Decode only to have it be forwarded back.

Combined, these two approaches ensure the fetch unit can maintain an IPC
of 2 after it has had time to be correctly trained, regardless of the
distribution of compressed instructions.
2020-01-01 22:22:17 +00:00

985 lines
36 KiB
Plaintext

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) Bluespec, Inc.
`include "ProcConfig.bsv"
import Vector::*;
import BuildVector::*;
import DefaultValue::*;
import ClientServer::*;
import GetPut::*;
import Assert::*;
import Cntrs::*;
import ConfigReg::*;
import FIFO::*;
import Fifo::*;
import Ehr::*;
import Connectable::*;
import Types::*;
import ProcTypes::*;
import CacheUtils::*;
import TlbTypes::*;
import SynthParam::*;
import VerificationPacket::*;
import Performance::*;
import HasSpecBits::*;
import Exec::*;
import FetchStage::*;
import ITlb::*;
import DTlb::*;
import L2Tlb::*;
import TlbConnect::*;
import EpochManager::*;
import PhysRFile::*;
import RFileSynth::*;
import RenamingTable::*;
import ReorderBuffer::*;
import ReorderBufferSynth::*;
import Scoreboard::*;
import ScoreboardSynth::*;
import SpecTagManager::*;
import Fpu::*;
import MulDiv::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReservationStationMem::*;
import ReservationStationFpuMulDiv::*;
import AluExePipeline::*;
import FpuMulDivExePipeline::*;
import MemExePipeline::*;
import SplitLSQ::*;
import StoreBuffer::*;
import GlobalSpecUpdate::*;
import CCTypes::*;
import L1CoCache::*;
import L1Bank::*;
import IBank::*;
import MMIOCore::*;
import RenameStage::*;
import CommitStage::*;
import Bypass::*;
import CsrFile :: *;
interface CoreReq;
method Action start(
Addr startpc,
Addr toHostAddr, Addr fromHostAddr
);
method Action perfReq(PerfLocation loc, PerfType t);
endinterface
interface CoreIndInv;
method ActionValue#(ProcPerfResp) perfResp;
method ActionValue#(void) terminate;
endinterface
interface CoreDeadlock;
interface Get#(L1DCRqStuck) dCacheCRqStuck;
interface Get#(L1DPRqStuck) dCachePRqStuck;
interface Get#(L1ICRqStuck) iCacheCRqStuck;
interface Get#(L1IPRqStuck) iCachePRqStuck;
interface Get#(RenameStuck) renameInstStuck;
interface Get#(RenameStuck) renameCorrectPathStuck;
interface Get#(CommitStuck) commitInstStuck;
interface Get#(CommitStuck) commitUserInstStuck;
interface Get#(void) checkStarted;
endinterface
interface CoreRenameDebug;
interface Get#(RenameErrInfo) renameErr;
endinterface
interface Core;
// core request & indication
interface CoreReq coreReq;
interface CoreIndInv coreIndInv;
// coherent caches to LLC
interface ChildCacheToParent#(L1Way, void) dCacheToParent;
interface ChildCacheToParent#(L1Way, void) iCacheToParent;
// DMA to LLC
interface TlbMemClient tlbToMem;
// MMIO
interface MMIOCoreToPlatform mmioToPlatform;
// stats enable
method ActionValue#(Bool) sendDoStats;
method Action recvDoStats(Bool x);
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface CoreDeadlock deadlock;
// debug rename
interface CoreRenameDebug renameDebug;
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
method Action setMEIP (Bit #(1) v);
method Action setSEIP (Bit #(1) v);
// Bluespec: external interrupt to enter debug mode
method Action setDEIP (Bit #(1) v);
endinterface
// fixpoint to instantiate modules
interface CoreFixPoint;
interface Vector#(AluExeNum, AluExePipeline) aluExeIfc;
interface Vector#(FpuMulDivExeNum, FpuMulDivExePipeline) fpuMulDivExeIfc;
interface MemExePipeline memExeIfc;
method Action killAll; // kill everything: used by commit stage
interface Reg#(Bool) doStatsIfc;
endinterface
(* synthesize *)
module mkCore#(CoreId coreId)(Core);
let verbose = False;
Reg#(Bool) outOfReset <- mkReg(False);
rule rl_outOfReset if (!outOfReset);
$fwrite(stderr, "mkProc came out of reset\n");
outOfReset <= True;
endrule
Reg#(Bool) started <- mkReg(False);
// front end
FetchStage fetchStage <- mkFetchStage;
ITlb iTlb = fetchStage.iTlbIfc;
ICoCache iMem = fetchStage.iMemIfc;
// back end
RFileSynth rf <- mkRFileSynth;
// Bluespec: CsrFile including external interrupt request methods
CsrFile csrf <- mkCsrFile(zeroExtend(coreId)); // hartid in CSRF should be core id
RegRenamingTable regRenamingTable <- mkRegRenamingTable;
EpochManager epochManager <- mkEpochManager;
SpecTagManager specTagManager <- mkSpecTagManager;
ReorderBufferSynth rob <- mkReorderBufferSynth;
// We have two scoreboards: one conservative and other aggressive
// - Aggressive sb is checked at rename stage, so inst after rename may be issued early
// - Conservative sb is checked at reg read stage, to ensure correctness
// Every pipeline should set both sb if it needs to write reg
// - Conservative sb is set when data is written into rf
// - Aggressive sb is set when pipeline sends out wakeup for reservation staion
// Note that wakeup can be sent early if it knows when the data will be produced
ScoreboardCons sbCons <- mkScoreboardCons; // conservative sb
ScoreboardAggr sbAggr <- mkScoreboardAggr; // aggressive sb
// MMIO: need to track in flight CSR inst or interrupt; note we can at most
// 1 CSR inst or 1 interrupt in ROB, so just use 1 bit track it. Commit
// stage use port 0 to reset this, and Rename stage use port 1 to set this.
Ehr#(2, Bool) csrInstOrInterruptInflight <- mkEhr(False);
Reg#(Bool) csrInstOrInterruptInflight_commit = csrInstOrInterruptInflight[0];
Reg#(Bool) csrInstOrInterruptInflight_rename = csrInstOrInterruptInflight[1];
MMIOCoreInput mmioInIfc = (interface MMIOCoreInput;
interface fetch = fetchStage.mmioIfc;
method getMSIP = csrf.getMSIP;
method setMSIP = csrf.setMSIP;
method setMTIP = csrf.setMTIP;
method noInflightCSRInstOrInterrupt = !csrInstOrInterruptInflight[0];
method setTime = csrf.setTime;
endinterface);
MMIOCore mmio <- mkMMIOCore(mmioInIfc);
// fix point module to instantiate other function units
module mkCoreFixPoint#(CoreFixPoint fix)(CoreFixPoint);
// spec update
Vector#(AluExeNum, SpeculationUpdate) aluSpecUpdate;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
aluSpecUpdate[i] = fix.aluExeIfc[i].specUpdate;
end
Vector#(FpuMulDivExeNum, SpeculationUpdate) fpuMulDivSpecUpdate;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
fpuMulDivSpecUpdate[i] = fix.fpuMulDivExeIfc[i].specUpdate;
end
GlobalSpecUpdate#(CorrectSpecPortNum, ConflictWrongSpecPortNum) globalSpecUpdate <- mkGlobalSpecUpdate(
joinSpeculationUpdate(
append(append(vec(regRenamingTable.specUpdate,
specTagManager.specUpdate,
fix.memExeIfc.specUpdate), aluSpecUpdate), fpuMulDivSpecUpdate)
),
rob.specUpdate
);
// whether perf data is collected
Reg#(Bool) doStatsReg <- mkConfigReg(False);
// redirect func
//function Action redirectFunc(Addr trap_pc, Maybe#(SpecTag) spec_tag, InstTag inst_tag );
//action
// if (verbose) $fdisplay(stdout, "[redirect_action] new pc = 0x%8x, spec_tag = ", trap_pc, fshow(spec_tag));
// epochManager.redirect;
// fetchStage.redirect(trap_pc);
// if (spec_tag matches tagged Valid .valid_spec_tag) begin
// globalSpecUpdate.incorrectSpec(valid_spec_tag, inst_tag);
// end
//endaction
//endfunction
// write aggressive elements + wakupe reservation stations
function Action writeAggr(Integer wrAggrPort, PhyRIndx dst);
action
sbAggr.setReady[wrAggrPort].put(dst);
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
fix.aluExeIfc[i].rsAluIfc.setRegReady[wrAggrPort].put(Valid (dst));
end
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
fix.fpuMulDivExeIfc[i].rsFpuMulDivIfc.setRegReady[wrAggrPort].put(Valid (dst));
end
fix.memExeIfc.rsMemIfc.setRegReady[wrAggrPort].put(Valid (dst));
endaction
endfunction
// write conservative elements
function Action writeCons(Integer wrConsPort, PhyRIndx dst, Data data);
action
rf.write[wrConsPort].wr(dst, data);
sbCons.setReady[wrConsPort].put(dst);
endaction
endfunction
Vector#(AluExeNum, FIFO#(FetchTrainBP)) trainBPQ <- replicateM(mkFIFO);
Vector#(AluExeNum, AluExePipeline) aluExe;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
Vector#(2, SendBypass) sendBypassIfc; // exe and finish
for(Integer sendPort = 0; sendPort < 2; sendPort = sendPort + 1) begin
sendBypassIfc[sendPort] = (interface SendBypass;
method Action send(PhyRIndx dst, Data data);
// broadcast bypass
Integer recvPort = valueof(AluExeNum) * sendPort + i;
for(Integer j = 0; j < valueof(FpuMulDivExeNum); j = j+1) begin
fix.fpuMulDivExeIfc[j].recvBypass[recvPort].recv(dst, data);
end
fix.memExeIfc.recvBypass[recvPort].recv(dst, data);
for(Integer j = 0; j < valueof(AluExeNum); j = j+1) begin
fix.aluExeIfc[j].recvBypass[recvPort].recv(dst, data);
end
endmethod
endinterface);
end
let aluExeInput = (interface AluExeInput;
method sbCons_lazyLookup = sbCons.lazyLookup[aluRdPort(i)].get;
method rf_rd1 = rf.read[aluRdPort(i)].rd1;
method rf_rd2 = rf.read[aluRdPort(i)].rd2;
method csrf_rd = csrf.rd;
method rob_getPC = rob.getOrigPC[i].get;
method rob_getPredPC = rob.getOrigPredPC[i].get;
method rob_getOrig_Inst = rob.getOrig_Inst[i].get;
method rob_setExecuted = rob.setExecuted_doFinishAlu[i].set;
method fetch_train_predictors = toPut(trainBPQ[i]).put;
method setRegReadyAggr = writeAggr(aluWrAggrPort(i));
interface sendBypass = sendBypassIfc;
method writeRegFile = writeCons(aluWrConsPort(i));
method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
if (verbose) begin
$display("[ALU redirect - %d] ", i, fshow(new_pc),
"; ", fshow(spec_tag), "; ", fshow(inst_tag));
end
epochManager.incrementEpoch;
fetchStage.redirect(new_pc);
globalSpecUpdate.incorrectSpec(False, spec_tag, inst_tag);
endmethod
method correctSpec = globalSpecUpdate.correctSpec[finishAluCorrectSpecPort(i)].put;
method doStats = doStatsReg._read;
endinterface);
aluExe[i] <- mkAluExePipeline(aluExeInput);
// truly call fetch method to train branch predictor
rule doFetchTrainBP;
let train <- toGet(trainBPQ[i]).get;
fetchStage.train_predictors(
train.pc, train.nextPc, train.iType, train.taken,
train.dpTrain, train.mispred, train.isCompressed
);
endrule
end
Vector#(FpuMulDivExeNum, FpuMulDivExePipeline) fpuMulDivExe;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
let fpuMulDivExeInput = (interface FpuMulDivExeInput;
method sbCons_lazyLookup = sbCons.lazyLookup[fpuMulDivRdPort(i)].get;
method rf_rd1 = rf.read[fpuMulDivRdPort(i)].rd1;
method rf_rd2 = rf.read[fpuMulDivRdPort(i)].rd2;
method rf_rd3 = rf.read[fpuMulDivRdPort(i)].rd3;
method csrf_rd = csrf.rd;
method rob_setExecuted = rob.setExecuted_doFinishFpuMulDiv[i].set;
method Action writeRegFile(PhyRIndx dst, Data data);
writeAggr(fpuMulDivWrAggrPort(i), dst);
writeCons(fpuMulDivWrConsPort(i), dst, data);
endmethod
method conflictWrongSpec = globalSpecUpdate.conflictWrongSpec[finishFpuMulDivConflictWrongSpecPort(i)].put(?);
method doStats = doStatsReg._read;
endinterface);
fpuMulDivExe[i] <- mkFpuMulDivExePipeline(fpuMulDivExeInput);
end
let memExeInput = (interface MemExeInput;
method sbCons_lazyLookup = sbCons.lazyLookup[memRdPort].get;
method rf_rd1 = rf.read[memRdPort].rd1;
method rf_rd2 = rf.read[memRdPort].rd2;
method csrf_rd = csrf.rd;
method rob_getPC = rob.getOrigPC[valueof(AluExeNum)].get; // last getPC port
method rob_setExecuted_doFinishMem = rob.setExecuted_doFinishMem;
method rob_setExecuted_deqLSQ = rob.setExecuted_deqLSQ;
method isMMIOAddr = mmio.isMMIOAddr;
method mmioReq = mmio.dataReq;
method mmioRespVal = mmio.dataRespVal;
method mmioRespDeq = mmio.dataRespDeq;
method setRegReadyAggr_mem = writeAggr(memWrAggrPort);
method setRegReadyAggr_forward = writeAggr(forwardWrAggrPort);
method writeRegFile = writeCons(memWrConsPort);
method doStats = doStatsReg._read;
endinterface);
let memExe <- mkMemExePipeline(memExeInput);
interface aluExeIfc = aluExe;
interface fpuMulDivExeIfc = fpuMulDivExe;
interface memExeIfc = memExe;
method Action killAll;
globalSpecUpdate.incorrectSpec(True, ?, ?);
endmethod
interface doStatsIfc = doStatsReg;
endmodule
CoreFixPoint coreFix <- moduleFix(mkCoreFixPoint);
Vector#(AluExeNum, ReservationStationAlu) reservationStationAlu;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
reservationStationAlu[i] = coreFix.aluExeIfc[i].rsAluIfc;
end
Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) reservationStationFpuMulDiv;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
reservationStationFpuMulDiv[i] = coreFix.fpuMulDivExeIfc[i].rsFpuMulDivIfc;
end
ReservationStationMem reservationStationMem = coreFix.memExeIfc.rsMemIfc;
DTlbSynth dTlb = coreFix.memExeIfc.dTlbIfc;
SplitLSQ lsq = coreFix.memExeIfc.lsqIfc;
StoreBuffer stb = coreFix.memExeIfc.stbIfc;
DCoCache dMem = coreFix.memExeIfc.dMemIfc;
// L2 TLB
L2Tlb l2Tlb <- mkL2Tlb;
mkTlbConnect(iTlb.toParent, dTlb.toParent, l2Tlb.toChildren);
// flags to flush
Reg#(Bool) flush_tlbs <- mkReg(False);
Reg#(Bool) update_vm_info <- mkReg(False);
Reg#(Bool) flush_reservation <- mkReg(False);
`ifdef SECURITY
Reg#(Bool) flush_caches <- mkReg(False);
Reg#(Bool) flush_brpred <- mkReg(False);
`else
Reg#(Bool) flush_caches <- mkReadOnlyReg(False);
Reg#(Bool) flush_brpred <- mkReadOnlyReg(False);
`endif
`ifdef SELF_INV_CACHE
Reg#(Bool) reconcile_i <- mkReg(False);
`else
Reg#(Bool) reconcile_i <- mkReadOnlyReg(False);
`endif
`ifdef SELF_INV_CACHE
`ifdef SYSTEM_SELF_INV_L1D
Reg#(Bool) reconcile_d <- mkReg(False);
`else // !SYSTEM_SELF_INV_L1D
Reg#(Bool) reconcile_d <- mkReadOnlyReg(False);
`endif // SYSTEM_SELF_INV_L1D
`else // !SELF_INV_CACHE
Reg#(Bool) reconcile_d <- mkReadOnlyReg(False);
`endif // SELF_INV_CACHE
// performance counters
Reg#(Bool) doStats = coreFix.doStatsIfc; // whether data is collected
`ifdef PERF_COUNT
// OOO execute stag (in AluExePipeline and MemExePipeline)
// commit stage (many in CommitStage.bsv)
// cycle
Count#(Data) cycleCnt <- mkCount(0);
// buffer/tags size
Count#(Data) ldqFullCycles <- mkCount(0);
Count#(Data) stqFullCycles <- mkCount(0);
Count#(Data) robFullCycles <- mkCount(0);
Count#(Data) aluRS0FullCycles <- mkCount(0);
Count#(Data) aluRS1FullCycles <- mkCount(0);
Count#(Data) fpuMulDivRSFullCycles <- mkCount(0);
Count#(Data) memRSFullCycles <- mkCount(0);
Count#(Data) epochFullCycles <- mkCount(0);
Count#(Data) specTagFullCycles <- mkCount(0);
// FIFOs to connect performance counters
FIFO#(ExeStagePerfType) exePerfReqQ <- mkFIFO1;
FIFO#(ComStagePerfType) comPerfReqQ <- mkFIFO1;
FIFO#(CoreSizePerfType) sizePerfReqQ <- mkFIFO1;
Fifo#(1, PerfResp#(ExeStagePerfType)) exePerfRespQ <- mkCFFifo;
Fifo#(1, PerfResp#(ComStagePerfType)) comPerfRespQ <- mkCFFifo;
Fifo#(1, PerfResp#(CoreSizePerfType)) sizePerfRespQ <- mkCFFifo;
// FIFO of perf resp
FIFO#(ProcPerfResp) perfRespQ <- mkFIFO1;
`endif
// FIFO of perf req
FIFO#(ProcPerfReq) perfReqQ <- mkFIFO1;
// -- End of performance counters
`ifdef CHECK_DEADLOCK
// when to start deadlock checking
Reg#(Bool) startDeadlockCheck <- mkReg(False);
FIFO#(void) deadlockCheckStartedQ <- mkFIFO;
rule doStartDeadlockCheck(!startDeadlockCheck && started);
startDeadlockCheck <= True;
deadlockCheckStartedQ.enq(?);
endrule
`endif
// Rename stage
let renameInput = (interface RenameInput;
interface fetchIfc = fetchStage;
interface robIfc = rob;
interface rtIfc = regRenamingTable;
interface sbConsIfc = sbCons;
interface sbAggrIfc = sbAggr;
interface csrfIfc = csrf;
interface emIfc = epochManager;
interface smIfc = specTagManager;
interface rsAluIfc = reservationStationAlu;
interface rsFpuMulDivIfc = reservationStationFpuMulDiv;
interface rsMemIfc = reservationStationMem;
interface lsqIfc = lsq;
method pendingMMIOPRq = mmio.hasPendingPRq;
method issueCsrInstOrInterrupt = csrInstOrInterruptInflight_rename._write(True);
method Bool checkDeadlock;
`ifdef CHECK_DEADLOCK
return startDeadlockCheck;
`else
return False;
`endif
endmethod
method doStats = coreFix.doStatsIfc._read;
endinterface);
RenameStage renameStage <- mkRenameStage(renameInput);
// commit stage
let commitInput = (interface CommitInput;
interface robIfc = rob;
interface rtIfc = regRenamingTable;
interface csrfIfc = csrf;
method stbEmpty = stb.isEmpty;
method stqEmpty = lsq.stqEmpty;
method lsqSetAtCommit = lsq.setAtCommit;
method tlbNoPendingReq = iTlb.noPendingReq && dTlb.noPendingReq;
method setFlushTlbs = flush_tlbs._write(True);
method setUpdateVMInfo = update_vm_info._write(True);
method setFlushReservation = flush_reservation._write(True);
method setFlushBrPred = flush_brpred._write(True);
method setFlushCaches = flush_caches._write(True);
method setReconcileI = reconcile_i._write(True);
method setReconcileD = reconcile_d._write(True);
method killAll = coreFix.killAll;
method redirectPc = fetchStage.redirect;
method setFetchWaitRedirect = fetchStage.setWaitRedirect;
method incrementEpoch = epochManager.incrementEpoch;
method commitCsrInstOrInterrupt = csrInstOrInterruptInflight_commit._write(False);
method doStats = coreFix.doStatsIfc._read;
method Bool checkDeadlock;
`ifdef CHECK_DEADLOCK
return startDeadlockCheck;
`else
return False;
`endif
endmethod
endinterface);
CommitStage commitStage <- mkCommitStage(commitInput);
// send rob enq time to reservation stations
(* fire_when_enabled, no_implicit_conditions *)
rule sendRobEnqTime;
InstTime t = rob.getEnqTime;
reservationStationMem.setRobEnqTime(t);
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
reservationStationFpuMulDiv[i].setRobEnqTime(t);
end
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
reservationStationAlu[i].setRobEnqTime(t);
end
endrule
// preempt has 2 functions here
// 1. break scheduling cycles
// 2. XXX since csrf is configReg now, we should not let this rule fire together with doCommit
// because we read csrf here and write csrf in doCommit
// TODO We can use wires to catch flush / updateVM enable sigals, because
// there cannot be any instruction in pipeline (there can be poisoned inst
// which cannot change CSR or link reg in D$), so doCommit cannot fire.
// MMIO manager may change pending interrupt bits, but will not affect VM
// info.
(* preempts = "prepareCachesAndTlbs, commitStage.doCommitTrap_handle" *)
(* preempts = "prepareCachesAndTlbs, commitStage.doCommitSystemInst" *)
rule prepareCachesAndTlbs(flush_reservation || flush_tlbs || update_vm_info);
if (flush_reservation) begin
flush_reservation <= False;
dMem.resetLinkAddr;
end
if (flush_tlbs) begin
flush_tlbs <= False;
iTlb.flush;
dTlb.flush;
end
if (update_vm_info) begin
update_vm_info <= False;
let vmI = csrf.vmI;
let vmD = csrf.vmD;
iTlb.updateVMInfo(vmI);
dTlb.updateVMInfo(vmD);
l2Tlb.updateVMInfo(vmI, vmD);
end
endrule
`ifdef SECURITY
// Use wires to capture flush regs and empty signals. This is ok because
// there cannot be any activity to make empty -> not-empty or need-flush ->
// no-need-flush when we are trying to flush.
PulseWire doFlushCaches <- mkPulseWire;
PulseWire doFlushBrPred <- mkPulseWire;
rule setDoFlushCaches(flush_caches && fetchStage.emptyForFlush && lsq.noWrongPathLoads);
doFlushCaches.send;
endrule
rule setDoFlushBrPred(flush_brpred && fetchStage.emptyForFlush);
doFlushBrPred.send;
endrule
// security flush cache: need to wait for wrong path loads or inst fetches
// to finish
rule flushCaches(doFlushCaches);
flush_caches <= False;
iMem.flush;
dMem.flush;
endrule
// security flush branch predictors: wait for wrong path inst fetches to
// finish
rule flushBrPred(doFlushBrPred);
flush_brpred <= False;
fetchStage.flush_predictors;
endrule
`endif
`ifdef SELF_INV_CACHE
// Use wires to capture flush regs and empty signals. This is ok because
// there cannot be any activity to make empty -> not-empty or need-flush ->
// no-need-flush when we are trying to flush.
PulseWire doReconcileI <- mkPulseWire;
// We don't really need to wait for fetch to be empty, but just in case we
// back pressure I TLB because I$ is reconciling.
rule setDoReconcileI(reconcile_i && fetchStage.emptyForFlush);
doReconcileI.send;
endrule
rule reconcileI(doReconcileI);
reconcile_i <= False;
iMem.reconcile;
endrule
`ifdef SYSTEM_SELF_INV_L1D
PulseWire doReconcileD <- mkPulseWire;
Reg#(Bool) waitReconcileD <- mkReg(False);
// We don't really need to wait for lsq empty, but just in case
rule setDoReconcileD(reconcile_d && lsq.noWrongPathLoads);
doReconcileD.send;
endrule
rule startReconcileD(doReconcileD && !waitReconcileD);
coreFix.memExeIfc.reconcile.request.put(?);
waitReconcileD <= True;
endrule
rule completeReconcileD(waitReconcileD);
let unused <- coreFix.memExeIfc.reconcile.response.get;
waitReconcileD <= False;
reconcile_d <= False;
endrule
`endif // SYSTEM_SELF_INV_L1D
`endif // SELF_INV_CACHE
rule readyToFetch(
!flush_reservation && !flush_tlbs && !update_vm_info
&& iTlb.flush_done && dTlb.flush_done
`ifdef SECURITY
&& !flush_caches && !flush_brpred
&& iMem.flush_done && dMem.flush_done
&& fetchStage.flush_predictors_done
`endif
`ifdef SELF_INV_CACHE
&& !reconcile_i && iMem.reconcile_done
`ifdef SYSTEM_SELF_INV_L1D
&& !reconcile_d
`endif
`endif
);
fetchStage.done_flushing();
endrule
`ifdef PERF_COUNT
// incr cycle count
(* fire_when_enabled, no_implicit_conditions *)
rule incCycleCnt(doStats);
cycleCnt.incr(1);
endrule
// incr buffer full cycles
(* fire_when_enabled, no_implicit_conditions *)
rule incLdQFull(doStats && lsq.ldqFull_ehrPort0);
ldqFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incStQFull(doStats && lsq.stqFull_ehrPort0);
stqFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incROBFull(doStats && rob.isFull_ehrPort0);
robFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incAluRS0Full(doStats && reservationStationAlu[0].isFull_ehrPort0);
aluRS0FullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incAluRS1Full(doStats && reservationStationAlu[1].isFull_ehrPort0);
aluRS1FullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incFpuMulDivRSFull(doStats && reservationStationFpuMulDiv[0].isFull_ehrPort0);
fpuMulDivRSFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incMemRSFull(doStats && reservationStationMem.isFull_ehrPort0);
memRSFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incEpochFull(doStats && epochManager.isFull_ehrPort0);
epochFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incSpecTagFull(doStats && specTagManager.isFull_ehrPort0);
specTagFullCycles.incr(1);
endrule
// broadcast whether we should collect data
rule broadcastDoStats;
let stats = csrf.doPerfStats;
doStats <= stats;
iMem.perf.setStatus(stats);
dMem.perf.setStatus(stats);
iTlb.perf.setStatus(stats);
dTlb.perf.setStatus(stats);
l2Tlb.perf.setStatus(stats);
fetchStage.perf.setStatus(stats);
if(stats && !doStats) begin
$display("[stats] enabled");
end
else if(!stats && doStats) begin
$display("[stats] disabled");
end
endrule
// dispatch perf req
rule dispathPerfReq;
perfReqQ.deq;
let r = perfReqQ.first;
case(r.loc)
ICache: begin
iMem.perf.req(unpack(truncate(r.pType)));
end
DCache: begin
dMem.perf.req(unpack(truncate(r.pType)));
end
ITlb: begin
iTlb.perf.req(unpack(truncate(r.pType)));
end
DTlb: begin
dTlb.perf.req(unpack(truncate(r.pType)));
end
L2Tlb: begin
l2Tlb.perf.req(unpack(truncate(r.pType)));
end
DecStage: begin
fetchStage.perf.req(unpack(truncate(r.pType)));
end
ExeStage: begin
exePerfReqQ.enq(unpack(truncate(r.pType)));
end
ComStage: begin
comPerfReqQ.enq(unpack(truncate(r.pType)));
end
CoreSize: begin
sizePerfReqQ.enq(unpack(truncate(r.pType)));
end
default: begin
$fwrite(stderr, "[WARNING] unrecognzied perf req location ", fshow(r.loc), "\n");
doAssert(False, "unknown perf location");
end
endcase
endrule
// handle perf req: exe stage
rule readPerfCnt_Exe;
function Data getAluCnt(ExeStagePerfType pType);
Data cnt = 0;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
cnt = cnt + coreFix.aluExeIfc[i].getPerf(pType);
end
return cnt;
endfunction
function Data getFpuMulDivCnt(ExeStagePerfType pType);
Data cnt = 0;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
cnt = cnt + coreFix.fpuMulDivExeIfc[i].getPerf(pType);
end
return cnt;
endfunction
let pType <- toGet(exePerfReqQ).get;
Data data = (case(pType)
SupRenameCnt, SpecNoneCycles, SpecNonMemCycles: renameStage.getPerf(pType);
ExeRedirectBr, ExeRedirectJr, ExeRedirectOther: getAluCnt(pType);
ExeTlbExcep, ExeScSuccessCnt,
ExeLrScAmoAcqCnt, ExeLrScAmoRelCnt,
ExeFenceAcqCnt, ExeFenceRelCnt, ExeFenceCnt,
ExeLdStallByLd, ExeLdStallBySt, ExeLdStallBySB,
ExeLdForward, ExeLdMemLat, ExeStMemLat,
ExeLdToUseLat, ExeLdToUseCnt: coreFix.memExeIfc.getPerf(pType);
ExeIntMulCnt, ExeIntDivCnt,
ExeFpFmaCnt, ExeFpDivCnt, ExeFpSqrtCnt: getFpuMulDivCnt(pType);
default: 0;
endcase);
exePerfRespQ.enq(PerfResp {
pType: pType,
data: data
});
endrule
// handle perf req: com stage
rule readPerfCnt_Com;
let pType <- toGet(comPerfReqQ).get;
Data data = (case(pType)
CycleCnt: cycleCnt;
default: commitStage.getPerf(pType);
endcase);
comPerfRespQ.enq(PerfResp {
pType: pType,
data: data
});
endrule
// handle perf req: core size
rule readPerfCnt_Size;
let pType <- toGet(sizePerfReqQ).get;
Data data = (case(pType)
LdQFullCycles: ldqFullCycles;
StQFullCycles: stqFullCycles;
ROBFullCycles: robFullCycles;
AluRS0FullCycles: aluRS0FullCycles;
AluRS1FullCycles: aluRS1FullCycles;
FpuMulDivRSFullCycles: fpuMulDivRSFullCycles;
MemRSFullCycles: memRSFullCycles;
EpochFullCycles: epochFullCycles;
SpecTagFullCycles: specTagFullCycles;
default: 0;
endcase);
sizePerfRespQ.enq(PerfResp {
pType: pType,
data: data
});
endrule
// gather perf resp
rule gatherPerfResp;
Maybe#(ProcPerfResp) resp = Invalid;
if(iMem.perf.respValid) begin
let r <- iMem.perf.resp;
resp = Valid(ProcPerfResp {
loc: ICache,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(dMem.perf.respValid) begin
let r <- dMem.perf.resp;
resp = Valid(ProcPerfResp {
loc: DCache,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(iTlb.perf.respValid) begin
let r <- iTlb.perf.resp;
resp = Valid(ProcPerfResp {
loc: ITlb,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(dTlb.perf.respValid) begin
let r <- dTlb.perf.resp;
resp = Valid(ProcPerfResp {
loc: DTlb,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(l2Tlb.perf.respValid) begin
let r <- l2Tlb.perf.resp;
resp = Valid(ProcPerfResp {
loc: L2Tlb,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(fetchStage.perf.respValid) begin
let r <- fetchStage.perf.resp;
resp = Valid(ProcPerfResp {
loc: DecStage,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(exePerfRespQ.notEmpty) begin
let r <- toGet(exePerfRespQ).get;
resp = Valid(ProcPerfResp {
loc: ExeStage,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(comPerfRespQ.notEmpty) begin
let r <- toGet(comPerfRespQ).get;
resp = Valid(ProcPerfResp {
loc: ComStage,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(sizePerfRespQ.notEmpty) begin
let r <- toGet(sizePerfRespQ).get;
resp = Valid (ProcPerfResp {
loc: CoreSize,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
// enq to resp Q
if(resp matches tagged Valid .r) begin
perfRespQ.enq(r);
end
endrule
`endif
interface CoreReq coreReq;
method Action start(
Bit#(64) startpc,
Addr toHostAddr, Addr fromHostAddr
);
fetchStage.start(startpc);
started <= True;
mmio.setHtifAddrs(toHostAddr, fromHostAddr);
// start rename debug
commitStage.startRenameDebug;
endmethod
method Action perfReq(PerfLocation loc, PerfType t);
perfReqQ.enq(ProcPerfReq {
loc: loc,
pType: t
});
endmethod
endinterface
interface CoreIndInv coreIndInv;
method ActionValue#(ProcPerfResp) perfResp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
let r = perfReqQ.first;
return ProcPerfResp {
loc: r.loc,
pType: r.pType,
data: 0
};
`endif
endmethod
method terminate = csrf.terminate;
endinterface
interface dCacheToParent = dMem.to_parent;
interface iCacheToParent = iMem.to_parent;
interface tlbToMem = l2Tlb.toMem;
interface mmioToPlatform = mmio.toP;
method sendDoStats = csrf.sendDoStats;
method recvDoStats = csrf.recvDoStats;
// deadlock check
interface CoreDeadlock deadlock;
interface dCacheCRqStuck = dMem.cRqStuck;
interface dCachePRqStuck = dMem.pRqStuck;
interface iCacheCRqStuck = iMem.cRqStuck;
interface iCachePRqStuck = iMem.pRqStuck;
interface renameInstStuck = renameStage.renameInstStuck;
interface renameCorrectPathStuck = renameStage.renameCorrectPathStuck;
interface commitInstStuck = commitStage.commitInstStuck;
interface commitUserInstStuck = commitStage.commitUserInstStuck;
`ifdef CHECK_DEADLOCK
interface checkStarted = toGet(deadlockCheckStartedQ);
`else
interface checkStarted = nullGet;
`endif
endinterface
// rename debug
interface CoreRenameDebug renameDebug;
interface renameErr = commitStage.renameErr;
endinterface
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
method Action setMEIP (v) = csrf.setMEIP (v);
method Action setSEIP (v) = csrf.setSEIP (v);
// Bluespec: external interrupt to enter debug mode
method Action setDEIP (v) = csrf.setDEIP (v);
endmodule