Initial load of files
This commit is contained in:
977
src_Core/CPU/Core.bsv
Normal file
977
src_Core/CPU/Core.bsv
Normal file
@@ -0,0 +1,977 @@
|
||||
|
||||
// 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);
|
||||
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 = True;
|
||||
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_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
|
||||
);
|
||||
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);
|
||||
endmodule
|
||||
|
||||
842
src_Core/CPU/CsrFile.bsv
Normal file
842
src_Core/CPU/CsrFile.bsv
Normal file
@@ -0,0 +1,842 @@
|
||||
|
||||
// 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 Types::*;
|
||||
import ProcTypes::*;
|
||||
import DefaultValue::*;
|
||||
import ConcatReg::*;
|
||||
import ConfigReg::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import Vector::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import BuildVector::*;
|
||||
//import TRNG::*;
|
||||
|
||||
interface CsrFile;
|
||||
// Read
|
||||
method Data rd(CSR csr);
|
||||
// normal write by CSRXXX inst to any CSR
|
||||
method Action csrInstWr(CSR csr, Data x);
|
||||
// normal write by FPU inst to FPU CSR
|
||||
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
|
||||
method Action fpuInstWr(Bit#(5) fflags); // FPU must become dirty
|
||||
|
||||
// Methods for handling traps
|
||||
method Maybe#(Interrupt) pending_interrupt;
|
||||
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr faultAddr);
|
||||
method ActionValue#(Addr) sret;
|
||||
method ActionValue#(Addr) mret;
|
||||
|
||||
// Outputs for CSRs that the rest of the processor needs to know about
|
||||
method VMInfo vmI;
|
||||
method VMInfo vmD;
|
||||
method CsrDecodeInfo decodeInfo;
|
||||
|
||||
// Updating minstret CSR outside of normal CSR write instructions. This
|
||||
// increment will see the effect of normal CSR write.
|
||||
method Action incInstret(SupCnt x);
|
||||
|
||||
// update copy of mtime
|
||||
method Action setTime(Data t);
|
||||
|
||||
// MSIP/MTIP bits for external world (e.g., for MMIO and timer interrupt).
|
||||
// XXX These methods should only be called when the processor backend
|
||||
// pipeline does not contain any CSRXXX inst or corresponding interrupt
|
||||
// inst (the inst which is turned into an interrupt). This ensures that
|
||||
// CSRXXX and interrupt are handled atomically. MSIP/MTIP should not
|
||||
// affect other insts (e.g., address translation of loads/stores),
|
||||
// synchronous exceptions and other types of interrupts.
|
||||
method Bit#(1) getMSIP;
|
||||
method Action setMSIP(Bit#(1) v);
|
||||
method Action setMTIP(Bit#(1) v);
|
||||
|
||||
// Bluespec: external interrupts targeting machine and supervisor modes
|
||||
method Action setMEIP (Bit #(1) v);
|
||||
method Action setSEIP (Bit #(1) v);
|
||||
|
||||
// performance stats is collected or not
|
||||
method Bool doPerfStats;
|
||||
// send/recv updates on stats CSR globally
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
method Action recvDoStats(Bool s);
|
||||
|
||||
// terminate
|
||||
method ActionValue#(void) terminate;
|
||||
endinterface
|
||||
|
||||
// Fancy Reg functions
|
||||
function Reg#(Bit#(n)) truncateReg(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m));
|
||||
return (interface Reg;
|
||||
method Bit#(n) _read = truncate(r._read);
|
||||
method Action _write(Bit#(n) x) = r._write({truncateLSB(r._read), x});
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) truncateRegLSB(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m));
|
||||
return (interface Reg;
|
||||
method Bit#(n) _read = truncateLSB(r._read);
|
||||
method Action _write(Bit#(n) x) = r._write({x, truncate(r._read)});
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) zeroExtendReg(Reg#(Bit#(m)) r) provisos (Add#(a__,m,n));
|
||||
return (interface Reg;
|
||||
method Bit#(n) _read = zeroExtend(r._read);
|
||||
method Action _write(Bit#(n) x) = r._write(truncate(x));
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(t) readOnlyReg(t r);
|
||||
return (interface Reg;
|
||||
method t _read = r;
|
||||
method Action _write(t x) = noAction;
|
||||
endinterface);
|
||||
endfunction
|
||||
// module version of readOnlyReg for convenience
|
||||
module mkReadOnlyReg#(t x)(Reg#(t));
|
||||
return readOnlyReg(x);
|
||||
endmodule
|
||||
|
||||
function Reg#(t) regFromReadOnly(ReadOnly#(t) r);
|
||||
return (interface Reg;
|
||||
method t _read = r._read;
|
||||
method Action _write(t x);
|
||||
noAction;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(t) addWriteSideEffect(Reg#(t) r, Action a);
|
||||
return (interface Reg;
|
||||
method t _read = r._read;
|
||||
method Action _write(t x);
|
||||
r._write(x);
|
||||
a;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Bool has_csr_permission(CSR csr, Bit#(2) prv, Bool write);
|
||||
Bit#(12) csr_index = pack(csr);
|
||||
return ((prv >= csr_index[9:8]) && (!write || (csr_index[11:10] != 2'b11)));
|
||||
endfunction
|
||||
|
||||
// non-standard terminate CSR
|
||||
interface Terminate;
|
||||
interface Reg#(Data) reg_ifc;
|
||||
method ActionValue#(void) terminate;
|
||||
endinterface
|
||||
|
||||
module mkTerminate(Terminate);
|
||||
FIFO#(void) terminateQ <- mkFIFO1;
|
||||
|
||||
interface Reg reg_ifc;
|
||||
method Action _write(Data x);
|
||||
terminateQ.enq(?);
|
||||
$display(
|
||||
"[Terminate CSR] being written (val = %x), ",
|
||||
"send terminate signal to host", x
|
||||
);
|
||||
endmethod
|
||||
method Data _read = 0;
|
||||
endinterface
|
||||
|
||||
method terminate = toGet(terminateQ).get;
|
||||
endmodule
|
||||
|
||||
// stats CSR: there is only one copy in the whole multiprocessor, so any write
|
||||
// to stats CSR will be broadcasted
|
||||
interface StatsCsr;
|
||||
interface Reg#(Data) reg_ifc;
|
||||
method Bool doPerfStats;
|
||||
// send/recv updates on stats CSR globally
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
method Action recvDoStats(Bool s);
|
||||
endinterface
|
||||
|
||||
module mkStatsCsr(StatsCsr);
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
|
||||
FIFO#(Bool) writeQ <- mkFIFO1;
|
||||
|
||||
interface Reg reg_ifc;
|
||||
method Data _read = zeroExtend(pack(doStats));
|
||||
method Action _write(Data x);
|
||||
writeQ.enq(unpack(truncate(x)));
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method Bool doPerfStats = doStats;
|
||||
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
writeQ.deq;
|
||||
return writeQ.first;
|
||||
endmethod
|
||||
|
||||
method Action recvDoStats(Bool s);
|
||||
doStats <= s;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// same as EHR except that read port 0 is not ordered with other methods. Read
|
||||
// port 1 will still get bypassing from write port 0.
|
||||
module mkConfigEhr#(t init)(Ehr#(n, t)) provisos(Bits#(t, w));
|
||||
Ehr#(n, t) data <- mkEhr(init);
|
||||
Wire#(t) read <- mkBypassWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setRead;
|
||||
read <= data[0];
|
||||
endrule
|
||||
|
||||
Ehr#(n, t) ifc = ?;
|
||||
ifc[0] = (interface Reg;
|
||||
method _read = read._read;
|
||||
method _write = data[0]._write;
|
||||
endinterface);
|
||||
for(Integer i = 1; i < valueOf(n); i = i+1) begin
|
||||
ifc[i] = (interface Reg;
|
||||
method _read = data[i]._read;
|
||||
method _write = data[i]._write;
|
||||
endinterface);
|
||||
end
|
||||
return ifc;
|
||||
endmodule
|
||||
|
||||
module mkCsrFile #(Data hartid)(CsrFile);
|
||||
RiscVISASubset isa = defaultValue;
|
||||
|
||||
// To save from bypassing logic, CSR reads will get stale value
|
||||
let mkCsrReg = mkConfigReg;
|
||||
let mkCsrEhr = mkConfigEhr;
|
||||
|
||||
// current prv level (this is not a csr...)
|
||||
Reg#(Bit#(2)) prv_reg <- mkCsrReg(prvM);
|
||||
|
||||
// Machine level CSRs
|
||||
// mstatus
|
||||
Reg#(Bit#(2)) xs_reg <- mkReadOnlyReg(0); // XXX no extension
|
||||
Reg#(Bit#(2)) fs_reg <- (isa.f || isa.d) ? mkCsrReg(0) : mkReadOnlyReg(0);
|
||||
Reg#(Bit#(1)) sd_reg = readOnlyReg(
|
||||
((xs_reg == 2'b11) || (fs_reg == 2'b11)) ? 1 : 0
|
||||
);
|
||||
Reg#(Bit#(2)) sxl_reg = readOnlyReg(getXLBits);
|
||||
Reg#(Bit#(2)) uxl_reg = readOnlyReg(getXLBits);
|
||||
Reg#(Bit#(1)) tsr_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) tw_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) tvm_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mxr_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) sum_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mprv_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(2)) mpp_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) spp_reg <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(2))) prev_prv_vec = vec(
|
||||
// prev_prv_vec[x]: privilege mode before trapping into mode x
|
||||
readOnlyReg(prvU), // upp
|
||||
concatReg2(readOnlyReg(1'b0), spp_reg), // spp
|
||||
readOnlyReg(2'b0), // reserved
|
||||
mpp_reg
|
||||
);
|
||||
Vector#(4, Reg#(Bit#(1))) ie_vec = replicate(
|
||||
readOnlyReg(0) // ie_vec[x]: interrupt enable for mode x
|
||||
);
|
||||
ie_vec[prvU] <- mkCsrReg(0);
|
||||
ie_vec[prvS] <- mkCsrReg(0);
|
||||
ie_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) prev_ie_vec = replicate(
|
||||
readOnlyReg(0) // prev_ie_vec[x]: ie_vec[x] before trapping into mode x
|
||||
);
|
||||
prev_ie_vec[prvU] <- mkCsrReg(0);
|
||||
prev_ie_vec[prvS] <- mkCsrReg(0);
|
||||
prev_ie_vec[prvM] <- mkCsrReg(0);
|
||||
Reg#(Data) mstatus_csr = concatReg24(
|
||||
sd_reg, readOnlyReg(27'b0), sxl_reg, uxl_reg, readOnlyReg(9'b0),
|
||||
tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg,
|
||||
mpp_reg, readOnlyReg(2'b0), spp_reg,
|
||||
prev_ie_vec[prvM], readOnlyReg(1'b0),
|
||||
prev_ie_vec[prvS], prev_ie_vec[prvU],
|
||||
ie_vec[prvM], readOnlyReg(1'b0),
|
||||
ie_vec[prvS], ie_vec[prvU]
|
||||
);
|
||||
// misa
|
||||
Reg#(Data) misa_csr = readOnlyReg({getXLBits, 36'b0, getExtensionBits(isa)});
|
||||
// medeleg: some exceptions don't exist, fix corresponding bits to 0
|
||||
Reg#(Bit#(1)) medeleg_15_reg <- mkCsrReg(0); // cause 15
|
||||
Reg#(Bit#(3)) medeleg_13_11_reg <- mkCsrReg(0); // case 13-11
|
||||
Reg#(Bit#(10)) medeleg_9_0_reg <- mkCsrReg(0); // cause 9-0
|
||||
Reg#(Data) medeleg_csr = concatReg6(
|
||||
readOnlyReg(48'b0), medeleg_15_reg,
|
||||
readOnlyReg(1'b0), medeleg_13_11_reg,
|
||||
readOnlyReg(1'b0), medeleg_9_0_reg
|
||||
);
|
||||
// mideleg: some interrupts don't exist, fix corresponding bits to 0
|
||||
Reg#(Bit#(1)) mideleg_11_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(3)) mideleg_9_7_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(3)) mideleg_5_3_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(2)) mideleg_1_0_reg <- mkCsrReg(0);
|
||||
Reg#(Data) mideleg_csr = concatReg8(
|
||||
readOnlyReg(52'b0), mideleg_11_reg,
|
||||
readOnlyReg(1'b0), mideleg_9_7_reg,
|
||||
readOnlyReg(1'b0), mideleg_5_3_reg,
|
||||
readOnlyReg(1'b0), mideleg_1_0_reg
|
||||
);
|
||||
// mie
|
||||
Vector#(4, Reg#(Bit#(1))) external_int_en_vec = replicate(readOnlyReg(0));
|
||||
external_int_en_vec[prvU] <- mkCsrReg(0);
|
||||
external_int_en_vec[prvS] <- mkCsrReg(0);
|
||||
external_int_en_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) timer_int_en_vec = replicate(readOnlyReg(0));
|
||||
timer_int_en_vec[prvU] <- mkCsrReg(0);
|
||||
timer_int_en_vec[prvS] <- mkCsrReg(0);
|
||||
timer_int_en_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) software_int_en_vec = replicate(readOnlyReg(0));
|
||||
software_int_en_vec[prvU] <- mkCsrReg(0);
|
||||
software_int_en_vec[prvS] <- mkCsrReg(0);
|
||||
software_int_en_vec[prvM] <- mkCsrReg(0);
|
||||
Reg#(Data) mie_csr = concatReg13(
|
||||
readOnlyReg(52'b0),
|
||||
external_int_en_vec[prvM], readOnlyReg(1'b0),
|
||||
external_int_en_vec[prvS], external_int_en_vec[prvU],
|
||||
timer_int_en_vec[prvM], readOnlyReg(1'b0),
|
||||
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
|
||||
software_int_en_vec[prvM], readOnlyReg(1'b0),
|
||||
software_int_en_vec[prvS], software_int_en_vec[prvU]
|
||||
);
|
||||
// mtvec
|
||||
Reg#(Bit#(62)) mtvec_base_hi_reg <- mkCsrReg(0); // this is BASE[63:2]
|
||||
Reg#(Bit#(1)) mtvec_mode_low_reg <- mkCsrReg(0); // this is MODE[0]
|
||||
Reg#(Data) mtvec_csr = concatReg3(
|
||||
mtvec_base_hi_reg, readOnlyReg(1'b0), mtvec_mode_low_reg
|
||||
);
|
||||
// mcounteren
|
||||
Reg#(Bit#(1)) mcounteren_ir_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mcounteren_tm_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mcounteren_cy_reg <- mkCsrReg(0);
|
||||
Reg#(Data) mcounteren_csr = concatReg5(
|
||||
readOnlyReg(32'b0),
|
||||
readOnlyReg(29'b0), // hpmcounter 3-31 not accessible in S mode
|
||||
mcounteren_ir_reg, mcounteren_tm_reg, mcounteren_cy_reg
|
||||
);
|
||||
// mscratch
|
||||
Reg#(Data) mscratch_csr <- mkCsrReg(0);
|
||||
// mepc: FIXME Since we don't have C extension, mepc should be 4-byte
|
||||
// aligned. However, spike is not checking this, so we don't implement it.
|
||||
Reg#(Data) mepc_csr <- mkCsrReg(0);
|
||||
// mcause
|
||||
Reg#(Bit#(1)) mcause_interrupt_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(4)) mcause_code_reg <- mkCsrReg(0);
|
||||
Reg#(Data) mcause_csr = concatReg3(
|
||||
mcause_interrupt_reg, readOnlyReg(59'b0), mcause_code_reg
|
||||
);
|
||||
// mtval (mbadaddr in spike)
|
||||
Reg#(Data) mtval_csr <- mkCsrReg(0);
|
||||
// mip
|
||||
Vector#(4, Reg#(Bit#(1))) external_int_pend_vec = replicate(readOnlyReg(0));
|
||||
external_int_pend_vec[prvU] <- mkCsrReg(0);
|
||||
external_int_pend_vec[prvS] <- mkCsrReg(0);
|
||||
external_int_pend_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) timer_int_pend_vec = replicate(readOnlyReg(0));
|
||||
timer_int_pend_vec[prvU] <- mkCsrReg(0);
|
||||
timer_int_pend_vec[prvS] <- mkCsrReg(0);
|
||||
timer_int_pend_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) software_int_pend_vec = replicate(readOnlyReg(0));
|
||||
software_int_pend_vec[prvU] <- mkCsrReg(0);
|
||||
software_int_pend_vec[prvS] <- mkCsrReg(0);
|
||||
software_int_pend_vec[prvM] <- mkCsrReg(0);
|
||||
Reg#(Data) mip_csr = concatReg13(
|
||||
readOnlyReg(52'b0),
|
||||
external_int_pend_vec[prvM], readOnlyReg(1'b0),
|
||||
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
|
||||
readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software
|
||||
readOnlyReg(1'b0),
|
||||
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
|
||||
software_int_pend_vec[prvM], readOnlyReg(1'b0),
|
||||
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
|
||||
);
|
||||
// minstret
|
||||
Ehr#(2, Data) minstret_ehr <- mkCsrEhr(0);
|
||||
Reg#(Data) minstret_csr = minstret_ehr[0];
|
||||
// mcycle
|
||||
Ehr#(2, Data) mcycle_ehr <- mkCsrEhr(0);
|
||||
Reg#(Data) mcycle_csr = mcycle_ehr[0];
|
||||
// mvendorid
|
||||
Reg#(Data) mvendorid_csr = readOnlyReg(0);
|
||||
// marchid
|
||||
Reg#(Data) marchid_csr = readOnlyReg(0);
|
||||
// mimpid
|
||||
Reg#(Data) mimpid_csr = readOnlyReg(0);
|
||||
// mhartid
|
||||
Reg#(Data) mhartid_csr = readOnlyReg(hartid);
|
||||
|
||||
// Supervisor level CSRs
|
||||
// sstatus: restricted view of mstatus
|
||||
Reg#(Data) sstatus_csr = concatReg17(
|
||||
sd_reg, readOnlyReg(29'b0), uxl_reg, readOnlyReg(12'b0),
|
||||
mxr_reg, sum_reg, readOnlyReg(1'b0), xs_reg, fs_reg,
|
||||
readOnlyReg(4'b0), spp_reg,
|
||||
readOnlyReg(2'b0), prev_ie_vec[prvS], prev_ie_vec[prvU],
|
||||
readOnlyReg(2'b0), ie_vec[prvS], ie_vec[prvU]
|
||||
);
|
||||
// sie: restricted view of mie
|
||||
Reg#(Data) sie_csr = concatReg9(
|
||||
readOnlyReg(54'b0),
|
||||
external_int_en_vec[prvS], external_int_en_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
software_int_en_vec[prvS], software_int_en_vec[prvU]
|
||||
);
|
||||
// stvec
|
||||
Reg#(Bit#(62)) stvec_base_hi_reg <- mkCsrReg(0); // BASE[63:2]
|
||||
Reg#(Bit#(1)) stvec_mode_low_reg <- mkCsrReg(0); // MODE[0]
|
||||
Reg#(Data) stvec_csr = concatReg3(
|
||||
stvec_base_hi_reg, readOnlyReg(1'b0), stvec_mode_low_reg
|
||||
);
|
||||
// scounteren
|
||||
Reg#(Bit#(1)) scounteren_ir_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) scounteren_tm_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) scounteren_cy_reg <- mkCsrReg(0);
|
||||
Reg#(Data) scounteren_csr = concatReg5(
|
||||
readOnlyReg(32'b0),
|
||||
readOnlyReg(29'b0), // hpmcounter 3-31 not accessible in U mode
|
||||
scounteren_ir_reg, scounteren_tm_reg, scounteren_cy_reg
|
||||
);
|
||||
// sscratch
|
||||
Reg#(Data) sscratch_csr <- mkCsrReg(0);
|
||||
// sepc: FIXME Since we don't have C extension, sepc should be 4-byte
|
||||
// aligned. However, spike is not checking this, so we don't implement it.
|
||||
Reg#(Data) sepc_csr <- mkCsrReg(0);
|
||||
// scause
|
||||
Reg#(Bit#(1)) scause_interrupt_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(4)) scause_code_reg <- mkCsrReg(0);
|
||||
Reg#(Data) scause_csr = concatReg3(
|
||||
scause_interrupt_reg, readOnlyReg(59'b0), scause_code_reg
|
||||
);
|
||||
// stval (sbadaddr in spike)
|
||||
Reg#(Data) stval_csr <- mkCsrReg(0);
|
||||
// sip: restricted view of mip
|
||||
Reg#(Data) sip_csr = concatReg9(
|
||||
readOnlyReg(54'b0),
|
||||
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
|
||||
);
|
||||
// satp (sptbr in spike): FIXME we only support Bare and Sv39, so we hack
|
||||
// the encoding of mode[3:0] field. Only mode[3] is relevant, other bits
|
||||
// are always 0
|
||||
Reg#(Bit#(1)) vm_mode_sv39_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(4)) vm_mode_reg = concatReg2(vm_mode_sv39_reg, readOnlyReg(3'b0));
|
||||
Reg#(Asid) asid_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(16)) full_asid_reg = zeroExtendReg(asid_reg);
|
||||
Reg#(Bit#(44)) ppn_reg <- mkCsrReg(0);
|
||||
Reg#(Data) satp_csr = concatReg3(vm_mode_reg, full_asid_reg, ppn_reg);
|
||||
|
||||
// User level CSRs
|
||||
// According to spike, any write to fflags/frm/fcsr will set fs_reg as
|
||||
// dirty, regardless of whether the write truly changes value or not.
|
||||
// Besides, any non-zero FP exception flags will also make fs_reg dirty.
|
||||
// fflags: if we directly change fflags_reg (instead of fflags_csr), then
|
||||
// we must set fs_reg manually
|
||||
Reg#(Bit#(5)) fflags_reg <- mkCsrReg(0);
|
||||
Reg#(Data) fflags_csr = addWriteSideEffect(
|
||||
zeroExtendReg(fflags_reg), fs_reg._write(2'b11)
|
||||
);
|
||||
// frm: if we directly change frm_reg (instead of frm_csr), then we must
|
||||
// set fs_reg manually
|
||||
Reg#(Bit#(3)) frm_reg <- mkCsrReg(0);
|
||||
Reg#(Data) frm_csr = addWriteSideEffect(
|
||||
zeroExtendReg(frm_reg), fs_reg._write(2'b11)
|
||||
);
|
||||
// fcsr
|
||||
Reg#(Data) fcsr_csr = addWriteSideEffect(
|
||||
zeroExtendReg(concatReg2(frm_reg, fflags_reg)), fs_reg._write(2'b11)
|
||||
);
|
||||
// cycle
|
||||
Reg#(Data) cycle_csr = readOnlyReg(mcycle_csr);
|
||||
// time
|
||||
Reg#(Data) time_reg <- mkCsrReg(0);
|
||||
Reg#(Data) time_csr = readOnlyReg(time_reg);
|
||||
// instret
|
||||
Reg#(Data) instret_csr = readOnlyReg(minstret_csr);
|
||||
// terminate (non-standard)
|
||||
Terminate terminate_module <- mkTerminate;
|
||||
Reg#(Data) terminate_csr = terminate_module.reg_ifc;
|
||||
// whether performance stats is collected
|
||||
StatsCsr stats_module <- mkStatsCsr;
|
||||
Reg#(Data) stats_csr = stats_module.reg_ifc;
|
||||
|
||||
`ifdef SECURITY
|
||||
// sanctum machine CSRs
|
||||
|
||||
// ### Enclave virtual base and mask
|
||||
// (per-core) registers
|
||||
// ( defines a virtual region for which enclave page tables are used in
|
||||
// place of OS-controlled page tables)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Data) mevbase_csr <- mkCsrReg(maxBound); // impossible base & mask,
|
||||
Reg#(Data) mevmask_csr <- mkCsrReg(0); // so no enclave accesses are possible
|
||||
|
||||
// ### Enclave page table base
|
||||
// (per core) register
|
||||
// ( pointer to a separate page table data structure used to translate enclave
|
||||
// virtual addresses)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Bit#(44)) eppn_reg <- mkCsrReg(0);
|
||||
Reg#(Data) meatp_csr = zeroExtendReg(eppn_reg);
|
||||
|
||||
// ### DRAM bitmap
|
||||
// (per core) registers (OS and Enclave)
|
||||
// ( white-lists the DRAM regions the core is allowed to access via OS and
|
||||
// enclave virtual addresses)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Data) mmrbm_csr <- mkCsrReg(maxBound);
|
||||
Reg#(Data) memrbm_csr <- mkCsrReg(0);
|
||||
|
||||
// ### Protected region base and mask
|
||||
// (per core) registers (OS and Enclave)
|
||||
// ( these are used to prevent address translation into a specific range of
|
||||
// physical addresses, for example to protect the security monitor from all software)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Data) mparbase_csr <- mkCsrReg(maxBound);
|
||||
Reg#(Data) mparmask_csr <- mkCsrReg(0);
|
||||
Reg#(Data) meparbase_csr <- mkCsrReg(0);
|
||||
Reg#(Data) meparmask_csr <- mkCsrReg(0);
|
||||
|
||||
// ### Turn on/off speculation
|
||||
Reg#(Bit#(2)) mspec_reg <- mkCsrReg(mSpecAll);
|
||||
Reg#(Data) mspec_csr = zeroExtendReg(mspec_reg);
|
||||
|
||||
// sanctum user CSR
|
||||
// ### true random number
|
||||
// For now, we skip secure boot, keep TRNG = 0
|
||||
Reg#(Data) trng_csr <- mkReadOnlyReg(0); //mkTRNG;
|
||||
`endif
|
||||
|
||||
rule incCycle;
|
||||
mcycle_ehr[1] <= mcycle_ehr[1] + 1;
|
||||
endrule
|
||||
|
||||
// Function for getting a csr given an index
|
||||
function Reg#(Data) get_csr(CSR csr);
|
||||
return (case (csr)
|
||||
// User CSRs
|
||||
CSRfflags: fflags_csr;
|
||||
CSRfrm: frm_csr;
|
||||
CSRfcsr: fcsr_csr;
|
||||
CSRcycle: cycle_csr;
|
||||
CSRtime: time_csr;
|
||||
CSRinstret: instret_csr;
|
||||
CSRterminate: terminate_csr;
|
||||
CSRstats: stats_csr;
|
||||
// Supervisor CSRs
|
||||
CSRsstatus: sstatus_csr;
|
||||
CSRsie: sie_csr;
|
||||
CSRstvec: stvec_csr;
|
||||
CSRscounteren: scounteren_csr;
|
||||
CSRsscratch: sscratch_csr;
|
||||
CSRsepc: sepc_csr;
|
||||
CSRscause: scause_csr;
|
||||
CSRstval: stval_csr;
|
||||
CSRsip: sip_csr;
|
||||
CSRsatp: satp_csr;
|
||||
// Machine CSRs
|
||||
CSRmstatus: mstatus_csr;
|
||||
CSRmisa: misa_csr;
|
||||
CSRmedeleg: medeleg_csr;
|
||||
CSRmideleg: mideleg_csr;
|
||||
CSRmie: mie_csr;
|
||||
CSRmtvec: mtvec_csr;
|
||||
CSRmcounteren: mcounteren_csr;
|
||||
CSRmscratch: mscratch_csr;
|
||||
CSRmepc: mepc_csr;
|
||||
CSRmcause: mcause_csr;
|
||||
CSRmtval: mtval_csr;
|
||||
CSRmip: mip_csr;
|
||||
CSRmcycle: mcycle_csr;
|
||||
CSRminstret: minstret_csr;
|
||||
CSRmvendorid: mvendorid_csr;
|
||||
CSRmarchid: marchid_csr;
|
||||
CSRmimpid: mimpid_csr;
|
||||
CSRmhartid: mhartid_csr;
|
||||
`ifdef SECURITY
|
||||
CSRmevbase: mevbase_csr;
|
||||
CSRmevmask: mevmask_csr;
|
||||
CSRmeatp: meatp_csr;
|
||||
CSRmmrbm: mmrbm_csr;
|
||||
CSRmemrbm: memrbm_csr;
|
||||
CSRmparbase: mparbase_csr;
|
||||
CSRmparmask: mparmask_csr;
|
||||
CSRmeparbase: meparbase_csr;
|
||||
CSRmeparmask: meparmask_csr;
|
||||
CSRmspec: mspec_csr;
|
||||
CSRtrng: trng_csr;
|
||||
`endif
|
||||
default: readOnlyReg(64'b0);
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
method Data rd(CSR csr);
|
||||
return get_csr(csr)._read;
|
||||
endmethod
|
||||
|
||||
method Action csrInstWr(CSR csr, Data x);
|
||||
get_csr(csr)._write(x);
|
||||
endmethod
|
||||
|
||||
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
|
||||
Bool fflags_change = (fflags & fflags_reg) != fflags;
|
||||
// we need to set fs_reg as dirty in two cases
|
||||
// 1. FP reg is written (i.e., fpu_dirty)
|
||||
// 2. FP exception (i.e., fflags) is non-zero (try to match spike)
|
||||
Bool need_set_dirty = fs_reg != 2'b11 && (fpu_dirty || fflags != 0);
|
||||
return fflags_change || need_set_dirty;
|
||||
endmethod
|
||||
|
||||
method Action fpuInstWr(Bit#(5) fflags);
|
||||
fs_reg <= 2'b11; // FPU must be dirty
|
||||
fflags_reg <= fflags_reg | fflags;
|
||||
endmethod
|
||||
|
||||
method Maybe#(Interrupt) pending_interrupt;
|
||||
// first get all the pending interrupts
|
||||
Bit#(InterruptNum) pend_ints = truncate(mie_csr & mip_csr);
|
||||
// now find out all the truly enabled interrupts (that needs handling)
|
||||
Bit#(InterruptNum) enabled_ints = 0;
|
||||
// check interrupts that needs to be handled at M mode: all interrupts
|
||||
// are by default handled at M mode unless it is delegated in
|
||||
// mideleg_csr, we just need to ignore those interrupts
|
||||
if(prv_reg < prvM || (prv_reg == prvM && ie_vec[prvM] == 1)) begin
|
||||
enabled_ints = pend_ints & ~truncate(mideleg_csr);
|
||||
end
|
||||
// check interrupts that needs to be handled at S mode only if no
|
||||
// interrupt needs to be handled at M mode: interrupts handled at S
|
||||
// mode must be delegated in mideleg_csr
|
||||
if (enabled_ints == 0 &&
|
||||
(prv_reg < prvS || (prv_reg == prvS && ie_vec[prvS] == 1))) begin
|
||||
enabled_ints = pend_ints & truncate(mideleg_csr);
|
||||
end
|
||||
// According to spike, return the interrupt bit at LSB
|
||||
function Bool isEnabled(Integer i) = (enabled_ints[i] == 1);
|
||||
Vector#(InterruptNum, Integer) idxVec = genVector;
|
||||
if(find(isEnabled, idxVec) matches tagged Valid .i) begin
|
||||
return Valid (unpack(fromInteger(i)));
|
||||
end
|
||||
else begin
|
||||
return Invalid;
|
||||
end
|
||||
endmethod
|
||||
|
||||
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr addr);
|
||||
// figure out trap cause & trap val
|
||||
Bit#(1) cause_interrupt = 0;
|
||||
Bit#(4) cause_code = 0;
|
||||
Data trap_val = 0;
|
||||
case(t) matches
|
||||
tagged Exception .e: begin
|
||||
cause_code = pack(e);
|
||||
trap_val = (case(e)
|
||||
InstAddrMisaligned, InstAccessFault,
|
||||
Breakpoint, InstPageFault: return pc;
|
||||
LoadAddrMisaligned, LoadAccessFault,
|
||||
StoreAddrMisaligned, StoreAccessFault,
|
||||
LoadPageFault, StorePageFault: return addr;
|
||||
default: return 0;
|
||||
endcase);
|
||||
end
|
||||
tagged Interrupt .i: begin
|
||||
cause_code = pack(i);
|
||||
cause_interrupt = 1;
|
||||
end
|
||||
endcase
|
||||
// function to figure out next PC
|
||||
function Addr getNextPc(Bit#(1) mode_low, Bit#(62) base_hi);
|
||||
Addr base = {base_hi, 2'b0};
|
||||
if(mode_low == 1 && cause_interrupt == 1) begin
|
||||
// vector jump: only for interrupt
|
||||
return base + zeroExtend({cause_code, 2'b0});
|
||||
end
|
||||
else begin // direct jump
|
||||
return base;
|
||||
end
|
||||
endfunction
|
||||
// check if trap is delegated
|
||||
Bool deleg = prv_reg <= prvS && (case(t) matches
|
||||
tagged Exception .e: return medeleg_csr[pack(e)] == 1;
|
||||
tagged Interrupt .i: return mideleg_csr[pack(i)] == 1;
|
||||
default: return False;
|
||||
endcase);
|
||||
// handle the trap
|
||||
if(deleg) begin // handle in S mode
|
||||
// ie/prv stack
|
||||
prev_prv_vec[prvS] <= prv_reg;
|
||||
prv_reg <= prvS;
|
||||
prev_ie_vec[prvS] <= ie_vec[prvS];
|
||||
ie_vec[prvS] <= 0;
|
||||
// record trap info
|
||||
sepc_csr <= pc;
|
||||
scause_interrupt_reg <= cause_interrupt;
|
||||
scause_code_reg <= cause_code;
|
||||
stval_csr <= trap_val;
|
||||
// return next pc
|
||||
return getNextPc(stvec_mode_low_reg, stvec_base_hi_reg);
|
||||
end
|
||||
else begin
|
||||
// ie/prv stack
|
||||
prev_prv_vec[prvM] <= prv_reg;
|
||||
prv_reg <= prvM;
|
||||
prev_ie_vec[prvM] <= ie_vec[prvM];
|
||||
ie_vec[prvM] <= 0;
|
||||
// record trap info
|
||||
mepc_csr <= pc;
|
||||
mcause_interrupt_reg <= cause_interrupt;
|
||||
mcause_code_reg <= cause_code;
|
||||
mtval_csr <= trap_val;
|
||||
// return next pc
|
||||
return getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg);
|
||||
end
|
||||
// XXX yield load reservation should be done outside this method
|
||||
endmethod
|
||||
|
||||
method ActionValue#(Addr) mret;
|
||||
prv_reg <= prev_prv_vec[prvM];
|
||||
prev_prv_vec[prvM] <= prvU;
|
||||
ie_vec[prvM] <= prev_ie_vec[prvM];
|
||||
prev_ie_vec[prvM] <= 1;
|
||||
return mepc_csr;
|
||||
endmethod
|
||||
|
||||
method ActionValue#(Addr) sret;
|
||||
prv_reg <= prev_prv_vec[prvS];
|
||||
prev_prv_vec[prvS] <= prvU;
|
||||
ie_vec[prvS] <= prev_ie_vec[prvS];
|
||||
prev_ie_vec[prvS] <= 1;
|
||||
return sepc_csr;
|
||||
endmethod
|
||||
|
||||
method VMInfo vmI;
|
||||
// for inst fetch, NO need to consider MPRV
|
||||
Bit#(2) prv = prv_reg;
|
||||
return VMInfo {
|
||||
prv: prv,
|
||||
asid: asid_reg,
|
||||
sv39: prv < prvM && vm_mode_sv39_reg == 1,
|
||||
exeReadable: mxr_reg == 1,
|
||||
userAccessibleByS: sum_reg == 1,
|
||||
basePPN: ppn_reg
|
||||
`ifdef SECURITY
|
||||
, sanctum_evbase: mevbase_csr,
|
||||
sanctum_evmask: mevmask_csr,
|
||||
sanctum_ebasePPN: eppn_reg,
|
||||
sanctum_mrbm: mmrbm_csr,
|
||||
sanctum_emrbm: memrbm_csr,
|
||||
sanctum_parbase: mparbase_csr,
|
||||
sanctum_parmask: mparmask_csr,
|
||||
sanctum_eparbase: meparbase_csr,
|
||||
sanctum_eparmask: meparmask_csr,
|
||||
// enclave / security monitor should never execute instructions
|
||||
// from untrusted shared region
|
||||
sanctum_authShared: False
|
||||
`endif
|
||||
};
|
||||
endmethod
|
||||
|
||||
method VMInfo vmD;
|
||||
// for load/store, need to consider MPRV
|
||||
Bit#(2) prv = (mprv_reg == 1) ? prev_prv_vec[prvM] : prv_reg;
|
||||
return VMInfo {
|
||||
prv: prv,
|
||||
asid: asid_reg,
|
||||
sv39: prv < prvM && vm_mode_sv39_reg == 1,
|
||||
exeReadable: mxr_reg == 1,
|
||||
userAccessibleByS: sum_reg == 1,
|
||||
basePPN: ppn_reg
|
||||
`ifdef SECURITY
|
||||
, sanctum_evbase: mevbase_csr,
|
||||
sanctum_evmask: mevmask_csr,
|
||||
sanctum_ebasePPN: eppn_reg,
|
||||
sanctum_mrbm: mmrbm_csr,
|
||||
sanctum_emrbm: memrbm_csr,
|
||||
sanctum_parbase: mparbase_csr,
|
||||
sanctum_parmask: mparmask_csr,
|
||||
sanctum_eparbase: meparbase_csr,
|
||||
sanctum_eparmask: meparmask_csr,
|
||||
// enclave / security monitor can read/write untrusted shared
|
||||
// region when speculation is off (either by mspec CSR or in M
|
||||
// mode)
|
||||
// XXX Because of the effects of mprv, we have to use prv_reg here
|
||||
// instead of prv. Otherwise, we may be in M mode, but prv=S, and
|
||||
// still forbid shared accesses
|
||||
sanctum_authShared: mspec_reg != mSpecAll || prv_reg == prvM
|
||||
`endif
|
||||
};
|
||||
endmethod
|
||||
|
||||
method CsrDecodeInfo decodeInfo = CsrDecodeInfo {
|
||||
frm: frm_reg,
|
||||
fEnabled: fs_reg != 0,
|
||||
prv: prv_reg,
|
||||
trapVM: tvm_reg == 1,
|
||||
timeoutWait: tw_reg == 1,
|
||||
trapSret: tsr_reg == 1,
|
||||
cycleReadableByS: mcounteren_cy_reg == 1,
|
||||
cycleReadableByU: mcounteren_cy_reg == 1 && scounteren_cy_reg == 1,
|
||||
instretReadableByS: mcounteren_ir_reg == 1,
|
||||
instretReadableByU: mcounteren_ir_reg == 1 && scounteren_ir_reg == 1,
|
||||
timeReadableByS: mcounteren_tm_reg == 1,
|
||||
timeReadableByU: mcounteren_tm_reg == 1 && scounteren_tm_reg == 1
|
||||
};
|
||||
|
||||
method Action incInstret(SupCnt x);
|
||||
minstret_ehr[1] <= minstret_ehr[1] + zeroExtend(x);
|
||||
endmethod
|
||||
|
||||
method Action setTime(Data t);
|
||||
time_reg <= t;
|
||||
endmethod
|
||||
|
||||
method getMSIP = software_int_pend_vec[prvM]._read;
|
||||
method setMSIP = software_int_pend_vec[prvM]._write;
|
||||
method setMTIP = timer_int_pend_vec[prvM]._write;
|
||||
|
||||
// Bluespec: external interrupts targeting machine and supervisor modes
|
||||
method Action setMEIP (Bit #(1) v);
|
||||
external_int_pend_vec[prvM] <= v;
|
||||
endmethod
|
||||
method Action setSEIP (Bit #(1) v);
|
||||
external_int_pend_vec[prvS] <= v;
|
||||
endmethod
|
||||
|
||||
method terminate = terminate_module.terminate;
|
||||
|
||||
// performance stats
|
||||
method doPerfStats = stats_module.doPerfStats;
|
||||
method sendDoStats = stats_module.sendDoStats;
|
||||
method recvDoStats = stats_module.recvDoStats;
|
||||
endmodule
|
||||
277
src_Core/CPU/LLC_AXI4_Adapter.bsv
Normal file
277
src_Core/CPU/LLC_AXI4_Adapter.bsv
Normal file
@@ -0,0 +1,277 @@
|
||||
package LLC_AXI4_Adapter;
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import ConfigReg :: *;
|
||||
import Assert :: *;
|
||||
import FIFOF :: *;
|
||||
import Vector :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
import Cur_Cycle :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import CreditCounter :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From MIT RISCY-OOO
|
||||
|
||||
import Types :: *;
|
||||
import CacheUtils :: *;
|
||||
import CCTypes :: *;
|
||||
|
||||
// ----------------
|
||||
// From Bluespec Pipes
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface LLC_AXI4_Adapter_IFC;
|
||||
method Action reset;
|
||||
|
||||
// Fabric master interface for memory
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mem_master;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
module mkLLC_AXi4_Adapter #(MemFifoClient #(idT, childT) llc)
|
||||
(LLC_AXI4_Adapter_IFC)
|
||||
provisos(Bits#(idT, a__),
|
||||
Bits#(childT, b__),
|
||||
FShow#(ToMemMsg#(idT, childT)),
|
||||
FShow#(MemRsMsg#(idT, childT)),
|
||||
Add#(SizeOf#(Line), 0, 512)); // assert Line sz = 512
|
||||
|
||||
// Verbosity: 0: quiet; 1: LLC transactions; 2: loop detail
|
||||
Integer verbosity = 2;
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
|
||||
|
||||
// ================================================================
|
||||
// Fabric request/response
|
||||
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
|
||||
|
||||
// For discarding write-responses
|
||||
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
|
||||
|
||||
// ================================================================
|
||||
// Functions to interact with the fabric
|
||||
|
||||
// Send a read-request into the fabric
|
||||
function Action fa_fabric_send_read_req (Fabric_Addr addr);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
|
||||
araddr: addr,
|
||||
arlen: 0, // burst len = arlen+1
|
||||
arsize: size,
|
||||
arburst: fabric_default_burst,
|
||||
arlock: fabric_default_lock,
|
||||
arcache: fabric_default_arcache,
|
||||
arprot: fabric_default_prot,
|
||||
arqos: fabric_default_qos,
|
||||
arregion: fabric_default_region,
|
||||
aruser: fabric_default_user};
|
||||
|
||||
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" ", fshow (mem_req_rd_addr));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// Send a write-request into the fabric
|
||||
function Action fa_fabric_send_write_req (Fabric_Addr addr, Fabric_Strb strb, Bit #(64) st_val);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
|
||||
awaddr: addr,
|
||||
awlen: 0, // burst len = awlen+1
|
||||
awsize: size,
|
||||
awburst: fabric_default_burst,
|
||||
awlock: fabric_default_lock,
|
||||
awcache: fabric_default_awcache,
|
||||
awprot: fabric_default_prot,
|
||||
awqos: fabric_default_qos,
|
||||
awregion: fabric_default_region,
|
||||
awuser: fabric_default_user};
|
||||
|
||||
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
|
||||
wdata: st_val,
|
||||
wstrb: strb,
|
||||
wlast: True,
|
||||
wuser: fabric_default_user};
|
||||
|
||||
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
|
||||
master_xactor.i_wr_data.enq (mem_req_wr_data);
|
||||
|
||||
// Expect a fabric response
|
||||
ctr_wr_rsps_pending.incr;
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" To fabric: ", fshow (mem_req_wr_addr));
|
||||
$display (" ", fshow (mem_req_wr_data));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Handle read requests and responses
|
||||
// Don't do reads while writes are outstanding.
|
||||
|
||||
// Each 512b cache line takes 8 beats, each handling 64 bits
|
||||
Reg #(Bit #(3)) rg_rd_req_beat <- mkReg (0);
|
||||
Reg #(Bit #(3)) rg_rd_rsp_beat <- mkReg (0);
|
||||
|
||||
FIFOF #(LdMemRq #(idT, childT)) f_pending_reads <- mkFIFOF;
|
||||
Reg #(Bit #(512)) rg_cline <- mkRegU;
|
||||
|
||||
rule rl_handle_read_req (llc.toM.first matches tagged Ld .ld
|
||||
&&& (ctr_wr_rsps_pending.value == 0));
|
||||
if ((cfg_verbosity > 0) && (rg_rd_req_beat == 0)) begin
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_req: Ld request from LLC to memory: beat %0d",
|
||||
cur_cycle, rg_rd_req_beat);
|
||||
$display (" ", fshow (ld));
|
||||
end
|
||||
|
||||
Addr line_addr = { ld.addr [63:6], 6'h0 }; // Addr of containing cache line
|
||||
Addr offset = zeroExtend ( { rg_rd_req_beat, 3'b_000 } ); // Addr offset of 64b word for this beat
|
||||
fa_fabric_send_read_req (line_addr | offset);
|
||||
|
||||
if (rg_rd_req_beat == 0)
|
||||
f_pending_reads.enq (ld);
|
||||
|
||||
if (rg_rd_req_beat == 7)
|
||||
llc.toM.deq;
|
||||
|
||||
rg_rd_req_beat <= rg_rd_req_beat + 1;
|
||||
endrule
|
||||
|
||||
rule rl_handle_read_rsps;
|
||||
let mem_rsp <- pop_o (master_xactor.o_rd_data);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_rsps: beat %0d ", cur_cycle, rg_rd_rsp_beat);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
end
|
||||
|
||||
if (mem_rsp.rresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_rsp: fabric response error; exit", cur_cycle);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
$finish (1);
|
||||
end
|
||||
|
||||
// Shift next 64 bits from fabric into the cache line being assembled
|
||||
let new_cline = { mem_rsp.rdata, rg_cline [511:64] };
|
||||
|
||||
if (rg_rd_rsp_beat == 7) begin
|
||||
let ldreq <- pop (f_pending_reads);
|
||||
MemRsMsg #(idT, childT) resp = MemRsMsg {data: unpack (new_cline),
|
||||
child: ldreq.child,
|
||||
id: ldreq.id};
|
||||
|
||||
llc.rsFromM.enq (resp);
|
||||
|
||||
if (cfg_verbosity > 1)
|
||||
$display (" Response to LLC: ", fshow (resp));
|
||||
end
|
||||
|
||||
rg_cline <= new_cline;
|
||||
rg_rd_rsp_beat <= rg_rd_rsp_beat + 1;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Handle write requests and responses
|
||||
|
||||
// Each 512b cache line takes 8 beats, each handling 64 bits
|
||||
Reg #(Bit #(3)) rg_wr_req_beat <- mkReg (0);
|
||||
Reg #(Bit #(3)) rg_wr_rsp_beat <- mkReg (0);
|
||||
|
||||
FIFOF #(WbMemRs) f_pending_writes <- mkFIFOF;
|
||||
|
||||
rule rl_handle_write_req (llc.toM.first matches tagged Wb .wb);
|
||||
if ((cfg_verbosity > 0) && (rg_wr_req_beat == 0)) begin
|
||||
$display ("%d: LLC_AXI4_Adapter.rl_handle_write_req: Wb request from LLC to memory:", cur_cycle);
|
||||
$display (" ", fshow (wb));
|
||||
end
|
||||
|
||||
Addr line_addr = { wb.addr [63:6], 6'h0 }; // Addr of containing cache line
|
||||
Line line_data = wb.data;
|
||||
Vector #(8, Bit #(8)) line_bes = unpack (pack (wb.byteEn));
|
||||
|
||||
Addr offset = zeroExtend ( { rg_wr_req_beat, 3'b_000 } ); // Addr offset of 64b word for this beat
|
||||
Bit #(64) data64 = line_data [rg_wr_req_beat];
|
||||
Bit #(8) strb8 = line_bes [rg_wr_req_beat];
|
||||
fa_fabric_send_write_req (line_addr | offset, strb8, data64);
|
||||
|
||||
if (rg_wr_req_beat == 0)
|
||||
f_pending_writes.enq (wb);
|
||||
|
||||
if (rg_wr_req_beat == 7)
|
||||
llc.toM.deq;
|
||||
|
||||
rg_wr_req_beat <= rg_wr_req_beat + 1;
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Discard write-responses from the fabric
|
||||
|
||||
rule rl_discard_write_rsp;
|
||||
let wr_resp <- pop_o (master_xactor.o_wr_resp);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_discard_write_rsp: beat %0d ", cur_cycle, rg_wr_rsp_beat);
|
||||
$display (" ", fshow (wr_resp));
|
||||
end
|
||||
|
||||
if (ctr_wr_rsps_pending.value == 0) begin
|
||||
$display ("%0d: ERROR: LLC_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
|
||||
cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1); // Assertion failure
|
||||
end
|
||||
|
||||
ctr_wr_rsps_pending.decr;
|
||||
|
||||
if (wr_resp.bresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1);
|
||||
end
|
||||
|
||||
if (rg_wr_rsp_beat == 7) begin
|
||||
let wrreq <- pop (f_pending_writes);
|
||||
// LLC does not expect any response for writes
|
||||
end
|
||||
|
||||
rg_wr_rsp_beat <= rg_wr_rsp_beat + 1;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action reset;
|
||||
ctr_wr_rsps_pending.clear;
|
||||
endmethod
|
||||
|
||||
// Fabric interface for memory
|
||||
interface mem_master = master_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
1076
src_Core/CPU/MMIOPlatform.bsv
Normal file
1076
src_Core/CPU/MMIOPlatform.bsv
Normal file
File diff suppressed because it is too large
Load Diff
253
src_Core/CPU/MMIO_AXI4_Adapter.bsv
Normal file
253
src_Core/CPU/MMIO_AXI4_Adapter.bsv
Normal file
@@ -0,0 +1,253 @@
|
||||
package MMIO_AXI4_Adapter;
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Assert :: *;
|
||||
import ConfigReg :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
import Cur_Cycle :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import CreditCounter :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From MIT RISCY-OOO
|
||||
|
||||
import ProcTypes :: *;
|
||||
|
||||
// ----------------
|
||||
// From Bluespec Pipes
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface MMIO_AXI4_Adapter_IFC;
|
||||
method Action reset;
|
||||
|
||||
interface Server #(MMIOCRq, MMIODataPRs) core_side;
|
||||
|
||||
// Fabric master interface for IO
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mmio_master;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
|
||||
|
||||
// Verbosity: 0: quiet; 1: transactions
|
||||
Integer verbosity = 2;
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
|
||||
|
||||
// ================================================================
|
||||
// Requests from and responses to core
|
||||
|
||||
FIFOF #(MMIOCRq) f_reqs_from_core <- mkFIFOF;
|
||||
FIFOF #(MMIODataPRs) f_rsps_to_core <- mkFIFOF;
|
||||
|
||||
// ================================================================
|
||||
// Fabric request/response
|
||||
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
|
||||
|
||||
// For discarding write-responses
|
||||
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
|
||||
|
||||
// ================================================================
|
||||
// Functions to interact with the fabric
|
||||
|
||||
// Send a read-request into the fabric
|
||||
function Action fa_fabric_send_read_req (Fabric_Addr addr);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
|
||||
araddr: addr,
|
||||
arlen: 0, // burst len = arlen+1
|
||||
arsize: size,
|
||||
arburst: fabric_default_burst,
|
||||
arlock: fabric_default_lock,
|
||||
arcache: fabric_default_arcache,
|
||||
arprot: fabric_default_prot,
|
||||
arqos: fabric_default_qos,
|
||||
arregion: fabric_default_region,
|
||||
aruser: fabric_default_user};
|
||||
|
||||
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display (" ", fshow (mem_req_rd_addr));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// Send a write-request into the fabric
|
||||
function Action fa_fabric_send_write_req (Fabric_Addr addr, Fabric_Strb strb, Bit #(64) st_val);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
|
||||
awaddr: addr,
|
||||
awlen: 0, // burst len = awlen+1
|
||||
awsize: size,
|
||||
awburst: fabric_default_burst,
|
||||
awlock: fabric_default_lock,
|
||||
awcache: fabric_default_awcache,
|
||||
awprot: fabric_default_prot,
|
||||
awqos: fabric_default_qos,
|
||||
awregion: fabric_default_region,
|
||||
awuser: fabric_default_user};
|
||||
|
||||
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
|
||||
wdata: st_val,
|
||||
wstrb: strb,
|
||||
wlast: True,
|
||||
wuser: fabric_default_user};
|
||||
|
||||
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
|
||||
master_xactor.i_wr_data.enq (mem_req_wr_data);
|
||||
|
||||
// Expect a fabric response
|
||||
ctr_wr_rsps_pending.incr;
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display (" To fabric: ", fshow (mem_req_wr_addr));
|
||||
$display (" ", fshow (mem_req_wr_data));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Handle read requests and responses.
|
||||
// Don't do a read while a write is outstanding.
|
||||
// This is just an adapter from MMIOCRq/MMIODataPRs to AXI4
|
||||
|
||||
rule rl_handle_read_req (f_reqs_from_core.first.func matches Ld
|
||||
&&& (ctr_wr_rsps_pending.value == 0));
|
||||
let req <- pop (f_reqs_from_core);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_req: Ld request", cur_cycle);
|
||||
$display (" ", fshow (req));
|
||||
end
|
||||
|
||||
fa_fabric_send_read_req (req.addr);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
|
||||
rule rl_handle_read_rsps;
|
||||
let mem_rsp <- pop_o (master_xactor.o_rd_data);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsps ", cur_cycle);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
end
|
||||
|
||||
if (mem_rsp.rresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsp: fabric response error; exit", cur_cycle);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
$finish (1);
|
||||
end
|
||||
|
||||
let rsp = MMIODataPRs {valid: True, data: mem_rsp.rdata};
|
||||
f_rsps_to_core.enq (rsp);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display (" Response MMIO to core: ", fshow (rsp));
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Handle write requests and responses
|
||||
|
||||
rule rl_handle_write_req (f_reqs_from_core.first.func matches St);
|
||||
let req <- pop (f_reqs_from_core);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%d: MMIO_AXI4_Adapter.rl_handle_write_req: St request:", cur_cycle);
|
||||
$display (" ", fshow (req));
|
||||
end
|
||||
|
||||
fa_fabric_send_write_req (req.addr, pack (req.byteEn), req.data);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Discard write-responses from the fabric
|
||||
|
||||
rule rl_discard_write_rsp;
|
||||
let wr_resp <- pop_o (master_xactor.o_wr_resp);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp", cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
end
|
||||
|
||||
if (ctr_wr_rsps_pending.value == 0) begin
|
||||
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
|
||||
cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1); // Assertion failure
|
||||
end
|
||||
|
||||
ctr_wr_rsps_pending.decr;
|
||||
|
||||
if (wr_resp.bresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1);
|
||||
end
|
||||
else begin
|
||||
let rsp = MMIODataPRs {valid: True, data: 0};
|
||||
f_rsps_to_core.enq (rsp);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// This adapter should only receive Ld/St requests, no Inst or AMO reqs.
|
||||
|
||||
function Bool fn_is_Ld_or_St (MMIOCRq req);
|
||||
return case (req.func) matches
|
||||
Ld : True;
|
||||
St : True;
|
||||
default: False;
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
rule rl_handle_non_Ld_St (! fn_is_Ld_or_St (f_reqs_from_core.first));
|
||||
let req <- pop (f_reqs_from_core);
|
||||
|
||||
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_handle_non_Ld_St",
|
||||
cur_cycle);
|
||||
$display (" ", fshow (req));
|
||||
$finish (1); // Assertion failure
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action reset;
|
||||
ctr_wr_rsps_pending.clear;
|
||||
endmethod
|
||||
|
||||
interface Server core_side = toGPServer (f_reqs_from_core, f_rsps_to_core);
|
||||
|
||||
// Fabric master interface for IO
|
||||
interface mmio_master = master_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
358
src_Core/CPU/Proc.bsv
Normal file
358
src_Core/CPU/Proc.bsv
Normal file
@@ -0,0 +1,358 @@
|
||||
package Proc;
|
||||
|
||||
// Copyright (c) 2018 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) 2019 Bluespec, Inc.
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import Connectable::*;
|
||||
import FIFOF :: *;
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From MIT RISCY-OOO
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import L1CoCache::*;
|
||||
import L2Tlb::*;
|
||||
import CCTypes::*;
|
||||
import CacheUtils::*;
|
||||
import LLCache::*;
|
||||
import MemLoader::*;
|
||||
import L1LLConnect::*;
|
||||
import LLCDmaConnect::*;
|
||||
import MMIOAddrs::*;
|
||||
import MMIOCore::*;
|
||||
import DramCommon::*;
|
||||
import Performance::*;
|
||||
|
||||
// ----------------
|
||||
// From McStriiv
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
import Core :: *;
|
||||
import Proc_IFC :: *;
|
||||
import MMIOPlatform :: *;
|
||||
import LLC_AXI4_Adapter :: *;
|
||||
import MMIO_AXI4_Adapter :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkProc (Proc_IFC);
|
||||
// cores
|
||||
Vector#(CoreNum, Core) core = ?;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
core[i] <- mkCore(fromInteger(i));
|
||||
end
|
||||
|
||||
// ----------------
|
||||
// Verbosity control for debugging
|
||||
|
||||
// Verbosity: 0=quiet; 1=instruction trace; 2=more detail
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
// ----------------
|
||||
// Reset requests and responses (TODO: to be implemented)
|
||||
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// Communication to/from External debug module (TODO: to be implemented)
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
|
||||
// Debugger run-control
|
||||
FIFOF #(Bool) f_run_halt_reqs <- mkFIFOF;
|
||||
FIFOF #(Bool) f_run_halt_rsps <- mkFIFOF;
|
||||
|
||||
// Stop-request from debugger (e.g., GDB ^C or Dsharp 'stop')
|
||||
Reg #(Bool) rg_stop_req <- mkReg (False);
|
||||
|
||||
// Count instrs after step-request from debugger (via dcsr.step)
|
||||
Reg #(Bit #(1)) rg_step_count <- mkReg (0);
|
||||
|
||||
// Debugger GPR read/write request/response
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_gpr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( XLEN)) f_gpr_rsps <- mkFIFOF1;
|
||||
|
||||
`ifdef ISA_F
|
||||
// Debugger FPR read/write request/response
|
||||
FIFOF #(MemoryRequest #(5, FLEN)) f_fpr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( FLEN)) f_fpr_rsps <- mkFIFOF1;
|
||||
`endif
|
||||
|
||||
// Debugger CSR read/write request/response
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_csr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( XLEN)) f_csr_rsps <- mkFIFOF1;
|
||||
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Tandem Verification (TODO: to be implemented)
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// MMIO
|
||||
|
||||
MMIO_AXI4_Adapter_IFC mmio_axi4_adapter <- mkMMIO_AXI4_Adapter;
|
||||
|
||||
// MMIO platform
|
||||
Vector#(CoreNum, MMIOCoreToPlatform) mmioToP;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
mmioToP[i] = core[i].mmioToPlatform;
|
||||
end
|
||||
MMIOPlatform mmioPlatform <- mkMMIOPlatform (mmioToP,
|
||||
mmio_axi4_adapter.core_side);
|
||||
|
||||
// last level cache
|
||||
LLCache llc <- mkLLCache;
|
||||
|
||||
// connect LLC to L1 caches
|
||||
Vector#(L1Num, ChildCacheToParent#(L1Way, void)) l1 = ?;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
l1[i] = core[i].dCacheToParent;
|
||||
l1[i + valueof(CoreNum)] = core[i].iCacheToParent;
|
||||
end
|
||||
mkL1LLConnect(llc.to_child, l1);
|
||||
|
||||
// ================================================================
|
||||
// LLC's DMA connections
|
||||
|
||||
// Core's tlbToMem
|
||||
Vector#(CoreNum, TlbMemClient) tlbToMem = ?;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
tlbToMem[i] = core[i].tlbToMem;
|
||||
end
|
||||
|
||||
// Stub out memLoader (TODO: can be Debug Module's access)
|
||||
let memLoaderStub = interface MemLoaderMemClient;
|
||||
interface memReq = nullFifoDeq;
|
||||
interface respSt = nullFifoEnq;
|
||||
endinterface;
|
||||
|
||||
mkLLCDmaConnect(llc.dma, memLoaderStub, tlbToMem);
|
||||
|
||||
// ================================================================
|
||||
// interface LLC to AXI4
|
||||
|
||||
LLC_AXI4_Adapter_IFC llc_axi4_adapter <- mkLLC_AXi4_Adapter (llc.to_mem);
|
||||
|
||||
// ================================================================
|
||||
// Connect stats
|
||||
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
rule broadcastStats;
|
||||
Bool doStats <- core[i].sendDoStats;
|
||||
for(Integer j = 0; j < valueof(CoreNum); j = j+1) begin
|
||||
core[j].recvDoStats(doStats);
|
||||
end
|
||||
llc.perf.setStatus(doStats);
|
||||
endrule
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// Stub out deadlock and renameDebug interfaces
|
||||
|
||||
for(Integer j = 0; j < valueof(CoreNum); j = j+1) begin
|
||||
rule rl_dummy1;
|
||||
let x <- core[j].deadlock.dCacheCRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy2;
|
||||
let x <- core[j].deadlock.dCachePRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy3;
|
||||
let x <- core[j].deadlock.iCacheCRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy4;
|
||||
let x <- core[j].deadlock.iCachePRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy5;
|
||||
let x <- core[j].deadlock.renameInstStuck.get;
|
||||
endrule
|
||||
rule rl_dummy6;
|
||||
let x <- core[j].deadlock.renameCorrectPathStuck.get;
|
||||
endrule
|
||||
rule rl_dummy7;
|
||||
let x <- core[j].deadlock.commitInstStuck.get;
|
||||
endrule
|
||||
rule rl_dummy8;
|
||||
let x <- core[j].deadlock.commitUserInstStuck.get;
|
||||
endrule
|
||||
rule rl_dummy9;
|
||||
let x <- core[j].deadlock.checkStarted.get;
|
||||
endrule
|
||||
|
||||
rule rl_dummy20;
|
||||
let x <- core[j].renameDebug.renameErr.get;
|
||||
endrule
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// Reset
|
||||
|
||||
rule rl_reset;
|
||||
let x <- pop (f_reset_reqs);
|
||||
|
||||
llc_axi4_adapter.reset;
|
||||
mmio_axi4_adapter.reset;
|
||||
|
||||
f_reset_rsps.enq (?);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Termination detection
|
||||
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
rule rl_terminate;
|
||||
let x <- core[i].coreIndInv.terminate;
|
||||
$display ("Core %d terminated", i);
|
||||
endrule
|
||||
end
|
||||
|
||||
// Print out values written 'tohost'
|
||||
rule rl_tohost;
|
||||
let x <- mmioPlatform.to_host;
|
||||
$display ("mmioPlatform.rl_tohost: 0x%0x (= %0d)", x, x);
|
||||
if (x != 0) begin
|
||||
// Standard RISC-V ISA tests finish by writing a value tohost with x[0]==1.
|
||||
// Further when x[63:1]==0, all tests within the program pass,
|
||||
// otherwise x[63:1] = the test within the program that failed.
|
||||
let failed_testnum = (x >> 1);
|
||||
if (failed_testnum == 0)
|
||||
$display ("PASS");
|
||||
else
|
||||
$display ("FAIL %0d", failed_testnum);
|
||||
$finish (0);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Reset
|
||||
interface Server hart0_server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// ----------------
|
||||
// Start the cores running
|
||||
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
|
||||
action
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1)
|
||||
core[i].coreReq.start (startpc, tohostAddr, fromhostAddr);
|
||||
endaction
|
||||
|
||||
mmioPlatform.start (tohostAddr, fromhostAddr);
|
||||
|
||||
$display ("Proc.start: startpc = 0x%0h, tohostAddr = 0x%0h, fromhostAddr = %0h",
|
||||
startpc, tohostAddr, fromhostAddr);
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// SoC fabric connections
|
||||
|
||||
// Fabric master interface for memory (from LLC)
|
||||
interface master0 = llc_axi4_adapter.mem_master;
|
||||
|
||||
// Fabric master interface for IO (from MMIOPlatform)
|
||||
interface master1 = mmio_axi4_adapter.mmio_master;
|
||||
|
||||
// ----------------
|
||||
// External interrupts
|
||||
|
||||
method Action m_external_interrupt_req (x);
|
||||
core[0].setMEIP (pack (x));
|
||||
endmethod
|
||||
|
||||
method Action s_external_interrupt_req (x);
|
||||
core[0].setSEIP (pack (x));
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Non-maskable interrupt
|
||||
|
||||
// TODO: fixup: NMIs should send CPU to an NMI vector (TBD in SoC_Map)
|
||||
method Action non_maskable_interrupt_req (Bool set_not_clear) = noAction;
|
||||
|
||||
// ----------------
|
||||
// For tracing
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
cfg_verbosity <= verbosity;
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Tandem Verifier
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Debug Module
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// run-control, other
|
||||
interface Server hart0_server_run_halt = toGPServer (f_run_halt_reqs, f_run_halt_rsps);
|
||||
|
||||
interface Put hart0_put_other_req;
|
||||
method Action put (Bit #(4) req);
|
||||
cfg_verbosity <= req;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
// GPR access
|
||||
interface MemoryServer hart0_gpr_mem_server = toGPServer (f_gpr_reqs, f_gpr_rsps);
|
||||
|
||||
`ifdef ISA_F
|
||||
// FPR access
|
||||
interface MemoryServer hart0_fpr_mem_server = toGPServer (f_fpr_reqs, f_fpr_rsps);
|
||||
`endif
|
||||
|
||||
// CSR access
|
||||
interface MemoryServer hart0_csr_mem_server = toGPServer (f_csr_reqs, f_csr_rsps);
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
endpackage
|
||||
99
src_Core/CPU/Proc_IFC.bsv
Normal file
99
src_Core/CPU/Proc_IFC.bsv
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Proc_IFC;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Memory :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
`endif
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// CPU interface
|
||||
|
||||
// Note: this Proc_IFC is similar, but not identical to CPU_IFC for Piccolo and Flute
|
||||
// Specifically, it removes interfaces for software and timer,
|
||||
// because the RISCY-OOO mkProc contains those elements.
|
||||
|
||||
interface Proc_IFC;
|
||||
// Reset
|
||||
interface Server #(Token, Token) hart0_server_reset;
|
||||
|
||||
// ----------------
|
||||
// Start the cores running
|
||||
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
|
||||
|
||||
// ----------------
|
||||
// SoC fabric connections
|
||||
|
||||
// Fabric master interface for memory (from LLC)
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master0;
|
||||
|
||||
// Fabric master interface for IO (from MMIOPlatform)
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master1;
|
||||
|
||||
// ----------------
|
||||
// External interrupts
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
method Action m_external_interrupt_req (Bool set_not_clear);
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
method Action s_external_interrupt_req (Bool set_not_clear);
|
||||
|
||||
// ----------------
|
||||
// Non-maskable interrupt
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
method Action non_maskable_interrupt_req (Bool set_not_clear);
|
||||
|
||||
// ----------------
|
||||
// Set core's verbosity
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Tandem Verifier
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Debug Module
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// run-control, other
|
||||
interface Server #(Bool, Bool) hart0_server_run_halt;
|
||||
interface Put #(Bit #(4)) hart0_put_other_req;
|
||||
|
||||
// GPR access
|
||||
interface MemoryServer #(5, XLEN) hart0_gpr_mem_server;
|
||||
|
||||
`ifdef ISA_F
|
||||
// FPR access
|
||||
interface MemoryServer #(5, FLEN) hart0_fpr_mem_server;
|
||||
`endif
|
||||
|
||||
// CSR access
|
||||
interface MemoryServer #(12, XLEN) hart0_csr_mem_server;
|
||||
`endif
|
||||
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
Reference in New Issue
Block a user