Initial load of files

This commit is contained in:
rsnikhil
2019-03-26 14:49:40 -04:00
parent bc62f17032
commit ee24a93944
1008 changed files with 354221 additions and 224 deletions

View File

@@ -0,0 +1,21 @@
Copyright (c) 2017, 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.

153
src_Core/RISCY_OOO/Makefile Normal file
View File

@@ -0,0 +1,153 @@
# This Makefile copies source BSV files from the MIT riscy-OOO repo
# for use in the Bluespec environment
RISCY_HOME = ~/Projects/RISCV/MIT-riscy/riscy-OOO
copyfiles: copy_RV64_OOO_files copy_procs_lib_files copy_coherence_src_files copy_fpgautils_files copy_connectal_files
PROCS_RV64G_OOO = procs/RV64G_OOO
.PHONY: copy_RV64_OOO_files
copy_RV64_OOO_files:
mkdir -p $(PROCS_RV64G_OOO)
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/AluExePipeline.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/CommitStage.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/FetchStage.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/FpuMulDivExePipeline.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/MemExePipeline.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ProcConfig.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/RenameStage.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReorderBufferSynth.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationAlu.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationFpuMulDiv.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationMem.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/RFileSynth.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ScoreboardSynth.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/SynthParam.bsv ./$(PROCS_RV64G_OOO)/
PROCS_LIB = procs/lib
.PHONY: copy_procs_lib_files
copy_procs_lib_files:
mkdir -p $(PROCS_LIB)
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Amo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Bht.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/BrPred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Btb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Bypass.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/CacheUtils.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ConcatReg.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Decode.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/DirPredictor.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/DTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Ehr.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/EpochManager.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Exec.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Fifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Fpu.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/FullAssocTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GlobalBrHistReg.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GlobalSpecUpdate.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GSelectPred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GSharePred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/HasSpecBits.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ITlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L1CoCache.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L1LLConnect.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L2SetAssocTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L2Tlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LatencyTimer.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCache.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCDmaConnect.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCRqMshrSecureModel.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemLoader.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemLoaderIF.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemoryTypes.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOAddrs.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOCore.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOInst.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MsgFifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MulDiv.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Performance.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/PhysRFile.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ProcTypes.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Ras.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/RenameDebugIF.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/RenamingTable.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ReorderBuffer.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ReservationStationEhr.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SafeCounter.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Scoreboard.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SetAssocTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecFifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecPoisonFifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecTagManager.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SplitLSQ.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/StoreBuffer.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TlbConnect.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TlbTypes.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TourPred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TourPredSecure.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TranslationCache.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Types.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/VerificationPacket.bsv ./$(PROCS_LIB)/
COHERENCE_SRC = coherence/src
.PHONY: copy_coherence_src_files
copy_coherence_src_files:
mkdir -p $(COHERENCE_SRC)
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CCPipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CCTypes.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CrossBar.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/IBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/ICRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/IPRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1Bank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1CRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1Pipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1PRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLCRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLPipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/MshrDeadlockChecker.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/RandomReplace.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/RWBramCore.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvIBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvIPipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvL1Bank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvL1Pipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvLLBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvLLPipe.bsv ./$(COHERENCE_SRC)/
FPGAUTILS_LIB = fpgautils/lib
FPGAUTILS_XILINX_FPU = fpgautils/xilinx/fpu
FPGAUTILS_XILINX_RESET_REGS = fpgautils/xilinx/reset_regs
.PHONY: copy_fpgautils_files
copy_fpgautils_files:
mkdir -p $(FPGAUTILS_LIB)
mkdir -p $(FPGAUTILS_XILINX_FPU)
mkdir -p $(FPGAUTILS_XILINX_RESET_REGS)
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/DramCommon.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/ResetGuard.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/SyncFifo.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxFpu.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxIntDiv.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxIntMul.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxSyncFifo.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/WaitAutoReset.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_sqrt_sim.v ./$(FPGAUTILS_XILINX_FPU)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_fma_sim.v ./$(FPGAUTILS_XILINX_FPU)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_div_sim.v ./$(FPGAUTILS_XILINX_FPU)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_RESET_REGS)/reset_guard.v ./$(FPGAUTILS_XILINX_RESET_REGS)/
CONNECTAL_BSV = connectal/bsv
CONNECTAL_LIB_BSV = connectal/lib/bsv
CONNECTAL_TESTS_SPI = connectal/tests/spi
.PHONY: copy_connectal_files
copy_connectal_files:
mkdir -p $(CONNECTAL_BSV)
mkdir -p $(CONNECTAL_LIB_BSV)
mkdir -p $(CONNECTAL_TESTS_SPI)
cp -p $(RISCY_HOME)/$(CONNECTAL_BSV)/ConnectalBramFifo.bsv ./$(CONNECTAL_BSV)/
cp -p $(RISCY_HOME)/$(CONNECTAL_BSV)/ConnectalClocks.bsv ./$(CONNECTAL_BSV)/
cp -p $(RISCY_HOME)/$(CONNECTAL_LIB_BSV)/Arith.bsv ./$(CONNECTAL_LIB_BSV)/
cp -p $(RISCY_HOME)/$(CONNECTAL_TESTS_SPI)/ConnectalProjectConfig.bsv ./$(CONNECTAL_TESTS_SPI)/
tree .

View File

@@ -0,0 +1,408 @@
// 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.
import Ehr::*;
import Fifo::*;
import Vector::*;
import RWBramCore::*;
import FShow::*;
import Types::*;
import CCTypes::*;
// general type param ordering: way < index < tag < msi < dir < owner < other < rep < line < pipeCmd
typedef union tagged {
void Invalid;
msiT DownDir; // cRs downgraded toState
msiT UpCs; // pRs upgraded toState
} RespState#(type msiT) deriving(Bits, Eq, FShow);
typedef struct {
pipeCmdT cmd;
// tag match & ram output
wayT way;
Bool pRqMiss; // pRq miss, valid only if go through tag match
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram;
// replace info, actually not needed, just output for debug purposes
repT repInfo;
} PipeOut#(
type wayT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
interface CCPipe#(
numeric type wayNum,
type indexT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
);
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, RespState#(msiT) toState);
method Bool notFull;
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) first;
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) unguard_first;
method Bool notEmpty;
method Action deqWrite(
Maybe#(pipeCmdT) newCmd,
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) wrRam,
Bool updateRep // update replacement info
);
// empty signal when we need to flush self-invalidate cache
method Bool emptyForFlush;
endinterface
// internal pipeline reg types
// three stages
// 1: enq
// 2: tag match, read data and dir
// 3: output
typedef struct {
pipeCmdT cmd;
// bypasses
Vector#(wayNum, Maybe#(CacheInfo#(tagT, msiT, dirT, ownerT, otherT))) infoVec;
Maybe#(repT) repInfo; // replacement info for the whole set
// CRs/PRs info
Maybe#(lineT) respLine;
RespState#(msiT) toState;
} Enq2Match#(
numeric type wayNum,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
typedef struct {
pipeCmdT cmd;
// tag match results
wayT way;
Bool pRqMiss;
// RAM outputs
// cs is merged with PRs toState
// dir is merged with CRs toState
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
repT repInfo;
// bypassed or resp line
Maybe#(lineT) line;
} Match2Out#(
type wayT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
typedef struct {
indexT index;
wayT way;
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram; // data to write into RAM
repT repInfo; // replacement info write into RAM
} BypassInfo#(
type wayT,
type indexT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT
) deriving(Bits, Eq, FShow);
typedef struct {
wayT way;
Bool pRqMiss;
} TagMatchResult#(type wayT) deriving(Bits, Eq, FShow);
typedef struct {
msiT cs;
} UpdateByUpCs#(type msiT) deriving(Bits, Eq, FShow);
typedef struct {
msiT cs;
dirT dir;
} UpdateByDownDir#(type msiT, type dirT) deriving(Bits, Eq, FShow);
// index to data ram: {way, normal index}
function dataIndexT getDataRamIndex(wayT w, indexT i) provisos(
Alias#(wayT, Bit#(_waySz)),
Alias#(indexT, Bit#(_indexSz)),
Alias#(dataIndexT, Bit#(TAdd#(_waySz, _indexSz)))
);
return {w, i};
endfunction
module mkCCPipe#(
ReadOnly#(Bool) initDone,
function indexT getIndex(pipeCmdT cmd),
function ActionValue#(TagMatchResult#(wayT)) tagMatch(
// actionvalue enable us to do checking inside the function
pipeCmdT cmd,
// below are current RAM outputs, is merged with ram write from final stage
// but is NOT merged with state changes carried in PRs/CRs
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, msiT) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
),
function ActionValue#(UpdateByUpCs#(msiT)) updateByUpCs(
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs
),
function ActionValue#(UpdateByDownDir#(msiT, dirT)) updateByDownDir(
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs, dirT oldDir
),
function ActionValue#(repT) updateRepInfo(repT oldRep, wayT hitWay),
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam,
RWBramCore#(indexT, repT) repRam,
RWBramCore#(dataIndexT, lineT) dataRam
)(
CCPipe#(wayNum, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)
) provisos (
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(_indexSz)),
Alias#(infoT, CacheInfo#(tagT, msiT, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, msiT, dirT, ownerT, otherT, lineT)),
Alias#(respStateT, RespState#(msiT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(enq2MatchT, Enq2Match#(wayNum, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(match2OutT, Match2Out#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(bypassInfoT, BypassInfo#(wayT, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT)),
Bits#(tagT, _tagSz),
Bits#(msiT, _msiSz),
Bits#(dirT, _dirSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz),
Bits#(repT, _repSz),
Bits#(lineT, _lineSz),
Bits#(pipeCmdT, _pipeCmdSz),
// index to data ram: {way, normal index}
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), _indexSz)))
);
// pipeline regs
Ehr#(3, Maybe#(enq2MatchT)) enq2Mat <- mkEhr(Invalid);
// port 0: bypass
Reg#(Maybe#(enq2MatchT)) enq2Mat_bypass = enq2Mat[0];
// port 1: tag match
Reg#(Maybe#(enq2MatchT)) enq2Mat_match = enq2Mat[1];
// port 2: enq
Reg#(Maybe#(enq2MatchT)) enq2Mat_enq = enq2Mat[2];
Ehr#(2, Maybe#(match2OutT)) mat2Out <- mkEhr(Invalid);
// port 0: out
Reg#(Maybe#(match2OutT)) mat2Out_out = mat2Out[0];
// port 1: tag match
Reg#(Maybe#(match2OutT)) mat2Out_match = mat2Out[1];
// bypass write to ram
RWire#(bypassInfoT) bypass <- mkRWire;
// stage 2: first get bypass
(* fire_when_enabled, no_implicit_conditions *)
rule doMatch_bypass(isValid(bypass.wget) && isValid(enq2Mat_bypass) && initDone);
bypassInfoT b = fromMaybe(?, bypass.wget);
enq2MatchT e2m = fromMaybe(?, enq2Mat_bypass);
if(b.index == getIndex(e2m.cmd)) begin
e2m.infoVec[b.way] = Valid (b.ram.info);
e2m.repInfo = Valid (b.repInfo);
end
enq2Mat_bypass <= Valid (e2m);
endrule
rule doTagMatch(isValid(enq2Mat_match) && !isValid(mat2Out_match) && initDone);
enq2MatchT e2m = fromMaybe(?, enq2Mat_match);
// get cache output & merge with bypass
Vector#(wayNum, infoT) infoVec;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].deqRdResp;
infoVec[i] = fromMaybe(infoRam[i].rdResp, e2m.infoVec[i]);
end
repRam.deqRdResp;
repT repInfo = fromMaybe(repRam.rdResp, e2m.repInfo);
// do tag match to get way to occupy
Vector#(wayNum, tagT) tagVec;
Vector#(wayNum, msiT) csVec;
Vector#(wayNum, ownerT) ownerVec;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
tagVec[i] = infoVec[i].tag;
csVec[i] = infoVec[i].cs;
ownerVec[i] = infoVec[i].owner;
end
let tmRes <- tagMatch(e2m.cmd, tagVec, csVec, ownerVec, repInfo);
wayT way = tmRes.way;
Bool pRqMiss = tmRes.pRqMiss;
// read data
indexT index = getIndex(e2m.cmd);
dataRam.rdReq(getDataRamIndex(way, index));
// set mat2out & merge with CRs/PRs & merge with data bypass
// resp data has higher priority than data bypass
match2OutT m2o = Match2Out {
cmd: e2m.cmd,
way: way,
pRqMiss: pRqMiss,
info: infoVec[way],
repInfo: repInfo,
line: e2m.respLine
};
if(e2m.toState matches tagged UpCs .s) begin
UpdateByUpCs#(msiT) upd <- updateByUpCs(
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs
);
m2o.info.cs = upd.cs;
end
else if(e2m.toState matches tagged DownDir .s) begin
UpdateByDownDir#(msiT, dirT) upd <- updateByDownDir(
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs, m2o.info.dir
);
m2o.info.cs = upd.cs;
m2o.info.dir = upd.dir;
end
if(bypass.wget matches tagged Valid .b &&& b.index == index &&& b.way == way &&& !isValid(m2o.line)) begin
// bypass has lower priority than resp data
m2o.line = Valid (b.ram.line);
end
mat2Out_match <= Valid (m2o);
// reset enq2mat
enq2Mat_match <= Invalid;
endrule
// construct output with bypass/resp data
function pipeOutT firstOut;
match2OutT m2o = fromMaybe(?, mat2Out_out);
return PipeOut {
cmd: m2o.cmd,
way: m2o.way,
pRqMiss: m2o.pRqMiss,
ram: RamData {
info: m2o.info,
line: fromMaybe(dataRam.rdResp, m2o.line)
},
repInfo: m2o.repInfo
};
endfunction
Bool enq_guard = !isValid(enq2Mat_enq) && initDone;
Bool deq_guard = isValid(mat2Out_out) && initDone;
// stage 1: enq req to pipeline: access info+rep RAM & bypass
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, respStateT toState) if(enq_guard);
// read ram
indexT index = getIndex(cmd);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].rdReq(index);
end
repRam.rdReq(index);
// write reg & get bypass
enq2MatchT e2m = Enq2Match {
cmd: cmd,
infoVec: replicate(Invalid),
repInfo: Invalid,
respLine: respLine,
toState: toState
};
if(bypass.wget matches tagged Valid .b &&& b.index == index) begin
e2m.infoVec[b.way] = Valid (b.ram.info);
e2m.repInfo = Valid (b.repInfo);
end
enq2Mat_enq <= Valid (e2m);
endmethod
method Bool notFull = enq_guard;
method pipeOutT first if(deq_guard);
return firstOut;
endmethod
method pipeOutT unguard_first;
return firstOut;
endmethod
method Bool notEmpty = deq_guard;
method Action deqWrite(Maybe#(pipeCmdT) newCmd, ramDataT wrRam, Bool updateRep) if(deq_guard);
match2OutT m2o = fromMaybe(?, mat2Out_out);
wayT way = m2o.way;
indexT index = getIndex(m2o.cmd);
// update replacement info
repT repInfo = m2o.repInfo;
if(updateRep) begin
repInfo <- updateRepInfo(m2o.repInfo, way);
end
// write ram
infoRam[way].wrReq(index, wrRam.info);
repRam.wrReq(index, repInfo);
dataRam.wrReq(getDataRamIndex(way, index), wrRam.line);
// set bypass to Enq and Match stages
bypass.wset(BypassInfo {
index: index,
way: way,
ram: wrRam,
repInfo: repInfo
});
// change pipeline reg
if(newCmd matches tagged Valid .cmd) begin
// update pipeline reg
mat2Out_out <= Valid (Match2Out {
cmd: cmd, // swapped in new cmd
way: way, // keep way same
pRqMiss: False, // reset (not valid for swapped in pRq)
info: wrRam.info, // get bypass
repInfo: repInfo, // get bypass
line: Valid (wrRam.line) // get bypass
});
end
else begin
// XXX deq ram resp, I think this should not block
dataRam.deqRdResp;
// reset pipeline reg
mat2Out_out <= Invalid;
end
endmethod
method Bool emptyForFlush;
return !isValid(mat2Out[0]) && !isValid(enq2Mat[0]);
endmethod
endmodule

View File

@@ -0,0 +1,464 @@
// 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.
import Types::*; // import from RISCY repo
import MemoryTypes::*; // import from RISCY repo
import Vector::*;
import FShow::*;
import CacheUtils::*;
import Assert::*;
import Connectable::*;
import GetPut::*;
import ClientServer::*;
typedef enum {
I = 2'd0,
S = 2'd1,
E = 2'd2,
M = 2'd3
} MESI deriving(Bits, Eq, FShow);
typedef MESI Msi;
instance Ord#(MESI);
function Bool \< ( MESI x, MESI y );
return pack(x) < pack(y);
endfunction
function Bool \<= ( MESI x, MESI y );
return pack(x) <= pack(y);
endfunction
function Bool \> ( MESI x, MESI y );
return pack(x) > pack(y);
endfunction
function Bool \>= ( MESI x, MESI y );
return pack(x) >= pack(y);
endfunction
function Ordering compare( MESI x, MESI y );
return compare(pack(x), pack(y));
endfunction
function MESI min( MESI x, MESI y );
return x < y ? x : y;
endfunction
function MESI max( MESI x, MESI y );
return x > y ? x : y;
endfunction
endinstance
// whether cache state is enough to serice upgrade req, i.e., no
// need to req parent
function Bool enoughCacheState(Msi cs, Msi to);
return cs >= to || cs >= E;
endfunction
// the maximum dir state that a peer can have so that it will not
// be downgraded to service upgrade req to state x
function Msi toCompat(Msi x);
return x == S ? S : I;
endfunction
typedef TDiv#(DataSz, 8) DataSzBytes;
typedef TLog#(DataSzBytes) LgDataSzBytes;
typedef Bit#(LgDataSzBytes) DataBytesOffset;
typedef TDiv#(InstSz, 8) InstSzBytes;
typedef TLog#(InstSzBytes) LgInstSzBytes;
// 64B cache line -- XXX same with parameters in CacheUtils.bsv
typedef CacheUtils::LogCLineNumData LgLineSzData;
typedef CacheUtils::LogCLineNumBytes LgLineSzBytes;
typedef CacheUtils::CLineAddrSz LineAddrSz;
typedef CacheUtils::CLineAddr LineAddr;
typedef CacheUtils::CLineNumData LineSzData;
typedef CacheUtils::CLineDataSel LineDataOffset;
typedef CacheUtils::CLineNumBytes LineSzBytes;
typedef CacheUtils::CLineByteEn LineByteEn;
typedef TDiv#(LineSzBytes, InstSzBytes) LineSzInst;
typedef Bit#(TLog#(LineSzInst)) LineInstOffset;
typedef Vector#(LineSzData, Data) Line;
function LineAddr getLineAddr(Addr addr) = truncateLSB(addr);
function LineDataOffset getLineDataOffset(Addr a);
return truncate(a >> valueOf(LgDataSzBytes));
endfunction
function LineInstOffset getLineInstOffset(Addr a);
return truncate(a >> valueof(LgInstSzBytes));
endfunction
function Line getUpdatedLine(Line curLine, LineByteEn wrBE, Line wrLine);
Vector#(LineSzBytes, Bit#(8)) curVec = unpack(pack(curLine));
Vector#(LineSzBytes, Bit#(8)) wrVec = unpack(pack(wrLine));
function Bit#(8) getNewByte(Integer i);
return wrBE[i] ? wrVec[i] : curVec[i];
endfunction
Vector#(LineSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
return unpack(pack(newVec));
endfunction
function Data getUpdatedData(Data curData, ByteEn wrBE, Data wrData);
Vector#(DataSzBytes, Bit#(8)) curVec = unpack(pack(curData));
Vector#(DataSzBytes, Bit#(8)) wrVec = unpack(pack(wrData));
function Bit#(8) getNewByte(Integer i);
return wrBE[i] ? wrVec[i] : curVec[i];
endfunction
Vector#(DataSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
return pack(newVec);
endfunction
// calculate tag
typedef TSub#(AddrSz, TAdd#(LgLineSzBytes, TAdd#(lgBankNum, lgSetNum)))
GetTagSz#(numeric type lgBankNum, numeric type lgSetNum);
// dependency tracking in MSHR
typedef union tagged {
cRqIdxT CRq;
pRqIdxT PRq;
} MshrIndex#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
// cache owner
typedef struct {
cRqIdxT mshrIdx;
Bool replacing;
} CRqOwner#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
pRqIdxT mshrIdx;
Bool hasSucc; // has successor in dependency chain
} PRqOwner#(type pRqIdxT) deriving(Bits, Eq, FShow);
typedef union tagged {
void Invalid;
CRqOwner#(cRqIdxT) CRq;
PRqOwner#(pRqIdxT) PRq;
} CacheOwner#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
// cache info: tag, cs, dir, owner, and other
typedef struct {
tagT tag;
msiT cs;
dirT dir;
ownerT owner;
otherT other;
} CacheInfo#(
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT
) deriving(Bits, Eq, FShow);
// ram output
typedef struct {
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
lineT line;
} RamData#(
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type lineT
) deriving(Bits, Eq, FShow);
// processor req/resp
typedef struct {
idT id;
Addr addr;
Msi toState;
// below are detailed mem op
MemOp op; // Ld, St, Lr, Sc, Amo
ByteEn byteEn; // valid when op == Sc
Data data; // valid when op == Sc/Amo
AmoInst amoInst; // valid when op == Amo
} ProcRq#(type idT) deriving(Bits, Eq, FShow);
interface L1ProcReq#(type idT);
method Action req(ProcRq#(idT) r);
endinterface
interface L1ProcResp#(type idT);
method Action respLd(idT id, Data resp);
method Action respLrScAmo(idT id, Data resp);
method ActionValue#(Tuple2#(LineByteEn, Line)) respSt(idT id);
method Action evict(LineAddr a); // called when cache line is evicted
endinterface
// RISCV-specific store-cond return values
typedef 0 ScSuccVal;
typedef 1 ScFailVal;
`ifdef DEBUG_ICACHE
typedef struct {
Bit#(64) id;
Line line;
} DebugICacheResp deriving(Bits, Eq, FShow);
`endif
// I$ req/resp
interface InstServer#(numeric type supSz);
interface Put#(Addr) req;
interface Get#(Vector#(supSz, Maybe#(Instruction))) resp;
`ifdef DEBUG_ICACHE
interface Get#(DebugICacheResp) done; // the id and cache line of the I$ req that truly performs
`endif
endinterface
typedef struct {
Addr addr;
`ifdef DEBUG_ICACHE
Bit#(64) id; // incremening id for each incoming I$ req (0,1,...)
`endif
} ProcRqToI deriving(Bits, Eq, FShow);
// child/parent req/resp
typedef struct {
Addr addr;
Msi fromState;
Msi toState;
Bool canUpToE; // meaningful to upgrade to E if toState is S
idT id; // slot id in child cache
childT child; // from which child
} CRqMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
childT child; // from which child
} CRsMsg#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
childT child; // to which child
} PRqMsg#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
childT child; // to which child
Maybe#(Line) data;
idT id; // slot id in cache
} PRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef union tagged {
PRqMsg#(childT) PRq;
PRsMsg#(idT, childT) PRs;
} PRqRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
interface ChildCacheToParent#(type cRqIdT, type childT);
interface FifoDeq#(CRsMsg#(childT)) rsToP;
interface FifoDeq#(CRqMsg#(cRqIdT, childT)) rqToP;
interface FifoEnq#(PRqRsMsg#(cRqIdT, childT)) fromP;
endinterface
interface ParentCacheToChild#(type cRqIdT, type childT);
interface FifoEnq#(CRsMsg#(childT)) rsFromC;
interface FifoEnq#(CRqMsg#(cRqIdT, childT)) rqFromC;
interface FifoDeq#(PRqRsMsg#(cRqIdT, childT)) toC;
endinterface
// unified child req & dma req
typedef union tagged {
cRqIdT Child;
dmaRqIdT Dma;
} LLRqId#(type cRqIdT, type dmaRqIdT) deriving(Bits, Eq, FShow);
typedef struct {
// common addr
Addr addr;
// child req stuff
Msi fromState;
Msi toState;
Bool canUpToE;
childT child;
// dma req stuff
LineByteEn byteEn;
// req id: distinguish between child and dma
LLRqId#(cRqIdT, dmaRqIdT) id;
} LLRq#(type cRqIdT, type dmaRqIdT, type childT) deriving(Bits, Eq, FShow);
// memory msg
typedef struct {
Addr addr;
childT child; // from which LLC/Dir
idT id; // ld req id and other info need encoding
} LdMemRq#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef struct { // LdMemRq id with more info encoded to handle DMA req in LLC
Bool refill; // the future mem resp will refill LLC cache line
// this is False for DMA read req that miss in LLC (i.e. resp won't refill LLC)
mshrIdxT mshrIdx; // mshr id
} LdMemRqId#(type mshrIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
LineByteEn byteEn;
Line data;
} WbMemRs deriving(Bits, Eq, FShow);
typedef union tagged {
LdMemRq#(idT, childT) Ld;
WbMemRs Wb;
} ToMemMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef struct {
Line data;
childT child; // send to which LLC/Dir
idT id; // original Ld req id
} MemRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
// Dma req/resp
typedef struct {
Addr addr;
LineByteEn byteEn; // all False means read
Line data;
idT id; // req id (resp may come out of order, may contain routing info)
} DmaRq#(type idT) deriving(Bits, Eq, FShow);
typedef struct {
Line data; // meaningless for write
idT id;
} DmaRs#(type idT) deriving(Bits, Eq, FShow);
interface DmaServer#(type dmaRqIdT);
interface FifoEnq#(DmaRq#(dmaRqIdT)) memReq;
interface FifoDeq#(DmaRs#(dmaRqIdT)) respLd;
interface FifoDeq#(dmaRqIdT) respSt;
`ifdef DEBUG_DMA
// signal when DMA req really takes effect
interface Get#(dmaRqIdT) wrMissResp;
interface Get#(dmaRqIdT) wrHitResp;
interface Get#(dmaRqIdT) rdMissResp;
interface Get#(dmaRqIdT) rdHitResp;
`endif
endinterface
// memory interface
interface MemFifoServer#(type idT, type childT);
interface FifoEnq#(ToMemMsg#(idT, childT)) fromC;
interface FifoDeq#(MemRsMsg#(idT, childT)) rsToC;
endinterface
interface MemFifoClient#(type idT, type childT);
interface FifoDeq#(ToMemMsg#(idT, childT)) toM;
interface FifoEnq#(MemRsMsg#(idT, childT)) rsFromM;
endinterface
instance Connectable#(MemFifoServer#(idT, childT), MemFifoClient#(idT, childT));
module mkConnection#(
MemFifoServer#(idT, childT) server,
MemFifoClient#(idT, childT) client
)(Empty);
rule doCToM;
client.toM.deq;
server.fromC.enq(client.toM.first);
endrule
rule doMToC;
server.rsToC.deq;
client.rsFromM.enq(server.rsToC.first);
endrule
endmodule
endinstance
instance Connectable#(MemFifoClient#(idT, childT), MemFifoServer#(idT, childT));
module mkConnection#(
MemFifoClient#(idT, childT) client,
MemFifoServer#(idT, childT) server
)(Empty);
mkConnection(server, client);
endmodule
endinstance
// MSHR dir pending bits
typedef union tagged {
void Invalid;
Msi ToSend; // need to send down req to downgrade to some state
Msi Waiting; // waiting for down resp for some state
} DirPend deriving(Bits, Eq, FShow);
function Vector#(childNum, DirPend) getDirPendInitVal;
return replicate(Invalid);
endfunction
function Bool getNeedReqChild(Vector#(childNum, DirPend) dirPend);
// function to determine whether we need to send req to some children
function Bool isToSend(DirPend dp);
if(dp matches tagged ToSend .s) begin
return True;
end
else begin
return False;
end
endfunction
return any(isToSend, dirPend);
endfunction
// Self-inv cache dir pending bits
typedef union tagged {
void Invalid;
childT ToSend;
childT Waiting;
} SelfInvDirPend#(type childT) deriving(Bits, Eq, FShow);
function SelfInvDirPend#(childT) getSelfInvDirPendInitVal;
return Invalid;
endfunction
function Bool getSelfInvNeedReqChild(SelfInvDirPend#(childT) dirPend);
return dirPend matches tagged ToSend .c ? True : False;
endfunction
// useful functions
function Action check(Bool v);
action
when(v, noAction);
endaction
endfunction
function Maybe#(Bit#(logv)) searchIndex
(function Bool pred(element_type x1), Vector#(vsize, element_type) vect)
provisos (Log#(vsize, logv));
return case (findIndex(pred, vect)) matches
tagged Valid .idx: return tagged Valid pack(idx);
Invalid: return tagged Invalid;
endcase;
endfunction
function a readReg(Reg#(a) r) provisos(Bits#(a, aSz)) = r;
function Vector#(n, a) readVector(Vector#(n, Reg#(a)) vr) provisos(Bits#(a, aSz)) = map(readReg, vr);
// doAssert now defined in Types.bsv
//function Action doAssert(Bool b, String str) = dynamicAssert(b, "%m: " + str);
function Get#(t) nullGet;
return (interface Get;
method ActionValue#(t) get if(False);
return ?;
endmethod
endinterface);
endfunction

View File

@@ -0,0 +1,215 @@
// 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.
import Vector::*;
import GetPut::*;
import Connectable::*;
import CacheUtils::*;
import CCTypes::*;
import Types::*;
import FShow::*;
import Fifo::*;
import Ehr::*;
typedef struct {
dstIdxT idx;
dstDataT data;
} XBarDstInfo#(type dstIdxT, type dstDataT) deriving(Bits, Eq);
module mkXBar#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
Vector#(srcNum, Get#(srcDataT)) srcIfc,
Vector#(dstNum, Put#(dstDataT)) dstIfc
)(Empty) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// proposed data transfer by each src
Vector#(srcNum, Ehr#(2, Maybe#(dstIdxT))) propDstIdx <- replicateM(mkEhr(Invalid));
Vector#(srcNum, Ehr#(2, dstDataT)) propDstData <- replicateM(mkEhr(?));
// enq command that should be carried out
Vector#(dstNum, Ehr#(2, Maybe#(dstDataT))) enqDst <- replicateM(mkEhr(Invalid));
// src propose data transfer when src is not empty
// and there is no unfinished src proposal
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
rule srcPropose(!isValid(propDstIdx[i][0]));
let d <- srcIfc[i].get;
let info = getDstInfo(fromInteger(i), d);
propDstIdx[i][0] <= Valid (info.idx);
propDstData[i][0] <= info.data;
endrule
end
// round-robin select src for each dst
Vector#(dstNum, Reg#(srcIdxT)) srcRR <- replicateM(mkReg(0));
// do selection for each dst & generate enq commands & deq src proposals
// dst which has unfinished enq command cannot select src
(* fire_when_enabled, no_implicit_conditions *)
rule dstSelectSrc;
// which src to deq (i.e. select) by each dst
Vector#(dstNum, Maybe#(srcIdxT)) deqSrcByDst = replicate(Invalid);
// each dst selects
for(Integer dst = 0; dst < valueOf(dstNum); dst = dst + 1) begin
// only select src to deq when dst has no unfinished enq command
if(!isValid(enqDst[dst][0])) begin
function Bool isFromSrc(srcIdxT src);
// src has proposed data to this dst or not
if(propDstIdx[src][1] matches tagged Valid .dstIdx &&& dstIdx == fromInteger(dst)) begin
return True;
end
else begin
return False;
end
endfunction
Maybe#(srcIdxT) whichSrc = Invalid; // the src to select
if(isFromSrc(srcRR[dst])) begin
// first check the src with priority
whichSrc = Valid (srcRR[dst]);
end
else begin
// otherwise just get one valid src
Vector#(srcNum, srcIdxT) srcIdxVec = genWith(fromInteger);
whichSrc = searchIndex(isFromSrc, srcIdxVec);
end
if(whichSrc matches tagged Valid .src) begin
// can do enq & deq
deqSrcByDst[dst] = whichSrc;
enqDst[dst][0] <= Valid (propDstData[src][1]); // set enq command
// change round robin
srcRR[dst] <= srcRR[dst] == fromInteger(valueOf(srcNum) - 1) ? 0 : srcRR[dst] + 1;
end
end
end
// deq selected src
function Bool isDeqSrc(srcIdxT src);
function Bool isMatch(Maybe#(srcIdxT) deqIdx);
return deqIdx matches tagged Valid .idx &&& idx == src ? True : False;
endfunction
return any(isMatch, deqSrcByDst);
endfunction
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
if(isDeqSrc(fromInteger(i))) begin
propDstIdx[i][1] <= Invalid;
$display("%t XBar %m: deq src %d", $time, i);
doAssert(isValid(propDstIdx[i][1]), "src must be proposing");
end
end
endrule
// enq dst & change round robin
for(Integer i = 0; i < valueOf(dstNum); i = i+1) begin
// XXX since dstIfc.put may conflict with other rules
// the following rule should only fire when it can make progress
// otherwise it may prevent other rules from firing forever
rule doEnq(enqDst[i][1] matches tagged Valid .d);
dstIfc[i].put(d);
enqDst[i][1] <= Invalid; // reset enq command
$display("%t XBAR %m: enq dst %d ; ", $time, i, fshow(d));
endrule
end
endmodule
// XBar with latency added at src or dst (mainly for model timing)
interface XBarDelay#(numeric type srcLat, numeric type dstLat);
endinterface
module mkXBarDelay#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
Vector#(srcNum, Get#(srcDataT)) srcIfc,
Vector#(dstNum, Put#(dstDataT)) dstIfc
)(XBarDelay#(srcLat, dstLat)) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// Add latency at src side
Vector#(srcNum, Get#(srcDataT)) src = srcIfc;
if(valueof(srcLat) > 0) begin
for(Integer i = 0; i < valueof(srcNum); i = i+1) begin
Vector#(srcLat, Fifo#(2, srcDataT)) delayQ <- replicateM(mkCFFifo);
mkConnection(srcIfc[i], toPut(delayQ[0]));
for(Integer j = 0; j < valueof(srcLat) - 1; j = j+1) begin
mkConnection(toGet(delayQ[j]), toPut(delayQ[j + 1]));
end
src[i] = toGet(delayQ[valueof(srcLat) - 1]);
end
end
// Add latency at dst side
Vector#(dstNum, Put#(dstDataT)) dst = dstIfc;
if(valueof(dstLat) > 0) begin
for(Integer i = 0; i < valueof(dstNum); i = i+1) begin
Vector#(dstLat, Fifo#(2, dstDataT)) delayQ <- replicateM(mkCFFifo);
mkConnection(toGet(delayQ[0]), dstIfc[i]);
for(Integer j = 0; j < valueof(dstLat) - 1; j = j+1) begin
mkConnection(toGet(delayQ[j + 1]), toPut(delayQ[j]));
end
dst[i] = toPut(delayQ[valueof(dstLat) - 1]);
end
end
mkXBar(getDstInfo, src, dst);
endmodule
interface CrossBar#(
numeric type srcNum,
numeric type srcLat,
type srcDataT,
numeric type dstNum,
numeric type dstLat,
type dstDataT
);
interface Vector#(srcNum, FifoEnq#(srcDataT)) srcIfc;
interface Vector#(dstNum, FifoDeq#(dstDataT)) dstIfc;
endinterface
module mkCrossBar#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data)
)(
CrossBar#(srcNum, srcLat, srcDataT, dstNum, dstLat, dstDataT)
) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// in/out FIFOs
Vector#(srcNum, Fifo#(2, srcDataT)) srcQ <- replicateM(mkCFFifo);
Vector#(dstNum, Fifo#(2, dstDataT)) dstQ <- replicateM(mkCFFifo);
XBarDelay#(srcLat, dstLat) xbar <- mkXBarDelay(getDstInfo, map(toGet, srcQ), map(toPut, dstQ));
interface srcIfc = map(toFifoEnq, srcQ);
interface dstIfc = map(toFifoDeq, dstQ);
endmodule

View File

@@ -0,0 +1,849 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import MemoryTypes::*;
import Amo::*;
import Cntrs::*;
import Vector::*;
import ConfigReg::*;
import FIFO::*;
import GetPut::*;
import ClientServer::*;
import CCTypes::*;
import ICRqMshr::*;
import IPRqMshr::*;
import CCPipe::*;
import L1Pipe ::*;
import FShow::*;
import DefaultValue::*;
import Fifo::*;
import CacheUtils::*;
import Performance::*;
import LatencyTimer::*;
import RandomReplace::*;
export ICRqStuck(..);
export IPRqStuck(..);
export IBank(..);
export mkIBank;
// L1 I$
// although pRq never appears in dependency chain
// we still need pRq MSHR to limit the number of pRq
// and thus limit the size of rsToPIndexQ
typedef struct {
Addr addr;
ICRqState state;
Bool waitP;
} ICRqStuck deriving(Bits, Eq, FShow);
typedef IPRqMshrStuck IPRqStuck;
interface IBank#(
numeric type supSz, // superscalar size
numeric type lgBankNum,
numeric type wayNum,
numeric type indexSz,
numeric type tagSz,
numeric type cRqNum,
numeric type pRqNum
);
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
interface InstServer#(supSz) to_proc; // to child, i.e. processor
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(ICRqStuck) cRqStuck;
interface Get#(IPRqStuck) pRqStuck;
// security: flush
method Action flush;
method Bool flush_done;
// performance
method Action setPerfStatus(Bool stats);
method Data getPerfData(L1IPerfType t);
endinterface
module mkIBank#(
Bit#(lgBankNum) bankId,
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
module#(IPRqMshr#(pRqNum)) mkIPRqMshrLocal,
module#(L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)) mkL1Pipeline
)(
IBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum, pRqNum)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, void)),
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, void, Line)),
Alias#(procRqT, ProcRqToI),
Alias#(cRqToPT, CRqMsg#(wayT, void)),
Alias#(cRsToPT, CRsMsg#(void)),
Alias#(pRqFromPT, PRqMsg#(void)),
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, void, RandRepInfo, Line, l1CmdT)),
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
// requirements
FShow#(pipeOutT),
Add#(tagSz, a__, AddrSz),
// make sure: cRqNum <= wayNum
Add#(cRqNum, b__, wayNum),
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
);
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
IPRqMshr#(pRqNum) pRqMshr <- mkIPRqMshrLocal;
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT) pipeline <- mkL1Pipeline;
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
Fifo#(2, cRsToPT) rsToPQ <- mkCFFifo;
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
FIFO#(MshrIndex#(cRqIdxT, pRqIdxT)) rsToPIndexQ <- mkSizedFIFO(valueOf(TAdd#(cRqNum, pRqNum)));
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
// temp fifo for pipelineResp & sendRsToP (reduce conflict)
FIFO#(cRqIdxT) rqToPIndexQ_pipelineResp <- mkFIFO;
FIFO#(cRqIdxT) rqToPIndexQ_sendRsToP <- mkFIFO;
// index Q to order all in flight cRq for in-order resp
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
`ifdef DEBUG_ICACHE
// id for each cRq, incremented when each new req comes
Reg#(Bit#(64)) cRqId <- mkReg(0);
// FIFO to signal the id of cRq that is performed
// FIFO has 0 cycle latency to match L1 D$ resp latency
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
`endif
// security flush
`ifdef SECURITY
Reg#(Bool) flushDone <- mkReg(True);
Reg#(Bool) flushReqStart <- mkReg(False);
Reg#(Bool) flushReqDone <- mkReg(False);
Reg#(Bool) flushRespDone <- mkReg(False);
Reg#(indexT) flushIndex <- mkReg(0);
Reg#(wayT) flushWay <- mkReg(0);
`else
Bool flushDone = True;
`endif
`ifdef PERF_COUNT
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) ldCnt <- mkCount(0);
Count#(Data) ldMissCnt <- mkCount(0);
Count#(Data) ldMissLat <- mkCount(0);
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
function Action incrReqCnt;
action
if(doStats) begin
ldCnt.incr(1);
end
endaction
endfunction
function Action incrMissCnt(cRqIdxT idx);
action
let lat <- latTimer.done(idx);
if(doStats) begin
ldMissLat.incr(zeroExtend(lat));
ldMissCnt.incr(1);
end
endaction
endfunction
`endif
function tagT getTag(Addr a) = truncateLSB(a);
// XXX since I$ may be requested by processor constantly
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
// otherwise the whole system may deadlock/livelock
// we stop accepting cRq when we need to flush for security
rule cRqTransfer(flushDone);
Addr addr <- toGet(rqFromCQ).get;
`ifdef DEBUG_ICACHE
procRqT r = ProcRqToI {addr: addr, id: cRqId};
cRqId <= cRqId + 1;
`else
procRqT r = ProcRqToI {addr: addr};
`endif
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
// send to pipeline
pipeline.send(CRq (L1PipeRqIn {
addr: r.addr,
mshrIdx: n
}));
// enq to indexQ for in order resp
cRqIndexQ.enq(n);
`ifdef PERF_COUNT
// performance counter: cRq type
incrReqCnt;
`endif
$display("%t I %m cRqTransfer: ", $time,
fshow(n), " ; ",
fshow(r)
);
endrule
// this descending urgency is necessary to avoid deadlock/livelock
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
fromPQ.deq;
pRqIdxT n <- pRqMshr.getEmptyEntryInit(req);
// send to pipeline
pipeline.send(PRq (L1PipeRqIn {
addr: req.addr,
mshrIdx: n
}));
$display("%t I %m pRqTransfer: ", $time,
fshow(n), " ; ",
fshow(req)
);
endrule
// this descending urgency is necessary to avoid deadlock/livelock
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
fromPQ.deq;
pipeline.send(PRs (L1PipePRsIn {
addr: resp.addr,
toState: S,
data: resp.data,
way: resp.id
}));
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
endrule
`ifdef SECURITY
// start flush when cRq MSHR is empty
rule startFlushReq(!flushDone && !flushReqStart && cRqMshr.emptyForFlush);
flushReqStart <= True;
endrule
(* descending_urgency = "pRsTransfer, flushTransfer" *)
(* descending_urgency = "pRqTransfer, flushTransfer" *)
rule flushTransfer(!flushDone && flushReqStart && !flushReqDone);
// We allocate a pRq MSHR entry for 2 reasons:
// (1) reuse the pRq logic to send resp to parent
// (2) control the number of downgrade resp to avoid stalling the cache
// pipeline
pRqIdxT n <- pRqMshr.getEmptyEntryInit(PRqMsg {
addr: ?,
toState: I,
child: ?
});
pipeline.send(Flush (L1PipeFlushIn {
index: flushIndex,
way: flushWay,
mshrIdx: n
}));
// increment flush index/way
if (flushWay < fromInteger(valueof(wayNum) - 1)) begin
flushWay <= flushWay + 1;
end
else begin
flushWay <= 0;
flushIndex <= flushIndex + 1; // index num should be power of 2
if (flushIndex == maxBound) begin
flushReqDone <= True;
end
end
$display("%t I %m flushTransfer: ", $time, fshow(n), " ; ",
fshow(flushIndex), " ; ", fshow(flushWay));
endrule
`endif
rule sendRsToP_cRq(rsToPIndexQ.first matches tagged CRq .n);
rsToPIndexQ.deq;
// get cRq replacement info
procRqT req = cRqMshr.sendRsToP_cRq.getRq(n);
cRqSlotT slot = cRqMshr.sendRsToP_cRq.getSlot(n);
// send resp to parent
cRsToPT resp = CRsMsg {
addr: {slot.repTag, truncate(req.addr)}, // get bank id & index from req
toState: I,
data: Invalid, // I$ never downgrade with data to writeback
child: ?
};
rsToPQ.enq(resp);
// req parent for upgrade now
// (prevent parent resp from coming to release MSHR entry before replace resp is sent)
rqToPIndexQ_sendRsToP.enq(n);
$display("%t I %m sendRsToP: ", $time,
fshow(rsToPIndexQ.first)," ; ",
fshow(req), " ; ",
fshow(slot), " ; ",
fshow(resp)
);
endrule
rule sendRsToP_pRq(rsToPIndexQ.first matches tagged PRq .n);
rsToPIndexQ.deq;
// get pRq info & send resp & release MSHR entry
pRqFromPT req = pRqMshr.sendRsToP_pRq.getRq(n);
cRsToPT resp = CRsMsg {
addr: req.addr,
toState: I, // I$ must downgrade to I
data: Invalid, // I$ never downgrade with data to writeback
child: ?
};
rsToPQ.enq(resp);
pRqMshr.sendRsToP_pRq.releaseEntry(n); // mshr entry released
$display("%t I %m sendRsToP: ", $time,
fshow(rsToPIndexQ.first), " ; ",
fshow(req), " ; ",
fshow(resp)
);
doAssert(req.toState == I, "I$ only has downgrade req to I");
endrule
rule sendRqToP;
rqToPIndexQ.deq;
cRqIdxT n = rqToPIndexQ.first;
procRqT req = cRqMshr.sendRqToP.getRq(n);
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
cRqToPT cRqToP = CRqMsg {
addr: req.addr,
fromState: I, // I$ upgrade from I
toState: S, // I$ upgrade to S
canUpToE: False,
id: slot.way,
child: ?
};
rqToPQ.enq(cRqToP);
$display("%t I %m sendRqToP: ", $time,
fshow(n), " ; ",
fshow(req), " ; ",
fshow(slot), " ; ",
fshow(cRqToP)
);
`ifdef PERF_COUNT
// performance counter: start miss timer
latTimer.start(n);
`endif
endrule
// last stage of pipeline: process req
// XXX: in L1, pRq cannot exist in dependency chain
// because there are only two ways to include pRq into chain
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
// (this cannot happen in I$)
// Thus, dependency chain in L1 only contains cRq
// pipeline outputs
pipeOutT pipeOut = pipeline.first;
ramDataT ram = pipeOut.ram;
// get proc req to select from cRqMshr
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
case(pipeOut.cmd) matches
tagged L1CRq .n: (n);
default: (fromMaybe(0, ram.info.owner)); // L1PRs
endcase
);
// function to get superscaler inst read result
function resultT readInst(Line line, Addr addr);
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
// the start offset for reading inst
LineInstOffset startSel = getLineInstOffset(addr);
// calculate the maximum inst count that could be read from line
LineInstOffset maxCntMinusOne = maxBound - startSel;
// read inst superscalaer
resultT val = ?;
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
if(fromInteger(i) <= maxCntMinusOne) begin
LineInstOffset sel = startSel + fromInteger(i);
val[i] = Valid (instVec[sel]);
end
else begin
val[i] = Invalid;
end
end
return val;
endfunction
// function to process cRq hit (MSHR slot may have garbage)
function Action cRqHit(cRqIdxT n, procRqT req);
action
$display("%t I %m pipelineResp: Hit func: ", $time,
fshow(n), " ; ",
fshow(req)
);
// check tag & cs: even this function is called by pRs, tag should match,
// because tag is written into cache before sending req to parent
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
"cRqHit but tag or cs incorrect"
);
// deq pipeline or swap in successor
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
pipeline.deqWrite(succ, RamData {
info: CacheInfo {
tag: getTag(req.addr), // should be the same as original tag
cs: ram.info.cs, // use cs in ram
dir: ?,
owner: succ,
other: ?
},
line: ram.line
}, True); // hit, so update rep info
// process req to get superscalar inst read results
// set MSHR entry as Done & save inst results
let instResult = readInst(ram.line, req.addr);
cRqMshr.pipelineResp.setResult(n, instResult);
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
fshow(succ), " ; ", fshow(instResult)
);
`ifdef DEBUG_ICACHE
// signal that this req is performed
cRqDoneQ.enq(DebugICacheResp {
id: req.id,
line: ram.line
});
`endif
endaction
endfunction
rule pipelineResp_cRq(pipeOut.cmd matches tagged L1CRq .n);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
procRqT procRq = pipeOutCRq;
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
// find end of dependency chain
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
// function to process cRq miss without replacement (MSHR slot may have garbage)
function Action cRqMissNoReplacement;
action
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
// it is impossible in L1 to have slot.waitP == True in this function
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
// and this func is only called when cs < S (otherwise will hit)
// because L1 has no children to wait for
doAssert(!cSlot.waitP && ram.info.cs == I, "waitP must be false and cs must be I");
// Thus we must send req to parent
// XXX first send to a temp indexQ to avoid conflict, then merge to rqToPIndexQ later
rqToPIndexQ_pipelineResp.enq(n);
// update mshr
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
way: pipeOut.way, // use way from pipeline
repTag: ?, // no replacement
waitP: True // must fetch from parent
});
// deq pipeline & set owner, tag
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: getTag(procRq.addr), // tag may be garbage if cs == I
cs: ram.info.cs,
dir: ?,
owner: Valid (n), // owner is req itself
other: ?
},
line: ram.line
}, False);
endaction
endfunction
// function to do replacement for cRq
function Action cRqReplacement;
action
// deq pipeline
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: getTag(procRq.addr), // set to req tag (old tag is replaced right now)
cs: I,
dir: ?,
owner: Valid (n), // owner is req itself
other: ?
},
line: ? // data is no longer used
}, False);
doAssert(ram.info.cs == S, "I$ replacement only replace S line");
// update MSHR to save replaced tag
// although we send req to parent later (when resp to parent is sent)
// we set state to WaitSt now, since the req to parent is already on schedule
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
way: pipeOut.way, // use way from pipeline
repTag: ram.info.tag, // tag being replaced for sending rs to parent
waitP: True
});
// send replacement resp to parent
rsToPIndexQ.enq(CRq (n));
endaction
endfunction
// function to set cRq to Depend, and make no further change to cache
function Action cRqSetDepNoCacheChange;
action
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
pipeline.deqWrite(Invalid, pipeOut.ram, False);
endaction
endfunction
if(ram.info.owner matches tagged Valid .cOwner) begin
if(cOwner != n) begin
// owner is another cRq, so must just go through tag match
// tag match must be hit (because replacement algo won't give a way with owner)
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"cRq should hit in tag match"
);
// should be added to a cRq in dependency chain & deq from pipeline
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
cRqSetDepNoCacheChange;
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
);
end
else begin
// owner is myself, so must be swapped in
// tag should match, since always swapped in by cRq, cs = S
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
"cRq swapped in by previous cRq, tag must match & cs = S"
);
// Hit
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
cRqHit(n, procRq);
end
end
else begin
// cache has no owner, cRq must just go through tag match
// check for cRqEOC to append to dependency chain
if(cRqEOC matches tagged Valid .k) begin
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
cRqMshr.pipelineResp.setSucc(k, Valid (n));
cRqSetDepNoCacheChange;
end
else if(ram.info.cs == I || ram.info.tag == getTag(procRq.addr)) begin
// No Replacement necessary
if(ram.info.cs > I) begin
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
cRqHit(n, procRq);
end
else begin
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
cRqMissNoReplacement;
end
end
else begin
$display("%t I %m pipelineResp: cRq: no owner, replace", $time);
cRqReplacement;
end
end
endrule
rule pipelineResp_pRs(pipeOut.cmd == L1PRs);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
$display("%t I %m pipelineResp: pRs: ", $time);
if(ram.info.owner matches tagged Valid .cOwner) begin
procRqT procRq = pipeOutCRq;
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"pRs must be a hit"
);
cRqHit(cOwner, procRq);
`ifdef PERF_COUNT
// performance counter: miss cRq
incrMissCnt(cOwner);
`endif
end
else begin
doAssert(False, ("pRs owner must match some cRq"));
end
endrule
rule pipelineResp_pRq(pipeOut.cmd matches tagged L1PRq .n);
pRqFromPT pRq = pRqMshr.pipelineResp.getRq(n);
$display("%t I %m pipelineResp: pRq: ", $time, fshow(n), " ; ", fshow(pRq));
doAssert(pRq.toState == I, "I$ pRq only downgrade to I");
// pRq is never in dependency chain, so it is never swapped in
// pRq must go through tag match, which either returns a tag matched way or asserts pRqMiss
// In I$ a tag matched way should always be processed since pRq always downgrades to I
// pRq is always directly handled: either dropped or Done
if(pipeOut.pRqMiss) begin
$display("%t I %m pipelineResp: pRq: drop", $time);
// pRq can be directly dropped, no successor (since just go through pipeline)
pRqMshr.pipelineResp.releaseEntry(n);
pipeline.deqWrite(Invalid, pipeOut.ram, False);
end
else begin
$display("%t I %m pipelineResp: pRq: valid process", $time);
// should process pRq
doAssert(ram.info.cs == S && pRq.toState == I && ram.info.tag == getTag(pRq.addr),
"pRq should be processed"
);
// line cannot be owned, because
// (1) pRq never own cache line
// (2) if owned by cRq, cRq would have hit and released ownership
doAssert(ram.info.owner == Invalid, "pRq cannot hit on line owned by anyone");
// write ram: set cs to I
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: ram.info.tag,
cs: I, // I$ is always downgraded by pRq to I
dir: ?,
owner: Invalid, // no successor
other: ?
},
line: ? // line is not useful
}, False);
// pRq is done
pRqMshr.pipelineResp.setDone(n);
// send resp to parent
rsToPIndexQ.enq(PRq (n));
end
endrule
`ifdef SECURITY
rule pipelineResp_flush(
!flushDone &&& !flushRespDone &&&
pipeOut.cmd matches tagged L1Flush .flush
);
pRqIdxT n = flush.mshrIdx;
$display("%t I %m pipelineResp: flush: ", $time, fshow(flush));
// During flush, cRq MSHR is empty, so cache line cannot have owner
doAssert(ram.info.owner == Invalid, "flushing line cannot have owner");
// flush always goes through cache pipeline, and is directly handled
// here: either dropped or Done
if(ram.info.cs == I) begin
$display("%t I %m pipelineResp: flush: drop", $time);
// flush can be directly dropped
pRqMshr.pipelineResp.releaseEntry(n);
end
else begin
$display("%t I %m pipelineResp: flush: valid process", $time);
pRqMshr.pipelineResp.setDone(n);
rsToPIndexQ.enq(PRq (n));
// record the flushed addr in MSHR so that sendRsToP rule knows
// which addr is invalidated
Bit#(LgLineSzBytes) offset = 0;
Addr addr = {ram.info.tag, flush.index, bankId, offset};
pRqMshr.pipelineResp.setFlushAddr(n, addr);
end
// always clear the cache line
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: ?,
cs: I, // downgraded to I
dir: ?,
owner: Invalid, // no successor
other: ?
},
line: ?
}, False);
// check if we have finished all flush
if (flush.index == maxBound &&
pipeOut.way == fromInteger(valueof(wayNum) - 1)) begin
flushRespDone <= True;
end
endrule
rule completeFlush(!flushDone && flushReqStart && flushReqDone && flushRespDone);
flushDone <= True;
flushReqStart <= False;
flushReqDone <= False;
flushRespDone <= False;
endrule
`endif
// merge rq to parent index into indexQ
rule rqIndexFromPipelineResp;
let n <- toGet(rqToPIndexQ_pipelineResp).get;
rqToPIndexQ.enq(n);
endrule
(* descending_urgency = "rqIndexFromPipelineResp, rqIndexFromSendRsToP" *)
rule rqIndexFromSendRsToP;
let n <- toGet(rqToPIndexQ_sendRsToP).get;
rqToPIndexQ.enq(n);
endrule
interface ChildCacheToParent to_parent;
interface rsToP = toFifoDeq(rsToPQ);
interface rqToP = toFifoDeq(rqToPQ);
interface fromP = toFifoEnq(fromPQ);
endinterface
interface InstServer to_proc;
interface Put req;
method Action put(Addr addr);
rqFromCQ.enq(addr);
endmethod
endinterface
interface Get resp;
method ActionValue#(resultT) get if(
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
);
cRqIndexQ.deq;
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
$display("%t I %m sendRsToC: ", $time,
fshow(cRqIndexQ.first), " ; ",
fshow(inst)
);
return inst;
endmethod
endinterface
`ifdef DEBUG_ICACHE
interface done = toGet(cRqDoneQ);
`endif
endinterface
interface Get cRqStuck;
method ActionValue#(ICRqStuck) get;
let s <- cRqMshr.stuck.get;
return ICRqStuck {
addr: s.req.addr,
state: s.state,
waitP: s.waitP
};
endmethod
endinterface
interface pRqStuck = pRqMshr.stuck;
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
method Action setPerfStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Data getPerfData(L1IPerfType t);
return (case(t)
`ifdef PERF_COUNT
L1ILdCnt: ldCnt;
L1ILdMissCnt: ldMissCnt;
L1ILdMissLat: ldMissLat;
`endif
default: 0;
endcase);
endmethod
endmodule
// Scheduling note
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
// pRsTransfer: -
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
// sendRqToP: read cRq MSHR req/slot that is requesting parent
// pipelineResp_cRq:
// -- read cRq MSHR req/state/slot currently processed
// -- write cRq MSHR state/slot/result currently processed
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
// -- not affected by write in cRqTransfer (state change is Empty->Init)
// -- not affected by write in sendRsC (state change is Done->Empty)
// pipelineResp_pRs:
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
// pipelineResp_pRq:
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
// ---- conflict analysis ----
// XXXTransfer is conflict with each other
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
// XXXTransfer should operate on different cRq/pRq from other rules
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
// sendRqToP and sendRsToP_cRq are read only
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
// ---- conclusion ----
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
// we have 5 ports from cRq MSHR
// 1. cRqTransfer
// 2. sendRsToC
// 3. sendRsToP_cRq
// 4. sendRqToP
// 5. pipelineResp
// we have 3 ports from pRq MSHR
// 1. pRqTransfer
// 2. sendRsToP_pRq
// 3. pipelineResp
// safe version: use EHR ports
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
// pipelineResp < sendRsToP_pRq < pRqTransfer
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
// we maintain the logical ordering in safe version

View File

@@ -0,0 +1,353 @@
// 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.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitSt, // wait pRs/cRs to come
Done, // resp is in index FIFO
Depend
} ICRqState deriving(Bits, Eq, FShow);
// CRq info returned to outside
typedef struct {
wayT way; // the way to occupy
tagT repTag; // tag being replaced
Bool waitP; // waiting for parent resp
} ICRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
instance DefaultValue#(ICRqSlot#(wayT, tagT));
defaultValue = ICRqSlot {
way: ?,
repTag: ?,
waitP: False
};
endinstance
typedef struct {
reqT req;
ICRqState state;
Bool waitP;
} ICRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
// MSHR data is purely for replacement resp to parent
// (resp to processor is done immediately, no data buffering needed)
// port for sendRsToP_cRq
interface ICRqMshr_sendRsToP_cRq#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for sendRqToP
interface ICRqMshr_sendRqToP#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for pipelineResp
interface ICRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT,
type resultT
);
method ICRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setResult(Bit#(TLog#(cRqNum)) n, resultT r); // set a valid result
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n,
ICRqState state,
ICRqSlot#(wayT, tagT) slot
);
// can only change state to NON-Empty state
// cannot be used to release MSHR entry (use releaseSlot instead)
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
// index in setSucc is usually different from other getXXX methods
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
// i.e. search the end of dependency chain
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface ICRqMshr_sendRsToC#(
numeric type cRqNum,
type resultT
);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
endinterface
interface ICRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT, // child req type
type resultT // inst result type
);
// port for cRqTransfer, initialization is done inside method
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
// port for sendRsToC
interface ICRqMshr_sendRsToC#(cRqNum, resultT) sendRsToC;
// port for sendRsToP_cRq
interface ICRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
// port for sendRqToP
interface ICRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
// port for pipelineResp
interface ICRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT, resultT) pipelineResp;
// port for security flush
method Bool emptyForFlush;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(ICRqMshrStuck#(reqT)) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkICRqMshrSafe#(
function Addr getAddrFromReq(reqT r)
)(
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, ICRqSlot#(wayT, tagT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz),
Bits#(resultT, _resultTSz)
);
// EHR ports
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer cRqTransfer_port = 2;
Integer sendRsToC_port = 1; // create a bypass behavior from pipelineResp to sendRsToC (to save a cycle)
Integer pipelineResp_port = 0;
Integer sendRqToP_port = 0; // sendRqToP is read only
Integer sendRsToP_cRq_port = 0; // sendRsToP_cRq is read only
Integer flush_port = 0; // flush port is read only
// MSHR entry state
Vector#(cRqNum, Ehr#(3, ICRqState)) stateVec <- replicateM(mkEhr(Empty));
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
// result
Vector#(cRqNum, Ehr#(3, Maybe#(resultT))) resultVec <- replicateM(mkEhr(Invalid));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t ICRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(ICRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(ICRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP
});
end
endrule
`endif
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
stateVec[n][cRqTransfer_port] <= Init;
slotVec[n][cRqTransfer_port] <= defaultValue;
resultVec[n][cRqTransfer_port] <= Invalid;
succValidVec[n][cRqTransfer_port] <= False;
reqVec[n][cRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface ICRqMshr_sendRsToC sendRsToC;
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][sendRsToC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
return resultVec[n][sendRsToC_port];
endmethod
endinterface
interface ICRqMshr_sendRsToP_cRq sendRsToP_cRq;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToP_cRq_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRsToP_cRq_port];
endmethod
endinterface
interface ICRqMshr_sendRqToP sendRqToP;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToP_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToP_port];
endmethod
endinterface
interface ICRqMshr_pipelineResp pipelineResp;
method ICRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Action setStateSlot(cRqIndexT n, ICRqState state, slotT slot);
doAssert(state != Empty, "use releaseEntry to set state to Empty");
stateVec[n][pipelineResp_port] <= state;
slotVec[n][pipelineResp_port] <= slot;
endmethod
method Action setResult(cRqIndexT n, resultT r);
resultVec[n][pipelineResp_port] <= Valid (r);
endmethod
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
endmethod
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
succValidVec[n][pipelineResp_port] <= isValid(succ);
succFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
ICRqState state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noSucc = !succValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
method Bool emptyForFlush;
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
Vector#(cRqNum, Integer) idxVec = genVector;
return all(isEmpty, idxVec);
endmethod
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exported version
module mkICRqMshr#(
function Addr getAddrFromReq(reqT r)
)(
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
) provisos (
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz),
Bits#(resultT, _resultTSz)
);
let m <- mkICRqMshrSafe(getAddrFromReq);
return m;
endmodule

View File

@@ -0,0 +1,214 @@
// 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.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import GetPut::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always directly handled at the end of pipeline
// PRq MSHR entry state
typedef enum {
Empty,
Init,
Done
} IPRqState deriving (Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
IPRqState state;
} IPRqMshrStuck deriving(Bits, Eq, FShow);
interface IPRqMshr_sendRsToP_pRq#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
endinterface
interface IPRqMshr_pipelineResp#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
method Action setDone(Bit#(TLog#(pRqNum)) n);
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
`endif
endinterface
interface IPRqMshr#(numeric type pRqNum);
// port for pRqTransfer
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
// port for sendRsToP_pRq
interface IPRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
// port for pipelineResp
interface IPRqMshr_pipelineResp#(pRqNum) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(IPRqMshrStuck) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkIPRqMshrSafe(
IPRqMshr#(pRqNum)
) provisos(
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
);
// EHR port
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer pRqTransfer_port = 2;
Integer sendRsToP_pRq_port = 1;
Integer pipelineResp_port = 0;
// MSHR entry state
Vector#(pRqNum, Ehr#(3, IPRqState)) stateVec <- replicateM(mkEhr(Empty));
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
// empty entry FIFO
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(pRqIndexT) initIdx <- mkReg(0);
// released entry index fifos
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
inited <= True;
$display("%t IPRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(IPRqMshrStuck) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(IPRqMshrStuck {
addr: reqVec[n][0].addr,
toState: reqVec[n][0].toState,
state: stateVec[n][0]
});
end
endrule
`endif
rule doReleaseEntry_sendRsToP_pRq(inited);
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
rule doReleaseEntry_pipelineResp(inited);
let n <- toGet(releaseEntryQ_pipelineResp).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
emptyEntryQ.deq;
pRqIndexT n = emptyEntryQ.first;
stateVec[n][pRqTransfer_port] <= Init;
reqVec[n][pRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface IPRqMshr_sendRsToP_pRq sendRsToP_pRq;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][sendRsToP_pRq_port];
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_sendRsToP_pRq.enq(n);
stateVec[n][sendRsToP_pRq_port] <= Empty;
endmethod
endinterface
interface IPRqMshr_pipelineResp pipelineResp;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method Action setDone(pRqIndexT n);
stateVec[n][pipelineResp_port] <= Done;
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_pipelineResp.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
endmethod
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
reqVec[n][pipelineResp_port] <= PRqMsg {
addr: a,
toState: I,
child: ?
};
endmethod
`endif
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exportd version
module mkIPRqMshr(IPRqMshr#(pRqNum));
let m <- mkIPRqMshrSafe;
return m;
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,378 @@
// 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.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitNewTag, // waiting replacement resp to send (but tag in RAM is already updated)
WaitSt, // wait pRs/cRs to come
Done, // resp is in index FIFO
Depend
} L1CRqState deriving(Bits, Eq, FShow);
// CRq info returned to outside
typedef struct {
wayT way; // the way to occupy
Msi cs; // current cache MSI, used in sending upgrade req to parent
tagT repTag; // tag being replaced: only valid in WaitOld/NewTag states
Bool waitP; // waiting for parent resp
} L1CRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
instance DefaultValue#(L1CRqSlot#(wayT, tagT));
defaultValue = L1CRqSlot {
way: ?,
cs: ?,
repTag: ?,
waitP: False
};
endinstance
typedef struct {
reqT req;
L1CRqState state;
Msi slotCs;
Bool waitP;
} L1CRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
// MSHR data is purely for replacement resp to parent
// (resp to processor is done immediately, no data buffering needed)
// port for cRqTransfer and retry
interface L1CRqMshr_transfer#(
numeric type cRqNum,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
endinterface
// port for sendRsToP_cRq
interface L1CRqMshr_sendRsToP_cRq#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Action setWaitSt_setSlot_clearData(
Bit#(TLog#(cRqNum)) n,
L1CRqSlot#(wayT, tagT) slot
);
// data is set to invalid, state is set to WaitSt here
endinterface
// port for sendRqToP
interface L1CRqMshr_sendRqToP#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for pipelineResp
interface L1CRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n,
L1CRqState state,
L1CRqSlot#(wayT, tagT) slot
);
// can only change state to NON-Empty state
// cannot be used to release MSHR entry (use releaseSlot instead)
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
// index in setSucc is usually different from other getXXX methods
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
// i.e. search the end of dependency chain
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface L1CRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT // child req type
);
// port for cRqTransfer and retry
interface L1CRqMshr_transfer#(cRqNum, reqT) cRqTransfer;
// port for sendRsToP_cRq
interface L1CRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
// port for sendRqToP
interface L1CRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
// port for pipelineResp
interface L1CRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT) pipelineResp;
// port for security flush
method Bool emptyForFlush;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1CRqMshrStuck#(reqT)) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkL1CRqMshrSafe#(
function Addr getAddrFromReq(reqT r)
)(
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, L1CRqSlot#(wayT, tagT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz)
);
// EHR ports
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer flush_port = 0; // flush port is read only
Integer sendRqToP_port = 0; // sendRqToP is read only
Integer sendRsToP_cRq_port = 0;
Integer pipelineResp_port = 1;
Integer cRqTransfer_port = 2;
// MSHR entry state
Vector#(cRqNum, Ehr#(3, L1CRqState)) stateVec <- replicateM(mkEhr(Empty));
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
RegFile#(cRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t L1CRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(L1CRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(L1CRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
slotCs: slotVec[n][0].cs,
waitP: slotVec[n][0].waitP
});
end
endrule
`endif
interface L1CRqMshr_transfer cRqTransfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][cRqTransfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
stateVec[n][cRqTransfer_port] <= Init;
slotVec[n][cRqTransfer_port] <= defaultValue;
dataValidVec[n][cRqTransfer_port] <= False;
succValidVec[n][cRqTransfer_port] <= False;
reqVec[n][cRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
endinterface
interface L1CRqMshr_sendRsToP_cRq sendRsToP_cRq;
method L1CRqState getState(cRqIndexT n);
return stateVec[n][sendRsToP_cRq_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToP_cRq_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRsToP_cRq_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToP_cRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
endmethod
method Action setWaitSt_setSlot_clearData(cRqIndexT n, slotT s);
stateVec[n][sendRsToP_cRq_port] <= WaitSt;
slotVec[n][sendRsToP_cRq_port] <= s;
dataValidVec[n][sendRsToP_cRq_port] <= False;
endmethod
endinterface
interface L1CRqMshr_sendRqToP sendRqToP;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToP_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToP_port];
endmethod
endinterface
interface L1CRqMshr_pipelineResp pipelineResp;
method L1CRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
method Action setStateSlot(cRqIndexT n, L1CRqState state, slotT slot);
doAssert(state != Empty, "use releaseEntry to set state to Empty");
stateVec[n][pipelineResp_port] <= state;
slotVec[n][pipelineResp_port] <= slot;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) line);
dataValidVec[n][pipelineResp_port] <= isValid(line);
dataFile.upd(n, fromMaybe(?, line));
endmethod
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
endmethod
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
succValidVec[n][pipelineResp_port] <= isValid(succ);
succFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
L1CRqState state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noSucc = !succValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
method Bool emptyForFlush;
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
Vector#(cRqNum, Integer) idxVec = genVector;
return all(isEmpty, idxVec);
endmethod
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exported version
module mkL1CRqMshr#(
function Addr getAddrFromReq(reqT r)
)(
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
) provisos (
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz)
);
let m <- mkL1CRqMshrSafe(getAddrFromReq);
return m;
endmodule

View File

@@ -0,0 +1,231 @@
// 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.
import Vector::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import GetPut::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always directly handled at the end of pipeline
// PRq MSHR entry state
typedef enum {
Empty,
Init,
Done
} L1PRqState deriving (Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
L1PRqState state;
} L1PRqMshrStuck deriving(Bits, Eq, FShow);
interface L1PRqMshr_sendRsToP_pRq#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
endinterface
interface L1PRqMshr_pipelineResp#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method L1PRqState getState(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
method Action setDone_setData(Bit#(TLog#(pRqNum)) n, Maybe#(Line) d);
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
`endif
endinterface
interface L1PRqMshr#(numeric type pRqNum);
// port for pRqTransfer
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
// port for sendRsToP_pRq
interface L1PRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
// port for pipelineResp
interface L1PRqMshr_pipelineResp#(pRqNum) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1PRqMshrStuck) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkL1PRqMshrSafe(
L1PRqMshr#(pRqNum)
) provisos(
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
);
// EHR port
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer sendRsToP_pRq_port = 0;
Integer pipelineResp_port = 1;
Integer pRqTransfer_port = 2;
// MSHR entry state
Vector#(pRqNum, Ehr#(3, L1PRqState)) stateVec <- replicateM(mkEhr(Empty));
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
// data valid bits
Vector#(pRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
RegFile#(pRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(pRqNum) - 1));
// empty entry FIFO
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(pRqIndexT) initIdx <- mkReg(0);
// released entry index fifos
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
inited <= True;
$display("%t L1PRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(L1PRqMshrStuck) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(L1PRqMshrStuck {
addr: reqVec[n][0].addr,
toState: reqVec[n][0].toState,
state: stateVec[n][0]
});
end
endrule
`endif
rule doReleaseEntry_sendRsToP_pRq(inited);
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
rule doReleaseEntry_pipelineResp(inited);
let n <- toGet(releaseEntryQ_pipelineResp).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
emptyEntryQ.deq;
pRqIndexT n = emptyEntryQ.first;
stateVec[n][pRqTransfer_port] <= Init;
reqVec[n][pRqTransfer_port] <= r;
dataValidVec[n][pRqTransfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface L1PRqMshr_sendRsToP_pRq sendRsToP_pRq;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][sendRsToP_pRq_port];
endmethod
method Maybe#(Line) getData(pRqIndexT n);
return dataValidVec[n][sendRsToP_pRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_sendRsToP_pRq.enq(n);
stateVec[n][sendRsToP_pRq_port] <= Empty;
endmethod
endinterface
interface L1PRqMshr_pipelineResp pipelineResp;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method L1PRqState getState(pRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method Action setDone_setData(pRqIndexT n, Maybe#(Line) line);
stateVec[n][pipelineResp_port] <= Done;
dataValidVec[n][pipelineResp_port] <= isValid(line);
dataFile.upd(n, fromMaybe(?, line));
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_pipelineResp.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
endmethod
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
reqVec[n][pipelineResp_port] <= PRqMsg {
addr: a,
toState: I,
child: ?
};
endmethod
`endif
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exportd version
module mkL1PRqMshr(L1PRqMshr#(pRqNum));
let m <- mkL1PRqMshrSafe;
return m;
endmodule

View File

@@ -0,0 +1,391 @@
// 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.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export L1PipeRqIn(..);
export L1PipePRsIn(..);
export L1PipeFlushIn(..);
export L1PipeIn(..);
export L1FlushCmd(..);
export L1Cmd(..);
export L1Pipe(..);
export mkL1Pipe;
// type param ordering: bank < way < index < tag < cRq < pRq
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
// replacement is always done immediately (never have replacing line)
// so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} L1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} L1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef struct {
indexT index;
wayT way;
pRqIdxT mshrIdx;
} L1PipeFlushIn#(
type wayT,
type indexT,
type pRqIdxT
) deriving(Bits, Eq, FShow);
typedef union tagged {
L1PipeRqIn#(cRqIdxT) CRq;
L1PipeRqIn#(pRqIdxT) PRq;
L1PipePRsIn#(wayT) PRs;
`ifdef SECURITY
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
`endif
} L1PipeIn#(
type wayT,
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in L1$
typedef struct {
indexT index;
pRqIdxT mshrIdx;
} L1FlushCmd#(type indexT, type pRqIdxT) deriving(Bits, Eq, FShow);
typedef union tagged {
cRqIdxT L1CRq;
pRqIdxT L1PRq;
void L1PRs;
`ifdef SECURITY
L1FlushCmd#(indexT, pRqIdxT) L1Flush;
`endif
} L1Cmd#(
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
interface L1Pipe#(
numeric type lgBankNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT,
type pRqIdxT
);
method Action send(L1PipeIn#(Bit#(TLog#(wayNum)), indexT, cRqIdxT, pRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), void, RandRepInfo, // no other
Line, L1Cmd#(indexT, cRqIdxT, pRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} L1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
L1PipeRqIn#(cRqIdxT) CRq;
L1PipeRqIn#(pRqIdxT) PRq;
L1PipePRsCmd#(wayT) PRs;
`ifdef SECURITY
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
`endif
} L1PipeCmd#(
type wayT,
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
module mkL1Pipe(
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, L1PipeIn#(wayT, indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeCmdT, L1PipeCmd#(wayT, indexT, cRqIdxT, pRqIdxT)),
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRq .r: r.addr;
tagged PRs .r: r.addr;
`ifdef SECURITY
// fake an address for flush req that has the same index
tagged Flush .r: (zeroExtend(r.index) << (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
`endif
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
`ifdef SECURITY
else if(cmd matches tagged Flush .flush) begin
// flush directly read from cmd
return TagMatchResult {
way: flush.way,
pRqMiss: False
};
end
`endif
else begin
// CRq/PRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else if(cmd matches tagged PRq .rq) begin
// pRq miss
return TagMatchResult {
way: 0, // default to 0
pRqMiss: True
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) == dataV, "valid resp data for upgrade from I");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 does not have dir");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRq .rq: begin
pipe.enq(PRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (L1PipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
`ifdef SECURITY
tagged Flush .flush: begin
pipe.enq(Flush (flush), Invalid, Invalid);
end
`endif
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: L1CRq (rq.mshrIdx);
tagged PRq .rq: L1PRq (rq.mshrIdx);
tagged PRs .rs: L1PRs;
`ifdef SECURITY
tagged Flush .flush: L1Flush (L1FlushCmd {
index: flush.index,
mshrIdx: flush.mshrIdx
});
`endif
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (L1PipeRqIn {addr: addr, mshrIdx: idx}));
`ifdef SECURITY
doAssert(pipe.first.cmd matches tagged Flush .f ? False : True,
"Cannot swap after a flush req");
`endif
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,447 @@
// 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.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in LLC, the head (h1) of a chain may be linked as the successor of the head (h2) of another chain
// when h2 is replacing the addr of h1
// h1 should be waken up and sent to pipeline when replacement is done (i.e. h2 gets to WaitSt)
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitOldTag,
WaitSt,
Done,
Depend
} LLCRqState deriving(Bits, Eq, FShow);
// we split data from slot info
// because data may be used to buffer mem resp data
typedef struct {
wayT way; // the way to occupy
tagT repTag; // tag being replaced, used in sending down req to children
Bool waitP; // wait parent resp
dirPendT dirPend; // pending child downgrade
} LLCRqSlot#(type wayT, type tagT, type dirPendT) deriving(Bits, Eq, FShow);
typedef struct {
reqT req;
LLCRqState state;
Bool waitP;
dirPendT dirPend;
} LLCRqMshrStuck#(type dirPendT, type reqT) deriving(Bits, Eq, FShow);
// interface for cRq/mRs/cRsTransfer
interface LLCRqMshr_transfer#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r, Maybe#(Line) d);
// check if any empty MSHR entry is available in order to get MSHR blocking
// stats. The argument is not really used here, just in case some other
// MSHR implementations may bank entries based on addr.
method Bool hasEmptyEntry(reqT r);
endinterface
// interface for mRsDeq
interface LLCRqMshr_mRsDeq#(numeric type cRqNum);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
endinterface
// interface for sendToM
interface LLCRqMshr_sendToM#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
endinterface
// interface for sendRsToDma and sendRsToC
interface LLCRqMshr_sendRsToDmaC#(
numeric type cRqNum,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
endinterface
// interface for sendRqToC
interface LLCRqMshr_sendRqToC#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setSlot(Bit#(TLog#(cRqNum)) n, LLCRqSlot#(wayT, tagT, dirPendT) s);
// find cRq that needs to send req to child to downgrade
// (either replacement, or incompatible children states)
// we can pass in a suggested req idx (which will have priority)
method Maybe#(Bit#(TLog#(cRqNum))) searchNeedRqChild(Maybe#(Bit#(TLog#(cRqNum))) suggestIdx);
endinterface
// interface for pipelineResp_xxx
interface LLCRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Maybe#(Bit#(TLog#(cRqNum))) getAddrSucc(Bit#(TLog#(cRqNum)) n);
method Maybe#(Bit#(TLog#(cRqNum))) getRepSucc(Bit#(TLog#(cRqNum)) n);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n, LLCRqState state,
LLCRqSlot#(wayT, tagT, dirPendT) slot
);
method Action setAddrSucc( // same address successor
Bit#(TLog#(cRqNum)) n,
Maybe#(Bit#(TLog#(cRqNum))) succ
);
method Action setRepSucc( // successor due to replacement
Bit#(TLog#(cRqNum)) n,
Maybe#(Bit#(TLog#(cRqNum))) succ
);
// find existing cRq which has gone through pipeline, but not in Done state, and has no addr successor
// (it could have rep successor)
// i.e. search the end of dependency chain for req to the same addr
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface LLCRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
interface LLCRqMshr_transfer#(cRqNum, wayT, tagT, dirPendT, reqT) transfer;
interface LLCRqMshr_mRsDeq#(cRqNum) mRsDeq;
interface LLCRqMshr_sendToM#(cRqNum, wayT, tagT, dirPendT, reqT) sendToM;
interface LLCRqMshr_sendRsToDmaC#(cRqNum, reqT) sendRsToDmaC;
interface LLCRqMshr_sendRqToC#(cRqNum, wayT, tagT, dirPendT, reqT) sendRqToC;
interface LLCRqMshr_pipelineResp#(cRqNum, wayT, tagT, dirPendT, reqT) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(LLCRqMshrStuck#(dirPendT, reqT)) stuck;
endinterface
function LLCRqSlot#(wayT, tagT, dirPendT) getLLCRqSlotInitVal(dirPendT dirPendInitVal);
return LLCRqSlot {
way: ?,
repTag: ?,
waitP: False,
dirPend: dirPendInitVal
};
endfunction
//////////////////
// safe version //
//////////////////
module mkLLCRqMshr#(
function Addr getAddrFromReq(reqT r),
function Bool needDownReq(dirPendT dirPend),
dirPendT dirPendInitVal
)(
LLCRqMshr#(cRqNum, wayT, tagT, dirPendT, reqT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, LLCRqSlot#(wayT, tagT, dirPendT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(dirPendT, _dirPendSz),
Bits#(reqT, _reqSz)
);
slotT slotInitVal = getLLCRqSlotInitVal(dirPendInitVal);
// logical ordering: sendToM < sendRqToC < sendRsToDma/C < mRsDeq < pipelineResp < transfer
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
// EHR ports
Integer sendToM_port = 0; // sendToM is read-only, so use port 0
Integer sendRqToC_port = 0; // read req/state/slot, write slot
Integer sendRsToDmaC_port = 0; // sendRsToDma/C read req/data, write state
Integer mRsDeq_port = 0; // mRsDeq only writes data
Integer pipelineResp_port = 1; // read/write lots of things
Integer transfer_port = 2; // cRqTransfer_xx, mRsTransfer_send, read/write lots of things
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// MSHR entry state
Vector#(cRqNum, Ehr#(3, LLCRqState)) stateVec <- replicateM(mkEhr(Empty));
// summary bit of dirPend in each entry: asserted when some dirPend[i] = ToSend
Vector#(cRqNum, Ehr#(3, Bool)) needReqChildVec <- replicateM(mkEhr(False));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(slotInitVal));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
Vector#(cRqNum, Ehr#(3, Line)) dataVec <- replicateM(mkEhr(?));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) addrSuccValidVec <- replicateM(mkEhr(False));
Vector#(cRqNum, Ehr#(3, Bool)) repSuccValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) addrSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
RegFile#(cRqIndexT, cRqIndexT) repSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
Fifo#(cRqNum, cRqIndexT) emptyEntryQ <- mkCFFifo;
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t LLCRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(LLCRqMshrStuck#(dirPendT, reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(LLCRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP,
dirPend: slotVec[n][0].dirPend
});
end
endrule
`endif
function Action writeSlot(Integer ehrPort, cRqIndexT n, slotT s);
action
slotVec[n][ehrPort] <= s;
// set dirPend summary bit
needReqChildVec[n][ehrPort] <= needDownReq(s.dirPend);
endaction
endfunction
interface LLCRqMshr_transfer transfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][transfer_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][transfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r, Maybe#(Line) d) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
reqVec[n][transfer_port] <= r;
stateVec[n][transfer_port] <= Init;
writeSlot(transfer_port, n, slotInitVal);
dataValidVec[n][transfer_port] <= isValid(d);
dataVec[n][transfer_port] <= validValue(d);
addrSuccValidVec[n][transfer_port] <= False;
repSuccValidVec[n][transfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
method Bool hasEmptyEntry(reqT r);
return emptyEntryQ.notEmpty;
endmethod
endinterface
interface LLCRqMshr_mRsDeq mRsDeq;
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][mRsDeq_port] <= isValid(d);
dataVec[n][mRsDeq_port] <= fromMaybe(?, d);
endmethod
endinterface
interface LLCRqMshr_sendToM sendToM;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendToM_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendToM_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendToM_port] ? Valid (dataVec[n][sendToM_port]) : Invalid;
endmethod
endinterface
interface LLCRqMshr_sendRsToDmaC sendRsToDmaC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToDmaC_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToDmaC_port] ? Valid (dataVec[n][sendRsToDmaC_port]) : Invalid;
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][sendRsToDmaC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
endinterface
interface LLCRqMshr_sendRqToC sendRqToC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToC_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][sendRqToC_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToC_port];
endmethod
method Action setSlot(cRqIndexT n, slotT s);
writeSlot(sendRqToC_port, n, s);
endmethod
method Maybe#(cRqIndexT) searchNeedRqChild(Maybe#(cRqIndexT) suggestIdx);
function Bool isNeedRqChild(cRqIndexT i);
return (stateVec[i][sendRqToC_port] == WaitOldTag || stateVec[i][sendRqToC_port] == WaitSt)
&& needReqChildVec[i][sendRqToC_port];
endfunction
if(suggestIdx matches tagged Valid .idx &&& isNeedRqChild(idx)) begin
return suggestIdx;
end
else begin
Vector#(cRqNum, cRqIndexT) idxVec = genWith(fromInteger);
return searchIndex(isNeedRqChild, idxVec);
end
endmethod
endinterface
interface LLCRqMshr_pipelineResp pipelineResp;
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][pipelineResp_port] ? Valid (dataVec[n][pipelineResp_port]) : Invalid;
endmethod
method Maybe#(cRqIndexT) getAddrSucc(cRqIndexT n);
return addrSuccValidVec[n][pipelineResp_port] ? Valid (addrSuccFile.sub(n)) : Invalid;
endmethod
method Maybe#(cRqIndexT) getRepSucc(cRqIndexT n);
return repSuccValidVec[n][pipelineResp_port] ? Valid (repSuccFile.sub(n)) : Invalid;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][pipelineResp_port] <= isValid(d);
dataVec[n][pipelineResp_port] <= fromMaybe(?, d);
endmethod
method Action setStateSlot(cRqIndexT n, LLCRqState state, slotT slot);
stateVec[n][pipelineResp_port] <= state;
writeSlot(pipelineResp_port, n, slot);
endmethod
method Action setAddrSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
addrSuccValidVec[n][pipelineResp_port] <= isValid(succ);
addrSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Action setRepSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
repSuccValidVec[n][pipelineResp_port] <= isValid(succ);
repSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
let state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noAddrSucc = !addrSuccValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noAddrSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule

View File

@@ -0,0 +1,371 @@
// 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.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export LLPipeCRqIn(..);
export LLPipeMRsIn(..);
export LLPipeIn(..);
export LLCmd(..);
export LLPipe(..);
export mkLLPipe;
// type param ordering: bank < child < way < index < tag < cRq
// input types
typedef struct {
Addr addr;
cRqIdxT mshrIdx;
} LLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState; // come from req in MSHR (E or M)
Line data; // come from memory must be valid
wayT way; // come from MSHR
} LLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
LLPipeCRqIn#(cRqIdxT) CRq;
CRsMsg#(childT) CRs;
LLPipeMRsIn#(wayT) MRs;
} LLPipeIn#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in LLC
typedef union tagged {
cRqIdxT LLCRq; // mshr idx of the cRq
childT LLCRs; // which child is downgrading
void LLMRs;
} LLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
interface LLPipe#(
numeric type lgBankNum,
numeric type childNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(LLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
method Bool notEmpty;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, Vector#(childNum, Msi),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) first;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, Vector#(childNum, Msi),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) unguard_first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, Vector#(childNum, Msi), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
childT child;
} LLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
wayT way;
} LLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
LLPipeCRqIn#(cRqIdxT) CRq;
LLPipeCRsCmd#(childT) CRs;
LLPipeMRsCmd#(wayT) MRs;
} LLPipeCmd#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
module mkLLPipe(
LLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(childT, Bit#(TLog#(childNum))),
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, Vector#(childNum, Msi)),
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, LLPipeIn#(childT, wayT, cRqIdxT)),
Alias#(pipeCmdT, LLPipeCmd#(childT, wayT, cRqIdxT)),
Alias#(llCmdT, LLCmd#(childT, cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: replicate(I),
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged CRs .r: r.addr;
tagged MRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t LL %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec)
);
if(cmd matches tagged MRs .rs) begin
// MRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/CRs: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (we do not check replacing bit in LLC)
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// cRs must hit, so only cRq cannot enter here
doAssert(cmd matches tagged CRq ._rq ? True : False,
"only cRq can tag match miss"
);
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
doAssert(isValid(repWay), "should always find a way to replace");
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
// update dir
dirT newDir = oldDir;
if(cmd matches tagged CRs .cRs) begin
if(dataV) begin
doAssert(oldDir[cRs.child] >= E, "cRs with data, dir must >= E");
end
else begin
doAssert(oldDir[cRs.child] < M, "cRs without data, dir must < M");
end
if(oldDir[cRs.child] == M) begin
doAssert(dataV, "must have data");
end
newDir[cRs.child] = toState;
end
else begin
// should not happen
doAssert(False, "only cRs updates dir");
end
// update cs
// XXX since child can upgrade from E to M silently, use data valid
// to determine if we need to upgrade to M. Note that the data
// valid field has not been overwritten by bypass in CCPipe.
Msi newCs = oldCs;
if(dataV) begin
doAssert(oldCs >= E, "cRs has data, cs must >= E");
newCs = M;
end
return UpdateByDownDir {cs: newCs, dir: newDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// get first output from CCPipe output
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: LLCRq (rq.mshrIdx);
tagged CRs .rs: LLCRs (rs.child);
tagged MRs .rs: LLMRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endfunction
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged CRs .rs: begin
pipe.enq(CRs (LLPipeCRsCmd {
addr: rs.addr,
child: rs.child
}), rs.data, DownDir (rs.toState));
end
tagged MRs .rs: begin
pipe.enq(MRs (LLPipeMRsCmd {
addr: rs.addr,
way: rs.way
}), Valid (rs.data), UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
return getFirst(pipe.first); // guarded version
endmethod
method pipeOutT unguard_first;
return getFirst(pipe.unguard_first); // unguarded version
endmethod
method notEmpty = pipe.notEmpty;
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin
newCmd = Valid (CRq (LLPipeCRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

View File

@@ -0,0 +1,83 @@
// 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.
import Ehr::*;
import CCTypes::*;
import Vector::*;
import ProcTypes::*;
interface MshrDeadlockChecker#(numeric type num);
method ActionValue#(Maybe#(Bit#(TLog#(num)))) getStuckIdx; // get deadlock MSHR idx
method Action initEntry(Bit#(TLog#(num)) n); // new MSHR entry allocated
method Action releaseEntry(Bit#(TLog#(num)) n); // existing MSHR entry released
endinterface
module mkMshrDeadlockChecker(MshrDeadlockChecker#(num)) provisos(
Alias#(idxT, Bit#(TLog#(num)))
);
Integer check_port = 0;
Integer incr_port = 1;
// timer for each entry to detect deadlock: being processed for 64M cycles
Vector#(num, Ehr#(2, Maybe#(DeadlockTimer))) timer <- replicateM(mkEhr(Invalid));
// when new entry is allocated, init timer
Vector#(num, PulseWire) init <- replicateM(mkPulseWire);
// when existing entry is released, end the timer
Vector#(num, PulseWire) done <- replicateM(mkPulseWire);
(* fire_when_enabled, no_implicit_conditions *)
rule incrTimer;
for(Integer i = 0; i < valueof(num); i = i+1) begin
if(init[i]) begin
timer[i][incr_port] <= Valid (0);
end
else if(done[i]) begin
timer[i][incr_port] <= Invalid;
end
else if(timer[i][incr_port] matches tagged Valid .t &&& t != maxBound) begin
timer[i][incr_port] <= Valid (t + 1);
end
end
endrule
method ActionValue#(Maybe#(idxT)) getStuckIdx;
function Bool isDeadlock(Integer i);
return timer[i][check_port] == Valid (maxBound);
endfunction
Vector#(num, Integer) idxVec = genVector;
if(searchIndex(isDeadlock, idxVec) matches tagged Valid .n) begin
timer[n][check_port] <= Valid (0);
return Valid (n);
end
else begin
return Invalid;
end
endmethod
method Action initEntry(idxT n);
init[n].send;
endmethod
method Action releaseEntry(idxT n);
done[n].send;
endmethod
endmodule

View File

@@ -0,0 +1,64 @@
// 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.
import BRAMCore::*;
import Fifo::*;
interface RWBramCore#(type addrT, type dataT);
method Action wrReq(addrT a, dataT d);
method Action rdReq(addrT a);
method dataT rdResp;
method Bool rdRespValid;
method Action deqRdResp;
endinterface
module mkRWBramCore(RWBramCore#(addrT, dataT)) provisos(
Bits#(addrT, addrSz), Bits#(dataT, dataSz)
);
BRAM_DUAL_PORT#(addrT, dataT) bram <- mkBRAMCore2(valueOf(TExp#(addrSz)), False);
BRAM_PORT#(addrT, dataT) wrPort = bram.a;
BRAM_PORT#(addrT, dataT) rdPort = bram.b;
// 1 elem pipeline fifo to add guard for read req/resp
// must be 1 elem to make sure rdResp is not corrupted
// BRAMCore should not change output if no req is made
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
method Action wrReq(addrT a, dataT d);
wrPort.put(True, a, d);
endmethod
method Action rdReq(addrT a);
rdReqQ.enq(?);
rdPort.put(False, a, ?);
endmethod
method dataT rdResp if(rdReqQ.notEmpty);
return rdPort.read;
endmethod
method rdRespValid = rdReqQ.notEmpty;
method Action deqRdResp;
rdReqQ.deq;
endmethod
endmodule

View File

@@ -0,0 +1,90 @@
// 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.
import Vector::*;
import Fifo::*;
import CCTypes::*;
import RWBramCore::*;
// random replace does not need any bookkeeping
typedef void RandRepInfo;
function RandRepInfo randRepInitInfo;
return ?;
endfunction
module mkRandRepRam(RWBramCore#(indexT, RandRepInfo));
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
method Action wrReq(indexT a, RandRepInfo d);
noAction;
endmethod
method Action rdReq(indexT a);
rdReqQ.enq(?);
endmethod
method RandRepInfo rdResp if(rdReqQ.notEmpty);
return ?;
endmethod
method rdRespValid = rdReqQ.notEmpty;
method deqRdResp = rdReqQ.deq;
endmodule
interface RandomReplace#(numeric type wayNum);
// find a way to replace, which is not locked
// and Invalid way has priority
method Maybe#(Bit#(TLog#(wayNum))) getReplaceWay(
Vector#(wayNum, Bool) unlocked,
Vector#(wayNum, Bool) invalid
);
endinterface
module mkRandomReplace(RandomReplace#(wayNum)) provisos(
Alias#(wayT, Bit#(TLog#(wayNum)))
);
Reg#(wayT) randWay <- mkReg(0);
rule tick;
randWay <= randWay == fromInteger(valueOf(wayNum) - 1) ? 0 : randWay + 1;
endrule
method Maybe#(wayT) getReplaceWay(Vector#(wayNum, Bool) unlocked, Vector#(wayNum, Bool) invalid);
// first search for invalid & unlocked way
function Bool isInvUnlock(Integer i);
return unlocked[i] && invalid[i];
endfunction
Vector#(wayNum, Integer) idxVec = genVector;
Maybe#(wayT) repWay = searchIndex(isInvUnlock, idxVec);
if(!isValid(repWay)) begin
// check whether random way is unlocked
if(unlocked[randWay]) begin
repWay = Valid (randWay);
end
else begin
// just find a unlocked way
repWay = searchIndex(id, unlocked);
end
end
return repWay;
endmethod
endmodule

View File

@@ -0,0 +1,629 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import MemoryTypes::*;
import Amo::*;
import Cntrs::*;
import Vector::*;
import ConfigReg::*;
import FIFO::*;
import GetPut::*;
import ClientServer::*;
import CCTypes::*;
import ICRqMshr::*;
import CCPipe::*;
import SelfInvIPipe ::*;
import FShow::*;
import DefaultValue::*;
import Fifo::*;
import CacheUtils::*;
import Performance::*;
import LatencyTimer::*;
import RandomReplace::*;
export SelfInvICRqStuck(..);
export SelfInvIPRqStuck(..);
export SelfInvIBank(..);
export mkSelfInvIBank;
// L1 I$, no pRq
typedef struct {
Addr addr;
ICRqState state;
Bool waitP;
} SelfInvICRqStuck deriving(Bits, Eq, FShow);
typedef void SelfInvIPRqStuck; // not used
interface SelfInvIBank#(
numeric type supSz, // superscalar size
numeric type lgBankNum,
numeric type wayNum,
numeric type indexSz,
numeric type tagSz,
numeric type cRqNum
);
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
interface InstServer#(supSz) to_proc; // to child, i.e. processor
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(SelfInvICRqStuck) cRqStuck;
interface Get#(SelfInvIPRqStuck) pRqStuck;
// security: flush (not implemented)
method Action flush;
method Bool flush_done;
// reconcile
method Action reconcile;
method Bool reconcile_done;
// performance
method Action setPerfStatus(Bool stats);
method Data getPerfData(L1IPerfType t);
endinterface
module mkSelfInvIBank#(
Bit#(lgBankNum) bankId,
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
module#(SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)) mkIPipeline
)(
SelfInvIBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
Alias#(otherT, void),
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, otherT, Line)),
Alias#(procRqT, ProcRqToI),
Alias#(cRqToPT, CRqMsg#(wayT, void)),
Alias#(cRsToPT, CRsMsg#(void)),
Alias#(pRqFromPT, PRqMsg#(void)),
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, otherT, RandRepInfo, Line, iCmdT)),
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
// requirements
FShow#(pipeOutT),
Add#(tagSz, a__, AddrSz),
// make sure: cRqNum <= wayNum
Add#(cRqNum, b__, wayNum),
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
);
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT) pipeline <- mkIPipeline;
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
// index Q to order all in flight cRq for in-order resp
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
// Reconcile states
Reg#(Bool) needReconcile <- mkReg(False);
Reg#(Bool) waitReconcileDone <- mkReg(False);
`ifdef DEBUG_ICACHE
// id for each cRq, incremented when each new req comes
Reg#(Bit#(64)) cRqId <- mkReg(0);
// FIFO to signal the id of cRq that is performed
// FIFO has 0 cycle latency to match L1 D$ resp latency
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
`endif
`ifdef PERF_COUNT
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) ldCnt <- mkCount(0);
Count#(Data) ldMissCnt <- mkCount(0);
Count#(Data) ldMissLat <- mkCount(0);
Count#(Data) reconcileCnt <- mkCount(0);
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
function Action incrReqCnt;
action
if(doStats) begin
ldCnt.incr(1);
end
endaction
endfunction
function Action incrMissCnt(cRqIdxT idx);
action
let lat <- latTimer.done(idx);
if(doStats) begin
ldMissLat.incr(zeroExtend(lat));
ldMissCnt.incr(1);
end
endaction
endfunction
`endif
function tagT getTag(Addr a) = truncateLSB(a);
// XXX since I$ may be requested by processor constantly
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
// otherwise the whole system may deadlock/livelock
// we stop accepting cRq when we need to reconcile
rule cRqTransfer(!needReconcile);
Addr addr <- toGet(rqFromCQ).get;
`ifdef DEBUG_ICACHE
procRqT r = ProcRqToI {addr: addr, id: cRqId};
cRqId <= cRqId + 1;
`else
procRqT r = ProcRqToI {addr: addr};
`endif
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
// send to pipeline
pipeline.send(CRq (SelfInvIPipeRqIn {
addr: r.addr,
mshrIdx: n
}));
// enq to indexQ for in order resp
cRqIndexQ.enq(n);
`ifdef PERF_COUNT
// performance counter: cRq type
incrReqCnt;
`endif
$display("%t I %m cRqTransfer: ", $time,
fshow(n), " ; ",
fshow(r)
);
endrule
// this descending urgency is necessary to avoid deadlock/livelock
// pRq cannot happen, because I$ is never exclusive
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
fromPQ.deq;
$display("%t I %m pRqTransfer: ", $time, fshow(req));
doAssert(False, "should not have pRq");
endrule
// this descending urgency is necessary to avoid deadlock/livelock
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
fromPQ.deq;
pipeline.send(PRs (SelfInvIPipePRsIn {
addr: resp.addr,
toState: S,
data: resp.data,
way: resp.id
}));
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
endrule
rule sendRqToP;
rqToPIndexQ.deq;
cRqIdxT n = rqToPIndexQ.first;
procRqT req = cRqMshr.sendRqToP.getRq(n);
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
cRqToPT cRqToP = CRqMsg {
addr: req.addr,
fromState: I, // I$ upgrade from I
toState: S, // I$ upgrade to S
canUpToE: False,
id: slot.way,
child: ?
};
rqToPQ.enq(cRqToP);
$display("%t I %m sendRqToP: ", $time,
fshow(n), " ; ",
fshow(req), " ; ",
fshow(slot), " ; ",
fshow(cRqToP)
);
`ifdef PERF_COUNT
// performance counter: start miss timer
latTimer.start(n);
`endif
endrule
// last stage of pipeline: process req
// XXX: in L1, pRq cannot exist in dependency chain
// because there are only two ways to include pRq into chain
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
// (this cannot happen in I$)
// Thus, dependency chain in L1 only contains cRq
// pipeline outputs
pipeOutT pipeOut = pipeline.first;
ramDataT ram = pipeOut.ram;
// get proc req to select from cRqMshr
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
case(pipeOut.cmd) matches
tagged ICRq .n: (n);
default: (fromMaybe(0, ram.info.owner)); // L1PRs
endcase
);
// function to get superscaler inst read result
function resultT readInst(Line line, Addr addr);
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
// the start offset for reading inst
LineInstOffset startSel = getLineInstOffset(addr);
// calculate the maximum inst count that could be read from line
LineInstOffset maxCntMinusOne = maxBound - startSel;
// read inst superscalaer
resultT val = ?;
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
if(fromInteger(i) <= maxCntMinusOne) begin
LineInstOffset sel = startSel + fromInteger(i);
val[i] = Valid (instVec[sel]);
end
else begin
val[i] = Invalid;
end
end
return val;
endfunction
// function to process cRq hit (MSHR slot may have garbage)
function Action cRqHit(cRqIdxT n, procRqT req);
action
$display("%t I %m pipelineResp: Hit func: ", $time,
fshow(n), " ; ",
fshow(req)
);
// check tag & cs: even this function is called by pRs, tag should match,
// because tag is written into cache before sending req to parent
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
"cRqHit but tag or cs incorrect"
);
// deq pipeline or swap in successor
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
pipeline.deqWrite(succ, RamData {
info: CacheInfo {
tag: getTag(req.addr), // should be the same as original tag
cs: ram.info.cs, // use cs in ram
dir: ?,
owner: succ,
other: ?
},
line: ram.line
}, True); // hit, so update rep info
// process req to get superscalar inst read results
// set MSHR entry as Done & save inst results
let instResult = readInst(ram.line, req.addr);
cRqMshr.pipelineResp.setResult(n, instResult);
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
fshow(succ), " ; ", fshow(instResult)
);
`ifdef DEBUG_ICACHE
// signal that this req is performed
cRqDoneQ.enq(DebugICacheResp {
id: req.id,
line: ram.line
});
`endif
endaction
endfunction
rule pipelineResp_cRq(pipeOut.cmd matches tagged ICRq .n);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
procRqT procRq = pipeOutCRq;
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
// find end of dependency chain
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
// function to process cRq miss without replacement (MSHR slot may have garbage)
// We never replace in self-inv I$, because all S lines can be replaced silently
function Action cRqMissNoReplacement;
action
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
// it is impossible in L1 to have slot.waitP == True in this function
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
doAssert(!cSlot.waitP, "waitP must be false");
// No exclusive in I$
doAssert(ram.info.cs <= S, "no exclusive in I$");
// This cannot be a hit
doAssert(ram.info.cs == I || ram.info.tag != getTag(procRq.addr), "cannot hit");
// Thus we must send req to parent
rqToPIndexQ.enq(n);
// update mshr
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
way: pipeOut.way, // use way from pipeline
repTag: ?, // no replacement
waitP: True // must fetch from parent
});
// deq pipeline & set owner, tag
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: getTag(procRq.addr), // tag may be garbage if cs == I or silent replace
cs: I, // line must be invalid
dir: ?,
owner: Valid (n), // owner is req itself
other: ?
},
line: ram.line
}, False);
endaction
endfunction
// function to set cRq to Depend, and make no further change to cache
function Action cRqSetDepNoCacheChange;
action
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
pipeline.deqWrite(Invalid, pipeOut.ram, False);
endaction
endfunction
if(ram.info.owner matches tagged Valid .cOwner) begin
if(cOwner != n) begin
// owner is another cRq, so must just go through tag match
// tag match must be hit (because replacement algo won't give a way with owner)
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"cRq should hit in tag match"
);
// should be added to a cRq in dependency chain & deq from pipeline
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
cRqSetDepNoCacheChange;
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
);
end
else begin
// owner is myself, so must be swapped in
// tag should match, since always swapped in by cRq, cs = S
// Reconcile happens only when no cRq in MSHR, so it won't affect swap
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
"cRq swapped in by previous cRq, tag must match & cs = S"
);
// Hit
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
cRqHit(n, procRq);
end
end
else begin
// cache has no owner, cRq must just go through tag match
// check for cRqEOC to append to dependency chain
if(cRqEOC matches tagged Valid .k) begin
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
cRqMshr.pipelineResp.setSucc(k, Valid (n));
cRqSetDepNoCacheChange;
end
else if(ram.info.cs > I && ram.info.tag == getTag(procRq.addr)) begin
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
cRqHit(n, procRq);
end
else begin
// can always sliently replace
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
cRqMissNoReplacement;
end
end
endrule
rule pipelineResp_pRs(pipeOut.cmd == IPRs);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
$display("%t I %m pipelineResp: pRs: ", $time);
if(ram.info.owner matches tagged Valid .cOwner) begin
procRqT procRq = pipeOutCRq;
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"pRs must be a hit"
);
cRqHit(cOwner, procRq);
`ifdef PERF_COUNT
// performance counter: miss cRq
incrMissCnt(cOwner);
`endif
end
else begin
doAssert(False, ("pRs owner must match some cRq"));
end
endrule
// Reconcile lines in S state: start after cRq MSHR is empty
// Since cRqTransfer rule cannot fire when needReconcile is true, we use a
// wire to catch cRqMshr.empty to avoid scheduling cycles
PulseWire cRqMshrEmpty <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setCRqMshrEmpty(cRqMshr.emptyForFlush);
cRqMshrEmpty.send;
endrule
rule startReconcile(needReconcile && !waitReconcileDone && cRqMshrEmpty);
pipeline.reconcile;
waitReconcileDone <= True;
$display("%t I %m startReconcile", $time);
`ifdef PERF_COUNT
if(doStats) begin
reconcileCnt.incr(1);
end
`endif
endrule
rule completeReconcile(needReconcile && waitReconcileDone && pipeline.reconcile_done);
needReconcile <= False;
waitReconcileDone <= False;
$display("%t I %m completeReconcile", $time);
endrule
interface ChildCacheToParent to_parent;
interface rsToP = nullFifoDeq;
interface rqToP = toFifoDeq(rqToPQ);
interface fromP = toFifoEnq(fromPQ);
endinterface
interface InstServer to_proc;
interface Put req;
method Action put(Addr addr);
rqFromCQ.enq(addr);
endmethod
endinterface
interface Get resp;
method ActionValue#(resultT) get if(
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
);
cRqIndexQ.deq;
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
$display("%t I %m sendRsToC: ", $time,
fshow(cRqIndexQ.first), " ; ",
fshow(inst)
);
return inst;
endmethod
endinterface
`ifdef DEBUG_ICACHE
interface done = toGet(cRqDoneQ);
`endif
endinterface
interface Get cRqStuck;
method ActionValue#(SelfInvICRqStuck) get;
let s <- cRqMshr.stuck.get;
return SelfInvICRqStuck {
addr: s.req.addr,
state: s.state,
waitP: s.waitP
};
endmethod
endinterface
interface pRqStuck = nullGet;
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
method Action reconcile if(!needReconcile);
needReconcile <= True;
endmethod
method Bool reconcile_done;
return !needReconcile;
endmethod
method Action setPerfStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Data getPerfData(L1IPerfType t);
return (case(t)
`ifdef PERF_COUNT
L1ILdCnt: ldCnt;
L1ILdMissCnt: ldMissCnt;
L1ILdMissLat: ldMissLat;
L1IReconcileCnt: reconcileCnt;
`endif
default: 0;
endcase);
endmethod
endmodule
// Scheduling note
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
// pRsTransfer: -
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
// sendRqToP: read cRq MSHR req/slot that is requesting parent
// pipelineResp_cRq:
// -- read cRq MSHR req/state/slot currently processed
// -- write cRq MSHR state/slot/result currently processed
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
// -- not affected by write in cRqTransfer (state change is Empty->Init)
// -- not affected by write in sendRsC (state change is Done->Empty)
// pipelineResp_pRs:
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
// pipelineResp_pRq:
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
// ---- conflict analysis ----
// XXXTransfer is conflict with each other
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
// XXXTransfer should operate on different cRq/pRq from other rules
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
// sendRqToP and sendRsToP_cRq are read only
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
// ---- conclusion ----
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
// we have 5 ports from cRq MSHR
// 1. cRqTransfer
// 2. sendRsToC
// 3. sendRsToP_cRq
// 4. sendRqToP
// 5. pipelineResp
// we have 3 ports from pRq MSHR
// 1. pRqTransfer
// 2. sendRsToP_pRq
// 3. pipelineResp
// safe version: use EHR ports
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
// pipelineResp < sendRsToP_pRq < pRqTransfer
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
// we maintain the logical ordering in safe version

View File

@@ -0,0 +1,466 @@
// Copyright (c) 2019 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.
import Assert::*;
import ConfigReg::*;
import Vector::*;
import FShow::*;
import Types::*;
import Fifo::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export SelfInvIPipeRqIn(..);
export SelfInvIPipePRsIn(..);
export SelfInvIPipeIn(..);
export SelfInvICmd(..);
export SelfInvIPipe(..);
export mkSelfInvIPipe;
// type param ordering: bank < way < index < tag < cRq
// In I cache, only cRq can occupy cache line, there is no pRq or explicity
// replacement, so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} SelfInvIPipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} SelfInvIPipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvIPipeRqIn#(cRqIdxT) CRq;
SelfInvIPipePRsIn#(wayT) PRs;
} SelfInvIPipeIn#(
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in I$
typedef union tagged {
cRqIdxT ICRq;
void IPRs;
} SelfInvICmd#(
type cRqIdxT
) deriving (Bits, Eq, FShow);
interface SelfInvIPipe#(
numeric type lgBankNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(SelfInvIPipeIn#(Bit#(TLog#(wayNum)), cRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), void, RandRepInfo, // no other
Line, SelfInvICmd#(cRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
Bool updateRep
);
// drop stale clean cache lines
method Action reconcile;
method Bool reconcile_done;
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} SelfInvIPipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvIPipeRqIn#(cRqIdxT) CRq;
SelfInvIPipePRsCmd#(wayT) PRs;
} SelfInvIPipeCmd#(
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// cache state array with reconcile port
// ram sub interface has same scheduling as RWBramCore:
// - wrReq CF rdReq (read cannot see write)
// - deqRdResp < rdReq
interface CacheStateArray#(type indexT);
interface RWBramCore#(indexT, Msi) ram;
method Action reconcile;
endinterface
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz))
);
staticAssert(pack(Msi'(I)) == 2'd0, "I = 0");
staticAssert(pack(Msi'(S)) == 2'd1, "S = 0");
Vector#(size, Reg#(Bit#(1))) state <- replicateM(mkConfigReg(0));
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
interface RWBramCore ram;
method Action wrReq(indexT idx, Msi s);
doAssert(s == I || s == S, "only I or S");
state[idx] <= truncate(pack(s));
endmethod
method Action rdReq(indexT idx);
rdRespQ.enq(unpack(zeroExtend(state[idx])));
endmethod
method rdResp = rdRespQ.first;
method rdRespValid = rdRespQ.notEmpty;
method deqRdResp = rdRespQ.deq;
endinterface
method Action reconcile;
function Action resetS(Reg#(Bit#(1)) s);
action
s <= 0;
endaction
endfunction
joinActions(map(resetS, state));
endmethod
endmodule
// Put cache state array and tag+owner ram together to form an array
typedef struct {
tagT tag;
ownerT owner;
otherT other;
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
method Action reconcile;
endinterface
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz)),
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
Bits#(tagT, _tagSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz)
);
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
interface RWBramCore ram;
method Action wrReq(indexT idx, infoT x);
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
tag: x.tag,
owner: x.owner,
other: x.other
});
csArray.ram.wrReq(idx, x.cs);
endmethod
method Action rdReq(indexT idx);
tagOwnerOtherRam.rdReq(idx);
csArray.ram.rdReq(idx);
endmethod
method infoT rdResp;
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
Msi cs = csArray.ram.rdResp;
return CacheInfo {
tag: tagOwnerOther.tag,
cs: cs,
dir: ?,
owner: tagOwnerOther.owner,
other: tagOwnerOther.other
};
endmethod
method Bool rdRespValid;
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
endmethod
method Action deqRdResp;
tagOwnerOtherRam.deqRdResp;
csArray.ram.deqRdResp;
endmethod
endinterface
method reconcile = csArray.reconcile;
endmodule
module mkSelfInvIPipe(
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, void),
Alias#(repT, RandRepInfo),
Alias#(pipeInT, SelfInvIPipeIn#(wayT, cRqIdxT)),
Alias#(pipeCmdT, SelfInvIPipeCmd#(wayT, cRqIdxT)),
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, iCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// info RAM
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
// rep RAM (dummy)
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
// data RAM
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
doAssert(cmd matches tagged CRq .rq ? True : False, "must be cRq");
// CRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState == S && oldCs == I, "upgrade cs I->S");
doAssert(dataV, "self inv L1 always needs data resp");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 should not have cRs");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// reconcile: wait until pipeline empty and drop all S states. Stall
// pipeline enq while we are waiting. Make the reconcile rule conflict with
// pipeline deq to remove any possible race (the guard of reconcile rule
// actually should have done the job).
// Since send method will not fire when needReconcile, we can use a wire to
// catch pipeline empty signal to avoid scheduling issue
Reg#(Bool) needReconcile <- mkReg(False);
RWire#(void) conflict_reconcile_deq <- mkRWire;
PulseWire pipeEmpty <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setPipeEmpty(pipe.emptyForFlush);
pipeEmpty.send;
endrule
rule doReconcile(initDone && needReconcile && pipeEmpty);
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
action
ifc.reconcile;
endaction
endfunction
joinActions(map(flush, infoArray));
// reconcile is done
needReconcile <= False;
// conflict with deq
conflict_reconcile_deq.wset(?);
$display("%t I %m doReconcile", $time);
endrule
// stall enq for reconcile
method Action send(pipeInT req) if(!needReconcile);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (SelfInvIPipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: ICRq (rq.mshrIdx);
tagged PRs .rs: IPRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (SelfInvIPipeRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
// conflict with reconcile
conflict_reconcile_deq.wset(?);
endmethod
method Action reconcile if(!needReconcile);
needReconcile <= True;
endmethod
method Bool reconcile_done;
return !needReconcile;
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,492 @@
// Copyright (c) 2019 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.
import Assert::*;
import ConfigReg::*;
import Vector::*;
import FShow::*;
import Fifo::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export SelfInvL1PipeRqIn(..);
export SelfInvL1PipePRsIn(..);
export SelfInvL1PipeIn(..);
export SelfInvL1Cmd(..);
export SelfInvL1Hits(..);
export SelfInvL1Pipe(..);
export mkSelfInvL1Pipe;
// type param ordering: bank < way < index < tag < cRq < pRq
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
// replacement is always done immediately (never have replacing line)
// so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} SelfInvL1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} SelfInvL1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
SelfInvL1PipePRsIn#(wayT) PRs;
} SelfInvL1PipeIn#(
type wayT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in L1$
typedef union tagged {
cRqIdxT L1CRq;
pRqIdxT L1PRq;
void L1PRs;
} SelfInvL1Cmd#(
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// The "other" field in CacheInfo tracks the number of hits on the cache line
// before self inv
typedef struct {
Bit#(TLog#(maxHitNum)) hits;
} SelfInvL1Hits#(numeric type maxHitNum) deriving(Bits, Eq, FShow);
interface SelfInvL1Pipe#(
numeric type lgBankNum,
numeric type wayNum,
numeric type maxHitNum,
type indexT,
type tagT,
type cRqIdxT,
type pRqIdxT
);
method Action send(SelfInvL1PipeIn#(Bit#(TLog#(wayNum)), cRqIdxT, pRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), RandRepInfo,
Line, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), Line) wrRam, // always write BRAM
Bool updateRep
);
// drop stale clean cache lines
method Action reconcile;
method Bool reconcile_done;
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} SelfInvL1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
SelfInvL1PipePRsCmd#(wayT) PRs;
} SelfInvL1PipeCmd#(
type wayT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// cache state array with reconcile port
// ram sub interface has same scheduling as RWBramCore:
// - wrReq CF rdReq (read cannot see write)
// - deqRdResp < rdReq
interface CacheStateArray#(type indexT);
interface RWBramCore#(indexT, Msi) ram;
method Action reconcile;
endinterface
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz))
);
Vector#(size, Reg#(Msi)) state <- replicateM(mkConfigReg(I));
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
interface RWBramCore ram;
method Action wrReq(indexT idx, Msi s);
state[idx] <= s;
endmethod
method Action rdReq(indexT idx);
rdRespQ.enq(state[idx]);
endmethod
method rdResp = rdRespQ.first;
method rdRespValid = rdRespQ.notEmpty;
method deqRdResp = rdRespQ.deq;
endinterface
method Action reconcile;
function Action resetS(Reg#(Msi) s);
action
if(s == S) begin
s <= I;
end
endaction
endfunction
joinActions(map(resetS, state));
endmethod
endmodule
// Put cache state array and tag+owner ram together to form an array
typedef struct {
tagT tag;
ownerT owner;
otherT other;
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
method Action reconcile;
endinterface
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz)),
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
Bits#(tagT, _tagSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz)
);
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
interface RWBramCore ram;
method Action wrReq(indexT idx, infoT x);
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
tag: x.tag,
owner: x.owner,
other: x.other
});
csArray.ram.wrReq(idx, x.cs);
endmethod
method Action rdReq(indexT idx);
tagOwnerOtherRam.rdReq(idx);
csArray.ram.rdReq(idx);
endmethod
method infoT rdResp;
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
Msi cs = csArray.ram.rdResp;
return CacheInfo {
tag: tagOwnerOther.tag,
cs: cs,
dir: ?,
owner: tagOwnerOther.owner,
other: tagOwnerOther.other
};
endmethod
method Bool rdRespValid;
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
endmethod
method Action deqRdResp;
tagOwnerOtherRam.deqRdResp;
csArray.ram.deqRdResp;
endmethod
endinterface
method reconcile = csArray.reconcile;
endmodule
module mkSelfInvL1Pipe(
SelfInvL1Pipe#(lgBankNum, wayNum, maxHitNum, indexT, tagT, cRqIdxT, pRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, SelfInvL1Hits#(maxHitNum)),
Alias#(repT, RandRepInfo),
Alias#(pipeInT, SelfInvL1PipeIn#(wayT, cRqIdxT, pRqIdxT)),
Alias#(pipeCmdT, SelfInvL1PipeCmd#(wayT, cRqIdxT, pRqIdxT)),
Alias#(l1CmdT, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// info RAM
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
// rep RAM (dummy)
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
// data RAM
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: SelfInvL1Hits {hits: 0}
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRq .r: r.addr;
tagged PRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/PRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else if(cmd matches tagged PRq .rq) begin
// pRq miss
return TagMatchResult {
way: 0, // default to 0
pRqMiss: True
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert(dataV, "self inv L1 always needs data resp");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 should not have cRs");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// reconcile: wait until pipeline empty and drop all S states. Stall
// pipeline enq while we are waiting. Make the reconcile rule conflict with
// pipeline deq to remove any possible race (the guard of reconcile rule
// actually should have done the job).
// Since send method will not fire when needReconcile, we can use a wire to
// catch pipeline empty signal to avoid scheduling issue
Reg#(Bool) needReconcile <- mkReg(False);
RWire#(void) conflict_reconcile_deq <- mkRWire;
PulseWire pipeEmpty <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setPipeEmpty(pipe.emptyForFlush);
pipeEmpty.send;
endrule
rule doReconcile(initDone && needReconcile && pipeEmpty);
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
action
ifc.reconcile;
endaction
endfunction
joinActions(map(flush, infoArray));
// reconcile is done
needReconcile <= False;
// conflict with deq
conflict_reconcile_deq.wset(?);
$display("%t L1 %m doReconcile", $time);
endrule
// stall enq for reconcile
method Action send(pipeInT req) if(!needReconcile);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRq .rq: begin
pipe.enq(PRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (SelfInvL1PipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: L1CRq (rq.mshrIdx);
tagged PRq .rq: L1PRq (rq.mshrIdx);
tagged PRs .rs: L1PRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (SelfInvL1PipeRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
// conflict with reconcile
conflict_reconcile_deq.wset(?);
endmethod
method Action reconcile if(!needReconcile);
needReconcile <= True;
endmethod
method Bool reconcile_done;
return !needReconcile;
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,366 @@
// 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.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
// type param ordering: bank < child < way < index < tag < cRq
// input types
typedef struct {
Addr addr;
cRqIdxT mshrIdx;
} SelfInvLLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState; // come from req in MSHR (E or M)
Line data; // come from memory must be valid
wayT way; // come from MSHR
} SelfInvLLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
CRsMsg#(childT) CRs;
SelfInvLLPipeMRsIn#(wayT) MRs;
} SelfInvLLPipeIn#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in LLC
typedef union tagged {
cRqIdxT LLCRq; // mshr idx of the cRq
childT LLCRs; // which child is downgrading
void LLMRs;
} SelfInvLLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
// Track only exclusive child in directory
typedef struct {
childT exChild;
Msi state; // I/E/M, we don't track S
} SelfInvDir#(type childT) deriving(Bits, Eq, FShow);
interface SelfInvLLPipe#(
numeric type lgBankNum,
numeric type childNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(SelfInvLLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
method Bool notEmpty;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) first;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) unguard_first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
childT child;
} SelfInvLLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
wayT way;
} SelfInvLLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
SelfInvLLPipeCRsCmd#(childT) CRs;
SelfInvLLPipeMRsCmd#(wayT) MRs;
} SelfInvLLPipeCmd#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
module mkSelfInvLLPipe(
SelfInvLLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(childT, Bit#(TLog#(childNum))),
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, SelfInvDir#(childT)),
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, SelfInvLLPipeIn#(childT, wayT, cRqIdxT)),
Alias#(pipeCmdT, SelfInvLLPipeCmd#(childT, wayT, cRqIdxT)),
Alias#(llCmdT, SelfInvLLCmd#(childT, cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: SelfInvDir {exChild: ?, state: I},
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged CRs .r: r.addr;
tagged MRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t LL %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec)
);
if(cmd matches tagged MRs .rs) begin
// MRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/CRs: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (we do not check replacing bit in LLC)
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// cRs must hit, so only cRq cannot enter here
doAssert(cmd matches tagged CRq ._rq ? True : False,
"only cRq can tag match miss"
);
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
doAssert(isValid(repWay), "should always find a way to replace");
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
// update dir
// The exclusive child is downgraded. Since we don't track S, just make
// dir invalid, child field is useless for I
dirT newDir = SelfInvDir {exChild: ?, state: I};
doAssert(oldDir.state >= E && toState <= S, "no more exclusive child");
doAssert(cmd matches tagged CRs .cRs &&& oldDir.exChild == cRs.child ? True : False,
"cRs child should match");
if(!dataV) begin
doAssert(oldDir.state < M, "cRs without data, dir must < M");
end
if(oldDir.state == M) begin
doAssert(dataV, "downgrade from M must have data");
doAssert(oldCs == M, "cs must be M");
end
// update cs
// XXX since child can upgrade from E to M silently, use data valid
// to determine if we need to upgrade to M. Note that the data
// valid field has not been overwritten by bypass in CCPipe.
Msi newCs = oldCs;
if(dataV) begin
doAssert(oldCs >= E, "cRs has data, cs must >= E");
newCs = M;
end
return UpdateByDownDir {cs: newCs, dir: newDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// get first output from CCPipe output
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: LLCRq (rq.mshrIdx);
tagged CRs .rs: LLCRs (rs.child);
tagged MRs .rs: LLMRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endfunction
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged CRs .rs: begin
pipe.enq(CRs (SelfInvLLPipeCRsCmd {
addr: rs.addr,
child: rs.child
}), rs.data, DownDir (rs.toState));
end
tagged MRs .rs: begin
pipe.enq(MRs (SelfInvLLPipeMRsCmd {
addr: rs.addr,
way: rs.way
}), Valid (rs.data), UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
return getFirst(pipe.first); // guarded version
endmethod
method pipeOutT unguard_first;
return getFirst(pipe.unguard_first); // unguarded version
endmethod
method notEmpty = pipe.notEmpty;
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin
newCmd = Valid (CRq (SelfInvLLPipeCRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

View File

@@ -0,0 +1,178 @@
// Copyright (c) 2015 Quanta Research Cambridge, Inc.
// 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.
import Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import CBus::*; // extendNP and truncateNP
`include "ConnectalProjectConfig.bsv"
import Arith::*;
import ConnectalClocks::*;
`ifdef SIMULATION
`ifdef BOARD_xsim
`define USE_XILINX_MACRO
`endif // xsim
`else // not SIMULATION
`ifdef XILINX
`define USE_XILINX_MACRO
`endif // XILINX
`endif // not SIMULATION
`ifdef USE_XILINX_MACRO
(* always_ready, always_enabled *)
interface X7FifoSyncMacro#(numeric type data_width);
method Bit#(1) empty();
method Bit#(1) full();
method Action din(Bit#(data_width) v);
method Action wren(Bit#(1) wren);
method Bit#(data_width) dout();
method Action rden(Bit#(1) rden);
method Bit#(9) wrcount();
method Bit#(9) rdcount();
endinterface
import "BVI" FIFO_DUALCLOCK_MACRO =
module vmkBramFifo#(String fifo_size, Clock wrClock, Reset wrReset, Clock rdClock, Reset rdReset)(X7FifoSyncMacro#(data_width));
`ifndef BSV_POSITIVE_RESET
let rdReset1 <- mkSyncReset(10, rdReset, wrClock);
let eitherReset <- mkResetEither(wrReset, rdReset1, clocked_by wrClock);
let positiveReset <- mkPositiveReset(10, eitherReset, wrClock);
// rst must be asserted for 5 read and write clock cycles
let fifoReset = positiveReset.positiveReset;
`else
let fifoReset = wrReset;
`endif
`ifdef XilinxUltrascale
parameter DEVICE = "ULTRASCALE";
`else
parameter DEVICE = "7SERIES";
`endif
parameter DATA_WIDTH = valueOf(data_width);
parameter FIFO_SIZE = fifo_size;
`ifndef SIMULATION
parameter FIRST_WORD_FALL_THROUGH = "TRUE"; // not supported by xsim
`else
parameter FIRST_WORD_FALL_THROUGH = True;
`endif
default_clock wrClock(WRCLK) = wrClock;
no_reset;
// RST is asynchronous
input_reset wrReset(RST) clocked_by(no_clock) = fifoReset;
input_clock rdClock(RDCLK) = rdClock;
method EMPTY empty() clocked_by (rdClock) reset_by (wrReset);
method FULL full() clocked_by (wrClock) reset_by (wrReset);
method din(DI) enable ((*inhigh*)EN_di) clocked_by (wrClock) reset_by (wrReset);
method wren(WREN) enable ((*inhigh*)EN_wren) clocked_by (wrClock) reset_by (wrReset);
method DO dout() clocked_by (rdClock) reset_by (wrReset);
method rden(RDEN) enable ((*inhigh*)EN_rden) clocked_by (rdClock) reset_by (wrReset);
// wrcount and rdcount ports are needed for xsim
method WRCOUNT wrcount() clocked_by (wrClock) reset_by (wrReset);
method RDCOUNT rdcount() clocked_by (rdClock) reset_by (wrReset);
schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
endmodule
module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
String fifo_size = "18Kb";
Vector#(TDiv#(sizet,36),X7FifoSyncMacro#(36)) fifos <- replicateM(vmkBramFifo(fifo_size, srcClock, srcReset, dstClock, dstReset));
Wire#(Bit#(1)) rdenWire <- mkDWire(0, clocked_by dstClock, reset_by dstReset);
Wire#(Bit#(1)) wrenWire <- mkDWire(0, clocked_by srcClock, reset_by srcReset);
Vector#(TDiv#(sizet,36),Wire#(Bit#(36))) dinWires <- replicateM(mkDWire(0, clocked_by srcClock, reset_by srcReset));
for (Integer i = 0; i < valueOf(TDiv#(sizet,36)); i = i+1) begin
Reg#(Bit#(9)) rdcount <- mkReg(0, clocked_by dstClock, reset_by dstReset);
Reg#(Bit#(9)) wrcount <- mkReg(0, clocked_by srcClock, reset_by srcReset);
rule rdenRule;
fifos[i].rden(rdenWire);
endrule
rule wrenRule;
fifos[i].wren(wrenWire);
endrule
rule inputs;
fifos[i].din(dinWires[i]);
endrule
rule countrds;
rdcount <= fifos[i].rdcount();
endrule
rule countwrs;
wrcount <= fifos[i].wrcount();
endrule
end
function Bool fifoNotEmpty(X7FifoSyncMacro#(36) f); return f.empty == 0; endfunction
function Bool fifoNotFull(X7FifoSyncMacro#(36) f); return f.full == 0; endfunction
method t first() if (all(fifoNotEmpty, fifos));
function Bit#(36) fifoFirst(Integer i); return fifos[i].dout(); endfunction
Vector#(TDiv#(sizet,36), Bit#(36)) v = genWith(fifoFirst);
return unpack(truncateNP(pack(v)));
endmethod
method Action deq() if (all(fifoNotEmpty, fifos));
rdenWire <= 1;
endmethod
method notEmpty = all(fifoNotEmpty, fifos);
method Action enq(t v) if (all(fifoNotFull, fifos));
Vector#(TDiv#(sizet,36), Bit#(36)) vs = unpack(extendNP(pack(v)));
Vector#(TDiv#(sizet,36), Integer) indices = genVector();
function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
mapM_(fifoEnq, indices);
wrenWire <= 1;
endmethod
method notFull = all(fifoNotEmpty, fifos);
endmodule
module mkDualClockBramFIFO#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFO#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
let syncFifo <- mkDualClockBramFIFOF(srcClock, srcReset, dstClock, dstReset);
method enq = syncFifo.enq;
method deq = syncFifo.deq;
method first = syncFifo.first;
endmodule
`else // compatibility mode
module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
let syncFifo <- mkSyncFIFO(512, srcClock, srcReset, dstClock);
method enq = syncFifo.enq;
method deq = syncFifo.deq;
method first = syncFifo.first;
method notFull = syncFifo.notFull;
method notEmpty = syncFifo.notEmpty;
endmodule
module mkDualClockBramFIFO#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFO#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
let syncFifo <- mkSyncFIFO(512, srcClock, srcReset, dstClock);
method enq = syncFifo.enq;
method deq = syncFifo.deq;
method first = syncFifo.first;
endmodule
`endif

View File

@@ -0,0 +1,130 @@
// Copyright (c) 2014 Quanta Research Cambridge, Inc.
// 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.
import Clocks::*;
`include "ConnectalProjectConfig.bsv"
`ifdef PcieClockPeriod
Real pcieClockPeriod = `PcieClockPeriod;
`endif
Real mainClockPeriod = `MainClockPeriod;
Real derivedClockPeriod =`DerivedClockPeriod;
(* always_ready, always_enabled *)
interface B2C;
interface Clock c;
interface Reset r;
method Action inputclock(Bit#(1) v);
method Action inputreset(Bit#(1) v);
endinterface
import "BVI" CONNECTNET2 =
module mkB2C(B2C);
default_clock no_clock;
default_reset no_reset;
output_clock c(OUT1);
output_reset r(OUT2);
method inputclock(IN1) enable((*inhigh*) en_inputclock) clocked_by(c);
method inputreset(IN2) enable((*inhigh*) en_inputreset) clocked_by(c);
schedule ( inputclock, inputreset) CF ( inputclock, inputreset);
endmodule
(* always_ready, always_enabled *)
interface B2C1;
interface Clock c;
method Action inputclock(Bit#(1) v);
endinterface
import "BVI" CONNECTNET =
module mkB2C1(B2C1);
default_clock clk();
default_reset rst();
output_clock c(OUT);
method inputclock(IN) enable((*inhigh*) en_inputclock);
schedule ( inputclock) CF ( inputclock);
endmodule
(* always_ready, always_enabled *)
interface C2B;
method Bit#(1) o();
endinterface
import "BVI" CONNECTNET =
module mkC2B#(Clock c)(C2B);
default_clock clk();
default_reset no_reset;
//default_reset rst();
input_clock ck(IN) = c;
method OUT o();
schedule ( o) CF ( o);
endmodule
(* always_ready, always_enabled *)
interface B2R;
interface Reset r;
method Action inputreset(Bit#(1) v);
endinterface
import "BVI" CONNECTNET =
module mkB2R(B2R);
default_clock clk();
default_reset rst();
output_reset r(OUT);
method inputreset(IN) enable((*inhigh*) en_inputclock);
schedule ( inputreset) CF ( inputreset);
endmodule
(* always_ready, always_enabled *)
interface R2B;
method Bit#(1) o();
endinterface
import "BVI" CONNECTNET =
module mkR2B#(Reset r)(C2B);
default_clock no_clock;
default_reset no_reset;
//default_reset rst();
input_reset rst(IN) = r;
method OUT o();
schedule ( o) CF ( o);
endmodule
interface PositiveReset;
interface Reset positiveReset;
endinterface
import "BVI" PositiveReset =
module mkPositiveReset#(Integer resetDelay, Reset reset, Clock clock)(PositiveReset);
parameter RSTDELAY = resetDelay;
default_clock clock(CLK) = clock;
default_reset reset(IN_RST) clocked_by (no_clock) = reset;
output_reset positiveReset(OUT_RST);
endmodule
interface FpgaReset;
interface Reset fpgaReset;
endinterface
import "BVI" FpgaReset =
module exposeFpgaReset#(Integer resetDelay, Clock clock)(FpgaReset);
parameter RSTDELAY = resetDelay;
default_clock clock(CLK) = clock;
no_reset;
output_reset fpgaReset(OUT_RST);
endmodule

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2014 Quanta Research Cambridge, Inc.
// 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.
import Vector::*;
function Bool booland(Bool x1, Bool x2); return x1 && x2; endfunction
function Bool boolor(Bool x1, Bool x2); return x1 || x2; endfunction
function Bool eq(a x1, a x2) provisos (Eq#(a)); return x1 == x2; endfunction
function a add(a x1, a x2) provisos (Arith#(a)); return x1 + x2; endfunction
function a mul(a x1, a x2) provisos (Arith#(a)); return x1 * x2; endfunction
function Bit#(b) rshift(Bit#(b) x1, Integer i); return x1 >> i; endfunction
function Vector#(n, a) vadd(Vector#(n, a) x1, Vector#(n, a) x2) provisos (Arith#(a));
return map(uncurry(add), zip(x1, x2));
endfunction
function Vector#(n, a) vmul(Vector#(n, a) x1, Vector#(n, a) x2) provisos (Arith#(a));
return map(uncurry(mul), zip(x1, x2));
endfunction
function Vector#(n, Bit#(b)) vrshift(Vector#(n, Bit#(b)) x1, Integer i);
return map(flip(rshift)(i), x1);
endfunction

View File

@@ -0,0 +1,15 @@
`define ConnectalVersion 15.11.1
`define NumberOfMasters 1
`define PinType Empty
`define PinTypeInclude Misc
`define NumberOfUserTiles 1
`define SlaveDataBusWidth 32
`define SlaveControlAddrWidth 5
`define BurstLenSize 10
`define project_dir $(DTOP)
`define MainClockPeriod 20
`define DerivedClockPeriod 10.000000
`define BsimHostInterface
`define PhysAddrWidth 40
`define SIMULATION
`define BOARD_bluesim

View File

@@ -0,0 +1,76 @@
// 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.
import Assert::*;
// User interface:
// all DRAM use 64B data block
// present user with 64-bit address space, but addr should be in terms of 64B
typedef 512 DramUserDataSz;
typedef TDiv#(DramUserDataSz, 8) DramUserBESz;
typedef 58 DramUserAddrSz;
typedef Bit#(DramUserDataSz) DramUserData;
typedef Bit#(DramUserBESz) DramUserBE;
typedef Bit#(DramUserAddrSz) DramUserAddr;
typedef struct { // read/write req
DramUserAddr addr;
DramUserData data;
DramUserBE wrBE; // all 0 means read,
// otherwise wrBE[i]=1 means to write byte i
} DramUserReq deriving(Bits, Eq, FShow);
interface DramUser#(
// maximum number of in-flight requests (not always used)
numeric type maxReadNum,
numeric type maxWriteNum,
// simulation delay (fully pipelined)
numeric type simDelay,
// error of dram controller
// XXX we should not check address overflow because of wrong-path loads
type errT
);
method Action req(DramUserReq r);
method ActionValue#(DramUserData) rdResp; // only read has resp
method ActionValue#(errT) err;
endinterface
// Full interface
interface DramFull#(
numeric type maxReadNum,
numeric type maxWriteNum,
numeric type simDelay,
type errT,
// pin type
type pinT
);
interface DramUser#(maxReadNum, maxWriteNum, simDelay, errT) user;
interface pinT pins;
endinterface
`ifdef BSIM
function Action doAssert(Bool b, String s) = action if(!b) $fdisplay(stderr, "\n%m: ASSERT FAIL!!"); dynamicAssert(b, s); endaction;
`else
function Action doAssert(Bool b, String s) = noAction;
`endif

View File

@@ -0,0 +1,38 @@
// 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.
import Clocks::*;
interface ResetGuard;
method Bool isReady;
endinterface
import "BVI" reset_guard =
module mkResetGuard(ResetGuard);
default_clock clk(CLK);
default_reset rst(RST);
method IS_READY isReady();
schedule (isReady) CF (isReady);
endmodule

View File

@@ -0,0 +1,114 @@
// 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.
import Clocks::*;
import FIFOF::*;
import Assert::*;
import ConnectalBramFifo::*;
import BRAMFIFO::*;
import XilinxSyncFifo::*;
import ResetGuard::*;
export mkSyncFifo;
export mkSyncBramFifo;
// sync fifo type selec macros: USE_CONNECTAL_BRAM_SYNC_FIFO, USE_BSV_BRAM_SYNC_FIFO, USE_XILINX_SYNC_FIFO
// we also guard the sync fifo from enq/deq before the reset
module mkSyncFifo#(
Integer depth, Clock srcClk, Reset srcRst, Clock dstClk, Reset dstRst
)(SyncFIFOIfc#(t)) provisos(
Bits#(t, tW),
NumAlias#(w, TMax#(1, tW)) // some sync fifo doesn't support 0-bit data
);
`ifdef USE_CONNECTAL_BRAM_SYNC_FIFO
staticAssert(depth <= 512, "depth of FIFO18 is 512");
FIFOF#(Bit#(w)) q <- mkDualClockBramFIFOF(srcClk, srcRst, dstClk, dstRst);
`elsif USE_BSV_BRAM_SYNC_FIFO
SyncFIFOIfc#(Bit#(w)) q <- mkSyncBRAMFIFO(depth, srcClk, srcRst, dstClk, dstRst);
`elsif USE_XILINX_SYNC_FIFO
staticAssert(depth <= 16, "depth of xilinx lut ram FIFO is 16");
SyncFIFOIfc#(Bit#(w)) q <- mkXilinxSyncFifo(srcClk, srcRst, dstClk);
`else
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(depth, srcClk, srcRst, dstClk);
`endif
// guard sync fifo operations
ResetGuard srcGuard <- mkResetGuard(clocked_by srcClk, reset_by srcRst);
ResetGuard dstGuard <- mkResetGuard(clocked_by dstClk, reset_by dstRst);
method notFull = q.notFull && srcGuard.isReady;
method Action enq(t x) if(srcGuard.isReady);
q.enq(zeroExtend(pack(x)));
endmethod
method notEmpty = q.notEmpty && dstGuard.isReady;
method t first if(dstGuard.isReady);
return unpack(truncate(q.first));
endmethod
method Action deq if(dstGuard.isReady);
q.deq;
endmethod
endmodule
module mkSyncBramFifo#(
Integer depth, Clock srcClk, Reset srcRst, Clock dstClk, Reset dstRst
)(SyncFIFOIfc#(t)) provisos(
Bits#(t, tW),
NumAlias#(w, TMax#(1, tW)) // some sync fifo doesn't support 0-bit data
);
`ifdef USE_CONNECTAL_BRAM_SYNC_FIFO
staticAssert(depth <= 512, "depth of FIFO18 is 512");
FIFOF#(Bit#(w)) q <- mkDualClockBramFIFOF(srcClk, srcRst, dstClk, dstRst);
`elsif USE_XILINX_SYNC_FIFO
staticAssert(depth <= 512, "depth of xilinx block ram FIFO is 512");
SyncFIFOIfc#(Bit#(w)) q <- mkXilinxSyncBramFifo(srcClk, srcRst, dstClk);
`else
SyncFIFOIfc#(Bit#(w)) q <- mkSyncBRAMFIFO(depth, srcClk, srcRst, dstClk, dstRst);
`endif
// guard sync fifo operations
ResetGuard srcGuard <- mkResetGuard(clocked_by srcClk, reset_by srcRst);
ResetGuard dstGuard <- mkResetGuard(clocked_by dstClk, reset_by dstRst);
method notFull = q.notFull && srcGuard.isReady;
method Action enq(t x) if(srcGuard.isReady);
q.enq(zeroExtend(pack(x)));
endmethod
method notEmpty = q.notEmpty && dstGuard.isReady;
method t first if(dstGuard.isReady);
return unpack(truncate(q.first));
endmethod
method Action deq if(dstGuard.isReady);
q.deq;
endmethod
endmodule

View File

@@ -0,0 +1,51 @@
// 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.
import ResetGuard::*;
// some xilinx IP automatically reset
// we wait several cycles to make sure the reset completes
// we also block signals on the IP before reset of current clock domain completes
// because such signals may be garbage
interface WaitAutoReset#(numeric type logCycles);
method Bool isReady;
endinterface
module mkWaitAutoReset(WaitAutoReset#(logCycles));
Reg#(Bit#(logCycles)) cnt <- mkReg(0);
Reg#(Bool) init <- mkReg(False);
ResetGuard rg <- mkResetGuard;
rule doInit(!init);
cnt <= cnt + 1;
if(cnt == maxBound) begin
init <= True;
end
endrule
method Bool isReady;
return init && rg.isReady;
endmethod
endmodule

View File

@@ -0,0 +1,387 @@
// 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.
import DefaultValue::*;
import GetPut::*;
import ClientServer::*;
import WaitAutoReset::*;
import FloatingPoint::*;
import FIFO::*;
export mkXilinxFpFmaIP;
export mkXilinxFpDivIP;
export mkXilinxFpSqrtIP;
export mkXilinxFpFmaSim;
export mkXilinxFpDivSim;
export mkXilinxFpSqrtSim;
export mkXilinxFpFma;
export mkXilinxFpDiv;
export mkXilinxFpSqrt;
typedef FloatingPoint::RoundMode FpuRoundMode;
typedef FloatingPoint::Exception FpuException;
export FpuRoundMode(..);
export FpuException(..);
// import Xilinx IP core
// xilinx raw FMA IP is a * b + c, later we need to adapt to a + b * c
interface FpFmaImport;
method Action enqA(Bit#(64) a);
method Action enqB(Bit#(64) b);
method Action enqC(Bit#(64) c);
method Action deq;
method Bit#(64) resp;
method Bit#(3) excep;
endinterface
import "BVI" fp_fma =
module mkFpFmaImport(FpFmaImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
method enqB(s_axis_b_tdata) enable(s_axis_b_tvalid) ready(s_axis_b_tready);
method enqC(s_axis_c_tdata) enable(s_axis_c_tvalid) ready(s_axis_c_tready);
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
schedule (enqA, enqB, enqC) CF (deq, resp, excep);
schedule (enqA) CF (enqB, enqC);
schedule (enqB) CF (enqC);
schedule (enqA) C (enqA);
schedule (enqB) C (enqB);
schedule (enqC) C (enqC);
schedule (deq) C (deq);
schedule (resp, excep, deq) CF (resp, excep);
endmodule
interface FpDivImport;
method Action enqA(Bit#(64) a);
method Action enqB(Bit#(64) b);
method Action deq;
method Bit#(64) resp;
method Bit#(4) excep;
endinterface
import "BVI" fp_div =
module mkFpDivImport(FpDivImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
method enqB(s_axis_b_tdata) enable(s_axis_b_tvalid) ready(s_axis_b_tready);
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
schedule (enqA, enqB) CF (deq, resp, excep);
schedule (enqA) CF (enqB);
schedule (enqA) C (enqA);
schedule (enqB) C (enqB);
schedule (deq) C (deq);
schedule (resp, excep, deq) CF (resp, excep);
endmodule
interface FpSqrtImport;
method Action enqA(Bit#(64) a);
method Action deq;
method Bit#(64) resp;
method Bit#(1) excep;
endinterface
import "BVI" fp_sqrt =
module mkFpSqrtImport(FpSqrtImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
schedule (enqA) CF (deq, resp, excep);
schedule (enqA) C (enqA);
schedule (deq) C (deq);
schedule (resp, excep, deq) CF (resp, excep);
endmodule
// adapt the IP core to Bluespec FPU interface
(* synthesize *)
module mkXilinxFpFmaIP(Server#(
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
WaitAutoReset#(8) init <- mkWaitAutoReset;
FpFmaImport fpFma <- mkFpFmaImport;
// xilinx raw ip is a * b + c
// what we need to provide is in1 + in2 * in3
// so in1 -> c, in2 -> a, in3 -> b
interface Put request;
method Action put(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) req) if(init.isReady);
let {maybe_in1, in2, in3, rm} = req;
Double in1 = fromMaybe(zero(False), maybe_in1);
fpFma.enqA(pack(in2));
fpFma.enqB(pack(in3));
fpFma.enqC(pack(in1));
if(rm != Rnd_Nearest_Even) begin
$fdisplay(stderr, "[Xlinx FMA] WARNING: unsupported rounding mode ", fshow(rm));
end
endmethod
endinterface
interface Get response;
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
fpFma.deq;
Double val = unpack(fpFma.resp);
Bit#(3) exBits = fpFma.excep;
FpuException excep = defaultValue;
excep.underflow = exBits[0] == 1;
excep.overflow = exBits[1] == 1;
excep.invalid_op = exBits[2] == 1;
return tuple2(val, excep);
endmethod
endinterface
endmodule
(* synthesize *)
module mkXilinxFpDivIP(Server#(
Tuple3#(Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
WaitAutoReset#(8) init <- mkWaitAutoReset;
FpDivImport fpDiv <- mkFpDivImport;
interface Put request;
method Action put(Tuple3#(Double, Double, FpuRoundMode) req) if(init.isReady);
let {a, b, rm} = req;
fpDiv.enqA(pack(a));
fpDiv.enqB(pack(b));
if(rm != Rnd_Nearest_Even) begin
$fdisplay(stderr, "[Xlinx DIV] WARNING: unsupported rounding mode ", fshow(rm));
end
endmethod
endinterface
interface Get response;
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
fpDiv.deq;
Double val = unpack(fpDiv.resp);
Bit#(4) exBits = fpDiv.excep;
FpuException excep = defaultValue;
excep.underflow = exBits[0] == 1;
excep.overflow = exBits[1] == 1;
excep.invalid_op = exBits[2] == 1;
excep.divide_0 = exBits[3] == 1;
return tuple2(val, excep);
endmethod
endinterface
endmodule
(* synthesize *)
module mkXilinxFpSqrtIP(Server#(
Tuple2#(Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
WaitAutoReset#(8) init <- mkWaitAutoReset;
FpSqrtImport fpSqrt <- mkFpSqrtImport;
interface Put request;
method Action put(Tuple2#(Double, FpuRoundMode) req) if(init.isReady);
let {a, rm} = req;
fpSqrt.enqA(pack(a));
if(rm != Rnd_Nearest_Even) begin
$fdisplay(stderr, "[Xlinx SQRT] WARNING: unsupported rounding mode ", fshow(rm));
end
endmethod
endinterface
interface Get response;
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
fpSqrt.deq;
Double val = unpack(fpSqrt.resp);
Bit#(1) exBits = fpSqrt.excep;
FpuException excep = defaultValue;
excep.invalid_op = exBits[0] == 1;
return tuple2(val, excep);
endmethod
endinterface
endmodule
// import simulation
interface FpFmaSim;
method Bit#(64) fma(Bit#(64) a, Bit#(64) b, Bit#(64) c);
endinterface
import "BVI" fp_fma_sim =
module mkFpFmaSim(FpFmaSim);
default_clock no_clock;
default_reset no_reset;
method RES fma(A, B, C);
endmodule
interface FpDivSim;
method Bit#(64) div(Bit#(64) a, Bit#(64) b);
endinterface
import "BVI" fp_div_sim =
module mkFpDivSim(FpDivSim);
default_clock no_clock;
default_reset no_reset;
method RES div(A, B);
endmodule
interface FpSqrtSim;
method Bit#(64) sqrt(Bit#(64) a);
endinterface
import "BVI" fp_sqrt_sim =
module mkFpSqrtSim(FpSqrtSim);
default_clock no_clock;
default_reset no_reset;
method RES sqrt(A);
endmodule
// adapt the simulation fp ops to Bluespec FPU interface
// Xilinx FPU has 2-element buffers inside
(* synthesize *)
module mkXilinxFpFmaSim(Server#(
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
FpFmaSim fpFma <- mkFpFmaSim;
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
// xilinx raw ip is a * b + c
// what we need to provide is in1 + in2 * in3
// so in1 -> c, in2 -> a, in3 -> b
interface Put request;
method Action put(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) req);
let {maybe_in1, in2, in3, rm} = req;
Double in1 = fromMaybe(zero(False), maybe_in1);
Double val = unpack(fpFma.fma(pack(in2), pack(in3), pack(in1)));
respQ.enq(tuple2(val, defaultValue));
endmethod
endinterface
interface response = toGet(respQ);
endmodule
(* synthesize *)
module mkXilinxFpDivSim(Server#(
Tuple3#(Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
FpDivSim fpDiv <- mkFpDivSim;
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
interface Put request;
method Action put(Tuple3#(Double, Double, FpuRoundMode) req);
let {a, b, rm} = req;
Double val = unpack(fpDiv.div(pack(a), pack(b)));
respQ.enq(tuple2(val, defaultValue));
endmethod
endinterface
interface response = toGet(respQ);
endmodule
(* synthesize *)
module mkXilinxFpSqrtSim(Server#(
Tuple2#(Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
FpSqrtSim fpSqrt <- mkFpSqrtSim;
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
interface Put request;
method Action put(Tuple2#(Double, FpuRoundMode) req);
let {a, rm} = req;
Double val = unpack(fpSqrt.sqrt(pack(a)));
respQ.enq(tuple2(val, defaultValue));
endmethod
endinterface
interface response = toGet(respQ);
endmodule
// wrap simulation and xilinx ip together
(* synthesize *)
module mkXilinxFpFma(Server#(
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
`ifdef BSIM
let m <- mkXilinxFpFmaSim;
`else
let m <- mkXilinxFpFmaIP;
`endif
return m;
endmodule
(* synthesize *)
module mkXilinxFpDiv(Server#(
Tuple3#(Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
`ifdef BSIM
let m <- mkXilinxFpDivSim;
`else
let m <- mkXilinxFpDivIP;
`endif
return m;
endmodule
(* synthesize *)
module mkXilinxFpSqrt(Server#(
Tuple2#(Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
`ifdef BSIM
let m <- mkXilinxFpSqrtSim;
`else
let m <- mkXilinxFpSqrtIP;
`endif
return m;
endmodule

View File

@@ -0,0 +1,203 @@
import FIFOF::*;
import FIFO::*;
import WaitAutoReset::*;
export XilinxIntDiv(..);
export mkXilinxIntDiv;
// import Xilinx IP core for unsigned division
// axi tuser: user tag + info needed to handle divide by 0 + sign info
typedef struct {
Bool divByZero;
Bit#(64) divByZeroRem; // remainder in case of divide by zero
Bool signedDiv; // signed division (so dividend/divisor has been abs)
Bit#(1) quotientSign; // sign bit of quotient (in case of signedDiv)
Bit#(1) remainderSign; // sign bit of remainder (in case of signedDiv)
Bit#(8) tag;
} IntDivUser deriving(Bits, Eq, FShow);
interface IntDivUnsignedImport;
method Action enqDividend(Bit#(64) dividend, IntDivUser user);
method Action enqDivisor(Bit#(64) divisor);
method Action deqResp;
method Bool respValid;
method Bit#(128) quotient_remainder;
method IntDivUser respUser;
endinterface
import "BVI" int_div_unsigned =
module mkIntDivUnsignedImport(IntDivUnsignedImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqDividend(
s_axis_dividend_tdata, s_axis_dividend_tuser
) enable(s_axis_dividend_tvalid) ready(s_axis_dividend_tready);
method enqDivisor(
s_axis_divisor_tdata
) enable(s_axis_divisor_tvalid) ready(s_axis_divisor_tready);
method deqResp() enable(m_axis_dout_tready) ready(m_axis_dout_tvalid);
method m_axis_dout_tvalid respValid;
method m_axis_dout_tdata quotient_remainder ready(m_axis_dout_tvalid);
method m_axis_dout_tuser respUser ready(m_axis_dout_tvalid);
schedule (enqDividend) C (enqDividend);
schedule (enqDivisor) C (enqDivisor);
schedule (deqResp) C (deqResp);
schedule (enqDividend) CF (enqDivisor, deqResp);
schedule (enqDivisor) CF (deqResp);
schedule (
respValid,
quotient_remainder,
respUser
) CF (
respValid,
quotient_remainder,
respUser,
enqDividend,
enqDivisor,
deqResp
);
endmodule
// simulation
module mkIntDivUnsignedSim(IntDivUnsignedImport);
FIFO#(Tuple2#(Bit#(64), IntDivUser)) dividendQ <- mkFIFO;
FIFO#(Bit#(64)) divisorQ <- mkFIFO;
FIFOF#(Tuple2#(Bit#(128), IntDivUser)) respQ <- mkSizedFIFOF(2);
rule compute;
dividendQ.deq;
divisorQ.deq;
let {dividend, user} = dividendQ.first;
let divisor = divisorQ.first;
UInt#(64) a = unpack(dividend);
UInt#(64) b = unpack(divisor);
Bit#(64) q = pack(a / b);
Bit#(64) r = pack(a % b);
respQ.enq(tuple2({q, r}, user));
endrule
method Action enqDividend(Bit#(64) dividend, IntDivUser user);
dividendQ.enq(tuple2(dividend, user));
endmethod
method Action enqDivisor(Bit#(64) divisor);
divisorQ.enq(divisor);
endmethod
method Action deqResp;
respQ.deq;
endmethod
method respValid = respQ.notEmpty;
method quotient_remainder = tpl_1(respQ.first);
method respUser = tpl_2(respQ.first);
endmodule
// Wrapper for user (add reset guard, check overflow/divided by 0). We cannot
// unify two dividers to one, because divider latency may not be a constant.
interface XilinxIntDiv#(type tagT);
method Action req(Bit#(64) dividend, Bit#(64) divisor, Bool signedDiv, tagT tag);
// response
method Action deqResp;
method Bool respValid;
method Bit#(64) quotient;
method Bit#(64) remainder;
method tagT respTag;
endinterface
module mkXilinxIntDiv(XilinxIntDiv#(tagT)) provisos (
Bits#(tagT, tagSz), Add#(tagSz, a__, 8)
);
`ifdef BSIM
IntDivUnsignedImport divIfc <- mkIntDivUnsignedSim;
`else
IntDivUnsignedImport divIfc <- mkIntDivUnsignedImport;
`endif
WaitAutoReset#(4) init <- mkWaitAutoReset;
method Action req(
Bit#(64) dividend, Bit#(64) divisor, Bool signedDiv, tagT tag
) if(init.isReady);
// compute the input ops to div unsigned IP
Bit#(1) dividend_sign = truncateLSB(dividend);
Bit#(1) divisor_sign = truncateLSB(divisor);
Bit#(64) a = dividend;
Bit#(64) b = divisor;
if(signedDiv) begin
if(dividend_sign == 1) begin
a = 0 - dividend;
end
if(divisor_sign == 1) begin
b = 0 - divisor;
end
end
// get the user struct (sign/divide by 0)
let user = IntDivUser {
divByZero: divisor == 0,
divByZeroRem: dividend,
signedDiv: signedDiv,
// quotient negative when dividend and divisor have different signs
quotientSign: dividend_sign ^ divisor_sign,
// remainder sign follows that of dividend
remainderSign: dividend_sign,
tag: zeroExtend(pack(tag))
};
divIfc.enqDividend(a, user);
divIfc.enqDivisor(b);
endmethod
// we also put reset guard on deq port to prevent random signals before
// reset from dequing or corrupting axi states
method Action deqResp if(init.isReady);
divIfc.deqResp;
endmethod
method respValid = divIfc.respValid && init.isReady;
method Bit#(64) quotient if(init.isReady);
let user = divIfc.respUser;
Bit#(64) q;
if(user.divByZero) begin
q = maxBound;
end
else begin
q = truncateLSB(divIfc.quotient_remainder);
if(user.signedDiv && user.quotientSign == 1) begin
q = 0 - q;
end
// signed overflow is automatically handled
end
return q;
endmethod
method Bit#(64) remainder if(init.isReady);
let user = divIfc.respUser;
Bit#(64) r;
if(user.divByZero) begin
r = user.divByZeroRem;
end
else begin
r = truncate(divIfc.quotient_remainder);
if(user.signedDiv && user.remainderSign == 1) begin
r = 0 - r;
end
// signed overflow is automatically handled
end
return r;
endmethod
method tagT respTag if(init.isReady);
return unpack(truncate(divIfc.respUser.tag));
endmethod
endmodule

View File

@@ -0,0 +1,205 @@
import Vector::*;
import FIFOF::*;
import Assert::*;
export XilinxIntMulSign(..);
export XilinxIntMul(..);
export mkXilinxIntMul;
// Xilinx int multiplier IP is a rigorous pipeline with a fixed latency. There
// is no back pressure in the raw IP. We will wrap it with flow control. To do
// this, we need to know the pipeline latency from macro
// XILINX_INT_MUL_LATENCY.
typedef `XILINX_INT_MUL_LATENCY IntMulLatency;
interface IntMulImport;
method Action req(Bit#(64) a, Bit#(64) b);
method Bit#(128) product;
endinterface
import "BVI" int_mul_signed =
module mkIntMulSignedImport(IntMulImport);
default_clock clk(CLK, (*unused*) unused_gate);
default_reset no_reset;
method req(A, B) enable((*inhigh*) EN);
method P product;
schedule (req) C (req);
schedule (product) CF (req, product);
endmodule
import "BVI" int_mul_unsigned =
module mkIntMulUnsignedImport(IntMulImport);
default_clock clk(CLK, (*unused*) unused_gate);
default_reset no_reset;
method req(A, B) enable((*inhigh*) EN);
method P product;
schedule (req) C (req);
schedule (product) CF (req, product);
endmodule
import "BVI" int_mul_signed_unsigned =
module mkIntMulSignedUnsignedImport(IntMulImport);
default_clock clk(CLK, (*unused*) unused_gate);
default_reset no_reset;
method req(A, B) enable((*inhigh*) EN);
method P product;
schedule (req) C (req);
schedule (product) CF (req, product);
endmodule
// mul req type
typedef enum {
Signed,
Unsigned,
SignedUnsigned
} XilinxIntMulSign deriving(Bits, Eq, FShow);
// simulation
module mkIntMulSim#(XilinxIntMulSign sign)(IntMulImport);
RWire#(Bit#(128)) newReq <- mkRWire;
Vector#(
IntMulLatency, Reg#(Maybe#(Bit#(128)))
) pipe <- replicateM(mkReg(Invalid));
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
for(Integer i = 1; i < valueof(IntMulLatency); i = i+1) begin
pipe[i] <= pipe[i - 1];
end
pipe[0] <= newReq.wget;
endrule
method Action req(Bit#(64) a, Bit#(64) b);
Int#(128) op1 = (case(sign)
Signed, SignedUnsigned: (unpack(signExtend(a)));
default: (unpack(zeroExtend(a)));
endcase);
Int#(128) op2 = (case(sign)
Signed: (unpack(signExtend(b)));
default: (unpack(zeroExtend(b)));
endcase);
Int#(128) prod = op1 * op2;
newReq.wset(pack(prod));
endmethod
method Bit#(128) product = fromMaybe(?, pipe[valueof(IntMulLatency) - 1]);
endmodule
// wrap up all mul IPs to have back pressure
interface XilinxIntMul#(type tagT);
method Action req(Bit#(64) a, Bit#(64) b, XilinxIntMulSign sign, tagT tag);
method Action deqResp;
method Bool respValid;
method Bit#(128) product;
method tagT respTag;
endinterface
module mkXilinxIntMul(XilinxIntMul#(tagT)) provisos(
Bits#(tagT, tagSz),
// credit based flow control types
NumAlias#(TAdd#(IntMulLatency, 1), maxCredit),
Alias#(Bit#(TLog#(TAdd#(maxCredit, 1))), creditT)
);
// different multilpliers: WaitAutoReset is not needed, since mul is a
// pipeline with fixed latency
`ifdef BSIM
IntMulImport mulSigned <- mkIntMulSim(Signed);
IntMulImport mulUnsigned <- mkIntMulSim(Unsigned);
IntMulImport mulSignedUnsigned <- mkIntMulSim(SignedUnsigned);
`else
IntMulImport mulSigned <- mkIntMulSignedImport;
IntMulImport mulUnsigned <- mkIntMulUnsignedImport;
IntMulImport mulSignedUnsigned <- mkIntMulSignedUnsignedImport;
`endif
// resp FIFO (unguarded) & flow ctrl credit
FIFOF#(
Tuple2#(Bit#(128), tagT)
) respQ <- mkUGSizedFIFOF(valueof(maxCredit));
Reg#(creditT) credit <- mkReg(fromInteger(valueof(maxCredit)));
// shift regs for sign + tag
Vector#(
IntMulLatency,
Reg#(Maybe#(Tuple2#(XilinxIntMulSign, tagT)))
) pipe <- replicateM(mkReg(Invalid));
// wire to catch input req
RWire#(Tuple2#(XilinxIntMulSign, tagT)) newReq <- mkRWire;
// wire to catch deq
PulseWire deqEn <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
creditT nextCredit = credit;
// incr credit if resp FIFO is deq
if(deqEn) begin
if(nextCredit >= fromInteger(valueof(maxCredit))) begin
$fdisplay(stderr, "\n%m: ASSERT FAIL!!");
dynamicAssert(False, "credit overflow");
end
nextCredit = nextCredit + 1;
end
// enq resp FIFO if something is outputed from mul
if (
pipe[valueof(IntMulLatency) - 1] matches tagged Valid {.sign, .tag}
) begin
Bit#(128) prod = (case(sign)
Signed: (mulSigned.product);
Unsigned: (mulUnsigned.product);
SignedUnsigned: (mulSignedUnsigned.product);
default: (?);
endcase);
respQ.enq(tuple2(prod, tag));
end
// shift pipe regs
for(Integer i = 1; i < valueof(IntMulLatency); i = i+1) begin
pipe[i] <= pipe[i - 1];
end
pipe[0] <= newReq.wget;
// decr credit if new req is taken
if(isValid(newReq.wget)) begin
if(nextCredit == 0) begin
$fdisplay(stderr, "\n%m: ASSERT FAIL!!");
dynamicAssert(False, "credit underflow");
end
nextCredit = nextCredit - 1;
end
// update credit
credit <= nextCredit;
endrule
method Action req(Bit#(64) a, Bit#(64) b,
XilinxIntMulSign sign, tagT tag) if(credit > 0);
case(sign)
Signed: mulSigned.req(a, b);
Unsigned: mulUnsigned.req(a, b);
SignedUnsigned: mulSignedUnsigned.req(a, b);
endcase
newReq.wset(tuple2(sign, tag)); // notify new req
endmethod
method Action deqResp if(respQ.notEmpty);
respQ.deq;
deqEn.send; // notify deq resp
endmethod
method respValid = respQ.notEmpty;
method Bit#(128) product if(respQ.notEmpty);
return tpl_1(respQ.first);
endmethod
method tagT respTag if(respQ.notEmpty);
return tpl_2(respQ.first);
endmethod
endmodule

View File

@@ -0,0 +1,181 @@
// 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.
import Vector::*;
import Clocks::*;
import CBus::*;
export mkXilinxSyncFifo;
export mkXilinxSyncBramFifo;
// import sync fifos from Xilinx FIFO generator
interface SyncFifoImport#(numeric type w);
method Bool full;
method Action enq(Bit#(w) x);
method Bool empty;
method Bit#(w) first;
method Action deq;
endinterface
import "BVI" sync_fifo_w32_d16 =
module mkSyncFifoImport_w32_d16#(Clock srcClk, Clock dstClk)(SyncFifoImport#(32));
default_clock no_clock;
default_reset no_reset;
input_clock (wr_clk) = srcClk;
input_clock (rd_clk) = dstClk;
method full full() clocked_by(srcClk) reset_by(no_reset);
method enq(din) enable(wr_en) clocked_by(srcClk) reset_by(no_reset);
method empty empty() clocked_by(dstClk) reset_by(no_reset);
method dout first() clocked_by(dstClk) reset_by(no_reset);
method deq() enable(rd_en) clocked_by(dstClk) reset_by(no_reset);
schedule (full, enq) CF (empty, first, deq);
schedule (full) CF (full, enq);
schedule (enq) C (enq);
schedule (empty, first) CF (empty, first, deq);
schedule (deq) C (deq);
endmodule
import "BVI" sync_bram_fifo_w36_d512 =
module mkSyncBramFifoImport_w36_d512#(Clock srcClk, Clock dstClk)(SyncFifoImport#(36));
default_clock no_clock;
default_reset no_reset;
input_clock (wr_clk) = srcClk;
input_clock (rd_clk) = dstClk;
method full full() clocked_by(srcClk) reset_by(no_reset);
method enq(din) enable(wr_en) clocked_by(srcClk) reset_by(no_reset);
method empty empty() clocked_by(dstClk) reset_by(no_reset);
method dout first() clocked_by(dstClk) reset_by(no_reset);
method deq() enable(rd_en) clocked_by(dstClk) reset_by(no_reset);
schedule (full, enq) CF (empty, first, deq);
schedule (full) CF (full, enq);
schedule (enq) C (enq);
schedule (empty, first) CF (empty, first, deq);
schedule (deq) C (deq);
endmodule
// wrap up imported BSV or simulation module
(* synthesize, no_default_clock, no_default_reset *)
module mkSyncFifo_w32_d16#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(Bit#(w))) provisos (NumAlias#(w, 32));
`ifdef BSIM
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(16, srcClk, srcRst, dstClk);
return q;
`else
SyncFifoImport#(w) q <- mkSyncFifoImport_w32_d16(srcClk, dstClk);
method notFull = !q.full;
method Action enq(Bit#(w) x) if(!q.full);
q.enq(x);
endmethod
method notEmpty = !q.empty;
method Bit#(w) first if(!q.empty);
return q.first;
endmethod
method Action deq if(!q.empty);
q.deq;
endmethod
`endif
endmodule
(* synthesize, no_default_clock, no_default_reset *)
module mkSyncBramFifo_w36_d512#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(Bit#(w))) provisos (NumAlias#(w, 36));
`ifdef BSIM
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(512, srcClk, srcRst, dstClk);
return q;
`else
SyncFifoImport#(w) q <- mkSyncBramFifoImport_w36_d512(srcClk, dstClk);
method notFull = !q.full;
method Action enq(Bit#(w) x) if(!q.full);
q.enq(x);
endmethod
method notEmpty = !q.empty;
method Bit#(w) first if(!q.empty);
return q.first;
endmethod
method Action deq if(!q.empty);
q.deq;
endmethod
`endif
endmodule
// generate parameterized-width fifo from fixed-with fifo
module mkGenXilinxSyncFifo#(
function module#(SyncFIFOIfc#(Bit#(fifoW))) mkfifo(Clock srcClk, Reset srcRst, Clock dstClk),
Clock srcClk, Reset srcRst, Clock dstClk
)(SyncFIFOIfc#(t)) provisos(
Bits#(t, dataW),
Add#(1, a__, dataW),
NumAlias#(fifoNum, TDiv#(dataW, fifoW))
);
Vector#(fifoNum, SyncFIFOIfc#(Bit#(fifoW))) fifos <- replicateM(mkfifo(srcClk, srcRst, dstClk));
function Bool getNotFull(SyncFIFOIfc#(Bit#(fifoW)) ifc) = ifc.notFull;
Bool isNotFull = all(getNotFull, fifos);
function Bool getNotEmpty(SyncFIFOIfc#(Bit#(fifoW)) ifc) = ifc.notEmpty;
Bool isNotEmpty = all(getNotEmpty, fifos);
method notFull = isNotFull;
method Action enq(t x);
Vector#(fifoNum, Bit#(fifoW)) data = unpack(zeroExtendNP(pack(x)));
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
fifos[i].enq(data[i]);
end
endmethod
method notEmpty = isNotEmpty;
method t first;
Vector#(fifoNum, Bit#(fifoW)) data = ?;
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
data[i] = fifos[i].first;
end
return unpack(truncateNP(pack(data)));
endmethod
method Action deq;
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
fifos[i].deq;
end
endmethod
endmodule
module mkXilinxSyncFifo#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(t)) provisos(
Bits#(t, dataW), Add#(1, a__, dataW)
);
SyncFIFOIfc#(t) m <- mkGenXilinxSyncFifo(mkSyncFifo_w32_d16, srcClk, srcRst, dstClk);
return m;
endmodule
module mkXilinxSyncBramFifo#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(t)) provisos(
Bits#(t, dataW), Add#(1, a__, dataW)
);
SyncFIFOIfc#(t) m <- mkGenXilinxSyncFifo(mkSyncBramFifo_w36_d512, srcClk, srcRst, dstClk);
return m;
endmodule

View File

@@ -0,0 +1,8 @@
module fp_div_sim(
input [63:0] A,
input [63:0] B,
output [63:0] RES
);
assign RES = $realtobits($bitstoreal(A) / $bitstoreal(B));
endmodule

View File

@@ -0,0 +1,9 @@
module fp_fma_sim(
input [63:0] A,
input [63:0] B,
input [63:0] C,
output [63:0] RES
);
assign RES = $realtobits($bitstoreal(A) * $bitstoreal(B) + $bitstoreal(C));
endmodule

View File

@@ -0,0 +1,7 @@
module fp_sqrt_sim(
input [63:0] A,
output [63:0] RES
);
assign RES = $realtobits($sqrt($bitstoreal(A)));
endmodule

View File

@@ -0,0 +1,69 @@
// 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.
// This module outputs a 1-bit signal
// The signal is initially 0 after programming the FPGA
// XXX: everything should be inited to 0 after programming
// When a reset arrives, the module starts counting
// In N cycles after the reset, the signal becomes 1
// This signal can be used as a guard for sync fifo operations
`ifdef BSV_POSITIVE_RESET
`define BSV_RESET_VALUE 1'b1
`define BSV_RESET_EDGE posedge
`else
`define BSV_RESET_VALUE 1'b0
`define BSV_RESET_EDGE negedge
`endif
module reset_guard(
input CLK,
input RST,
output IS_READY
);
reg ready = 0;
reg rst_done = 0;
always@(posedge CLK) begin
if(RST == `BSV_RESET_VALUE) begin
ready <= 0;
rst_done <= 1;
// synopsys translate_off
if(!rst_done) begin
$display("[reset_guard] %t %m reset happen", $time);
end
// synopsys translate_on
end
else if(rst_done) begin
ready <= 1;
// synopsys translate_off
if(!ready) begin
$display("[reset_guard] %t %m guard ready", $time);
end
// synopsys translate_on
end
end
assign IS_READY = ready;
endmodule

View File

@@ -0,0 +1,382 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import FIFO::*;
import GetPut::*;
import BuildVector::*;
import Cntrs::*;
import Types::*;
import ProcTypes::*;
import SynthParam::*;
import Exec::*;
import Performance::*;
import BrPred::*;
import DirPredictor::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReorderBuffer::*;
import SpecFifo::*;
import HasSpecBits::*;
import Bypass::*;
// ALU pipeline has 4 stages
// dispatch -> reg read -> exe -> finish (write reg)
// bypass is sent out from the end of exe stage
// and reg read stage will recv bypass
typedef struct {
// inst info
DecodedInst dInst;
PhyRegs regs;
InstTag tag;
DirPredTrainInfo dpTrain;
// specualtion
Maybe#(SpecTag) spec_tag;
} AluDispatchToRegRead deriving(Bits, Eq, FShow);
typedef struct {
// inst info
DecodedInst dInst;
Maybe#(PhyDst) dst;
InstTag tag;
DirPredTrainInfo dpTrain;
// src reg vals & pc & ppc
Data rVal1;
Data rVal2;
Addr pc;
Addr ppc;
// specualtion
Maybe#(SpecTag) spec_tag;
} AluRegReadToExe deriving(Bits, Eq, FShow);
typedef struct {
// inst info
IType iType;
Maybe#(PhyDst) dst;
InstTag tag;
DirPredTrainInfo dpTrain;
// result
Data data; // alu compute result
Maybe#(Data) csrData; // data to write CSR file
ControlFlow controlFlow;
// speculation
Maybe#(SpecTag) spec_tag;
} AluExeToFinish deriving(Bits, Eq, FShow);
// XXX currently ALU/Br should not have any exception, so we don't have cause feild above
// TODO FIXME In future, if branch target is unaligned to 4 bytes, we may have exception
// and probably JR/JAL should NOT write dst reg when exception happens
// However, currently JR/JAL renaming is included in the previous checkpoint
// so we need to explicitly check exception and don't write reg
// synthesized pipeline fifos
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluDispatchToRegRead) AluDispToRegFifo;
(* synthesize *)
module mkAluDispToRegFifo(AluDispToRegFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluRegReadToExe) AluRegToExeFifo;
(* synthesize *)
module mkAluRegToExeFifo(AluRegToExeFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_SB_deq_wrong_C_enq#(1, AluExeToFinish) AluExeToFinFifo;
(* synthesize *)
module mkAluExeToFinFifo(AluExeToFinFifo);
let m <- mkSpecFifo_SB_deq_enq_SB_deq_wrong_C_enq(False);
return m;
endmodule
typedef struct {
Addr pc;
Addr nextPc;
IType iType;
Bool taken;
DirPredTrainInfo dpTrain;
Bool mispred;
} FetchTrainBP deriving(Bits, Eq, FShow);
interface AluExeInput;
// conservative scoreboard check in reg read stage
method RegsReady sbCons_lazyLookup(PhyRegs r);
// Phys reg file
method Data rf_rd1(PhyRIndx rindx);
method Data rf_rd2(PhyRIndx rindx);
// CSR file
method Data csrf_rd(CSR csr);
// ROB
method Addr rob_getPC(InstTag t);
method Addr rob_getPredPC(InstTag t);
method Action rob_setExecuted(InstTag t, Maybe#(Data) csrData, ControlFlow cf);
// Fetch stage
method Action fetch_train_predictors(FetchTrainBP train);
// global broadcast methods
// set aggressive sb & wake up inst in RS
method Action setRegReadyAggr(PhyRIndx dst);
// send bypass from exe and finish stage
interface Vector#(2, SendBypass) sendBypass;
// write reg file & set conservative sb
method Action writeRegFile(PhyRIndx dst, Data data);
// redirect
method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
// spec update
method Action correctSpec(SpecTag t);
// performance
method Bool doStats;
endinterface
interface AluExePipeline;
// recv bypass from exe and finish stages of each ALU pipeline
interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
interface ReservationStationAlu rsAluIfc;
interface SpeculationUpdate specUpdate;
method Data getPerf(ExeStagePerfType t);
endinterface
module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
Bool verbose = True;
// alu reservation station
ReservationStationAlu rsAlu <- mkReservationStationAlu;
// pipeline fifos
let dispToRegQ <- mkAluDispToRegFifo;
let regToExeQ <- mkAluRegToExeFifo;
let exeToFinQ <- mkAluExeToFinFifo;
// wire to recv bypass
Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
// index to send bypass, ordering doesn't matter
Integer exeSendBypassPort = 0;
Integer finishSendBypassPort = 1;
`ifdef PERF_COUNT
// performance counters
Count#(Data) exeRedirectBrCnt <- mkCount(0);
Count#(Data) exeRedirectJrCnt <- mkCount(0);
Count#(Data) exeRedirectOtherCnt <- mkCount(0);
`endif
rule doDispatchAlu;
rsAlu.doDispatch;
let x = rsAlu.dispatchData;
if(verbose) $display("[doDispatchAlu] ", fshow(x));
// set reg ready aggressively
if(x.regs.dst matches tagged Valid .dst) begin
inIfc.setRegReadyAggr(dst.indx);
end
// go to next stage
dispToRegQ.enq(ToSpecFifo {
data: AluDispatchToRegRead {
dInst: x.data.dInst,
regs: x.regs,
tag: x.tag,
dpTrain: x.data.dpTrain,
spec_tag: x.spec_tag
},
spec_bits: x.spec_bits
});
endrule
rule doRegReadAlu;
dispToRegQ.deq;
let dispToReg = dispToRegQ.first;
let x = dispToReg.data;
if(verbose) $display("[doRegReadAlu] ", fshow(dispToReg));
// check conservative scoreboard
let regsReady = inIfc.sbCons_lazyLookup(x.regs);
// get rVal1 (check bypass)
Data rVal1 = ?;
if(x.dInst.csr matches tagged Valid .csr) begin
rVal1 = inIfc.csrf_rd(csr);
end
else if(x.regs.src1 matches tagged Valid .src1) begin
rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
end
// get rVal2 (check bypass)
Data rVal2 = ?;
if(x.regs.src2 matches tagged Valid .src2) begin
rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
end
// get PC and PPC
let pc = inIfc.rob_getPC(x.tag);
let ppc = inIfc.rob_getPredPC(x.tag);
// go to next stage
regToExeQ.enq(ToSpecFifo {
data: AluRegReadToExe {
dInst: x.dInst,
dst: x.regs.dst,
tag: x.tag,
dpTrain: x.dpTrain,
rVal1: rVal1,
rVal2: rVal2,
pc: pc,
ppc: ppc,
spec_tag: x.spec_tag
},
spec_bits: dispToReg.spec_bits
});
endrule
rule doExeAlu;
regToExeQ.deq;
let regToExe = regToExeQ.first;
let x = regToExe.data;
if(verbose) $display("[doExeAlu] ", fshow(regToExe));
// execution
ExecResult exec_result = basicExec(x.dInst, x.rVal1, x.rVal2, x.pc, x.ppc);
// when inst needs to store csrData in ROB, it must have iType = Csr, cannot mispredict
if(isValid(x.dInst.csr)) begin
doAssert(x.dInst.iType == Csr, "Only Csr inst needs to update csrData in ROB");
doAssert(!exec_result.controlFlow.mispredict, "Csr inst cannot mispredict");
doAssert(exec_result.controlFlow.nextPc == x.ppc && x.ppc == x.pc + 4, "Csr inst ppc = pc+4");
end
// send bypass
if(x.dst matches tagged Valid .dst) begin
inIfc.sendBypass[exeSendBypassPort].send(dst.indx, exec_result.data);
end
// go to next stage
exeToFinQ.enq(ToSpecFifo {
data: AluExeToFinish {
iType: x.dInst.iType,
dst: x.dst,
tag: x.tag,
dpTrain: x.dpTrain,
data: exec_result.data,
csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : Invalid,
controlFlow: exec_result.controlFlow,
spec_tag: x.spec_tag
},
spec_bits: regToExe.spec_bits
});
endrule
rule doFinishAlu;
exeToFinQ.deq;
let exeToFin = exeToFinQ.first;
let x = exeToFin.data;
if(verbose) $display("[doFinishAlu] ", fshow(exeToFin));
// send bypass & write reg file
if(x.dst matches tagged Valid .dst) begin
inIfc.sendBypass[finishSendBypassPort].send(dst.indx, x.data);
inIfc.writeRegFile(dst.indx, x.data);
end
// update the instruction in the reorder buffer.
inIfc.rob_setExecuted(
x.tag,
x.csrData,
x.controlFlow
);
// handle spec tags for branch predictions
(* split *)
if (x.controlFlow.mispredict) (* nosplit *) begin
// wrong branch predictin, we must have spec tag
doAssert(isValid(x.spec_tag), "mispredicted branch must have spec tag");
inIfc.redirect(x.controlFlow.nextPc, validValue(x.spec_tag), x.tag);
// must be a branch, train branch predictor
doAssert(x.iType == Jr || x.iType == Br, "only jr and br can mispredict");
inIfc.fetch_train_predictors(FetchTrainBP {
pc: x.controlFlow.pc,
nextPc: x.controlFlow.nextPc,
iType: x.iType,
taken: x.controlFlow.taken,
dpTrain: x.dpTrain,
mispred: True
});
`ifdef PERF_COUNT
// performance counter
if(inIfc.doStats) begin
case(x.iType)
Br: exeRedirectBrCnt.incr(1);
Jr: exeRedirectJrCnt.incr(1);
default: exeRedirectOtherCnt.incr(1);
endcase
end
`endif
end
else (* nosplit *) begin
// release spec tag
if (x.spec_tag matches tagged Valid .valid_spec_tag) begin
inIfc.correctSpec(valid_spec_tag);
end
// train branch predictor if needed
// since we can only do 1 training in a cycle, split the rule
// XXX not training JAL, reduce chance of conflicts
if(x.iType == Jr || x.iType == Br) begin
inIfc.fetch_train_predictors(FetchTrainBP {
pc: x.controlFlow.pc,
nextPc: x.controlFlow.nextPc,
iType: x.iType,
taken: x.controlFlow.taken,
dpTrain: x.dpTrain,
mispred: False
});
end
end
endrule
interface recvBypass = map(getRecvBypassIfc, bypassWire);
interface rsAluIfc = rsAlu;
interface specUpdate = joinSpeculationUpdate(vec(
rsAlu.specUpdate,
dispToRegQ.specUpdate,
regToExeQ.specUpdate,
exeToFinQ.specUpdate
));
method Data getPerf(ExeStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
ExeRedirectBr: exeRedirectBrCnt;
ExeRedirectJr: exeRedirectJrCnt;
ExeRedirectOther: exeRedirectOtherCnt;
`endif
default: 0;
endcase);
endmethod
endmodule

View File

@@ -0,0 +1,756 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import GetPut::*;
import Cntrs::*;
import ConfigReg::*;
import FIFO::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import Performance::*;
import ReorderBuffer::*;
import ReorderBufferSynth::*;
import RenamingTable::*;
import CsrFile::*;
import StoreBuffer::*;
import VerificationPacket::*;
import RenameDebugIF::*;
typedef struct {
// info about the inst blocking at ROB head
Addr pc;
IType iType;
Maybe#(Trap) trap;
RobInstState state;
Bool claimedPhyReg;
Bool ldKilled;
Bool memAccessAtCommit;
Bool lsqAtCommitNotified;
Bool nonMMIOStDone;
Bool epochIncremented;
SpecBits specBits;
// info about LSQ/TLB
Bool stbEmpty;
Bool stqEmpty;
Bool tlbNoPendingReq;
// CSR info: previlige mode
Bit#(2) prv;
// inst count
Data instCount;
} CommitStuck deriving(Bits, Eq, FShow);
interface CommitInput;
// func units
interface ReorderBufferSynth robIfc;
interface RegRenamingTable rtIfc;
interface CsrFile csrfIfc;
// no stores
method Bool stbEmpty;
method Bool stqEmpty;
// notify LSQ that inst has reached commit
interface Vector#(SupSize, Put#(LdStQTag)) lsqSetAtCommit;
// TLB has stopped processing now
method Bool tlbNoPendingReq;
// set flags
method Action setFlushTlbs;
method Action setUpdateVMInfo;
method Action setFlushReservation;
method Action setFlushBrPred; // security
method Action setFlushCaches; // security
method Action setReconcileI; // recocile I$
method Action setReconcileD; // recocile D$
// redirect
method Action killAll;
method Action redirectPc(Addr trap_pc);
method Action setFetchWaitRedirect;
method Action incrementEpoch;
// record if we commit a CSR inst or interrupt
method Action commitCsrInstOrInterrupt;
// performance
method Bool doStats;
// deadlock check
method Bool checkDeadlock;
endinterface
typedef struct {
RenameError err;
Addr pc;
IType iType;
Maybe#(Trap) trap;
SpecBits specBits;
} RenameErrInfo deriving(Bits, Eq, FShow);
interface CommitStage;
// performance
method Data getPerf(ComStagePerfType t);
// deadlock check
interface Get#(CommitStuck) commitInstStuck;
interface Get#(CommitStuck) commitUserInstStuck;
// rename debug
method Action startRenameDebug;
interface Get#(RenameErrInfo) renameErr;
endinterface
// we apply actions the end of commit rule
// use struct to record actions to be done
typedef struct {
Addr pc;
Addr addr;
Trap trap;
} CommitTrap deriving(Bits, Eq, FShow);
module mkCommitStage#(CommitInput inIfc)(CommitStage);
Bool verbose = True;
// func units
ReorderBufferSynth rob = inIfc.robIfc;
RegRenamingTable regRenamingTable = inIfc.rtIfc;
CsrFile csrf = inIfc.csrfIfc;
// FIXME FIXME FIXME wires to set atCommit in LSQ: avoid scheduling cycle.
// Using wire should be fine, because LSQ does not need to see atCommit
// signal immediately. XXX The concern is about killAll which checks
// atCommit in LSQ, but we never call killAll and setAtCommit in the same
// cycle.
Vector#(SupSize, RWire#(LdStQTag)) setLSQAtCommit <- replicateM(mkRWire);
for(Integer i = 0; i< valueof(SupSize); i = i+1) begin
(* fire_when_enabled, no_implicit_conditions *)
rule doSetLSQAtCommit(setLSQAtCommit[i].wget matches tagged Valid .tag);
inIfc.lsqSetAtCommit[i].put(tag);
endrule
end
// commit stage performance counters
`ifdef PERF_COUNT
// inst
Count#(Data) instCnt <- mkCount(0);
Count#(Data) userInstCnt <- mkCount(0);
Count#(Data) supComUserCnt <- mkCount(0);
// branch/jump inst
Count#(Data) comBrCnt <- mkCount(0);
Count#(Data) comJmpCnt <- mkCount(0);
Count#(Data) comJrCnt <- mkCount(0);
// mem inst
Count#(Data) comLdCnt <- mkCount(0);
Count#(Data) comStCnt <- mkCount(0);
Count#(Data) comLrCnt <- mkCount(0);
Count#(Data) comScCnt <- mkCount(0);
Count#(Data) comAmoCnt <- mkCount(0);
// load mispeculation
Count#(Data) comLdKillByLdCnt <- mkCount(0);
Count#(Data) comLdKillByStCnt <- mkCount(0);
Count#(Data) comLdKillByCacheCnt <- mkCount(0);
// exception/sys inst related
Count#(Data) comSysCnt <- mkCount(0);
Count#(Data) excepCnt <- mkCount(0);
Count#(Data) interruptCnt <- mkCount(0);
// flush tlb
Count#(Data) flushTlbCnt <- mkCount(0);
// flush security
Count#(Data) flushSecurityCnt <- mkCount(0);
Count#(Data) flushBPCnt <- mkCount(0);
Count#(Data) flushCacheCnt <- mkCount(0);
`endif
// deadlock check
`ifdef CHECK_DEADLOCK
// timer to check deadlock
Reg#(DeadlockTimer) commitInstTimer <- mkReg(0);
Reg#(DeadlockTimer) commitUserInstTimer <- mkReg(0);
// FIFOs to output deadlock info
FIFO#(CommitStuck) commitInstStuckQ <- mkFIFO1;
FIFO#(CommitStuck) commitUserInstStuckQ <- mkFIFO1;
// wires to indicate that deadlock is reported, so reset timers
PulseWire commitInstStuckSent <- mkPulseWire;
PulseWire commitUserInstStuckSent <- mkPulseWire;
// wires to reset timers since processor is making progress
PulseWire commitInst <- mkPulseWire;
PulseWire commitUserInst <- mkPulseWire;
function CommitStuck commitStuck;
let x = rob.deqPort[0].deq_data;
return CommitStuck {
pc: x.pc,
iType: x.iType,
trap: x.trap,
state: x.rob_inst_state,
claimedPhyReg: x.claimed_phy_reg,
ldKilled: isValid(x.ldKilled),
memAccessAtCommit: x.memAccessAtCommit,
lsqAtCommitNotified: x.lsqAtCommitNotified,
nonMMIOStDone: x.nonMMIOStDone,
epochIncremented: x.epochIncremented,
specBits: x.spec_bits,
stbEmpty: inIfc.stbEmpty,
stqEmpty: inIfc.stqEmpty,
tlbNoPendingReq: inIfc.tlbNoPendingReq,
prv: csrf.decodeInfo.prv,
instCount: csrf.rd(CSRinstret)
};
endfunction
(* fire_when_enabled *)
rule checkDeadlock_commitInst(inIfc.checkDeadlock && commitInstTimer == maxBound);
commitInstStuckQ.enq(commitStuck);
commitInstStuckSent.send;
endrule
(* fire_when_enabled *)
rule checkDeadlock_commitUserInst(inIfc.checkDeadlock && commitUserInstTimer == maxBound);
commitUserInstStuckQ.enq(commitStuck);
commitUserInstStuckSent.send;
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incrDeadlockTimer(inIfc.checkDeadlock);
function DeadlockTimer getNextTimer(DeadlockTimer t);
return t == maxBound ? maxBound : t + 1;
endfunction
commitInstTimer <= (commitInst || commitInstStuckSent) ? 0 : getNextTimer(commitInstTimer);
commitUserInstTimer <= (commitUserInst || commitUserInstStuckSent) ? 0 : getNextTimer(commitUserInstTimer);
endrule
`endif
`ifdef RENAME_DEBUG
// rename debug
Reg#(Bool) renameDebugStarted <- mkConfigReg(False);
Reg#(Maybe#(RenameErrInfo)) renameErrInfo <- mkConfigReg(Invalid);
Bool canSetRenameErr = renameDebugStarted && renameErrInfo == Invalid; // only set err info once
// only send 1 error msg
FIFO#(RenameErrInfo) renameErrQ <- mkFIFO1;
rule sendRenameErr(renameDebugStarted &&& renameErrInfo matches tagged Valid .info);
renameErrQ.enq(info);
renameDebugStarted <= False;
endrule
`endif
// we commit trap in two cycles: first cycle deq ROB and flush; second
// cycle handles trap, redirect and handles system consistency
Reg#(Maybe#(CommitTrap)) commitTrap <- mkReg(Invalid); // saves new pc here
// maintain system consistency when system state (CSR) changes or for security
function Action makeSystemConsistent(Bool flushTlb,
Bool flushSecurity,
Bool reconcileI);
action
`ifndef SECURITY
flushSecurity = False;
`endif
`ifndef DISABLE_SECURE_FLUSH_TLB
if(flushTlb || flushSecurity) begin
`else
if(flushTlb) begin
`endif
inIfc.setFlushTlbs;
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushTlbCnt.incr(1);
end
`endif
end
// notify TLB to keep update of CSR changes
inIfc.setUpdateVMInfo;
// always wait store buffer and SQ to be empty
when(inIfc.stbEmpty && inIfc.stqEmpty, noAction);
// We wait TLB to finish all requests and become sync with memory.
// Notice that currently TLB is read only, so TLB is always in sync
// with memory (i.e., there is no write to commit to memory). Since all
// insts have been killed, nothing can be issued to D TLB at this time.
// Since fetch stage is set to wait for redirect, fetch1 stage is
// stalled, and nothing can be issued to I TLB at this time.
// Therefore, we just need to make sure that I and D TLBs are not
// handling any miss req. Besides, when I and D TLBs do not have any
// miss req, L2 TLB must be idling.
when(inIfc.tlbNoPendingReq, noAction);
// yield load reservation in cache
inIfc.setFlushReservation;
// flush for security, we can delay the stall for fetch-empty and
// wrong-path-load-empty until we really do the flush. This delay is
// valid because these wrong path inst/req will not interfere with
// whatever CSR changes we are making now.
if(flushSecurity) begin
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushSecurityCnt.incr(1);
end
`endif
`ifndef DISABLE_SECURE_FLUSH_BP
inIfc.setFlushBrPred;
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushBPCnt.incr(1);
end
`endif
`endif
`ifndef DISABLE_SECURE_FLUSH_CACHE
inIfc.setFlushCaches;
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushCacheCnt.incr(1);
end
`endif
`endif
end
`ifdef SELF_INV_CACHE
// reconcile I$
if(reconcileI) begin
inIfc.setReconcileI;
end
`ifdef SYSTEM_SELF_INV_L1D
// FIXME is this reconcile of D$ necessary?
inIfc.setReconcileD;
`endif
`endif
endaction
endfunction
// TODO Currently we don't check spec bits == 0 when we commit an
// instruction. This is because killings of wrong path instructions are
// done in a single cycle. However, when we make killings distributed or
// pipelined, then we need to check spec bits at commit port.
rule doCommitTrap_flush(
!isValid(commitTrap) &&&
rob.deqPort[0].deq_data.trap matches tagged Valid .trap
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitTrap] ", fshow(x));
// record trap info
Addr vaddr = ?;
if(x.ppc_vaddr_csrData matches tagged VAddr .va) begin
vaddr = va;
end
commitTrap <= Valid (CommitTrap {
trap: trap,
pc: x.pc,
addr: vaddr
});
// flush everything. Only increment epoch and stall fetch when we haven
// not done it yet (we may have already done them at rename stage)
inIfc.killAll;
if(!x.epochIncremented) begin
inIfc.incrementEpoch;
inIfc.setFetchWaitRedirect;
end
// faulting mem inst may have claimed phy reg, we should not commit it;
// instead, we kill the renaming by calling killAll
`ifdef PERF_COUNT
// performance counter
if(inIfc.doStats) begin
if(trap matches tagged Exception .e) begin
excepCnt.incr(1);
end
else begin
interruptCnt.incr(1);
end
end
`endif
// checks
doAssert(x.rob_inst_state == Executed, "must be executed");
doAssert(x.spec_bits == 0, "cannot have spec bits");
endrule
rule doCommitTrap_handle(commitTrap matches tagged Valid .trap);
// reset commitTrap
commitTrap <= Invalid;
// notify commit of interrupt (so MMIO pRq may be handled)
if(trap.trap matches tagged Interrupt .inter) begin
inIfc.commitCsrInstOrInterrupt;
end
// trap handling & redirect
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
inIfc.redirectPc(new_pc);
// system consistency
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
// does not include prv info, and it has to flush when prv changes.
// XXX As approximation, Trap may cause context switch, so flush for
// security
makeSystemConsistent(False, True, False);
endrule
// commit misspeculated load
rule doCommitKilledLd(
!isValid(commitTrap) &&&
!isValid(rob.deqPort[0].deq_data.trap) &&&
rob.deqPort[0].deq_data.ldKilled matches tagged Valid .killBy
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitKilledLd] ", fshow(x));
// kill everything, redirect, and increment epoch
inIfc.killAll;
inIfc.redirectPc(x.pc);
inIfc.incrementEpoch;
// the killed Ld should have claimed phy reg, we should not commit it;
// instead, we have kill the renaming by calling killAll
`ifdef PERF_COUNT
if(inIfc.doStats) begin
case(killBy)
Ld: comLdKillByLdCnt.incr(1);
St: comLdKillByStCnt.incr(1);
Cache: comLdKillByCacheCnt.incr(1);
endcase
end
`endif
// checks
doAssert(!x.epochIncremented, "cannot increment epoch before");
doAssert(x.rob_inst_state == Executed, "must be executed");
doAssert(x.spec_bits == 0, "cannot have spec bits");
endrule
// commit system inst
rule doCommitSystemInst(
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
isSystem(rob.deqPort[0].deq_data.iType)
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitSystemInst] ", fshow(x));
// we claim a phy reg for every inst, so commit its renaming
regRenamingTable.commit[0].commit;
Bool write_satp = False; // flush tlb when satp csr is modified
Bool flush_security = False; // flush for security when the flush csr is written
if(x.iType == Csr) begin
// notify commit of CSR (so MMIO pRq may be handled)
inIfc.commitCsrInstOrInterrupt;
// write CSR
let csr_idx = validValue(x.csr);
Data csr_data = ?;
if(x.ppc_vaddr_csrData matches tagged CSRData .d) begin
csr_data = d;
end
else begin
doAssert(False, "must have csr data");
end
csrf.csrInstWr(csr_idx, csr_data);
// check if satp is modified or not
write_satp = csr_idx == CSRsatp;
`ifdef SECURITY
flush_security = csr_idx == CSRmflush;
`endif
end
// redirect (Sret and Mret redirect pc is got from CSRF)
Addr next_pc = x.ppc_vaddr_csrData matches tagged PPC .ppc ? ppc : (x.pc + 4);
doAssert(next_pc == x.pc + 4, "ppc must be pc + 4");
if(x.iType == Sret) begin
next_pc <- csrf.sret;
end
else if(x.iType == Mret) begin
next_pc <- csrf.mret;
end
inIfc.redirectPc(next_pc);
// rename stage only sends out system inst when ROB is empty, so no
// need to flush ROB again
// system consistency
// flush TLB for SFence.VMA and when SATP CSR is modified
// XXX as approximation, sret/mret may mean context switch, so flush
// for security
makeSystemConsistent(
x.iType == SFence || write_satp, // TODO flush TLB when change sanctum regs?
flush_security || x.iType == Sret || x.iType == Mret,
x.iType == FenceI // reconcile I$ for fence.i
);
// incr inst cnt
csrf.incInstret(1);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
comSysCnt.incr(1);
// inst count stats
instCnt.incr(1);
if(csrf.decodeInfo.prv == 0) begin
userInstCnt.incr(1);
end
end
`endif
`ifdef CHECK_DEADLOCK
commitInst.send;
if(csrf.decodeInfo.prv == 0) begin
commitUserInst.send;
end
`endif
// checks
doAssert(x.epochIncremented, "must have already incremented epoch");
doAssert((x.iType == Csr) == isValid(x.csr), "only CSR has valid csr idx");
doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
doAssert(x.spec_bits == 0, "cannot have spec bits");
doAssert(x.claimed_phy_reg, "must have claimed phy reg");
`ifdef RENAME_DEBUG
if(!x.claimed_phy_reg && canSetRenameErr) begin
renameErrInfo <= Valid (RenameErrInfo {
err: NonTrapCommitLackClaim,
pc: x.pc,
iType: x.iType,
trap: x.trap,
specBits: x.spec_bits
});
end
`endif
endrule
// Lr/Sc/Amo/MMIO cannot proceed to executed until we notify LSQ that it
// has reached the commit stage
rule notifyLSQCommit(
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state != Executed &&
rob.deqPort[0].deq_data.memAccessAtCommit &&
!rob.deqPort[0].deq_data.lsqAtCommitNotified
);
let x = rob.deqPort[0].deq_data;
let inst_tag = rob.deqPort[0].getDeqInstTag;
if(verbose) $display("[notifyLSQCommit] ", fshow(x), "; ", fshow(inst_tag));
// notify LSQ, and record in ROB that notification is done
setLSQAtCommit[0].wset(x.lsqTag);
rob.setLSQAtCommitNotified(inst_tag);
endrule
// commit normal: fire when at least one commit can be done
rule doCommitNormalInst(
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
!isSystem(rob.deqPort[0].deq_data.iType)
);
// stop superscalar commit after we
// 1. see a trap or system inst or killed Ld
// 2. inst is not ready to commit
Bool stop = False;
// We merge writes on FPU csr and apply writes at the end of the rule
Bit#(5) fflags = 0;
Bool will_dirty_fpu_state = False;
// rename error
Maybe#(RenameErrInfo) renameError = Invalid;
// incr committed inst cnt at the end of rule
SupCnt comInstCnt = 0;
SupCnt comUserInstCnt = 0;
`ifdef PERF_COUNT
// incr some performance counter at the end of rule
SupCnt brCnt = 0;
SupCnt jmpCnt = 0;
SupCnt jrCnt = 0;
SupCnt ldCnt = 0;
SupCnt stCnt = 0;
SupCnt lrCnt = 0;
SupCnt scCnt = 0;
SupCnt amoCnt = 0;
`endif
// compute what actions to take
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && rob.deqPort[i].canDeq) begin
let x = rob.deqPort[i].deq_data;
let inst_tag = rob.deqPort[i].getDeqInstTag;
// check can be committed or not
if(x.rob_inst_state != Executed || isValid(x.ldKilled) || isValid(x.trap) || isSystem(x.iType)) begin
// inst not ready for commit, or system inst, or trap, or killed, stop here
stop = True;
end
else begin
if (verbose) $display("[doCommitNormalInst - %d] ", i, fshow(inst_tag), " ; ", fshow(x));
// inst can be committed, deq it
rob.deqPort[i].deq;
// every inst here should have been renamed, commit renaming
regRenamingTable.commit[i].commit;
doAssert(x.claimed_phy_reg, "should have renamed");
`ifdef RENAME_DEBUG
// send debug msg for rename error
if(!x.claimed_phy_reg && !isValid(renameError)) begin
renameError = Valid (RenameErrInfo {
err: NonTrapCommitLackClaim,
pc: x.pc,
iType: x.iType,
trap: x.trap,
specBits: x.spec_bits
});
end
`endif
// cumulate writes to FPU csr
fflags = fflags | x.fflags;
will_dirty_fpu_state = will_dirty_fpu_state || x.will_dirty_fpu_state;
// for non-mmio st, notify SQ that store is committed
if(x.nonMMIOStDone) begin
setLSQAtCommit[i].wset(x.lsqTag);
end
// inst commit counter
comInstCnt = comInstCnt + 1;
if(csrf.decodeInfo.prv == 0) begin
comUserInstCnt = comUserInstCnt + 1; // user space inst
end
`ifdef PERF_COUNT
// performance counter
case(x.iType)
Br: brCnt = brCnt + 1;
J : jmpCnt = jmpCnt + 1;
Jr: jrCnt = jrCnt + 1;
Ld: ldCnt = ldCnt + 1;
St: stCnt = stCnt + 1;
Lr: lrCnt = lrCnt + 1;
Sc: scCnt = scCnt + 1;
Amo: amoCnt = amoCnt + 1;
endcase
`endif
end
end
end
// write FPU csr
if(csrf.fpuInstNeedWr(fflags, will_dirty_fpu_state)) begin
csrf.fpuInstWr(fflags);
end
// incr inst cnt
csrf.incInstret(comInstCnt);
`ifdef RENAME_DEBUG
// set rename error
if(canSetRenameErr && isValid(renameError)) begin
renameErrInfo <= renameError;
end
`endif
`ifdef CHECK_DEADLOCK
commitInst.send; // ROB head is removed
if(comUserInstCnt > 0) begin
commitUserInst.send;
end
`endif
`ifdef PERF_COUNT
// performance counter
if(inIfc.doStats) begin
// branch stats
comBrCnt.incr(zeroExtend(brCnt));
comJmpCnt.incr(zeroExtend(jmpCnt));
comJrCnt.incr(zeroExtend(jrCnt));
// mem stats
comLdCnt.incr(zeroExtend(ldCnt));
comStCnt.incr(zeroExtend(stCnt));
comLrCnt.incr(zeroExtend(lrCnt));
comScCnt.incr(zeroExtend(scCnt));
comAmoCnt.incr(zeroExtend(amoCnt));
// inst count stats
instCnt.incr(zeroExtend(comInstCnt));
userInstCnt.incr(zeroExtend(comUserInstCnt));
if(comUserInstCnt > 1) begin
supComUserCnt.incr(1);
end
end
`endif
endrule
method Data getPerf(ComStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
InstCnt: instCnt;
UserInstCnt: userInstCnt;
SupComUserCnt: supComUserCnt;
ComBrCnt: comBrCnt;
ComJmpCnt: comJmpCnt;
ComJrCnt: comJrCnt;
ComLdCnt: comLdCnt;
ComStCnt: comStCnt;
ComLrCnt: comLrCnt;
ComScCnt: comScCnt;
ComAmoCnt: comAmoCnt;
ComLdKillByLd: comLdKillByLdCnt;
ComLdKillBySt: comLdKillByStCnt;
ComLdKillByCache: comLdKillByCacheCnt;
ComSysCnt: comSysCnt;
ExcepCnt: excepCnt;
InterruptCnt: interruptCnt;
FlushTlbCnt: flushTlbCnt;
FlushSecurityCnt: flushSecurityCnt;
FlushBPCnt: flushBPCnt;
FlushCacheCnt: flushCacheCnt;
`endif
default: 0;
endcase);
endmethod
`ifdef CHECK_DEADLOCK
interface commitInstStuck = toGet(commitInstStuckQ);
interface commitUserInstStuck = toGet(commitUserInstStuckQ);
`else
interface commitInstStuck = nullGet;
interface commitUserInstStuck = nullGet;
`endif
`ifdef RENAME_DEBUG
method Action startRenameDebug if(!renameDebugStarted);
renameDebugStarted <= True;
endmethod
interface renameErr = toGet(renameErrQ);
`else
method Action startRenameDebug;
noAction;
endmethod
interface renameErr = nullGet;
`endif
endmodule

View File

@@ -0,0 +1,641 @@
// 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.
`include "ProcConfig.bsv"
import BrPred::*;
import DirPredictor::*;
import Btb::*;
import ClientServer::*;
import Connectable::*;
import Decode::*;
import Ehr::*;
import Fifo::*;
import GetPut::*;
import MemoryTypes::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import Ras::*;
import EpochManager::*;
import Performance::*;
import Vector::*;
import Assert::*;
import Cntrs::*;
import ConfigReg::*;
import TlbTypes::*;
import ITlb::*;
import CCTypes::*;
import L1CoCache::*;
import MMIOInst::*;
interface FetchStage;
// pipeline
interface Vector#(SupSize, SupFifoDeq#(FromFetchStage)) pipelines;
// tlb and mem connections
interface ITlb iTlbIfc;
interface ICoCache iMemIfc;
interface MMIOInstToCore mmioIfc;
// starting and stopping
method Action start(Addr pc);
method Action stop();
// redirection methods
method Action setWaitRedirect;
method Action redirect(Addr pc);
method Action done_flushing();
method Action train_predictors(
Addr pc, Addr next_pc, IType iType, Bool taken,
DirPredTrainInfo dpTrain, Bool mispred
);
// security
method Bool emptyForFlush;
method Action flush_predictors;
method Bool flush_predictors_done;
// debug
method FetchDebugState getFetchState;
// performance
interface Perf#(DecStagePerfType) perf;
endinterface
typedef struct {
Addr pc;
Epoch mainEp;
Bool waitForRedirect;
Bool waitForFlush;
} FetchDebugState deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr pred_next_pc;
Bool decode_epoch;
Epoch main_epoch;
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr phys_pc;
Addr pred_next_pc;
Maybe#(Exception) cause;
Bool access_mmio; // inst fetch from MMIO
Bool decode_epoch;
Epoch main_epoch;
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr ppc;
Bool decode_epoch;
Epoch main_epoch;
Instruction inst;
Maybe#(Exception) cause;
} Fetch3ToDecode deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr ppc;
Epoch main_epoch;
DirPredTrainInfo dpTrain;
Instruction inst;
DecodedInst dInst;
ArchRegs regs;
Maybe#(Exception) cause;
} FromFetchStage deriving (Bits, Eq, FShow);
// train next addr pred (BTB)
typedef struct {
Addr pc;
Addr nextPc;
} TrainNAP deriving(Bits, Eq, FShow);
(* synthesize *)
module mkFetchStage(FetchStage);
// rule ordering: Fetch1 (BTB+TLB) < Fetch3 (decode & dir pred) < redirect method
// Fetch1 < Fetch3 to avoid bypassing path on PC and epochs
let verbose = True;
// Basic State Elements
Reg#(Bool) started <- mkReg(False);
// Stall fetch when trap happens or system inst is renamed
// All inst younger than the trap/system inst will be killed
// Since CSR may be modified, sending wrong path request to TLB may cause problem
// So we stall until the next redirection happens
// The next redirect is either by the trap/system inst or an older one
Reg#(Bool) waitForRedirect <- mkReg(False);
// We don't want setWaitForRedirect method and redirect method to happen together
// make them conflict
RWire#(void) setWaitRedirect_redirect_conflict <- mkRWire;
// Stall fetch during the flush triggered by the procesing trap/system inst in commit stage
// We stall until the flush is done
Reg#(Bool) waitForFlush <- mkReg(False);
Ehr#(3, Addr) pc_reg <- mkEhr(0);
Integer pc_fetch1_port = 0;
Integer pc_decode_port = 1;
Integer pc_redirect_port = 2;
// Epochs
Reg#(Bool) decode_epoch <- mkReg(False);
Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch
// Pipeline Stage FIFOs
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo;
Fifo#(2, Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo();
SupFifo#(SupSize, 2, FromFetchStage) out_fifo <- mkSupFifo;
// Can the fifo size be smaller?
// Branch Predictors
NextAddrPred nextAddrPred <- mkBtb;
let dirPred <- mkDirPredictor;
ReturnAddrStack ras <- mkRas;
// Wire to train next addr pred (NAP)
RWire#(TrainNAP) napTrainByExe <- mkRWire;
RWire#(TrainNAP) napTrainByDec <- mkRWire;
Fifo#(1, TrainNAP) napTrainByDecQ <- mkPipelineFifo; // cut off critical path
// TLB and Cache connections
ITlb iTlb <- mkITlb;
ICoCache iMem <- mkICoCache;
MMIOInst mmio <- mkMMIOInst;
Server#(Addr, TlbResp) tlb_server = iTlb.to_proc;
Server#(Addr, Vector#(SupSize, Maybe#(Instruction))) mem_server = iMem.to_proc;
// performance counters
Fifo#(1, DecStagePerfType) perfReqQ <- mkCFFifo; // perf req FIFO
`ifdef PERF_COUNT
Reg#(Bool) doStats <- mkConfigReg(False);
// decode stage redirect
Count#(Data) decRedirectBrCnt <- mkCount(0);
Count#(Data) decRedirectJmpCnt <- mkCount(0);
Count#(Data) decRedirectJrCnt <- mkCount(0);
Count#(Data) decRedirectOtherCnt <- mkCount(0);
// perf resp FIFO
Fifo#(1, PerfResp#(DecStagePerfType)) perfRespQ <- mkCFFifo;
rule doPerfReq;
let t <- toGet(perfReqQ).get;
Data d = (case(t)
DecRedirectBr: decRedirectBrCnt;
DecRedirectJmp: decRedirectJmpCnt;
DecRedirectJr: decRedirectJrCnt;
DecRedirectOther: decRedirectOtherCnt;
default: 0;
endcase);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
// We don't send req to TLB when waiting for redirect or TLB flush. Since
// there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
// TLB idle to change VM CSR / signal flush TLB, there is no wrong path
// request afterwards to race with the system code that manage paget table.
rule doFetch1(started && !waitForRedirect && !waitForFlush);
let pc = pc_reg[pc_fetch1_port];
// Chain of prediction for the next instructions
// We need a BTB with a register file with enough ports!
// Instead of cascading predictions, we can always feed pc+4*i into
// predictor, because we will break superscaler fetch if nextpc != pc+4
Vector#(SupSize, Addr) pred_future_pc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
pred_future_pc[i] = nextAddrPred.predPc(pc + fromInteger(4 * i));
end
// Next pc is the first nextPc that breaks the chain of pc+4 or
// that is at the end of a cacheline.
Vector#(SupSize,Integer) indexes = genVector;
function Bool findNextPc(Addr pc, Integer i);
Bool notLastInst = getLineInstOffset(pc + fromInteger(4*i)) != maxBound;
Bool noJump = pred_future_pc[i] == pc + fromInteger(4*(i+1));
return (!(notLastInst && noJump));
endfunction
Integer posLastSup = fromMaybe(valueof(SupSize) - 1, find(findNextPc(pc), indexes));
Addr pred_next_pc = pred_future_pc[posLastSup];
pc_reg[pc_fetch1_port] <= pred_next_pc;
// Send TLB request
tlb_server.request.put(pc);
let out = Fetch1ToFetch2 {
pc: pc,
pred_next_pc: pred_next_pc,
decode_epoch: decode_epoch,
main_epoch: f_main_epoch};
f12f2.enq(tuple2(fromInteger(posLastSup),out));
if (verbose) $display("Fetch1: ", fshow(out));
endrule
rule doFetch2;
let {nbSup,in} = f12f2.first;
f12f2.deq;
// Get TLB response
match {.phys_pc, .cause} <- tlb_server.response.get;
// Access main mem or boot rom
Bool access_mmio = False;
if (!isValid(cause)) begin
case(mmio.getFetchTarget(phys_pc))
MainMem: begin
// Send ICache request
mem_server.request.put(phys_pc);
end
BootRom: begin
// Send MMIO req. Luckily boot rom is also aligned with
// cache line size, so all nbSup+1 insts can be fetched
// from boot rom. It won't happen that insts fetched from
// boot rom is less than requested.
mmio.bootRomReq(phys_pc, nbSup);
access_mmio = True;
end
default: begin
// Access fault
cause = Valid (InstAccessFault);
end
endcase
end
let out = Fetch2ToFetch3 {
pc: in.pc,
phys_pc: phys_pc,
pred_next_pc: in.pred_next_pc,
cause: cause,
access_mmio: access_mmio,
decode_epoch: in.decode_epoch,
main_epoch: in.main_epoch };
f22f3.enq(tuple2(nbSup,out));
if (verbose) $display("Fetch2: ", fshow(out));
endrule
// Break out of i$
rule doFetch3;
let {nbSup, fetch3In} = f22f3.first;
f22f3.deq();
if (verbose) $display("Fetch3 %d",fetch3In.pc);
// Get ICache/MMIO response if no exception
// In case of exception, we still need to process at least inst_data[0]
// (it will be turned to an exception later), so inst_data[0] must be
// valid.
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
if(!isValid(fetch3In.cause)) begin
if(fetch3In.access_mmio) begin
if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
inst_d <- mmio.bootRomResp;
end
else begin
if(verbose) $display("get answer from memory %d", fetch3In.pc);
inst_d <- mem_server.response.get;
end
end
if(verbose) $display("epoch instr: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
instdata.enq(inst_d);
f32d.enq(f22f3.first);
endrule
rule doDecode;
let {nbSup, fetch3In} = f32d.first;
f32d.deq();
let inst_data = instdata.first();
instdata.deq();
// The main_epoch check is required to make sure this stage doesn't
// redirect the PC if a later stage already redirected the PC.
if (fetch3In.main_epoch == f_main_epoch) begin
Bool decode_epoch_local = decode_epoch; // next value for decode epoch
Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
`ifdef PERF_COUNT
// performance counter: inst being redirect by decode stage
// Note that only 1 redirection may happen in a cycle
Maybe#(IType) redirectInst = Invalid;
`endif
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
if (inst_data[i] != tagged Invalid && fromInteger(i) <= nbSup) begin
// get the input to decode
let in = Fetch3ToDecode {
pc: fetch3In.pc+fromInteger(4*i),
// last inst, next pc may not be pc+4
ppc: fromInteger(i) == nbSup ? fetch3In.pred_next_pc :
fetch3In.pc+fromInteger(4*(i+1)),
decode_epoch: fetch3In.decode_epoch,
main_epoch: fetch3In.main_epoch,
inst: fromMaybe(?,inst_data[i]),
cause: fetch3In.cause
};
let cause = in.cause;
if (verbose) $display("Decode %d\n",i);
// do decode and branch prediction
// Drop here if does not match the decode_epoch.
if (in.decode_epoch == decode_epoch_local) begin
doAssert(in.main_epoch == f_main_epoch, "main epoch must match");
let decode_result = decode(in.inst);
// update cause if there was not an early detected exception
if (!isValid(cause)) begin
cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
end
let dInst = decode_result.dInst;
let regs = decode_result.regs;
DirPredTrainInfo dp_train = ?; // dir pred training bookkeeping
// update predicted next pc
if (!isValid(cause)) begin
// direction predict
Bool pred_taken = False;
if(dInst.iType == Br) begin
let pred_res <- dirPred.pred[i].pred(in.pc);
pred_taken = pred_res.taken;
dp_train = pred_res.train;
end
Maybe#(Addr) nextPc = decodeBrPred(in.pc, dInst, pred_taken);
// return address stack link reg is x1 or x5
function Bool linkedR(Maybe#(ArchRIndx) register);
Bool res = False;
if (register matches tagged Valid .r &&& (r == tagged Gpr 1 || r == tagged Gpr 5)) begin
res = True;
end
return res;
endfunction
Bool dst_link = linkedR(regs.dst);
Bool src1_link = linkedR(regs.src1);
Addr push_addr = in.pc + 4;
Addr pop_addr = ras.ras[i].first;
if (dInst.iType == J && dst_link) begin
// rs1 is invalid, i.e., not link: push
ras.ras[i].popPush(False, Valid (push_addr));
end
else if (dInst.iType == Jr) begin // jalr
if (!dst_link && src1_link) begin
// rd is link while rs1 is not: pop
nextPc = Valid (pop_addr);
ras.ras[i].popPush(True, Invalid);
end
else if (!src1_link && dst_link) begin
// rs1 is not link while rd is link: push
ras.ras[i].popPush(False, Valid (push_addr));
end
else if (dst_link && src1_link) begin
// both rd and rs1 are links
if (regs.src1 != regs.dst) begin
// not same reg: first pop, then push
nextPc = Valid (pop_addr);
ras.ras[i].popPush(True, Valid (push_addr));
end
else begin
// same reg: push
ras.ras[i].popPush(False, Valid (push_addr));
end
end
end
if(verbose) begin
$display("Branch prediction: ", fshow(dInst.iType), " ; ", fshow(in.pc), " ; ",
fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
end
// check previous mispred
if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
if (verbose) $display("ppc and decodeppc : %h %h", in.ppc, decode_pred_next_pc);
decode_epoch_local = !decode_epoch_local;
redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
in.ppc = decode_pred_next_pc;
// train next addr pred when mispredict
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
`ifdef PERF_COUNT
// performance stats: record decode redirect
doAssert(redirectInst == Invalid, "at most 1 decode redirect per cycle");
redirectInst = Valid (dInst.iType);
`endif
end
end
let out = FromFetchStage{pc: in.pc,
ppc: in.ppc,
main_epoch: in.main_epoch,
dpTrain: dp_train,
inst: in.inst,
dInst: dInst,
regs: decode_result.regs,
cause: cause };
out_fifo.enqS[i].enq(out);
if (verbose) $display("Decode: ", fshow(out));
end
else begin
if (verbose) $display("Drop decoded within a superscalar");
// just drop wrong path instructions
end
end
else if (inst_data[i] == tagged Invalid && fromInteger(i) <= nbSup) begin
// inst num is less than expected; this should not happen
// because both I$ and boot rom are aligned to cache line
// size.
doAssert(False, "Fetched insts not enough");
end
end
// update PC and epoch
if(redirectPc matches tagged Valid .nextPc) begin
pc_reg[pc_decode_port] <= nextPc;
end
decode_epoch <= decode_epoch_local;
// send training data for next addr pred
if (trainNAP matches tagged Valid .x) begin
napTrainByDecQ.enq(x);
end
`ifdef PERF_COUNT
// performance counter: check whether redirect happens
if(redirectInst matches tagged Valid .iType &&& doStats) begin
case(iType)
Br: decRedirectBrCnt.incr(1);
J : decRedirectJmpCnt.incr(1);
Jr: decRedirectJrCnt.incr(1);
default: decRedirectOtherCnt.incr(1);
endcase
end
`endif
end
else begin
if (verbose) $display("drop in fetch3decode");
end
endrule
// train next addr pred: we use a wire to catch outputs of napTrainByDecQ.
// This prevents napTrainByDecQ from clogging doDecode rule when
// superscalar size is large
(* fire_when_enabled *)
rule setTrainNAPByDec;
napTrainByDecQ.deq;
napTrainByDec.wset(napTrainByDecQ.first);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule doTrainNAP(isValid(napTrainByDec.wget) || isValid(napTrainByExe.wget));
// Give priority to train from exe. This is because exe has train data
// only when misprediction happens, i.e., train by dec is already at
// wrong path.
TrainNAP train = fromMaybe(validValue(napTrainByDec.wget), napTrainByExe.wget);
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
endrule
// Security: we can flush when front end is empty, i.e.
// (1) Fetch1 is stalled for waiting flush
// (2) all internal FIFOs are empty (the output sup fifo needs not to be
// empty, but why leave this security hole)
Bool empty_for_flush = waitForFlush &&
!f12f2.notEmpty && !f22f3.notEmpty &&
!f32d.notEmpty && out_fifo.internalEmpty;
interface Vector pipelines = out_fifo.deqS;
interface iTlbIfc = iTlb;
interface iMemIfc = iMem;
interface mmioIfc = mmio.toCore;
method Action start(Addr start_pc);
pc_reg[0] <= start_pc;
started <= True;
waitForRedirect <= False;
waitForFlush <= False;
endmethod
method Action stop();
started <= False;
endmethod
method Action setWaitRedirect;
waitForRedirect <= True;
setWaitRedirect_redirect_conflict.wset(?); // conflict with redirect
endmethod
method Action redirect(Addr new_pc);
if (verbose) $display("Redirect: newpc %h, old f_main_epoch %d, new f_main_epoch %d",new_pc,f_main_epoch,f_main_epoch+1);
pc_reg[pc_redirect_port] <= new_pc;
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
// redirect comes, stop stalling for redirect
waitForRedirect <= False;
setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
// this redirect may be caused by a trap/system inst in commit stage
// we conservatively set wait for flush TODO make this an input parameter
waitForFlush <= True;
endmethod
method Action done_flushing() if (waitForFlush);
// signal that the pipeline can resume fetching
waitForFlush <= False;
// XXX The guard prevents the readyToFetch rule in Core.bsv from firing every cycle
// The guard also makes this method sequence before (restricted) redirect method
// So the effect of setting waitForFlush in redirect method will not be overwritten
// Then we don't need to make two methods conflict
// It's fine for the effect of this method to be overwritten, because it fires very often
endmethod
method Action train_predictors(
Addr pc, Addr next_pc, IType iType, Bool taken,
DirPredTrainInfo dpTrain, Bool mispred
);
//if (iType == J || (iType == Br && next_pc < pc)) begin
// // Only train the next address predictor for jumps and backward branches
// // next_pc != pc + 4 is a substitute for taken
// nextAddrPred.update(pc, next_pc, taken);
//end
if (iType == Br) begin
// Train the direction predictor for all branches
dirPred.update(pc, taken, dpTrain, mispred);
end
// train next addr pred when mispred
if(mispred) begin
napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
end
endmethod
// security
method Bool emptyForFlush;
return empty_for_flush;
endmethod
method Action flush_predictors;
nextAddrPred.flush;
dirPred.flush;
ras.flush;
endmethod
method Bool flush_predictors_done;
return nextAddrPred.flush_done && dirPred.flush_done && ras.flush_done;
endmethod
method FetchDebugState getFetchState;
return FetchDebugState {
pc: pc_reg[0],
waitForRedirect: waitForRedirect,
waitForFlush: waitForFlush,
mainEp: f_main_epoch
};
endmethod
interface Perf perf;
method Action setStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Action req(DecStagePerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(DecStagePerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
`ifdef PERF_COUNT
method Bool respValid = perfRespQ.notEmpty;
`else
method Bool respValid = perfReqQ.notEmpty;
`endif
endinterface
endmodule

View File

@@ -0,0 +1,325 @@
// 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.
`include "ProcConfig.bsv"
import DefaultValue::*;
import Cntrs::*;
import Vector::*;
import BuildVector::*;
import Types::*;
import ProcTypes::*;
import SynthParam::*;
import Exec::*;
import Performance::*;
import ReservationStationEhr::*;
import ReservationStationFpuMulDiv::*;
import ReorderBuffer::*;
import HasSpecBits::*;
import SpecFifo::*;
import MulDiv::*;
import Fpu::*;
import Bypass::*;
typedef struct {
// inst info
ExecFunc execFunc;
PhyRegs regs;
InstTag tag;
// FpuMulDiv must not have valid spec tag
} FpuMulDivDispatchToRegRead deriving(Bits, Eq, FShow);
typedef struct {
// inst info
ExecFunc execFunc;
Maybe#(PhyDst) dst;
InstTag tag;
// src reg vals
Data rVal1;
Data rVal2;
Data rVal3;
} FpuMulDivRegReadToExe deriving(Bits, Eq, FShow);
typedef struct {
// inst info
ExecFunc execFunc;
Maybe#(PhyDst) dst;
InstTag tag;
} FpuMulDivExeToFinish deriving(Bits, Eq, FShow);
// synthesized pipeline fifos
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, FpuMulDivDispatchToRegRead) FpuMulDivDispToRegFifo;
(* synthesize *)
module mkFpuMulDivDispToRegFifo(FpuMulDivDispToRegFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, FpuMulDivRegReadToExe) FpuMulDivRegToExeFifo;
(* synthesize *)
module mkFpuMulDivRegToExeFifo(FpuMulDivRegToExeFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
interface FpuMulDivExeInput;
// conservative scoreboard check in reg read stage
method RegsReady sbCons_lazyLookup(PhyRegs r);
// Phys reg file
method Data rf_rd1(PhyRIndx rindx);
method Data rf_rd2(PhyRIndx rindx);
method Data rf_rd3(PhyRIndx rindx);
// CSR file
method Data csrf_rd(CSR csr);
// ROB
method Action rob_setExecuted(InstTag t, Bit#(5) fflags);
// global broadcast methods
// write reg file & set both conservative and aggressive sb & wake up inst
method Action writeRegFile(PhyRIndx dst, Data data);
// spec update
method Action conflictWrongSpec;
// performance
method Bool doStats;
endinterface
interface FpuMulDivExePipeline;
// recv bypass from the ALU exe and finish stages
interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
interface ReservationStationFpuMulDiv rsFpuMulDivIfc;
interface SpeculationUpdate specUpdate;
// performance
method Data getPerf(ExeStagePerfType t);
endinterface
module mkFpuMulDivExePipeline#(FpuMulDivExeInput inIfc)(FpuMulDivExePipeline);
Bool verbose = True;
// fpu mul div reservation station
ReservationStationFpuMulDiv rsFpuMulDiv <- mkReservationStationFpuMulDiv;
// pipeline fifos
let dispToRegQ <- mkFpuMulDivDispToRegFifo;
let regToExeQ <- mkFpuMulDivRegToExeFifo;
// wire to recv bypass
Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
// mul div fpu func units
MulDivExec mulDivExec <- mkMulDivExec;
FpuExec fpuExec <- mkFpuExecPipeline;
// fpu/mul/div performance counters
`ifdef PERF_COUNT
Count#(Data) exeIntMulCnt <- mkCount(0);
Count#(Data) exeIntDivCnt <- mkCount(0);
Count#(Data) exeFpFmaCnt <- mkCount(0);
Count#(Data) exeFpDivCnt <- mkCount(0);
Count#(Data) exeFpSqrtCnt <- mkCount(0);
`endif
rule doDispatchFpuMulDiv;
rsFpuMulDiv.doDispatch;
let x = rsFpuMulDiv.dispatchData;
if(verbose) $display("[doDispatchFpuMulDiv] ", fshow(x));
// FPU MUL DIV never have exception or misprecition, so no spec tag
doAssert(!isValid(x.spec_tag), "FpuMulDiv should not carry any spec tag");
// go to next stage
dispToRegQ.enq(ToSpecFifo {
data: FpuMulDivDispatchToRegRead {
execFunc: x.data.execFunc,
regs: x.regs,
tag: x.tag
},
spec_bits: x.spec_bits
});
endrule
rule doRegReadFpuMulDiv;
dispToRegQ.deq;
let dispToReg = dispToRegQ.first;
let x = dispToReg.data;
if(verbose) $display("[doRegReadFpuMulDiv] ", fshow(dispToReg));
// check conservative scoreboard
let regsReady = inIfc.sbCons_lazyLookup(x.regs);
// get rVal1 (check bypass)
Data rVal1 = ?;
if(x.regs.src1 matches tagged Valid .src1) begin
rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
end
// get rVal2 (check bypass)
Data rVal2 = ?;
if(x.regs.src2 matches tagged Valid .src2) begin
rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
end
// get rVal3 (check bypass)
Data rVal3 = ?;
if(x.regs.src3 matches tagged Valid .src3) begin
rVal3 <- readRFBypass(src3, regsReady.src3, inIfc.rf_rd3(src3), bypassWire);
end
// go to next stage
regToExeQ.enq(ToSpecFifo {
data: FpuMulDivRegReadToExe {
execFunc: x.execFunc,
dst: x.regs.dst,
tag: x.tag,
rVal1: rVal1,
rVal2: rVal2,
rVal3: rVal3
},
spec_bits: dispToReg.spec_bits
});
endrule
rule doExeFpuMulDiv;
regToExeQ.deq;
let regToExe = regToExeQ.first;
let x = regToExe.data;
let spec_bits = regToExe.spec_bits;
if(verbose) $display("[doExeFpuMulDiv] ", fshow(regToExe));
// send to exe unit
Data rVal1 = x.rVal1;
Data rVal2 = x.rVal2;
Data rVal3 = x.rVal3;
case (x.execFunc) matches
tagged Fpu .fpu_inst: begin
fpuExec.exec(fpu_inst, rVal1, rVal2, rVal3, x.dst, x.tag, spec_bits);
end
tagged MulDiv .muldiv_inst: begin
mulDivExec.exec(muldiv_inst, rVal1, rVal2, x.dst, x.tag, spec_bits);
end
default: begin
doAssert(False, "unknown execFunc for doExeFpuMulDiv");
end
endcase
endrule
function Action doFinish(Maybe#(PhyDst) dst, InstTag tag, Data data, Bit#(5) fflags);
action
// write to register file
if(dst matches tagged Valid .valid_dst) begin
inIfc.writeRegFile(valid_dst.indx, data);
end
// update the instruction in the reorder buffer.
inIfc.rob_setExecuted(tag, fflags);
// since FPU op has no spec tag, this doFinish rule is ordered before
// other rules that calls incorrectSpec, and BSV compiler creates
// cycles in scheduling. We manually creates a conflict between this
// rule and incorrectSpec to break the cycle
//inIfc.conflictWrongSpec;
endaction
endfunction
rule doFinishFpSimple;
FpuResp resp <- fpuExec.simpleResp;
if(verbose) $display("[doFinishFpSimple] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
endrule
rule doFinishFpFma;
FpuResp resp <- fpuExec.fmaResp;
if(verbose) $display("[doFinishFpFma] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeFpFmaCnt.incr(1);
end
`endif
endrule
rule doFinishFpDiv;
FpuResp resp <- fpuExec.divResp;
if(verbose) $display("[doFinishFpDiv] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeFpDivCnt.incr(1);
end
`endif
endrule
rule doFinishFpSqrt;
FpuResp resp <- fpuExec.sqrtResp;
if(verbose) $display("[doFinishFpSqrt] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeFpSqrtCnt.incr(1);
end
`endif
endrule
rule doFinishIntMul;
MulDivResp resp <- mulDivExec.mulResp;
if(verbose) $display("[doFinishIntMul] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.data, 0);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeIntMulCnt.incr(1);
end
`endif
endrule
rule doFinishIntDiv;
MulDivResp resp <- mulDivExec.divResp;
if(verbose) $display("[doFinishIntDiv] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.data, 0);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeIntDivCnt.incr(1);
end
`endif
endrule
interface recvBypass = map(getRecvBypassIfc, bypassWire);
interface rsFpuMulDivIfc = rsFpuMulDiv;
interface specUpdate = joinSpeculationUpdate(vec(
rsFpuMulDiv.specUpdate,
dispToRegQ.specUpdate,
regToExeQ.specUpdate,
fpuExec.specUpdate,
mulDivExec.specUpdate
));
method Data getPerf(ExeStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
ExeIntMulCnt: exeIntMulCnt;
ExeIntDivCnt: exeIntDivCnt;
ExeFpFmaCnt: exeFpFmaCnt;
ExeFpDivCnt: exeFpDivCnt;
ExeFpSqrtCnt: exeFpSqrtCnt;
`endif
default: 0;
endcase);
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,326 @@
// 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.
// Core size and cache size are controlled by macros CORE_XXX and CACHE_XXX. In
// MICRO 2018 paper, configuration BASE-T is defining CORE_SMALL and
// CACHE_LARGE, and configuration BASE-R is defining CORE_MEDIUM and
// CACHE_LARGE. Due to resource restrictions, 4-core multiprocessor on AWS is
// defining CORE_TINY and CACHE_MC.
//
// ==== common parameters ====
//
`define rv64 True
`define m True
`define a True
`define f True
`define d True
//`define NUM_CORES 1 // defined in make file
//`define PERF_COUNT // defined in makefile
`define REUSE_FMA // use FMA for add and mul
`define LOG_BOOT_ROM_BYTES 12 // 4KB boot rom
// tournament predictor, other options are: BHT, TOUR, GSELECT, GSHARE. NOTE
// that the predictors are of different size.
`define DIR_PRED_TOUR
`define LOG_DEADLOCK_CYCLES 26 // 64M cycles for deadlock detection
// Be lazy in reservation station wake and phy reg file, and enqs. LSQ is by
// default lazy. 1-elem spec FIFOs (pipeline stage regs) are by default not
// lazy.
`define LAZY_RS_RF True
`define RS_LAZY_ENQ True
`define ROB_LAZY_ENQ True
`define L1_TLB_SIZE 32 // L1 fully assoc TLB size
`define L2_TLB_HUGE_SIZE 8 // L2 2MB/1GB TLB size
`define LOG_L2_TLB_4KB_SIZE 10 // L2 4KB TLB log size (1024 entries)
`define LOG_L2_TLB_4KB_WAYS 2 // L2 4KB TLB log ways (4 ways)
// FMA bookkeeping FIFO: add 1 to allow simultaneous enq/deq
`define BOOKKEEPING_FP_FMA_SIZE TAdd#(`XILINX_FP_FMA_LATENCY, 1)
// INT MUL bookkeeping FIFO: add 1 to allow simultaneous enq/deq, another 1
// because of internal flow control in MUL unit
`define BOOKKEEPING_INT_MUL_SIZE TAdd#(`XILINX_INT_MUL_LATENCY, 2)
// non-blocking DTLB
`define DTLB_REQ_NUM 4
// non-blocking L2 TLB
`define L2TLB_REQ_NUM 2
`define DRAM_MAX_READS TExp#(`LOG_LLC_WAYS) // max reads in DRAM, match LLC ways
`define DRAM_MAX_WRITES 16 // write buffer size in AWS DRAM controller
`define DRAM_MAX_REQS 24
`define DRAM_LATENCY 120 // model a constant dram latency
`ifdef SECURITY
`define LOG_DRAM_REGION_NUM 6 // 64 DRAM regions
`define LOG_DRAM_REGION_SIZE 25 // 32MB for each DRAM region
`endif
//
// ==== CACHE SIZE ====
//
`ifdef CACHE_SMALL
// L1
`define LOG_L1_LINES 8 // 16KB
`define LOG_L1_WAYS 3 // 8 ways
// LLC
`define LOG_LLC_LINES 12 // 256KB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
`ifdef CACHE_LARGE
// L1
`define LOG_L1_LINES 9 // 32KB
`define LOG_L1_WAYS 3 // 8 ways
// LLC
`define LOG_LLC_LINES 14 // 1MB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
`ifdef CACHE_MC_1MB
// L1
`define LOG_L1_LINES 9 // 32KB
`define LOG_L1_WAYS 2 // 4 ways
// LLC
`define LOG_LLC_LINES 14 // 1MB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
`ifdef CACHE_MC_2MB
// L1
`define LOG_L1_LINES 9 // 32KB
`define LOG_L1_WAYS 2 // 4 ways
// LLC
`define LOG_LLC_LINES 15 // 2MB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
//
// ==== CORE SIZE ====
//
`ifdef CORE_TINY
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 48
// speculation
`define NUM_EPOCHS 8
`define NUM_SPEC_TAGS 8
// Smaller L1 TLB
`undef TLB_SIZE
`define TLB_SIZE 16
// LSQ
`define LDQ_SIZE 18
`define STQ_SIZE 10
`define SB_SIZE 2
// reservation station sizes
`define RS_ALU_SIZE 10
`define RS_MEM_SIZE 10
`define RS_FPUMULDIV_SIZE 10
`endif
`ifdef CORE_SMALL
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 64
// speculation
`define NUM_EPOCHS 12
`define NUM_SPEC_TAGS 12
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 16
`endif
`ifdef CORE_MEDIUM
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 80
// speculation
`define NUM_EPOCHS 12
`define NUM_SPEC_TAGS 12
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 16
`endif
`ifdef CORE_SMALL_WIDE
// superscalar
`define sizeSup 4
// ROB
`define ROB_SIZE 64
// speculation
`define NUM_EPOCHS 16
`define NUM_SPEC_TAGS 16
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 8
`define RS_MEM_SIZE 8
`define RS_FPUMULDIV_SIZE 16
`endif
`ifdef CORE_BOOM
// we extend SMALL to match BOOM's ROB and memory latency, we also increase
// spec tags because of increased ROB size
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 80
// speculation
`define NUM_EPOCHS 12
`define NUM_SPEC_TAGS 12
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 16
// change memory latency to 80
`undef DRAM_LATENCY
`define DRAM_LATENCY 80
`endif
`ifdef CORE_LARGE
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 128
// speculation
`define NUM_EPOCHS 16
`define NUM_SPEC_TAGS 32
// LSQ
`define LDQ_SIZE 48
`define STQ_SIZE 28
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 32
`define RS_MEM_SIZE 32
`define RS_FPUMULDIV_SIZE 32
`endif
`ifdef CORE_LARGE_WIDE
// superscalar
`define sizeSup 4
// ROB
`define ROB_SIZE 128
// speculation
`define NUM_EPOCHS 16
`define NUM_SPEC_TAGS 32
// LSQ
`define LDQ_SIZE 48
`define STQ_SIZE 28
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 32
`endif
//
// ==== derived parameters ====
//

View File

@@ -0,0 +1,34 @@
// 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.
`include "ProcConfig.bsv"
import PhysRFile::*;
import SynthParam::*;
typedef RFile#(RFileWrPortNum, RFileRdPortNum) RFileSynth;
(* synthesize *)
module mkRFileSynth(RFileSynth);
let m <- mkRFile(`LAZY_RS_RF);
return m;
endmodule

View File

@@ -0,0 +1,963 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import GetPut::*;
import Cntrs::*;
import Fifo::*;
import FIFO::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import SynthParam::*;
import Performance::*;
import Exec::*;
import FetchStage::*;
import RenamingTable::*;
import ReorderBuffer::*;
import ReorderBufferSynth::*;
import Scoreboard::*;
import ScoreboardSynth::*;
import CsrFile::*;
import SpecTagManager::*;
import EpochManager::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReservationStationMem::*;
import ReservationStationFpuMulDiv::*;
import SplitLSQ::*;
typedef struct {
FetchDebugState fetch;
EpochDebugState epoch;
} RenameStuck deriving(Bits, Eq, FShow);
interface RenameInput;
// func units
interface FetchStage fetchIfc; // just for debug
interface ReorderBufferSynth robIfc;
interface RegRenamingTable rtIfc;
interface ScoreboardCons sbConsIfc;
interface ScoreboardAggr sbAggrIfc;
interface CsrFile csrfIfc;
interface EpochManager emIfc;
interface SpecTagManager smIfc;
interface Vector#(AluExeNum, ReservationStationAlu) rsAluIfc;
interface Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) rsFpuMulDivIfc;
interface ReservationStationMem rsMemIfc;
interface SplitLSQ lsqIfc;
// pending MMIO req from platform
method Bool pendingMMIOPRq;
// record that a CSR inst or interrupt is sent to ROB
method Action issueCsrInstOrInterrupt;
// deadlock check
method Bool checkDeadlock;
// performance
method Bool doStats;
endinterface
interface RenameStage;
// performance count
method Data getPerf(ExeStagePerfType t);
// deadlock check
interface Get#(RenameStuck) renameInstStuck;
interface Get#(RenameStuck) renameCorrectPathStuck;
endinterface
module mkRenameStage#(RenameInput inIfc)(RenameStage);
Bool verbose = True;
// func units
FetchStage fetchStage = inIfc.fetchIfc;
ReorderBufferSynth rob = inIfc.robIfc;
RegRenamingTable regRenamingTable = inIfc.rtIfc;
ScoreboardCons sbCons = inIfc.sbConsIfc;
ScoreboardAggr sbAggr = inIfc.sbAggrIfc;
CsrFile csrf = inIfc.csrfIfc;
EpochManager epochManager = inIfc.emIfc;
SpecTagManager specTagManager = inIfc.smIfc;
Vector#(AluExeNum, ReservationStationAlu) reservationStationAlu = inIfc.rsAluIfc;
Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) reservationStationFpuMulDiv = inIfc.rsFpuMulDivIfc;
ReservationStationMem reservationStationMem = inIfc.rsMemIfc;
SplitLSQ lsq = inIfc.lsqIfc;
// performance counter
`ifdef PERF_COUNT
Count#(Data) supRenameCnt <- mkCount(0);
`ifdef SECURITY
Count#(Data) specNoneCycles <- mkCount(0);
Count#(Data) specNonMemCycles <- mkCount(0);
`endif
`endif
// deadlock check
`ifdef CHECK_DEADLOCK
// timer to check deadlock
Reg#(DeadlockTimer) renameInstTimer <- mkReg(0);
Reg#(DeadlockTimer) renameCorrectPathTimer <- mkReg(0);
// FIFOs to output deadlock info
FIFO#(RenameStuck) renameInstStuckQ <- mkFIFO1;
FIFO#(RenameStuck) renameCorrectPathStuckQ <- mkFIFO1;
// wires to indicate that deadlock is reported, so reset timers
PulseWire renameInstStuckSent <- mkPulseWire;
PulseWire renameCorrectPathStuckSent <- mkPulseWire;
// wires to reset timers since processor is making progress
PulseWire renameWrongPath <- mkPulseWire;
PulseWire renameCorrectPath <- mkPulseWire;
let renameStuck = RenameStuck {
fetch: fetchStage.getFetchState,
epoch: epochManager.getEpochState
};
(* fire_when_enabled *)
rule checkDeadlock_renameInst(inIfc.checkDeadlock && renameInstTimer == maxBound);
renameInstStuckQ.enq(renameStuck);
renameInstStuckSent.send;
endrule
(* fire_when_enabled *)
rule checkDeadlock_renameCorrecPath(inIfc.checkDeadlock && renameCorrectPathTimer == maxBound);
renameCorrectPathStuckQ.enq(renameStuck);
renameCorrectPathStuckSent.send;
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incrDeadlockTimer(inIfc.checkDeadlock);
function DeadlockTimer getNextTimer(DeadlockTimer t);
return t == maxBound ? maxBound : t + 1;
endfunction
renameInstTimer <= (renameCorrectPath || renameWrongPath || renameInstStuckSent) ? 0 : getNextTimer(renameInstTimer);
renameCorrectPathTimer <= (renameCorrectPath || renameCorrectPathStuckSent) ? 0 : getNextTimer(renameCorrectPathTimer);
endrule
`endif
// kill wrong path inst
// XXX we have to make this a separate rule instead of merging it with rename correct path
// This is because the rename correct path rule is conflict with other rules that redirect
// If wrong path inst keeps coming in, the rename rule may only kill wrong path, but blocks the redirect rule
rule doRenaming_wrongPath(
!epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // first wrong path, so at least kill one
);
// we stop when we see a correct path inst
Bool stop = False;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && fetchStage.pipelines[i].canDeq) begin
let x = fetchStage.pipelines[i].first;
if(epochManager.checkEpoch[i].check(x.main_epoch)) begin
// correct path; stop killing
stop = True;
end
else begin
// wrong path, kill it & update prev epoch
fetchStage.pipelines[i].deq;
epochManager.updatePrevEpoch[i].update(x.main_epoch);
if(verbose) $display("[doRenaming - %d] wrong path: pc = %16x", i, x.pc);
end
end
end
`ifdef CHECK_DEADLOCK
renameWrongPath.send;
`endif
endrule
// check for exceptions and interrupts
function Maybe#(Trap) getTrap(FromFetchStage x);
Maybe#(Trap) trap = tagged Invalid;
let csr_state = csrf.decodeInfo;
let pending_interrupt = csrf.pending_interrupt;
let new_exception = checkForException(x.dInst, x.regs, csr_state);
if (isValid(x.cause)) begin
// previously found exception
trap = tagged Valid (tagged Exception fromMaybe(?, x.cause));
end else if (isValid(pending_interrupt)) begin
// pending interrupt
trap = tagged Valid (tagged Interrupt fromMaybe(?, pending_interrupt));
end else if (isValid(new_exception)) begin
// newly found exception
trap = tagged Valid (tagged Exception fromMaybe(?, new_exception));
end
return trap;
endfunction
// trap for first inst to rename
Maybe#(Trap) firstTrap = getTrap(fetchStage.pipelines[0].first);
// XXX Stall renaming till ROB is empty if we need to replay this inst (i.e. system inst)
// This is a rough fix for a bug with the FPU CSR registers.
// i.e. stall when doReplay(inst) is true
function Action incrEpochStallFetch;
action
epochManager.incrementEpoch;
// stall fetch until redirect
fetchStage.setWaitRedirect;
endaction
endfunction
// rename single trap
rule doRenaming_Trap(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& isValid(firstTrap) // take trap
&& rob.isEmpty // stall for ROB empty
);
fetchStage.pipelines[0].deq;
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
if(verbose) $display("[doRenaming] trap: ", fshow(x));
// update prev epoch
epochManager.updatePrevEpoch[0].update(main_epoch);
// Flip epoch without redirecting
// This avoids doing incorrect work
incrEpochStallFetch;
// just place it in the reorder buffer
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: False, // no renaming is done
trap: firstTrap,
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: False,
rob_inst_state: Executed,
lsqTag: ?,
ldKilled: Invalid,
memAccessAtCommit: False,
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: True, // we have incremented epoch
spec_bits: specTagManager.currentSpecBits
};
rob.enqPort[0].enq(y);
// record if we issue an interrupt
if(firstTrap matches tagged Valid (tagged Interrupt .i)) begin
inIfc.issueCsrInstOrInterrupt;
end
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
endrule
// print rename info
function Action printRename(Integer i,
RegsReady regs_ready_cons,
RegsReady regs_ready_aggr,
ArchRegs arch_regs,
PhyRegs phy_regs);
action
$display(" [doRenaming - %d] regs_ready: cons ", i, fshow(regs_ready_cons), " ; aggr ", fshow(regs_ready_aggr));
if (arch_regs.src1 matches tagged Valid .valid_src) begin
if (phy_regs.src1 matches tagged Valid .valid_src_renamed) begin
$display(" [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
end else begin
$display(" [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
doAssert(False, "rename src1 invalid");
end
end
if (arch_regs.src2 matches tagged Valid .valid_src) begin
if (phy_regs.src2 matches tagged Valid .valid_src_renamed) begin
$fdisplay(stdout, " [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
end else begin
$fdisplay(stdout, " [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
doAssert(False, "rename src2 invalid");
end
end
if (arch_regs.src3 matches tagged Valid .valid_src) begin
if (phy_regs.src3 matches tagged Valid .valid_src_renamed) begin
$fdisplay(stdout, " [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
end else begin
$fdisplay(stdout, " [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
doAssert(False, "rename src3 invalid");
end
end
if (arch_regs.dst matches tagged Valid .valid_dst) begin
if (phy_regs.dst matches tagged Valid .valid_dst_renamed) begin
$fdisplay(stdout, " [DST RENAMING] ", fshow(valid_dst), " => ", fshow(valid_dst_renamed));
end else begin
$fdisplay(stdout, " [DST RENAMING] ERROR: ", fshow(valid_dst), " -> INVALID");
doAssert(False, "rename dst invalid");
end
end
endaction
endfunction
// check for system inst that needs to replay
Bool firstReplay = doReplay(fetchStage.pipelines[0].first.dInst.iType);
// System inst is renamed only when ROB is empty
rule doRenaming_SystemInst(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& !isValid(firstTrap) // not trap
&& firstReplay // system inst needs replay
&& rob.isEmpty // stall for ROB empty
);
fetchStage.pipelines[0].deq;
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
if(verbose) $display("[doRenaming] system inst: ", fshow(x));
// update prev epoch
epochManager.updatePrevEpoch[0].update(main_epoch);
// Flip epoch without redirecting. This avoids doing incorrect work
incrEpochStallFetch;
// get spec bits (should be 0), and no need to checkout spec tag
let spec_bits = specTagManager.currentSpecBits;
doAssert(spec_bits == 0, "cannot have spec bits");
// do renaming (renaming is non-speculative)
let rename_result = regRenamingTable.rename[0].getRename(arch_regs);
let phy_regs = rename_result.phy_regs;
regRenamingTable.rename[0].claimRename(arch_regs, spec_bits);
// scoreboard lookup
let regs_ready_cons = sbCons.eagerLookup[0].get(phy_regs);
let regs_ready_aggr = sbAggr.eagerLookup[0].get(phy_regs);
sbCons.setBusy[0].set(phy_regs.dst);
sbAggr.setBusy[0].set(phy_regs.dst);
// print rename info
if (verbose) begin
printRename(0, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
end
// get ROB tag
let inst_tag = rob.enqPort[0].getEnqInstTag;
// CSR inst will be sent to ALU exe pipeline
Bool to_exec = False;
if (dInst.execFunc matches tagged Alu .alu) begin
to_exec = True;
doAssert(dInst.iType == Csr, "only CSR inst send to exe");
end
else begin
doAssert(dInst.iType == FenceI ||
dInst.iType == SFence ||
dInst.iType == Sret ||
dInst.iType == Mret,
"non-CSR inst not send to exe");
doAssert(dInst.execFunc == tagged Other,
"non-exe inst exec func is other");
end
// send to ALU reservation station
if (to_exec) begin
reservationStationAlu[0].enq(ToReservationStation {
data: AluRSData {dInst: dInst, dpTrain: dpTrain},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: Invalid,
regs_ready: regs_ready_aggr // alu will recv bypass
});
end
// send to ROB
Bool will_dirty_fpu_state = False;
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
will_dirty_fpu_state = True;
doAssert(False, "system inst never touches FP regs");
end
RobInstState rob_inst_state = to_exec ? NotDone : Executed;
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state,
lsqTag: ?,
ldKilled: Invalid,
memAccessAtCommit: False,
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: True, // system inst has incremented epoch
spec_bits: spec_bits
};
rob.enqPort[0].enq(y);
// record if we issue an CSR inst
if(dInst.iType == Csr) begin
inIfc.issueCsrInstOrInterrupt;
end
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
endrule
`ifdef SECURITY
// speculation control:
// M mode: turn off speculation for mem inst only
// non-M mode: controlled by mspec CSR
Bool machineMode = csrf.decodeInfo.prv == prvM;
Bool specNone = !machineMode && csrf.rd(CSRmspec) == zeroExtend(mSpecNone);
Bool specNonMem = machineMode || csrf.rd(CSRmspec) == zeroExtend(mSpecNonMem);
`ifdef PERF_COUNT
rule incSpecNoneCycles(inIfc.doStats && specNone);
specNoneCycles.incr(1);
endrule
rule incSpecNonMemCycles(inIfc.doStats && specNonMem);
specNonMemCycles.incr(1);
endrule
`endif
// first inst is mem inst
function Bool isMemInst(ExecFunc f);
return f matches tagged Mem .m ? True : False;
endfunction
Bool firstMem = isMemInst(fetchStage.pipelines[0].first.dInst.execFunc);
// In case speculation is turned off for mem inst only, we rename mem inst
// only when ROB is empty
rule doRenaming_MemInst(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& !isValid(firstTrap) // not trap
&& !firstReplay // not system inst
// turn off speculation for mem inst only, and first inst is mem
&& (specNonMem && firstMem)
&& rob.isEmpty // stall for ROB empty to process mem inst
);
fetchStage.pipelines[0].deq;
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
if(verbose) $display("[doRenaming] mem inst: ", fshow(x));
// update prev epoch
epochManager.updatePrevEpoch[0].update(main_epoch);
// get spec bits (should be 0), and no need to checkout spec tag
let spec_bits = specTagManager.currentSpecBits;
doAssert(spec_bits == 0, "cannot have spec bits");
// do renaming (renaming is non-speculative)
let rename_result = regRenamingTable.rename[0].getRename(arch_regs);
let phy_regs = rename_result.phy_regs;
regRenamingTable.rename[0].claimRename(arch_regs, spec_bits);
// scoreboard lookup
let regs_ready_cons = sbCons.eagerLookup[0].get(phy_regs);
let regs_ready_aggr = sbAggr.eagerLookup[0].get(phy_regs);
sbCons.setBusy[0].set(phy_regs.dst);
sbAggr.setBusy[0].set(phy_regs.dst);
// print rename info
if (verbose) begin
printRename(0, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
end
// get ROB tag
let inst_tag = rob.enqPort[0].getEnqInstTag;
// LSQ tag
LdStQTag lsq_tag = ?;
// send to MEM reservation station
if (dInst.execFunc matches tagged Mem .mem_inst) begin
Bool isLdQ = isLdQMemFunc(mem_inst.mem_func);
Maybe#(LdStQTag) lsqEnqTag = isLdQ ? lsq.enqLdTag : lsq.enqStTag;
if (lsqEnqTag matches tagged Valid .lsqTag) begin
// can process, send to Mem rs and LSQ
lsq_tag = lsqTag; // record LSQ tag
if (dInst.iType != Fence) begin // Fence does not go to RS
reservationStationMem.enq(ToReservationStation {
data: MemRSData {
mem_func: mem_inst.mem_func,
imm: validValue(dInst.imm),
ldstq_tag: lsqTag
},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: Invalid,
regs_ready: regs_ready_aggr // mem currently recv bypass
});
end
doAssert(ppc == pc + 4, "Mem next PC is not PC+4");
doAssert(!isValid(dInst.csr), "Mem never explicitly read/write CSR");
doAssert((dInst.iType != Fence) == isValid(dInst.imm),
"Mem (non-Fence) needs imm for virtual addr");
// put in ldstq
if(isLdQ) begin
lsq.enqLd(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
else begin
lsq.enqSt(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
end
else begin
// cannot process this inst, stall
when(False, noAction);
end
end
else begin
doAssert(False, "Must be mem inst");
end
// send to ROB
Bool will_dirty_fpu_state = False;
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
will_dirty_fpu_state = True;
end
RobInstState rob_inst_state = NotDone; // mem inst always needs execution
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state,
lsqTag: lsq_tag,
ldKilled: Invalid,
memAccessAtCommit: False, // set by ROB in case of fence
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: False,
spec_bits: spec_bits
};
rob.enqPort[0].enq(y);
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
endrule
`endif
// Count based scheduling in case of $n$ RS for the same inst type. We
// assume all such RS are of the same size, and prioritize RS with smaller
// valid (occupied) entries.
function Maybe#(idxT) scheduleRS(
Vector#(n, countT) valid_cnt, Vector#(n, Bool) rdy
) provisos(
Ord#(countT), Alias#(idxT, Bit#(TLog#(n))), Add#(1, a__, n)
);
function Bit#(TLog#(n)) getRS(idxT a, idxT b);
if(!rdy[a]) begin
return b;
end
else if(!rdy[b]) begin
return a;
end
else begin
// prioritize RS with smaller valid-entry count
return valid_cnt[a] < valid_cnt[b] ? a : b;
end
endfunction
Vector#(n, idxT) idxVec = genWith(fromInteger);
idxT idx = fold(getRS, idxVec);
return rdy[idx] ? Valid (idx) : Invalid;
endfunction
// rename correct path inst
rule doRenaming(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& !isValid(firstTrap) // not trap
&& !firstReplay // not system inst
`ifdef SECURITY
// stall for ROB empty if we don't allow speculation at all
&& (!specNone || rob.isEmpty)
// don't process mem inst if we don't allow speculation for mem inst only
&& !(specNonMem && firstMem)
`endif
);
// we stop superscalar rename when an instruction cannot be processed:
// (a) It has trap
// (b) It is wrong path
// (c) It is system inst (we handle system inst in a separate rule)
// (d) It does not have enough resource
Bool stop = False;
// We automatically stop after an inst cannot be deq from fetch stage
// because canDeq signal for sup-fifo is consecutive
// Note that epoch will not change in this rule
// track limited resource usage
Vector#(AluExeNum, Bool) aluExeUsed = replicate(False);
Vector#(FpuMulDivExeNum, Bool) fpuMulDivExeUsed = replicate(False);
Bool memExeUsed = False;
Bool specTagClaimed = False; // specTagManager
// track rename activity
Bool doCorrectPath = False;
SupCnt renameCnt = 0;
// initial spec bits at the beginning of this cycle
// we may update it during the processing
SpecBits spec_bits = specTagManager.currentSpecBits;
// ALU RS valid counts
Vector#(AluExeNum, Bit#(TLog#(TAdd#(`RS_ALU_SIZE, 1)))) aluRSCount;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
aluRSCount[i] = reservationStationAlu[i].approximateCount;
end
// FPU/MUL/DIV RS valid counts
Vector#(FpuMulDivExeNum, Bit#(TLog#(TAdd#(`RS_FPUMULDIV_SIZE, 1)))) fpuMulDivRSCount;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
fpuMulDivRSCount[i] = reservationStationFpuMulDiv[i].approximateCount;
end
// We apply actions at the end of each iteration
// We **cannot** apply actions at the end of rule,
// because intermediate iterations may change state
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && fetchStage.pipelines[i].canDeq) begin
let x = fetchStage.pipelines[i].first; // don't deq now, inst may not have resource
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
// check for wrong path, if wrong path, don't process it, leave to the other rule in next cycle
if(!epochManager.checkEpoch[i].check(main_epoch)) begin
stop = True;
end
// for correct path
// check ROB can be enq, otherwise cannot process
if(!rob.enqPort[i].canEnq) begin
stop = True;
end
// check trap, if trap, cannot process, leave to the other rule in next cycle
if(isValid(getTrap(x))) begin
stop = True;
end
// for system inst, process in next cycle (in a different rule)
if(doReplay(dInst.iType)) begin
stop = True;
end
`ifdef SECURITY
// When speculation is not allowed at all, the second inst
// cannot be processed
if(specNone && i != 0) begin
stop = True;
end
// When speculation is not allowed for mem inst only, stop when
// we seen any mem inst
if(specNonMem && isMemInst(dInst.execFunc)) begin
stop = True;
end
`endif
// check renaming table can be enq, otherwise cannot process now
if(!regRenamingTable.rename[i].canRename) begin
stop = True;
end
// Figure out if there is new speculation and if there is
// speculative renaming happening.
Bool new_speculation = False;
Bool speculative_renaming = False; // deprecated: this is originally for Ld/St checkpoint
if (dInst.execFunc matches tagged Br .br) begin
// This instruction can cause a redirection due to branch
// misprediction. Lets claim a checkpoint for this instruction.
// If this instruction is a JAL or JRAL instruction, then the
// checkpoint should include the renaming for the destination
// register.
new_speculation = True;
speculative_renaming = False;
end
// if need spec tag, check spec tag is available, otherwise cannot process
if(new_speculation && (specTagClaimed || !specTagManager.canClaim)) begin
stop = True;
end
if(!stop) begin
// we can continue to analyze this inst
// Claim a speculation tag from the specTagManager if necessary
Maybe#(SpecTag) spec_tag = tagged Invalid;
if (new_speculation) begin
spec_tag = tagged Valid specTagManager.nextSpecTag;
end
// get renaming
// If the renaming is speculative, then the renaming will
// depend on the current spec_tag too.
let renaming_spec_bits = spec_bits | (speculative_renaming ? (1 << fromMaybe(?,spec_tag)) : 0);
let rename_result = regRenamingTable.rename[i].getRename(arch_regs);
let phy_regs = rename_result.phy_regs;
// scoreboard lookup
let regs_ready_cons = sbCons.eagerLookup[i].get(phy_regs);
let regs_ready_aggr = sbAggr.eagerLookup[i].get(phy_regs);
// get ROB tag
let inst_tag = rob.enqPort[i].getEnqInstTag;
// LSQ tag
LdStQTag lsq_tag = ?;
// check execution pipelines availability
// this determines whether this inst can finally be processed
// so we will directly take actions on exe pipelines
Bool to_exec = False;
Bool to_mem = False;
Bool to_FpuMulDiv = False;
case (dInst.execFunc) matches
tagged Alu .alu: to_exec = True;
tagged Br .br: to_exec = True;
tagged MulDiv .muldiv: to_FpuMulDiv = True;
tagged Fpu .fpu: to_FpuMulDiv = True;
tagged Mem .mem: to_mem = True;
default:
// no need for execution, directly become Executed
noAction;
endcase
if (to_exec) begin
// find an ALU pipeline
function Bool aluValid(Integer k) = !aluExeUsed[k] && reservationStationAlu[k].canEnq;
Vector#(AluExeNum, Bool) aluReady = map(aluValid, genVector);
if(scheduleRS(aluRSCount, aluReady) matches tagged Valid .k) begin
// can process, send to ALU rs
aluExeUsed[k] = True; // mark resource used
reservationStationAlu[k].enq(ToReservationStation {
data: AluRSData {dInst: dInst, dpTrain: dpTrain},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: spec_tag,
regs_ready: regs_ready_aggr // alu will recv bypass
});
end
else begin
// cannot process this inst, stop
stop = True;
end
end
else if (to_FpuMulDiv) begin
function Bool fpuMulDivValid(Integer k) = !fpuMulDivExeUsed[k] && reservationStationFpuMulDiv[k].canEnq;
Vector#(FpuMulDivExeNum, Bool) fpuMulDivReady = map(fpuMulDivValid, genVector);
if(scheduleRS(fpuMulDivRSCount, fpuMulDivReady) matches tagged Valid .k) begin
// can process, send to FPU MUL DIV rs
fpuMulDivExeUsed[k] = True; // mark resource used
reservationStationFpuMulDiv[k].enq(ToReservationStation {
data: FpuMulDivRSData {execFunc: dInst.execFunc},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: spec_tag,
regs_ready: regs_ready_aggr // fpu mul div recv bypass
});
doAssert(ppc == pc + 4, "FpuMulDiv next PC is not PC+4");
doAssert(!isValid(dInst.csr), "FpuMulDiv never explicitly read/write CSR");
doAssert(!isValid(spec_tag), "should not have spec tag");
end
else begin
// cannot process this inst, stop
stop = True;
end
end
else if (to_mem) begin
if (dInst.execFunc matches tagged Mem .mem_inst) begin
Bool isLdQ = isLdQMemFunc(mem_inst.mem_func);
Maybe#(LdStQTag) lsqEnqTag = isLdQ ? lsq.enqLdTag : lsq.enqStTag;
if (!memExeUsed &&& reservationStationMem.canEnq &&&
lsqEnqTag matches tagged Valid .lsqTag) begin
// can process, send to Mem rs and LSQ
memExeUsed = True; // mark resource used
lsq_tag = lsqTag; // record LSQ tag
if (dInst.iType != Fence) begin // fence does not go to RS
reservationStationMem.enq(ToReservationStation {
data: MemRSData {
mem_func: mem_inst.mem_func,
imm: validValue(dInst.imm),
ldstq_tag: lsqTag
},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: spec_tag,
regs_ready: regs_ready_aggr // mem currently recv bypass
});
end
doAssert(ppc == pc + 4, "Mem next PC is not PC+4");
doAssert(!isValid(dInst.csr), "Mem never explicitly read/write CSR");
doAssert((dInst.iType != Fence) == isValid(dInst.imm),
"Mem (non-Fence) needs imm for virtual addr");
doAssert(!isValid(spec_tag), "should not have spec tag");
// put in ldstq
if(isLdQ) begin
lsq.enqLd(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
else begin
lsq.enqSt(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
end
else begin
// cannot process this inst, stop
stop = True;
end
end
else begin
stop = True;
doAssert(False, "non memory instruction has to_mem == True");
end
end
// apply remaining actions if inst can be processed
if(!stop) begin
if(verbose) $display("[doRenaming - %d] ", i, fshow(x));
// deq fetch & update epochs match
fetchStage.pipelines[i].deq;
epochManager.updatePrevEpoch[i].update(main_epoch);
// Claim a speculation tag
if (new_speculation) begin
specTagClaimed = True; // mark resource used
specTagManager.claimSpecTag;
end
// Do renaming
regRenamingTable.rename[i].claimRename(arch_regs, renaming_spec_bits);
// Scoreboard Operations
sbCons.setBusy[i].set(phy_regs.dst);
sbAggr.setBusy[i].set(phy_regs.dst);
// display information
if (verbose) begin
printRename(i, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
end
// Enqueue into reorder buffer
Bool will_dirty_fpu_state = False;
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
will_dirty_fpu_state = True;
end
RobInstState rob_inst_state = (to_exec || to_mem || to_FpuMulDiv) ? NotDone : Executed;
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state,
lsqTag: lsq_tag,
ldKilled: Invalid,
memAccessAtCommit: False, // set by ROB in case of fence
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: False,
spec_bits: spec_bits
};
rob.enqPort[i].enq(y);
// record activity
doCorrectPath = True;
renameCnt = renameCnt + 1;
// update spec bits if spec tag is claimed
if(spec_tag matches tagged Valid .t) begin
spec_bits = spec_bits | (1 << t);
end
end
end
end
end
// only fire this rule if we make some progress
// otherwise this rule may block other rules forever
when(doCorrectPath, noAction);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
if(renameCnt > 1) begin
supRenameCnt.incr(1);
end
end
`endif
`ifdef CHECK_DEADLOCK
if(doCorrectPath) begin
renameCorrectPath.send;
end
`endif
endrule
`ifdef CHECK_DEADLOCK
interface renameInstStuck = toGet(renameInstStuckQ);
interface renameCorrectPathStuck = toGet(renameCorrectPathStuckQ);
`else
interface renameInstStuck = nullGet;
interface renameCorrectPathStuck = nullGet;
`endif
method Data getPerf(ExeStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
SupRenameCnt: supRenameCnt;
`ifdef SECURITY
SpecNoneCycles: specNoneCycles;
SpecNonMemCycles: specNoneCycles;
`endif
`endif
default: 0;
endcase);
endmethod
endmodule

View File

@@ -0,0 +1,44 @@
// 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.
`include "ProcConfig.bsv"
import ReorderBuffer::*;
import SynthParam::*;
typedef ReorderBufferRowEhr#(AluExeNum, FpuMulDivExeNum) RobRowSynth;
(* synthesize *)
module mkRobRowSynth(RobRowSynth);
let m <- mkReorderBufferRowEhr;
return m;
endmodule
// use superscalar ROB
typedef SupReorderBuffer#(AluExeNum, FpuMulDivExeNum) ReorderBufferSynth;
(* synthesize *)
module mkReorderBufferSynth(ReorderBufferSynth);
let m <- mkSupReorderBuffer(`ROB_LAZY_ENQ, mkRobRowSynth);
return m;
endmodule

View File

@@ -0,0 +1,43 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import ReservationStationEhr::*;
import SynthParam::*;
import BrPred::*;
import DirPredictor::*;
typedef struct {
DecodedInst dInst;
DirPredTrainInfo dpTrain;
} AluRSData deriving(Bits, Eq, FShow);
// ALU pipeline is aggressive, i.e. it recv bypass and early RS wakeup
typedef ReservationStation#(`RS_ALU_SIZE, WrAggrPortNum, AluRSData) ReservationStationAlu;
(* synthesize *)
module mkReservationStationAlu(ReservationStationAlu);
ReservationStationAlu m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, valueof(AluExeNum) > 1);
return m;
endmodule

View File

@@ -0,0 +1,40 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import ReservationStationEhr::*;
import SynthParam::*;
typedef struct {
ExecFunc execFunc;
} FpuMulDivRSData deriving(Bits, Eq, FShow);
// FPU MUL DIV pipeline is aggressive, i.e. it recv bypass and early RS wakeup
typedef ReservationStation#(`RS_FPUMULDIV_SIZE, WrAggrPortNum, FpuMulDivRSData) ReservationStationFpuMulDiv;
(* synthesize *)
module mkReservationStationFpuMulDiv(ReservationStationFpuMulDiv);
let m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, valueof(FpuMulDivExeNum) > 1);
return m;
endmodule

View File

@@ -0,0 +1,42 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import ReservationStationEhr::*;
import SynthParam::*;
typedef struct {
MemFunc mem_func;
ImmData imm;
LdStQTag ldstq_tag;
} MemRSData deriving(Bits, Eq, FShow);
// MEM pipeline is aggressive, i.e. it recv bypass and early RS wakeup
typedef ReservationStation#(`RS_MEM_SIZE, WrAggrPortNum, MemRSData) ReservationStationMem;
(* synthesize *)
module mkReservationStationMem(ReservationStationMem);
let m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, False);
return m;
endmodule

View File

@@ -0,0 +1,42 @@
// 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.
import Scoreboard::*;
import SynthParam::*;
// conservative sb
typedef RenamingScoreboard#(WrConsPortNum, SbLazyLookupPortNum) ScoreboardCons;
(* synthesize *)
module mkScoreboardCons(ScoreboardCons);
let m <- mkRenamingScoreboard;
return m;
endmodule
// aggressive sb
typedef RenamingScoreboard#(WrAggrPortNum, 0) ScoreboardAggr;
(* synthesize *)
module mkScoreboardAggr(ScoreboardAggr);
let m <- mkRenamingScoreboard;
return m;
endmodule

View File

@@ -0,0 +1,77 @@
// 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.
import DefaultValue::*;
import Types::*;
import ProcTypes::*;
import HasSpecBits::*;
import ReservationStationEhr::*;
// ALU exe num = superscalar size
// FPU exe num = superscalar size / 2
typedef SupSize AluExeNum;
typedef TDiv#(SupSize, 2) FpuMulDivExeNum;
// Phy RFile
// write: Alu < FpuMulDiv < Mem
// read: Alu, FpuMulDiv, Mem
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
// sb lazy lookup num: same as RFile read, becaues all pipelines recv bypass
typedef RFileRdPortNum SbLazyLookupPortNum;
// ports for writing conservative elements
// i.e. write rf & set conservative sb & wake up rs in conservative pipeline (i.e. not recv bypass)
// ordering: Alu < FpuMulDiv < Mem
typedef RFileWrPortNum WrConsPortNum;
function Integer aluWrConsPort(Integer i) = i;
function Integer fpuMulDivWrConsPort(Integer i) = valueof(AluExeNum) + i;
Integer memWrConsPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
// ports for writing aggressive elements
// i.e. set aggressive sb & wake up rs in aggressive pipeline (i.e. recv bypass)
// Mem pipeline has two places: mem resp and forwarding. Since the rule
// containing forwarding calls lsq.issue, and the rule containing mem resp
// calls lsq.wakeupLd, we order forward before mem.
// Ordering: Alu < FpuMulDiv < Forward < Mem
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) WrAggrPortNum;
function Integer aluWrAggrPort(Integer i) = i;
function Integer fpuMulDivWrAggrPort(Integer i) = valueof(AluExeNum) + i;
Integer forwardWrAggrPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
Integer memWrAggrPort = 1 + valueof(FpuMulDivExeNum) + valueof(AluExeNum);
// ports for read rf & lazy lookup in sb, ordering doesn't matter
function Integer aluRdPort(Integer i) = i;
function Integer fpuMulDivRdPort(Integer i) = valueof(AluExeNum) + i;
Integer memRdPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
// ports for correct spec, ordering doesn't matter
typedef TAdd#(2, AluExeNum) CorrectSpecPortNum;
function Integer finishAluCorrectSpecPort(Integer i) = i;
Integer deqLSQCorrectSpecPort = valueof(AluExeNum);
Integer finishMemCorrectSpecPort = 1 + valueof(AluExeNum);
// ports for manual conflict with wrong spec, ordering doesn't matter
typedef FpuMulDivExeNum ConflictWrongSpecPortNum;
function Integer finishFpuMulDivConflictWrongSpecPort(Integer i) = i;

View File

@@ -0,0 +1,84 @@
// 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.
import Types::*;
import ProcTypes::*;
(* noinline *)
function Data amoExec(AmoInst amo_inst, Data current_data, Data in_data, Bool upper_32_bits);
// upper_32_bits is true if the operation is a 32-bit operation and the
// address is the upper 32-bits of a 64-bit region.
Data new_data = 0;
Data old_data = current_data;
if (!amo_inst.doubleWord) begin
if (!upper_32_bits) begin
current_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(current_data[31:0]) : zeroExtend(current_data[31:0]);
in_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(in_data[31:0]) : zeroExtend(in_data[31:0]);
end else begin
// use upper 32-bits instead
current_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(current_data[63:32]) : zeroExtend(current_data[63:32]);
in_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(in_data[31:0]) : zeroExtend(in_data[31:0]);
end
end
case (amo_inst.func)
Swap: new_data = in_data;
Add: new_data = current_data + in_data;
Xor: new_data = current_data ^ in_data;
And: new_data = current_data & in_data;
Or: new_data = current_data | in_data;
Min: new_data = sMin(current_data, in_data);
Max: new_data = sMax(current_data, in_data);
Minu: new_data = uMin(current_data, in_data);
Maxu: new_data = uMax(current_data, in_data);
endcase
if (!amo_inst.doubleWord) begin
if (!upper_32_bits) begin
return {old_data[63:32], new_data[31:0]};
end else begin
// change upper 32-bits instead
return {new_data[31:0], old_data[31:0]};
end
end else begin
return new_data;
end
endfunction
function Bit#(t) sMax( Bit#(t) a, Bit#(t) b );
Int#(t) x = max(unpack(a), unpack(b));
return pack(x);
endfunction
function Bit#(t) sMin( Bit#(t) a, Bit#(t) b );
Int#(t) x = min(unpack(a), unpack(b));
return pack(x);
endfunction
function Bit#(t) uMax( Bit#(t) a, Bit#(t) b );
UInt#(t) x = max(unpack(a), unpack(b));
return pack(x);
endfunction
function Bit#(t) uMin( Bit#(t) a, Bit#(t) b );
UInt#(t) x = min(unpack(a), unpack(b));
return pack(x);
endfunction

View File

@@ -0,0 +1,81 @@
// 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.
import Types::*;
import ProcTypes::*;
import RegFile::*;
import Vector::*;
import BrPred::*;
export BhtTrainInfo;
export mkBht;
typedef Bit#(0) BhtTrainInfo; // no training info needs to be remembered
// Local BHT Typedefs
typedef 128 BhtEntries;
typedef Bit#(TLog#(BhtEntries)) BhtIndex;
(* synthesize *)
module mkBht(DirPredictor#(BhtTrainInfo));
// Read and Write ordering doesn't matter since this is a predictor
// mkRegFileWCF is the RegFile version of mkConfigReg
RegFile#(BhtIndex, Bit#(2)) hist <- mkRegFileWCF(0,fromInteger(valueOf(BhtEntries)-1));
function BhtIndex getIndex(Addr pc);
return truncate(pc >> 2);
endfunction
Vector#(SupSize, DirPred#(BhtTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(BhtTrainInfo)) pred(Addr pc);
let index = getIndex(pc);
Bit#(2) cnt = hist.sub(index);
Bool taken = cnt[1] == 1;
return DirPredResult {
taken: taken,
train: 0
};
endmethod
endinterface);
end
interface pred = predIfc;
method Action update(Addr pc, Bool taken, BhtTrainInfo train, Bool mispred);
let index = getIndex(pc);
let current_hist = hist.sub(index);
Bit#(2) next_hist;
if(taken) begin
next_hist = (current_hist == 2'b11) ? 2'b11 : current_hist + 1;
end else begin
next_hist = (current_hist == 2'b00) ? 2'b00 : current_hist - 1;
end
hist.upd(index, next_hist);
endmethod
method flush = noAction;
method flush_done = True;
endmodule

View File

@@ -0,0 +1,68 @@
// 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.
import Types::*;
import ProcTypes::*;
import Vector::*;
(* noinline *)
function Maybe#(Addr) decodeBrPred( Addr pc, DecodedInst dInst, Bool histTaken );
Addr pcPlus4 = pc + 4;
Data imm_val = fromMaybe(?, getDInstImm(dInst));
Maybe#(Addr) nextPc = tagged Invalid;
if( dInst.iType == J ) begin
Addr jTarget = pc + imm_val;
nextPc = tagged Valid jTarget;
end else if( dInst.iType == Br ) begin
if( histTaken ) begin
nextPc = tagged Valid (pc + imm_val);
end else begin
nextPc = tagged Valid pcPlus4;
end
end else if( dInst.iType == Jr ) begin
// target is unknown until RegFetch
nextPc = tagged Invalid;
end else begin
nextPc = tagged Valid pcPlus4;
end
return nextPc;
endfunction
// general types for direction predictor
typedef struct {
Bool taken;
trainInfoT train; // info that a branch must keep for future training
} DirPredResult#(type trainInfoT) deriving(Bits, Eq, FShow);
interface DirPred#(type trainInfoT);
method ActionValue#(DirPredResult#(trainInfoT)) pred(Addr pc);
endinterface
interface DirPredictor#(type trainInfoT);
interface Vector#(SupSize, DirPred#(trainInfoT)) pred;
method Action update(Addr pc, Bool taken, trainInfoT train, Bool mispred);
method Action flush;
method Bool flush_done;
endinterface

View File

@@ -0,0 +1,121 @@
// 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.
import Types::*;
import ProcTypes::*;
import ConfigReg::*;
import RegFile::*;
import Vector::*;
export NextAddrPred(..);
export mkBtb;
interface NextAddrPred;
method Addr predPc(Addr pc);
method Action update(Addr pc, Addr nextPc, Bool taken);
// security
method Action flush;
method Bool flush_done;
endinterface
// Local BTB Typedefs
typedef 256 BtbEntries; // 4KB BTB
typedef Bit#(TLog#(BtbEntries)) BtbIndex;
typedef Bit#(TSub#(TSub#(AddrSz, TLog#(BtbEntries)), 2)) BtbTag;
typedef struct {
Addr pc;
Addr nextPc;
Bool taken;
} BtbUpdate deriving(Bits, Eq, FShow);
// No synthesize boundary because we need to call predPC several times
module mkBtb(NextAddrPred);
// Read and Write ordering doesn't matter since this is a predictor
// mkRegFileWCF is the RegFile version of mkConfigReg
RegFile#(BtbIndex, Addr) next_addrs <- mkRegFileWCF(0,fromInteger(valueOf(BtbEntries)-1));
RegFile#(BtbIndex, BtbTag) tags <- mkRegFileWCF(0,fromInteger(valueOf(BtbEntries)-1));
Vector#(BtbEntries, Reg#(Bool)) valid <- replicateM(mkConfigReg(False));
RWire#(BtbUpdate) updateEn <- mkRWire;
`ifdef SECURITY
Reg#(Bool) flushDone <- mkReg(True);
`else
Bool flushDone = True;
`endif
function BtbIndex getIndex(Addr pc) = truncate(pc >> 2);
function BtbTag getTag(Addr pc) = truncateLSB(pc);
// no flush, accept update
(* fire_when_enabled, no_implicit_conditions *)
rule canonUpdate(flushDone &&& updateEn.wget matches tagged Valid .upd);
let pc = upd.pc;
let nextPc = upd.nextPc;
let taken = upd.taken;
let index = getIndex(pc);
let tag = getTag(pc);
if(taken) begin
valid[index] <= True;
tags.upd(index, tag);
next_addrs.upd(index, nextPc);
end else if( tags.sub(index) == tag ) begin
// current instruction has target in btb, so clear it
valid[index] <= False;
end
endrule
`ifdef SECURITY
// flush, clear everything (and drop update)
rule doFlush(!flushDone);
writeVReg(valid, replicate(False));
flushDone <= True;
endrule
`endif
method Addr predPc(Addr pc);
BtbIndex index = getIndex(pc);
BtbTag tag = getTag(pc);
if(valid[index] && tag == tags.sub(index))
return next_addrs.sub(index);
else
return (pc + 4);
endmethod
method Action update(Addr pc, Addr nextPc, Bool taken);
updateEn.wset(BtbUpdate {pc: pc, nextPc: nextPc, taken: taken});
endmethod
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
endmodule

View File

@@ -0,0 +1,75 @@
// 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.
import Types::*;
import ProcTypes::*;
import Vector::*;
interface SendBypass;
method Action send(PhyRIndx dst, Data data);
endinterface
interface RecvBypass;
method Action recv(PhyRIndx dst, Data data);
endinterface
function ActionValue#(Data) readRFBypass(
PhyRIndx src,
Bool rfReady, Data rfData,
Vector#(n, RWire#(Tuple2#(PhyRIndx, Data))) bypassWire
);
actionvalue
if(rfReady) begin
return rfData;
end
else begin
// search through all bypass
function Maybe#(Data) getBypassData(RWire#(Tuple2#(PhyRIndx, Data)) w);
if(w.wget matches tagged Valid {.dst, .d} &&& dst == src) begin
return Valid (d);
end
else begin
return Invalid;
end
endfunction
Vector#(n, Maybe#(Data)) bypassData = map(getBypassData, bypassWire);
if(find(isValid, bypassData) matches tagged Valid .b) begin
doAssert(isValid(b), "bypass found must be valid");
return validValue(b);
end
else begin
when(False, noAction);
return ?;
end
end
endactionvalue
endfunction
function RecvBypass getRecvBypassIfc(RWire#(Tuple2#(PhyRIndx, Data)) w);
return (interface RecvBypass;
method Action recv(PhyRIndx dst, Data data);
w.wset(tuple2(dst, data));
endmethod
endinterface);
endfunction

View File

@@ -0,0 +1,153 @@
// 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.
`include "ProcConfig.bsv"
import BRAM::*;
import Types::*;
import MemoryTypes::*;
import GetPut::*;
import ClientServer::*;
import Connectable::*;
import Vector::*;
import Fifo::*;
import Ehr::*;
import FIFO::*;
import FIFOF::*;
import Performance::*;
import FShow::*;
import MsgFifo::*;
// 64B cache line
typedef 8 CLineNumData;
typedef TLog#(CLineNumData) LogCLineNumData;
typedef Bit#(LogCLineNumData) CLineDataSel;
function CLineDataSel getCLineDataSel(Addr a);
return truncate(a >> valueOf(TLog#(NumBytes)));
endfunction
typedef TMul#(CLineNumData, DataSz) CacheLineSz;
typedef Bit#(CacheLineSz) CacheLine;
typedef TMul#(CLineNumData, NumBytes) CLineNumBytes;
typedef TLog#(CLineNumBytes) LogCLineNumBytes;
typedef Vector#(CLineNumBytes, Bool) CLineByteEn;
function Bool isCLineAlignAddr(Addr a);
Bit#(LogCLineNumBytes) offset = truncate(a);
return offset == 0;
endfunction
// cache line addr (drop the offset within cache line)
typedef TSub#(AddrSz, LogCLineNumBytes) CLineAddrSz;
typedef Bit#(CLineAddrSz) CLineAddr;
// cache line v.s. instruction
typedef TDiv#(CacheLineSz, InstSz) CLineNumInst;
typedef Bit#(TLog#(CLineNumInst)) CLineInstSel;
function CLineInstSel getCLineInstSel(Addr a);
return truncate(a >> valueof(TLog#(TDiv#(InstSz, 8))));
endfunction
// FIFO enq/deq ifc
interface FifoEnq#(type t);
method Bool notFull;
method Action enq(t x);
endinterface
function FifoEnq#(t) toFifoEnq(Fifo#(n, t) f);
return (interface FifoEnq;
method notFull = f.notFull;
method enq = f.enq;
endinterface);
endfunction
function FifoEnq#(t) nullFifoEnq;
return (interface FifoEnq;
method Bool notFull = True;
method Action enq(t x);
noAction;
endmethod
endinterface);
endfunction
instance ToPut#(FifoEnq#(t), t);
function Put#(t) toPut(FifoEnq#(t) ifc);
return (interface Put;
method Action put(t x);
ifc.enq(x);
endmethod
endinterface);
endfunction
endinstance
interface FifoDeq#(type t);
method Bool notEmpty;
method Action deq;
method t first;
endinterface
function FifoDeq#(t) toFifoDeq(Fifo#(n, t) f);
return (interface FifoDeq;
method notEmpty = f.notEmpty;
method deq = f.deq;
method first = f.first;
endinterface);
endfunction
function FifoDeq#(t) nullFifoDeq;
return (interface FifoDeq;
method Bool notEmpty = False;
method Action deq if(False);
noAction;
endmethod
method t first if(False);
return ?;
endmethod
endinterface);
endfunction
instance ToGet#(FifoDeq#(t), t);
function Get#(t) toGet(FifoDeq#(t) ifc);
return (interface Get;
method ActionValue#(t) get;
ifc.deq;
return ifc.first;
endmethod
endinterface);
endfunction
endinstance
instance Connectable#(FifoEnq#(t), FifoDeq#(t));
module mkConnection#(FifoEnq#(t) enq, FifoDeq#(t) deq)(Empty);
mkConnection(toGet(deq), toPut(enq));
endmodule
endinstance
instance Connectable#(FifoDeq#(t), FifoEnq#(t));
module mkConnection#(FifoDeq#(t) deq, FifoEnq#(t) enq)(Empty);
mkConnection(toGet(deq), toPut(enq));
endmodule
endinstance

View File

@@ -0,0 +1,506 @@
// This file was created by gen_ConcatReg.py. If you want to modify this file,
// please modify gen_ConcatReg.py instead. If you need a wider concatReg
// function, change the value of n in gen_ConcatReg.py and run it again.
// The Bluespec provided BuildVector.bsv provides another example of
// constructing a function that takes a variable number of arguments
// Typeclass for creating _concatReg with a variable number of arguments.
typeclass ConcatReg#(type r, numeric type n1, numeric type n2)
dependencies ((r,n1) determines n2, (r,n2) determines n1);
// dependencies (r determines (n1,n2));
function r _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
endtypeclass
// Base case
instance ConcatReg#(Reg#(Bit#(n3)), n1, n2) provisos (Add#(n1, n2, n3));
function Reg#(Bit#(TAdd#(n1,n2))) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
return (interface Reg;
method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read};
method Action _write(Bit#(TAdd#(n1,n2)) x);
r1._write(truncateLSB(x));
r2._write(truncate(x));
endmethod
endinterface);
endfunction
endinstance
// Recursion
instance ConcatReg#(function r f(Reg#(Bit#(n3)) r3), n1, n2) provisos (ConcatReg#(r, TAdd#(n1, n2), n3));
function function r f(Reg#(Bit#(n3)) r3) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
return _concatReg(interface Reg;
method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read};
method Action _write(Bit#(TAdd#(n1,n2)) x);
r1._write(truncateLSB(x));
r2._write(truncate(x));
endmethod
endinterface);
endfunction
endinstance
// Wrapper function for users. This can take a variable number of arguments.
// You will need to use asReg() for the third argument and beyond.
function r concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2) provisos(ConcatReg#(r, n1, n2));
return _concatReg(asReg(r1),asReg(r2));
endfunction
// Automatically generated macros with a set number of registers.
// These don't require asReg when used.
function Reg#(Bit#(n)) concatReg2(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2
) provisos (
Add#(n1,n2,n)
);
return concatReg(asReg(r1),asReg(r2));
endfunction
function Reg#(Bit#(n)) concatReg3(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3
) provisos (
Add#(TAdd#(n1,n2),n3,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3));
endfunction
function Reg#(Bit#(n)) concatReg4(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4
) provisos (
Add#(TAdd#(TAdd#(n1,n2),n3),n4,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4));
endfunction
function Reg#(Bit#(n)) concatReg5(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5
) provisos (
Add#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5));
endfunction
function Reg#(Bit#(n)) concatReg6(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6));
endfunction
function Reg#(Bit#(n)) concatReg7(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7));
endfunction
function Reg#(Bit#(n)) concatReg8(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8));
endfunction
function Reg#(Bit#(n)) concatReg9(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9));
endfunction
function Reg#(Bit#(n)) concatReg10(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10));
endfunction
function Reg#(Bit#(n)) concatReg11(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11));
endfunction
function Reg#(Bit#(n)) concatReg12(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12));
endfunction
function Reg#(Bit#(n)) concatReg13(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13));
endfunction
function Reg#(Bit#(n)) concatReg14(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14));
endfunction
function Reg#(Bit#(n)) concatReg15(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15));
endfunction
function Reg#(Bit#(n)) concatReg16(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16));
endfunction
function Reg#(Bit#(n)) concatReg17(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17));
endfunction
function Reg#(Bit#(n)) concatReg18(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18));
endfunction
function Reg#(Bit#(n)) concatReg19(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19));
endfunction
function Reg#(Bit#(n)) concatReg20(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20));
endfunction
function Reg#(Bit#(n)) concatReg21(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21));
endfunction
function Reg#(Bit#(n)) concatReg22(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21,
Reg#(Bit#(n22)) r22
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22));
endfunction
function Reg#(Bit#(n)) concatReg23(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21,
Reg#(Bit#(n22)) r22,
Reg#(Bit#(n23)) r23
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23));
endfunction
function Reg#(Bit#(n)) concatReg24(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21,
Reg#(Bit#(n22)) r22,
Reg#(Bit#(n23)) r23,
Reg#(Bit#(n24)) r24
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23),n24,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23),asReg(r24));
endfunction

View File

@@ -0,0 +1,606 @@
// 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.
`include "ProcConfig.bsv"
import ClientServer::*;
import DefaultValue::*;
import GetPut::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import Performance::*;
import FullAssocTlb::*;
import ConfigReg::*;
import Fifo::*;
import Cntrs::*;
import SafeCounter::*;
import CacheUtils::*;
import LatencyTimer::*;
import HasSpecBits::*;
import Vector::*;
import Ehr::*;
export DTlbReq(..);
export DTlbResp(..);
export DTlbRqToP(..);
export DTlbTransRsFromP(..);
export DTlbToParent(..);
export DTlb(..);
export mkDTlb;
typedef `L1_TLB_SIZE DTlbSize;
// req & resp with core
// D TLB also keeps the information of the requesting inst, so we don't need
// extra bookkeeping outside D TLB.
typedef struct {
instT inst;
SpecBits specBits;
} DTlbReq#(type instT) deriving(Bits, Eq, FShow);
typedef struct {
TlbResp resp;
instT inst;
SpecBits specBits;
} DTlbResp#(type instT) deriving(Bits, Eq, FShow);
// req & resp with L2 TLB
typedef struct {
Vpn vpn;
DTlbReqIdx id;
} DTlbRqToP deriving(Bits, Eq, FShow);
typedef struct {
// may get page fault: i.e. hit invalid page or
// get non-leaf page at last-level page table
Maybe#(TlbEntry) entry;
DTlbReqIdx id;
} DTlbTransRsFromP deriving(Bits, Eq, FShow);
interface DTlbToParent;
interface FifoDeq#(DTlbRqToP) rqToP;
interface FifoEnq#(DTlbTransRsFromP) ldTransRsFromP;
// after DTLB flush itself, it notifies L2, and wait L2 to flush
interface Client#(void, void) flush;
endinterface
interface DTlb#(type instT);
// system consistency related
method Bool flush_done;
method Action flush;
method Action updateVMInfo(VMInfo vm);
method Bool noPendingReq;
// req/resp with core
method Action procReq(DTlbReq#(instT) req);
method DTlbResp#(instT) procResp;
method Action deqProcResp;
// req/resp with L2 TLB
interface DTlbToParent toParent;
// speculation
interface SpeculationUpdate specUpdate;
// performance
interface Perf#(L1TlbPerfType) perf;
endinterface
typedef FullAssocTlb#(DTlbSize) DTlbArray;
module mkDTlbArray(DTlbArray);
let m <- mkFullAssocTlb(True); // randomness in replacement
return m;
endmodule
// a pending tlb req may be in following states
typedef union tagged {
void None;
void WaitParent;
DTlbReqIdx WaitPeer;
} DTlbWait deriving(Bits, Eq, FShow);
module mkDTlb#(
function TlbReq getTlbReq(instT inst)
)(DTlb::DTlb#(instT)) provisos(Bits#(instT, a__));
Bool verbose = True;
// TLB array
DTlbArray tlb <- mkDTlbArray;
// processor init flushing by setting this flag
Reg#(Bool) needFlush <- mkReg(False);
// after flushing ITLB itself, we want parent TLB to flush
Reg#(Bool) waitFlushP <- mkReg(False);
// current processor VM information
Reg#(VMInfo) vm_info <- mkReg(defaultValue);
// pending reqs
// pendWait should be meaningful even when entry is invalid. pendWait =
// WaitParent True means this entry is waiting for parent TLB resp;
// pendWait = WaitPeer means this entry is waiting for a resp initiated by
// another req. Thus, pendWait must be None when entry is invalid.
Vector#(DTlbReqNum, Ehr#(2, Bool)) pendValid <- replicateM(mkEhr(False));
Vector#(DTlbReqNum, Reg#(DTlbWait)) pendWait <- replicateM(mkReg(None));
Vector#(DTlbReqNum, Reg#(Bool)) pendPoisoned <- replicateM(mkRegU);
Vector#(DTlbReqNum, Reg#(instT)) pendInst <- replicateM(mkRegU);
Vector#(DTlbReqNum, Reg#(TlbResp)) pendResp <- replicateM(mkRegU);
Vector#(DTlbReqNum, Ehr#(2, SpecBits)) pendSpecBits <- replicateM(mkEhr(?));
// ordering of methods/rules that access pend reqs
// procReq mutually exclusive with doPRs (no procReq when pRs ready)
// procResp < {doPRs, procReq}
// wrongSpec C {procReq, doPRs, procResp}
// correctSpec C wrongSpec
// correctSpec CF doPRs
// {procReq, procResp} < correctSpec (correctSpec is always at end)
RWire#(void) wrongSpec_procResp_conflict <- mkRWire;
RWire#(void) wrongSpec_doPRs_conflict <- mkRWire;
RWire#(void) wrongSpec_procReq_conflict <- mkRWire;
let pendValid_noMiss = getVEhrPort(pendValid, 0);
let pendValid_wrongSpec = getVEhrPort(pendValid, 0);
let pendValid_procResp = getVEhrPort(pendValid, 0); // write
let pendValid_doPRs = getVEhrPort(pendValid, 1); // assert
let pendValid_procReq = getVEhrPort(pendValid, 1); // write
let pendSpecBits_wrongSpec = getVEhrPort(pendSpecBits, 0);
let pendSpecBits_procResp = getVEhrPort(pendSpecBits, 0);
let pendSpecBits_procReq = getVEhrPort(pendSpecBits, 0); // write
let pendSpecBits_correctSpec = getVEhrPort(pendSpecBits, 1);
// free list of pend entries, to cut off path from procResp to procReq
Fifo#(DTlbReqNum, DTlbReqIdx) freeQ <- mkCFFifo;
Reg#(Bool) freeQInited <- mkReg(False);
Reg#(DTlbReqIdx) freeQInitIdx <- mkReg(0);
// req & resp with parent TLB
Fifo#(DTlbReqNum, DTlbRqToP) rqToPQ <- mkCFFifo; // large enough so won't block on enq
Fifo#(2, DTlbTransRsFromP) ldTransRsFromPQ <- mkCFFifo;
// When a resp comes, we first process for the initiating req, then process
// other reqs that in WaitPeer.
Reg#(Maybe#(DTlbReqIdx)) respForOtherReq <- mkReg(Invalid);
// flush req/resp with parent TLB
Fifo#(1, void) flushRqToPQ <- mkCFFifo;
Fifo#(1, void) flushRsFromPQ <- mkCFFifo;
// perf counters
Fifo#(1, L1TlbPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(L1TlbPerfType)) perfRespQ <- mkCFFifo;
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) accessCnt <- mkCount(0);
Count#(Data) missParentCnt <- mkCount(0);
Count#(Data) missParentLat <- mkCount(0);
Count#(Data) missPeerCnt <- mkCount(0);
Count#(Data) missPeerLat <- mkCount(0);
Count#(Data) hitUnderMissCnt <- mkCount(0);
Count#(Data) allMissCycles <- mkCount(0);
LatencyTimer#(DTlbReqNum, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles
rule doPerf;
let t <- toGet(perfReqQ).get;
Data d = (case(t)
L1TlbAccessCnt: (accessCnt);
L1TlbMissParentCnt: (missParentCnt);
L1TlbMissParentLat: (missParentLat);
L1TlbMissPeerCnt: (missPeerCnt);
L1TlbMissPeerLat: (missPeerLat);
L1TlbHitUnderMissCnt: (hitUnderMissCnt);
L1TlbAllMissCycles: (allMissCycles);
default: (0);
endcase);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
rule incrAllMissCycles(doStats);
function Bool isMiss(DTlbWait x) = x != None;
when(all(isMiss, readVReg(pendWait)), allMissCycles.incr(1));
endrule
`endif
// do flush: start when all misses resolve
Bool noMiss = all(\== (False) , readVReg(pendValid_noMiss));
rule doStartFlush(needFlush && !waitFlushP && noMiss);
tlb.flush;
// request parent TLB to flush
flushRqToPQ.enq(?);
waitFlushP <= True;
if(verbose) $display("[DTLB] flush begin");
endrule
rule doFinishFlush(needFlush && waitFlushP);
flushRsFromPQ.deq;
needFlush <= False;
waitFlushP <= False;
if(verbose) $display("[DTLB] flush done");
endrule
// get resp from parent TLB
// At high level, this rule is always exclusive with doStartFlush, though
// we don't bother to make compiler understand this...
rule doPRs(ldTransRsFromPQ.notEmpty);
let pRs = ldTransRsFromPQ.first;
// the current req being served is either the initiating req or other
// req pending on the same resp
let idx = fromMaybe(pRs.id, respForOtherReq);
TlbReq r = getTlbReq(pendInst[idx]);
if(pendPoisoned[idx]) begin
// poisoned inst, do nothing
if(verbose) $display("[DTLB] refill poisoned: idx %d; ", idx, fshow(r));
end
else if(pRs.entry matches tagged Valid .en) begin
// check permission
if(hasVMPermission(vm_info,
en.pteType,
en.ppn,
en.level,
r.write ? DataStore : DataLoad)) begin
// fill TLB, and record resp
tlb.addEntry(en);
let trans_addr = translate(r.addr, en.ppn, en.level);
pendResp[idx] <= tuple2(trans_addr, Invalid);
if(verbose) begin
$display("[DTLB] refill: idx %d; ", idx, fshow(r),
"; ", fshow(trans_addr));
end
end
else begin
// page fault
Exception fault = r.write ? StorePageFault : LoadPageFault;
pendResp[idx] <= tuple2(?, Valid (fault));
if(verbose) begin
$display("[DTLB] refill no permission: idx %d; ", idx, fshow(r));
end
end
end
else begin
// page fault
Exception fault = r.write ? StorePageFault : LoadPageFault;
pendResp[idx] <= tuple2(?, Valid (fault));
if(verbose) $display("[DTLB] refill page fault: idx %d; ", idx, fshow(r));
end
// get parent resp, miss resolved, reset wait bit
pendWait[idx] <= None;
doAssert(pendValid_doPRs[idx], "entry must be valid");
if(isValid(respForOtherReq)) begin
doAssert(pendWait[idx] == WaitPeer (pRs.id), "entry must be waiting for resp");
end
else begin
doAssert(pendWait[idx] == WaitParent, "entry must be waiting for resp");
end
// find another req waiting for this resp
function Bool waitForResp(DTlbReqIdx i);
// we can ignore pendValid here, because not-None pendWait implies
// pendValid is true
return pendWait[i] == WaitPeer (pRs.id) && i != idx;
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
if(find(waitForResp, idxVec) matches tagged Valid .i) begin
// still have req waiting for this resp, keep processing
respForOtherReq <= Valid (i);
doAssert(pendValid_doPRs[i], "waiting entry must be valid");
end
else begin
// all req done, deq the pRs
respForOtherReq <= Invalid;
ldTransRsFromPQ.deq;
end
`ifdef PERF_COUNT
// perf: miss
let lat <- latTimer.done(idx);
if(doStats) begin
if(isValid(respForOtherReq)) begin
missPeerLat.incr(zeroExtend(lat));
missPeerCnt.incr(1);
end
else begin
missParentLat.incr(zeroExtend(lat));
missParentCnt.incr(1);
end
end
`endif
// conflict with wrong spec
wrongSpec_doPRs_conflict.wset(?);
endrule
// init freeQ
rule doInitFreeQ(!freeQInited);
freeQ.enq(freeQInitIdx);
freeQInitIdx <= freeQInitIdx + 1;
if(freeQInitIdx == fromInteger(valueof(DTlbReqNum) - 1)) begin
freeQInited <= True;
end
endrule
// idx of entries that are ready to resp to proc
function Maybe#(DTlbReqIdx) validProcRespIdx;
function Bool validResp(DTlbReqIdx i);
return pendValid_procResp[i] && pendWait[i] == None && !pendPoisoned[i];
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
return find(validResp, idxVec);
endfunction
function Maybe#(DTlbReqIdx) poisonedProcRespIdx;
function Bool poisonedResp(DTlbReqIdx i);
return pendValid_procResp[i] && pendWait[i] == None && pendPoisoned[i];
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
return find(poisonedResp, idxVec);
endfunction
// drop poisoned resp
rule doPoisonedProcResp(poisonedProcRespIdx matches tagged Valid .idx &&& freeQInited);
pendValid_procResp[idx] <= False;
freeQ.enq(idx);
// conflict with wrong spec
wrongSpec_procResp_conflict.wset(?);
endrule
method Action flush if(!needFlush);
needFlush <= True;
waitFlushP <= False;
// this won't interrupt current processing, since
// (1) miss process will continue even if needFlush=True
// (2) flush truly starts when there is no pending req
endmethod
method Bool flush_done = !needFlush;
method Action updateVMInfo(VMInfo vm);
vm_info <= vm;
endmethod
// Since this method is called at commit stage to determine no in-flight
// TLB req, even poisoned req should be considered as pending, because it
// may be in L2 TLB.
method Bool noPendingReq = noMiss;
// We do not accept new req when flushing flag is set. We also do not
// accept new req when parent resp is ready. This avoids bypass in TLB. We
// also check rqToPQ not full. This simplifies the guard, i.e., it does not
// depend on whether we hit in TLB or not.
method Action procReq(DTlbReq#(instT) req) if(
!needFlush && !ldTransRsFromPQ.notEmpty && rqToPQ.notFull && freeQInited
);
// allocate MSHR entry
freeQ.deq;
DTlbReqIdx idx = freeQ.first;
doAssert(!pendValid_procReq[idx], "free entry cannot be valid");
doAssert(pendWait[idx] == None, "entry cannot wait for parent resp");
pendValid_procReq[idx] <= True;
pendPoisoned[idx] <= False;
pendInst[idx] <= req.inst;
pendSpecBits_procReq[idx] <= req.specBits;
// pendWait and pendResp are set later in this method
// try to translate
TlbReq r = getTlbReq(req.inst);
`ifdef SECURITY
// Security check
// Forbid any data load shared outside of the protection domain
// if shared load are not allowed
// No need to special case M mode with special vm_info value because we
// assume that we allow shared load all the time when in M mode.
// (Because we are always non speculative in M mode)
if (!vm_info.sanctum_authShared && outOfProtectionDomain(vm_info, r.addr))begin
pendWait[idx] <= None;
pendResp[idx] <= tuple2(?, Valid (LoadAccessFault));
end
`else
// No security check
if (False) begin
noAction;
end
`endif
else if (vm_info.sv39) begin
let vpn = getVpn(r.addr);
let trans_result = tlb.translate(vpn, vm_info.asid);
if (trans_result.hit) begin
// TLB hit
let entry = trans_result.entry;
// check permission
if (hasVMPermission(vm_info,
entry.pteType,
entry.ppn,
entry.level,
r.write ? DataStore : DataLoad)) begin
// update TLB replacement info
tlb.updateRepByHit(trans_result.index);
// translate addr
Addr trans_addr = translate(r.addr, entry.ppn, entry.level);
pendWait[idx] <= None;
pendResp[idx] <= tuple2(trans_addr, Invalid);
if(verbose) begin
$display("[DTLB] req (hit): idx %d; ", idx, fshow(r),
"; ", fshow(trans_result));
end
`ifdef PERF_COUNT
// perf: hit under miss
if(doStats && readVReg(pendWait) != replicate(None)) begin
hitUnderMissCnt.incr(1);
end
`endif
end
else begin
// page fault
Exception fault = r.write ? StorePageFault : LoadPageFault;
pendWait[idx] <= None;
pendResp[idx] <= tuple2(?, Valid (fault));
if(verbose) $display("[DTLB] req no permission: idx %d; ", idx, fshow(r));
end
end
else begin
// TLB miss, req to parent TLB only if there is no existing req
// for the same VPN already waiting for parent TLB resp
function Bool reqSamePage(DTlbReqIdx i);
// we can ignore pendValid here, because not-None pendWait implies
// pendValid is true
let r_i = getTlbReq(pendInst[i]);
return pendWait[i] == WaitParent && getVpn(r.addr) == getVpn(r_i.addr);
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
if(find(reqSamePage, idxVec) matches tagged Valid .i) begin
// peer entry has already requested, so don't send duplicate req
pendWait[idx] <= WaitPeer (i);
doAssert(pendValid_procReq[i], "peer entry must be valid");
if(verbose) begin
$display("[DTLB] req miss, pend on peer: idx %d, ",
idx, "; ", fshow(r), "; ", fshow(i));
end
end
else begin
// this is the first req for this VPN
pendWait[idx] <= WaitParent;
rqToPQ.enq(DTlbRqToP {
vpn: vpn,
id: idx
});
if(verbose) begin
$display("[DTLB] req miss, send to parent: idx %d, ",
idx, fshow(r));
end
end
`ifdef PERF_COUNT
// perf: miss
latTimer.start(idx);
`endif
end
end
else begin
// bare mode
pendWait[idx] <= None;
pendResp[idx] <= tuple2(r.addr, Invalid);
if(verbose) $display("DTLB %m req (bare): ", fshow(r));
end
`ifdef PERF_COUNT
// perf: access
if(doStats) begin
accessCnt.incr(1);
end
`endif
// conflict with wrong spec
wrongSpec_procReq_conflict.wset(?);
endmethod
method Action deqProcResp if(
validProcRespIdx matches tagged Valid .idx &&& freeQInited
);
pendValid_procResp[idx] <= False;
freeQ.enq(idx);
// conflict with wrong spec
wrongSpec_procResp_conflict.wset(?);
endmethod
method DTlbResp#(instT) procResp if(
validProcRespIdx matches tagged Valid .idx &&& freeQInited
);
return DTlbResp {
inst: pendInst[idx],
resp: pendResp[idx],
specBits: pendSpecBits_procResp[idx]
};
endmethod
interface DTlbToParent toParent;
interface rqToP = toFifoDeq(rqToPQ);
interface ldTransRsFromP = toFifoEnq(ldTransRsFromPQ);
interface Client flush;
interface request = toGet(flushRqToPQ);
interface response = toPut(flushRsFromPQ);
endinterface
endinterface
interface SpeculationUpdate specUpdate;
method Action incorrectSpeculation(Bool kill_all, SpecTag x);
// poison entries
for(Integer i = 0 ; i < valueOf(DTlbReqNum) ; i = i+1) begin
if(kill_all || pendSpecBits_wrongSpec[i][x] == 1'b1) begin
pendPoisoned[i] <= True;
end
end
// make conflicts with procReq, doPRs, procResp
wrongSpec_procReq_conflict.wset(?);
wrongSpec_doPRs_conflict.wset(?);
wrongSpec_procResp_conflict.wset(?);
endmethod
method Action correctSpeculation(SpecBits mask);
// clear spec bits for all entries
for(Integer i = 0 ; i < valueOf(DTlbReqNum) ; i = i+1) begin
let new_spec_bits = pendSpecBits_correctSpec[i] & mask;
pendSpecBits_correctSpec[i] <= new_spec_bits;
end
endmethod
endinterface
interface Perf perf;
method Action setStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Action req(L1TlbPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(L1TlbPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,743 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import MemoryTypes::*;
import Vector::*;
import DefaultValue::*;
Bit#(3) memWU = 3'b110;
// Smaller decode functions
function Maybe#(MemInst) decodeMemInst(Instruction inst);
MemInst mem_inst = MemInst{ mem_func: ?,
amo_func: None,
unsignedLd: False,
byteEn: replicate(False),
aq: False,
rl: False };
Bool illegalInst = False;
Opcode opcode = unpackOpcode(inst[6:0]);
let funct5 = inst[31:27];
let funct3 = inst[14:12];
// mem_func + amo_func
MemFunc mem_func = Ld;
AmoFunc amo_func = None;
if (opcode == Load || opcode == LoadFp) begin
mem_func = Ld;
end else if (opcode == Store || opcode == StoreFp) begin
mem_func = St;
end else if (opcode == Amo) begin
case (funct5)
fnLR : mem_func = Lr;
fnSC : mem_func = Sc;
fnAMOSWAP,
fnAMOADD,
fnAMOXOR,
fnAMOAND,
fnAMOOR,
fnAMOMIN,
fnAMOMAX,
fnAMOMINU,
fnAMOMAXU : mem_func = Amo;
default : illegalInst = True;
endcase
// now for amo_func
case (funct5)
fnAMOSWAP : amo_func = Swap;
fnAMOADD : amo_func = Add;
fnAMOXOR : amo_func = Xor;
fnAMOAND : amo_func = And;
fnAMOOR : amo_func = Or;
fnAMOMIN : amo_func = Min;
fnAMOMAX : amo_func = Max;
fnAMOMINU : amo_func = Minu;
fnAMOMAXU : amo_func = Maxu;
endcase
end else begin
illegalInst = True;
end
// unsignedLd
// it doesn't matter if this is set to True for stores
Bool unsignedLd = False;
case (funct3)
memB, memH, memW, memD:
unsignedLd = False;
memBU, memHU, memWU:
unsignedLd = True;
default:
illegalInst = True;
endcase
// This is a minor fix to make our processor's results match spike since
// they don't sign extend when loading single precision values from memory
if (opcode == LoadFp) begin
unsignedLd = True;
end
// byteEn
// TODO: Some combinations of operations and byteEn's are illegal.
// They should be detected here.
ByteEn byteEn = replicate(False);
case (funct3)
memB, memBU : byteEn[0] = True;
memH, memHU : begin
byteEn[0] = True;
byteEn[1] = True;
end
memW, memWU : begin
byteEn[0] = True;
byteEn[1] = True;
byteEn[2] = True;
byteEn[3] = True;
end
memD : byteEn = replicate(True);
default : illegalInst = True;
endcase
// aq + rl
Bool aq = False;
Bool rl = False;
if (opcode == Amo) begin
// aq and rl are only defined for Amo operations
aq = unpack(inst[ 26 ]);
rl = unpack(inst[ 25 ]);
end
if (illegalInst) begin
return tagged Invalid;
end else begin
return tagged Valid ( MemInst{
mem_func: mem_func,
amo_func: amo_func,
unsignedLd: unsignedLd,
byteEn: byteEn,
aq: aq,
rl: rl } );
end
endfunction
(* noinline *)
function DecodeResult decode(Instruction inst);
RiscVISASubset isa = defaultValue;
// initialize dInst with default values
DecodedInst dInst = DecodedInst {
iType: Unsupported,
execFunc: tagged Other,
csr: tagged Invalid,
imm: tagged Invalid
};
ArchRegs regs = ArchRegs {
src1: tagged Invalid,
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid
};
Bool illegalInst = False;
Opcode opcode = unpackOpcode(inst[ 6 : 0 ]);
let rd = inst[ 11 : 7 ];
let funct3 = inst[ 14 : 12 ];
let rs1 = inst[ 19 : 15 ];
let rs2 = inst[ 24 : 20 ];
let funct7 = inst[ 31 : 25 ];
// For "F" and "D" ISA extensions
let funct5 = inst[ 31 : 27 ];
let fmt = inst[ 26 : 25 ];
let rs3 = inst[ 31 : 27 ];
let funct2 = inst[ 26 : 25 ];
// For "A" ISA extension
Bool aq = unpack(inst[ 26 ]);
Bool rl = unpack(inst[ 25 ]);
ImmData immI = signExtend(inst[31:20]);
ImmData immS = signExtend({ inst[31:25], inst[11:7] });
ImmData immB = signExtend({ inst[31], inst[7], inst[30:25], inst[11:8], 1'b0});
ImmData immU = signExtend({ inst[31:12], 12'b0 });
ImmData immJ = signExtend({ inst[31], inst[19:12], inst[20], inst[30:21], 1'b0});
// Results of mini-decoders
Maybe#(MemInst) mem_inst = decodeMemInst(inst);
// TODO better detection of illegal insts
case (opcode)
OpImm: begin
dInst.iType = Alu;
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Add;
fnSLT: Slt;
fnSLTU: Sltu;
fnAND: And;
fnOR: Or;
fnXOR: Xor;
fnSLL: Sll;
fnSR: (immI[10] == 0 ? Srl : Sra);
endcase);
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
OpImm32: begin
dInst.iType = Alu;
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Addw;
fnSLL: Sllw;
fnSR: (immI[10] == 0 ? Srlw : Sraw);
endcase);
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
Op: begin
dInst.iType = Alu;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Invalid;
dInst.csr = tagged Invalid;
case (funct7)
opALU1: begin
dInst.execFunc = tagged Alu (case(funct3)
fnADD: Add;
fnSLT: Slt;
fnSLTU: Sltu;
fnAND: And;
fnOR: Or;
fnXOR: Xor;
fnSLL: Sll;
fnSR: Srl;
endcase);
end
opALU2: begin
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Sub;
fnSR: Sra;
endcase);
end
opMULDIV: begin
if (isa.m) begin
// Processor includes "M" extension
MulDivFunc func = (case(funct3)
fnMUL : Mul;
fnMULH : Mulh;
fnMULHSU : Mulh;
fnMULHU : Mulh;
fnDIV : Div;
fnDIVU : Div;
fnREM : Rem;
fnREMU : Rem;
endcase);
Bool w = False;
MulDivSign sign = (case(funct3)
fnMUL : Signed;
fnMULH : Signed;
fnMULHSU : SignedUnsigned;
fnMULHU : Unsigned;
fnDIV : Signed;
fnDIVU : Unsigned;
fnREM : Signed;
fnREMU : Unsigned;
endcase);
dInst.execFunc = tagged MulDiv (MulDivInst {
func: func, w: w, sign: sign
});
end else begin
// Processor doesn't include "M" extension
illegalInst = True;
end
end
endcase
end
Op32: begin
dInst.iType = Alu;
case (funct7)
opALU1: begin
dInst.execFunc = tagged Alu (case(funct3)
fnADD: Addw;
fnSLL: Sllw;
fnSR: Srlw;
endcase);
end
opALU2: begin
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Subw;
fnSR: Sraw;
endcase);
end
opMULDIV: begin
if (isa.m) begin
// Processor includes "M" extension
// TODO mark MULH as illegal inst
MulDivFunc func = (case(funct3)
fnMUL : Mul;
fnMULH : Mulh; // illegal
fnMULHSU : Mulh; // illegal
fnMULHU : Mulh; // illegal
fnDIV : Div;
fnDIVU : Div;
fnREM : Rem;
fnREMU : Rem;
endcase);
Bool w = True;
MulDivSign sign = (case(funct3)
fnMUL : Signed;
fnMULH : Signed; // illegal
fnMULHSU : SignedUnsigned; // illegal
fnMULHU : Unsigned; // illegal
fnDIV : Signed;
fnDIVU : Unsigned;
fnREM : Signed;
fnREMU : Unsigned;
endcase);
dInst.execFunc = tagged MulDiv (MulDivInst{
func: func, w: w, sign: sign
});
end else begin
// Processor doesn't include "M" extension
illegalInst = True;
end
end
endcase
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Invalid;
dInst.csr = tagged Invalid;
end
Lui: begin // treated as an x0 + immU
dInst.iType = Alu;
dInst.execFunc = tagged Alu Add;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr 0);
regs.src2 = Invalid;
dInst.imm = Valid(immU);
dInst.csr = tagged Invalid;
end
Auipc: begin
dInst.iType = Auipc;
dInst.execFunc = tagged Alu Add;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.imm = Valid(immU);
dInst.csr = tagged Invalid;
end
Jal: begin
dInst.iType = J;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.imm = Valid(immJ);
dInst.csr = tagged Invalid;
dInst.execFunc = tagged Br AT;
end
Jalr: begin
dInst.iType = Jr;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
dInst.execFunc = tagged Br AT;
end
Branch: begin
dInst.iType = Br;
dInst.execFunc = tagged Br (case(funct3)
fnBEQ: Eq;
fnBNE: Neq;
fnBLT: Lt;
fnBLTU: Ltu;
fnBGE: Ge;
fnBGEU: Geu;
endcase);
regs.dst = Invalid;
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(immB);
dInst.csr = tagged Invalid;
end
Load: begin
dInst.iType = Ld;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
Store: begin
dInst.iType = St;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Invalid;
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(immS);
dInst.csr = tagged Invalid;
end
Amo: begin
if (!isa.a) begin
// unsupported
illegalInst = True;
end else begin
// AMO defaults
dInst.iType = Amo;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(0);
dInst.csr = Invalid;
case (funct5)
fnLR: begin
dInst.iType = Lr;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(0);
dInst.csr = Invalid;
end
fnSC: begin
dInst.iType = Sc;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(0);
dInst.csr = Invalid;
end
fnAMOSWAP,
fnAMOADD,
fnAMOXOR,
fnAMOAND,
fnAMOOR,
fnAMOMIN,
fnAMOMAX,
fnAMOMINU,
fnAMOMAXU: begin
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
end
default: begin
illegalInst = True;
end
endcase
end
end
// Instructions for "F" and "D" ISA extensions - FPU
OpFp: begin
// check if instruction is supported
if ((fmt == fmtS && !isa.f) || (fmt == fmtD && !isa.d) || (fmt != fmtS && fmt != fmtD)) begin
illegalInst = True;
end else begin
// Instruction is supported
dInst.iType = Fpu;
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Valid(tagged Fpu rs2);
dInst.imm = Invalid;
dInst.csr = tagged Invalid;
FpuFunc func = (case (funct5)
opFADD: FAdd;
opFSUB: FSub;
opFMUL: FMul;
opFDIV: FDiv;
opFSQRT: FSqrt;
opFSGNJ: ((funct3 == 0) ? FSgnj : ((funct3 == 1) ? FSgnjn : FSgnjx));
opFMINMAX: ((funct3 == 0) ? FMin : FMax);
opFCMP: ((funct3 == 0) ? FLe : ((funct3 == 1) ? FLt : FEq));
opFMV_XF: ((funct3 == 0) ? FMv_XF : FClass); // also CLASS
opFMV_FX: FMv_FX;
opFCVT_FF: FCvt_FF;
opFCVT_WF: ((rs2 == 0) ? FCvt_WF : ((rs2 == 1) ? FCvt_WUF : ((rs2 == 2) ? FCvt_LF : FCvt_LUF)));
opFCVT_FW: ((rs2 == 0) ? FCvt_FW : ((rs2 == 1) ? FCvt_FWU : ((rs2 == 2) ? FCvt_FL : FCvt_FLU)));
endcase);
FpuPrecision precision = (fmt == fmtS) ? Single : Double;
dInst.execFunc = tagged Fpu(FpuInst{func: func, rm: unpack(funct3), precision: precision});
// Special cases
case (funct5)
opFSQRT: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFCMP: begin
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Valid(tagged Fpu rs2);
end
opFMV_XF: begin
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFMV_FX: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
end
opFCVT_FF: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFCVT_WF: begin
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFCVT_FW: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
end
endcase
end
end
LoadFp: begin
// check if instruction is supported
if (!isa.f && !isa.d) begin
// FIXME: Check more cases
illegalInst = True;
end else begin
// Same decode logic as Int Ld
dInst.iType = Ld;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
end
StoreFp: begin
// check if instruction is supported
if (!isa.f && !isa.d) begin
// FIXME: Check more cases
illegalInst = True;
end else begin
// Same decode logic as Int St
dInst.iType = St;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Invalid;
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Fpu rs2);
dInst.imm = Valid(immS);
dInst.csr = tagged Invalid;
end
end
Fmadd, Fmsub, Fnmsub, Fnmadd: begin
// check if instruction is supported
if ((fmt == fmtS && !isa.f) ||
(fmt == fmtD && !isa.d) ||
(fmt != fmtS && fmt != fmtD)) begin
dInst.iType = Unsupported;
illegalInst = True;
end else begin
// Instruction is supported
dInst.iType = Fpu;
FpuFunc func = ?;
case (opcode)
Fmadd: func = FMAdd;
Fmsub: func = FMSub;
Fnmsub: func = FNMSub;
Fnmadd: func = FNMAdd;
default: illegalInst = True;
endcase
dInst.execFunc = tagged Fpu (FpuInst {
func: func,
rm: unpack(funct3),
precision: fmt == fmtS ? Single : Double
});
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Valid(tagged Fpu rs2);
regs.src3 = Valid(rs3);
regs.dst = Valid(tagged Fpu rd);
dInst.csr = Invalid;
dInst.imm = Invalid;
end
end
MiscMem: begin
case (funct3)
fnFENCEI: begin
dInst.iType = FenceI;
dInst.execFunc = tagged Other;
end
fnFENCE: begin
// extract bits for P/S IORW
Bool old_st = unpack(inst[26] | inst[24]); // PO, PW
Bool young_ld = unpack(inst[23] | inst[21]); // SI, SR
// get acq/reconcile and rel/commit needed to enforce
// different orderings
`ifdef TSO_MM
// Orderings enfored by fence in TSO:
// St -> Ld: commit
// Others: N/A
Bool reconcile = False;
Bool commit = old_st && young_ld;
`else
// Orderings enforced by fence in WMM
// St -> St: commit
// St -> Ld: commit + reconcile
// Ld -> Ld: reconcile
// Ld -> St: N/A
Bool reconcile = young_ld; // reconcile when younger load is in ordering
Bool commit = old_st; // commit when older store is in ordering
`endif
// set up fence inst
if (reconcile || commit) begin
dInst.iType = Fence;
dInst.execFunc = tagged Mem (MemInst {
mem_func: Fence,
amo_func: None,
unsignedLd: False,
byteEn: replicate(False),
aq: reconcile,
rl: commit
});
end
else begin
dInst.iType = Nop;
dInst.execFunc = tagged Other;
end
end
default: illegalInst = True;
endcase
regs.dst = Invalid;
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.imm = Invalid;
dInst.csr = Invalid;
end
System: begin
if (funct3 == fnPRIV) begin
if (funct7 == privSFENCEVMA) begin
dInst.iType = SFence;
// FIXME SFENCE.VMA is implemented in coarse grain,
// ignoring rs1 and rs2
end
else begin
case (truncate(immI))
privSRET: dInst.iType = Sret;
privMRET: dInst.iType = Mret;
privECALL: dInst.iType = Ecall;
privEBREAK: dInst.iType = Ebreak;
privWFI: dInst.iType = Nop; // treat as NOP
default: illegalInst = True;
endcase
end
regs.dst = Invalid;
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.csr = Invalid;
dInst.imm = Invalid;
end
else begin // fnCSRRWI, fnCSRRW, fnCSRRSI, fnCSRRS, fnCSRRCI, fnCSRRC
dInst.iType = Csr;
dInst.execFunc = (case (funct3)
fnCSRRWI, fnCSRRW: tagged Alu Csrw;
fnCSRRSI, fnCSRRS: tagged Alu Csrs;
fnCSRRCI, fnCSRRC: tagged Alu Csrc;
endcase);
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Invalid; // going to be CSR Reg
regs.src2 = (funct3[2] == 0 ? Valid(tagged Gpr rs1) : Invalid);
dInst.imm = (funct3[2] == 0 ? Invalid : Valid(zeroExtend(rs1)));
dInst.csr = Valid(unpackCSR(truncate(immI)));
end
end
default: begin
illegalInst = True;
end
endcase
// XXX to ensure renaming + phy regs works correctly, must remove any write
// to x0
if(regs.dst matches tagged Valid .dst &&& dst == tagged Gpr 0) begin
regs.dst = tagged Invalid;
end
return DecodeResult{dInst: dInst, regs: regs, illegalInst: illegalInst};
endfunction
// All this does is add the CSR state to the decoding
function FpuInst updateRoundingMode(FpuInst fpu_f, CsrDecodeInfo csrState);
let new_fpu_f = fpu_f;
new_fpu_f.rm = (fpu_f.rm == RDyn) ? unpack(csrState.frm) : fpu_f.rm;
return new_fpu_f;
endfunction

View File

@@ -0,0 +1,85 @@
// 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.
`include "ProcConfig.bsv"
import Assert::*;
import Types::*;
import ProcTypes::*;
import Vector::*;
import BrPred::*;
import Bht::*;
import GSelectPred::*;
import GSharePred::*;
import TourPred::*;
import TourPredSecure::*;
export DirPredTrainInfo(..);
export mkDirPredictor;
`ifdef DIR_PRED_BHT
typedef BhtTrainInfo DirPredTrainInfo;
`endif
`ifdef DIR_PRED_GSELECT
typedef GSelectTrainInfo DirPredTrainInfo;
`endif
`ifdef DIR_PRED_GSHARE
typedef GShareTrainInfo DirPredTrainInfo;
`endif
`ifdef DIR_PRED_TOUR
typedef TourTrainInfo DirPredTrainInfo;
`endif
(* synthesize *)
module mkDirPredictor(DirPredictor#(DirPredTrainInfo));
`ifdef DIR_PRED_BHT
`ifdef SECURITY
staticAssert(False, "BHT with flush methods is not implemented");
`endif
let m <- mkBht;
`endif
`ifdef DIR_PRED_GSELECT
`ifdef SECURITY
staticAssert(False, "GSelect with flush methods is not implemented");
`endif
let m <- mkGSelectPred;
`endif
`ifdef DIR_PRED_GSHARE
`ifdef SECURITY
staticAssert(False, "GShare with flush methods is not implemented");
`endif
let m <- mkGSharePred;
`endif
`ifdef DIR_PRED_TOUR
`ifdef SECURITY
let m <- mkTourPredSecure;
`else
let m <- mkTourPred;
`endif
`endif
return m;
endmodule

View File

@@ -0,0 +1,96 @@
// 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.
/*
Comments: This EHR design generates the following scheduling constraints (forall i):
forall j >= i, r[i] < w[j]
forall j < i, r[i] > w[j]
forall j > i, w[i] < w[j]
w[i] conflicts with w[i]
forall j, r[i] is conflict free with r[j]
*/
import Vector::*;
import RWire::*;
import RevertingVirtualReg::*;
typedef Vector#(n, Reg#(t)) Ehr#(numeric type n, type t);
function Vector#(n, t) readVEhr(i ehr_index, Vector#(n, Ehr#(n2, t)) vec_ehr) provisos (PrimIndex#(i, __a));
function Reg#(t) get_ehr_index(Ehr#(n2, t) e) = e[ehr_index];
return readVReg(map(get_ehr_index, vec_ehr));
endfunction
// extract vector ports from vector of EHRs
function Vector#(n, Reg#(t)) getVEhrPort(Vector#(n, Ehr#(m, t)) ehrs, Integer p);
function Reg#(t) get(Ehr#(m, t) e) = e[p];
return map(get, ehrs);
endfunction
module mkEhr#(t init)(Ehr#(n, t)) provisos(Bits#(t, tSz));
Vector#(n, RWire#(t)) lat <- replicateM(mkUnsafeRWire);
Vector#(n, Vector#(n, RWire#(Bool))) dummy <- replicateM(replicateM(mkUnsafeRWire));
Vector#(n, Reg#(Bool)) dummy2 <- replicateM(mkRevertingVirtualReg(True)); // this must be true
Reg#(t) rl <- mkReg(init);
Ehr#(n, t) r = newVector;
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
t upd = rl;
for(Integer i = 0; i < valueOf(n); i = i + 1)
if(lat[i].wget matches tagged Valid .x)
upd = x;
rl <= upd;
endrule
for(Integer i = 0; i < valueOf(n); i = i + 1)
r[i] = (interface Reg;
method Action _write(t x);
lat[i].wset(x);
dummy2[i] <= True;
for(Integer j = 0; j < i; j = j + 1)
dummy[i][j].wset(isValid(lat[j].wget));
endmethod
method t _read;
t upd = rl;
Bool yes = True;
for(Integer j = i; j < valueOf(n); j = j + 1)
yes = yes && dummy2[j];
for(Integer j = 0; j < i; j = j + 1)
begin
if(lat[j].wget matches tagged Valid .x)
upd = x;
end
return yes? upd : unpack(0);
// use a non-? val here! otherwise new BSV compiler will stop optimize at ? val
// this affects judging if two rules are exclusive
endmethod
endinterface);
return r;
endmodule

View File

@@ -0,0 +1,133 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import Ehr::*;
import Types::*;
import ProcTypes::*;
typedef struct {
Epoch curEp;
Epoch checkedEp;
} EpochDebugState deriving(Bits, Eq, FShow);
interface EM_checkEpoch;
method Bool check(Epoch e);
endinterface
interface EM_updatePrevEpoch;
method Action update(Epoch e);
endinterface
interface EpochManager;
interface Vector#(SupSize, EM_checkEpoch) checkEpoch;
interface Vector#(SupSize, EM_updatePrevEpoch) updatePrevEpoch;
method Epoch getEpoch;
method Action incrementEpoch;
// for debug
method EpochDebugState getEpochState;
// performance: count full cycle
method Bool isFull_ehrPort0;
endinterface
(* synthesize *)
module mkEpochManager(EpochManager);
Reg#(Epoch) curr_epoch <- mkReg(0);
Reg#(Epoch) prev_checked_epoch <- mkReg(0);
Epoch next_epoch = (curr_epoch== fromInteger(valueOf(NumEpochs)-1)) ? 0 : (curr_epoch+1);
// epochs in the core are within range [prev_checked_epoch, curr_epoch]
// prev_checked_epoch can be updated in a lazy way
Vector#(SupSize, Ehr#(2, Maybe#(Epoch))) updatePrevEn <- replicateM(mkEhr(Invalid));
(* fire_when_enabled, no_implicit_conditions *)
rule canon_prev_checked_epoch;
Vector#(SupSize, Maybe#(Epoch)) updates = readVEhr(1, updatePrevEn);
// find the last update
if(find(isValid, reverse(updates)) matches tagged Valid .upd) begin
doAssert(isValid(upd), "must be valid");
prev_checked_epoch <= validValue(upd);
end
// reset EHR
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
updatePrevEn[i][1] <= Invalid;
end
endrule
Vector#(SupSize, EM_updatePrevEpoch) updateIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
updateIfc[i] = (interface EM_updatePrevEpoch;
method Action update(Epoch e);
updatePrevEn[i][0] <= Valid (e); // record update action
`ifdef BSIM
// sanity check
Epoch checkedEpoch = prev_checked_epoch;
for(Integer j = 0; j < i; j = j+1) begin
if(updatePrevEn[j][1] matches tagged Valid .ep) begin
checkedEpoch = ep;
end
end
if(checkedEpoch <= curr_epoch) begin
doAssert(checkedEpoch <= e && e <= curr_epoch, "e in [checkedEpoch, curr_epoch]");
end
else begin
doAssert(checkedEpoch <= e || e <= curr_epoch, "e in [checkedEpoch, max] + [0, curr_epoch]");
end
`endif
endmethod
endinterface);
end
Vector#(SupSize, EM_checkEpoch) checkIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
checkIfc[i] = (interface EM_checkEpoch;
method Bool check(Epoch e);
return (e == curr_epoch);
endmethod
endinterface);
end
interface updatePrevEpoch = updateIfc;
interface checkEpoch = checkIfc;
method Epoch getEpoch;
return curr_epoch;
endmethod
method Action incrementEpoch if(prev_checked_epoch != next_epoch);
curr_epoch <= next_epoch;
endmethod
method EpochDebugState getEpochState;
return EpochDebugState {
curEp: curr_epoch,
checkedEp: prev_checked_epoch
};
endmethod
method Bool isFull_ehrPort0;
return next_epoch == prev_checked_epoch;
endmethod
endmodule

View File

@@ -0,0 +1,250 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import Vector::*;
(* noinline *)
function Data alu(Data a, Data b, AluFunc func);
Data res = (case(func)
Add : (a + b);
Addw : signExtend((a + b)[31:0]);
Sub : (a - b);
Subw : signExtend((a[31:0] - b[31:0])[31:0]);
And : (a & b);
Or : (a | b);
Xor : (a ^ b);
Slt : zeroExtend( pack( signedLT(a, b) ) );
Sltu : zeroExtend( pack( a < b ) );
Sll : (a << b[5:0]);
Sllw : signExtend((a[31:0] << b[4:0])[31:0]);
Srl : (a >> b[5:0]);
Sra : signedShiftRight(a, b[5:0]);
Srlw : signExtend((a[31:0] >> b[4:0])[31:0]);
Sraw : signExtend(signedShiftRight(a[31:0], b[4:0])[31:0]);
Csrw : b;
Csrs : (a | b); // same as Or
Csrc : (a & ~b);
default : 0;
endcase);
return res;
endfunction
(* noinline *)
function Bool aluBr(Data a, Data b, BrFunc brFunc);
Bool brTaken = (case(brFunc)
Eq : (a == b);
Neq : (a != b);
Lt : signedLT(a, b);
Ltu : (a < b);
Ge : signedGE(a, b);
Geu : (a >= b);
AT : True;
NT : False;
default : False;
endcase);
return brTaken;
endfunction
(* noinline *)
function Addr brAddrCalc(Addr pc, Data val, IType iType, Data imm, Bool taken);
Addr pcPlus4 = pc + 4;
Addr targetAddr = (case (iType)
J : (pc + imm);
Jr : {(val + imm)[valueOf(AddrSz)-1:1], 1'b0};
Br : (taken? pc + imm : pcPlus4);
default : pcPlus4;
endcase);
return targetAddr;
endfunction
(* noinline *)
function ControlFlow getControlFlow(DecodedInst dInst, Data rVal1, Data rVal2, Addr pc, Addr ppc);
ControlFlow cf = unpack(0);
Bool taken = dInst.execFunc matches tagged Br .br_f ? aluBr(rVal1, rVal2, br_f) : False;
Addr nextPc = brAddrCalc(pc, rVal1, dInst.iType, validValue(getDInstImm(dInst)), taken);
Bool mispredict = nextPc != ppc;
cf.pc = pc;
cf.nextPc = nextPc;
cf.taken = taken;
cf.mispredict = mispredict;
return cf;
endfunction
(* noinline *)
function ExecResult basicExec(DecodedInst dInst, Data rVal1, Data rVal2, Addr pc, Addr ppc);
// just data, addr, and control flow
Data data = 0;
Data csr_data = 0;
Addr addr = 0;
ControlFlow cf = ControlFlow{pc: pc, nextPc: 0, taken: False, mispredict: False};
Data aluVal2 = fromMaybe(rVal2, getDInstImm(dInst)); //isValid(dInst.imm) ? fromMaybe(?, dInst.imm) : rVal2;
// Get the alu function. By default, it adds. This is used by memory instructions
AluFunc alu_f = dInst.execFunc matches tagged Alu .alu_f ? alu_f : Add;
Data alu_result = alu(rVal1, aluVal2, alu_f);
// Default branch function is not taken
BrFunc br_f = dInst.execFunc matches tagged Br .br_f ? br_f : NT;
cf.taken = aluBr(rVal1, rVal2, br_f);
cf.nextPc = brAddrCalc(pc, rVal1, dInst.iType, validValue(getDInstImm(dInst)), cf.taken);
cf.mispredict = cf.nextPc != ppc;
data = (case (dInst.iType)
St, Sc, Amo : rVal2;
J, Jr : (pc + 4); // could be computed with alu
Auipc : (pc + fromMaybe(?, getDInstImm(dInst))); // could be computed with alu
Csr : rVal1;
default : alu_result;
endcase);
csr_data = alu_result;
addr = (case (dInst.iType)
Ld, St, Lr, Sc, Amo : alu_result;
default : cf.nextPc;
endcase);
return ExecResult{data: data, csrData: csr_data, addr: addr, controlFlow: cf};
endfunction
(* noinline *)
function Maybe#(Exception) checkForException(
DecodedInst dInst,
ArchRegs regs,
CsrDecodeInfo csrState
); // regs needed to check if x0 is a src
Maybe#(Exception) exception = Invalid;
let prv = csrState.prv;
if(dInst.iType == Ecall) begin
exception = Valid (case(prv)
prvU: EnvCallU;
prvS: EnvCallS;
prvM: EnvCallM;
default: IllegalInst;
endcase);
end
else if(dInst.iType == Ebreak) begin
exception = Valid (Breakpoint);
end
else if(dInst.iType == Mret) begin
if(prv < prvM) begin
exception = Valid (IllegalInst);
end
end
else if(dInst.iType == Sret) begin
if(prv < prvS) begin
exception = Valid (IllegalInst);
end
else if(prv == prvS && csrState.trapSret) begin
exception = Valid (IllegalInst);
end
end
else if(dInst.iType == SFence) begin
if(prv == prvS && csrState.trapVM) begin
exception = Valid (IllegalInst);
end
end
else if(dInst.iType == Csr) begin
let csr = pack(fromMaybe(?, dInst.csr));
Bool csr_has_priv = (prv >= csr[9:8]);
if(!csr_has_priv) begin
exception = Valid (IllegalInst);
end
else if(prv == prvS && csrState.trapVM &&
validValue(dInst.csr) == CSRsatp) begin
exception = Valid (IllegalInst);
end
// TODO check permission for accessing cycle/inst/time, and check
// read-only CSRs being written
end
else if(dInst.iType == Fpu) begin
if(dInst.execFunc matches tagged Fpu .fpu_f) begin
// Get rounding mode
let rm = (fpu_f.rm == RDyn) ? unpack(csrState.frm) : fpu_f.rm;
case(rm)
RNE, RTZ, RDN, RUP, RMM: exception = exception; // legal modes
default : exception = Valid (IllegalInst);
endcase
end
else begin
// Fpu instruction without FPU execFunc
exception = Valid (IllegalInst);
end
end
return exception;
endfunction
// check mem access misaligned: byteEn is unshifted (just from Decode)
function Bool memAddrMisaligned(Addr addr, ByteEn byteEn);
if(byteEn[7]) begin
return addr[2:0] != 0;
end
else if(byteEn[3]) begin
return addr[1:0] != 0;
end
else if(byteEn[1]) begin
return addr[0] != 0;
end
else begin
return False;
end
endfunction
function Data gatherLoad(Addr addr, ByteEn byteEn, Bool unsignedLd, Data data);
function extend = unsignedLd ? zeroExtend : signExtend;
Bit#(IndxShamt) offset = truncate(addr);
if(byteEn[7]) begin
return extend(data);
end else if(byteEn[3]) begin
Vector#(2, Bit#(32)) dataVec = unpack(data);
return extend(dataVec[offset[2]]);
end else if(byteEn[1]) begin
Vector#(4, Bit#(16)) dataVec = unpack(data);
return extend(dataVec[offset[2:1]]);
end else begin
Vector#(8, Bit#(8)) dataVec = unpack(data);
return extend(dataVec[offset]);
end
endfunction
function Tuple2#(ByteEn, Data) scatterStore(Addr addr, ByteEn byteEn, Data data);
Bit#(IndxShamt) offset = truncate(addr);
if(byteEn[7]) begin
return tuple2(byteEn, data);
end else if(byteEn[3]) begin
return tuple2(unpack(pack(byteEn) << (offset)), data << {(offset), 3'b0});
end else if(byteEn[1]) begin
return tuple2(unpack(pack(byteEn) << (offset)), data << {(offset), 3'b0});
end else begin
return tuple2(unpack(pack(byteEn) << (offset)), data << {(offset), 3'b0});
end
endfunction

View File

@@ -0,0 +1,652 @@
// 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.
`include "ProcConfig.bsv"
/*
This file contains many FIFO implementations
1) Conflict Free FIFO with 2 elements
2) Pipeline FIFO with 1 element
3) Bypass FIFO with 1 element
4) Conflict Free FIFO with n elements (ptr based implementation)
5) Pipeline FIFO with n elements (ptr based implementation)
6) Searchable n-element FIFO has an extra search method added to the n-element FIFOs
a) Searchable conflict-free n-element FIFO
b) Searchable pipelined n-element FIFO
Clear always happens after enq and deq (but before canonicalize when applicable)
All these FIFOs have been tested with various pipelines. In particular n-element FIFO can be put in place of the respective 2-element or 1-element FIFO
*/
import Ehr::*;
import GetPut::*;
import Vector::*;
import FIFOF::*;
import Types::*; // for assertion
interface Fifo#(numeric type n, type t);
method Bool notFull;
method Action enq(t x);
method Bool notEmpty;
method Action deq;
method t first;
method Action clear;
endinterface
interface SupFifoEnq#(type t);
method Bool canEnq;
method Action enq(t x);
endinterface
interface SupFifoDeq#(type t);
method Bool canDeq;
method Action deq;
method t first;
endinterface
interface SupFifo#(numeric type k, numeric type n, type t);
interface Vector#(k, SupFifoEnq#(t)) enqS;
interface Vector#(k, SupFifoDeq#(t)) deqS;
method Bool internalEmpty; // for security
endinterface
function Integer getMaxIndex( FifoState#(n) s );
return valueOf(n)-1;
endfunction
interface FifoState#(numeric type n);
interface Reg#(Bit#(TLog#(n))) enqP;
interface Reg#(Bit#(TLog#(n))) deqP;
interface Reg#(Bool) empty;
interface Reg#(Bool) full;
method Bool isValidIndex(Integer i);
endinterface
function FifoState#(n) toFifoState( Reg#(Bit#(TLog#(n))) _enqP, Reg#(Bit#(TLog#(n))) _deqP, Reg#(Bool) _empty, Reg#(Bool) _full );
return (interface FifoState;
interface Reg enqP = _enqP;
interface Reg deqP = _deqP;
interface Reg empty = _empty;
interface Reg full = _full;
method Bool isValidIndex( Integer i );
Bool gte_deqP = fromInteger(i) >= _deqP;
Bool lt_enqP = fromInteger(i) < _enqP;
return _full || (_deqP > _enqP && (gte_deqP || lt_enqP)) || (_enqP > _deqP && gte_deqP && lt_enqP);
endmethod
endinterface);
endfunction
function Bool isValidIndex( FifoState#(n) s, Integer i );
Bool gte_deqP = fromInteger(i) >= s.deqP;
Bool lt_enqP = fromInteger(i) < s.enqP;
return s.full || (s.deqP > s.enqP && (gte_deqP || lt_enqP)) || (s.enqP > s.deqP && gte_deqP && lt_enqP);
endfunction
instance ToGet#(Fifo#(n, t), t);
function Get#(t) toGet(Fifo#(n, t) f);
return (interface Get;
method ActionValue#(t) get;
f.deq;
return f.first;
endmethod
endinterface);
endfunction
endinstance
instance ToPut#(Fifo#(n, t), t);
function Put#(t) toPut(Fifo#(n, t) f);
return (interface Put;
method Action put(t r);
f.enq(r);
endmethod
endinterface);
endfunction
endinstance
// instance ToGet#(SupFifo#(k,n, t), t);
// function Get#(Vector#(k,Maybe#(t))) toGet(SupFifo#(k,n, t) f);
// return (interface Get;
// method ActionValue#(Vector#(k,Maybe#(t)) get;
// //
// f.deq;
// return f.first;
// endmethod
// endinterface);
// endfunction
// endinstance
// instance ToPut#(SupFifo#(k,n, t), t);
// function Put#(Vector#(k,Maybe#(t))) toPut(SupFifo#(k,n, t) f);
// return (interface Put;
// method Action put(Vector#(k,Maybe#(t)) r);
//
// f.enq(r);
// endmethod
// endinterface);
// endfunction
// endinstance
module mkSupFifo(SupFifo#(k,n,t)) provisos (
Bits#(t,tSz),
FShow#(t),
Add#(TExp#(TLog#(k)), 0, k) // k must be power of 2
);
Vector#(k, FIFOF#(t)) internalFifos <- replicateM(mkUGSizedFIFOF(valueOf(n)));
//I need a custom counter here for now will work only for power of two
Ehr#(TAdd#(1,k), Bit#(TLog#(k))) enqueueFifo <- mkEhr(unpack(0));
Ehr#(TAdd#(1,k), Bit#(TLog#(k))) dequeueFifo <- mkEhr(unpack(0));
Vector#(k, Ehr#(2,Maybe#(t))) enqueueElement <- replicateM(mkEhr(tagged Invalid));
Vector#(k, Ehr#(2, Bool)) willDequeue <- replicateM(mkEhr(False));
function SupFifoEnq#(t) getEnqIfc(Integer i);
Bit#(TLog#(k)) fifoIdx = enqueueFifo[0]+fromInteger(i);
Bool can_enq = internalFifos[fifoIdx].notFull;
return (interface SupFifoEnq;
method Bool canEnq = can_enq;
method Action enq(t x) if(can_enq);
enqueueElement[i][0] <= tagged Valid x;
endmethod
endinterface);
endfunction
function SupFifoDeq#(t) getDeqIfc(Integer i);
Bit#(TLog#(k)) fifoIdx = dequeueFifo[0]+fromInteger(i);
Bool can_deq = internalFifos[fifoIdx].notEmpty;
return (interface SupFifoDeq;
method Bool canDeq = can_deq;
method t first if(can_deq);
return internalFifos[fifoIdx].first;
endmethod
method Action deq if(can_deq);
willDequeue[i][0] <= True;
endmethod
endinterface);
endfunction
(* fire_when_enabled, no_implicit_conditions *)
rule canonicalize;
for (Integer i = 0; i < valueOf(k); i = i+1) begin
if(enqueueElement[i][1] matches tagged Valid .el) begin
enqueueFifo[i] <= enqueueFifo[0]+fromInteger(i)+1;
internalFifos[enqueueFifo[0]+fromInteger(i)].enq(el);
doAssert(internalFifos[enqueueFifo[0]+fromInteger(i)].notFull, "FIFO must be not full");
if(i > 0) begin
doAssert(isValid(enqueueElement[i-1][1]), "FIFO enq must be consecutive");
end
end
if (willDequeue[i][1]) begin
dequeueFifo[i] <= dequeueFifo[0] + fromInteger(i) + 1;
internalFifos[dequeueFifo[0]+fromInteger(i)].deq;
doAssert(internalFifos[dequeueFifo[0]+fromInteger(i)].notEmpty, "FIFO must be not empty");
if(i > 0) begin
doAssert(willDequeue[i-1][1], "FIFO deq must be consecutive");
end
end
enqueueElement[i][1] <= tagged Invalid;
willDequeue[i][1] <= False;
end
endrule
Vector#(k,Integer) indexes = genVector;
interface Vector enqS = map(getEnqIfc, indexes);
interface Vector deqS = map(getDeqIfc, indexes);
method Bool internalEmpty;
function Bool isEmpty(Integer i) = !internalFifos[i].notEmpty;
return all(isEmpty, indexes);
endmethod
endmodule
// A Conflict free implementation of n element FIFO
// {notEmpty, first} < deq < clear < canonicalize
// notFull < enq < clear < canonicalize
// deq conflict free with enq
// canonicalize has no effect after clear anyway
// FIXME: This FIFO is broken because {enq, deq} can't be CF with {notEmpty, notFull}
module mkCFFifo( Fifo#(n, t) ) provisos (Bits#(t,tSz));
// n is size of fifo
// t is data type of fifo
// storage elements
Vector#(n, Reg#(t)) data <- replicateM(mkReg(unpack(0)));
Reg#(Bit#(TLog#(n))) enqP <- mkReg(0);
Reg#(Bit#(TLog#(n))) deqP <- mkReg(0);
Reg#(Bool) empty <- mkReg(True);
Reg#(Bool) full <- mkReg(False);
// requests
Ehr#(3, Maybe#(t)) enqReq <- mkEhr(tagged Invalid);
Ehr#(3, Maybe#(void)) deqReq <- mkEhr(tagged Invalid);
Ehr#(2, Maybe#(void)) clearReq <- mkEhr(tagged Invalid);
// useful value
Bit#(TLog#(n)) max_index = fromInteger(valueOf(n)-1);
// Update the state of the fifo to match any enqueue, dequeue, or clear
// These attributes are statically checked by the compiler
(* fire_when_enabled *) // WILL_FIRE == CAN_FIRE
(* no_implicit_conditions *) // CAN_FIRE == guard (True)
rule canonicalize;
if( isValid(clearReq[1]) ) begin
enqP <= 0;
deqP <= 0;
full <= False;
empty <= True;
end else begin
let next_enqP = enqP;
let next_deqP = deqP;
let next_full = full;
let next_empty = empty;
if( isValid(enqReq[2]) ) begin
data[enqP] <= fromMaybe(?, enqReq[2]);
next_enqP = (enqP == max_index) ? 0 : enqP + 1;
end
if( isValid(deqReq[2]) ) begin
next_deqP = (deqP == max_index) ? 0 : deqP + 1;
end
// compute next_full and next_empty
// XXX need to set both next_empty and next_full for 1-element FIFO
if( next_deqP == next_enqP ) begin
if( isValid(enqReq[2]) ) begin
// enqueued to full
next_full = True;
next_empty = False;
end else if( isValid(deqReq[2]) ) begin
// dequeued to empty
next_empty = True;
next_full = False;
end else begin
// no deq or enq, so no change
end
end else begin
// next_deqP != next_enqP so neither full nor empty
next_full = False;
next_empty = False;
end
// update the states
enqP <= next_enqP;
deqP <= next_deqP;
full <= next_full;
empty <= next_empty;
end
// clear pending requests
clearReq[1] <= tagged Invalid;
enqReq[2] <= tagged Invalid;
deqReq[2] <= tagged Invalid;
endrule
method Bool notFull = !full;
method Action enq(t x) if( !full );
// Tell later stages an enqueue was requested
enqReq[0] <= tagged Valid x;
endmethod
method Bool notEmpty = !empty;
method Action deq if( !empty );
// Tell later stages a dequeue was requested
deqReq[0] <= tagged Valid;
endmethod
method t first if( !empty );
return data[deqP];
endmethod
method Action clear;
// Clear any existing enq/deq requests
// gets desired {enq, deq} < clear schedule
enqReq[1] <= tagged Invalid;
deqReq[1] <= tagged Invalid;
// Tell later stages a clear was requested
clearReq[0] <= tagged Valid;
endmethod
endmodule
// A pipelined implementation of n element FIFO
// {notEmpty, first} < deq < notFull < enq < clear
module mkPipelineFifo( Fifo#(n, t) ) provisos (Bits#(t,tSz));
// n is size of fifo
// t is data type of fifo
Vector#(n, Reg#(t)) data <- replicateM(mkReg(unpack(0)));
Ehr#(2, Bit#(TLog#(n))) enqP <- mkEhr(0);
Ehr#(2, Bit#(TLog#(n))) deqP <- mkEhr(0);
Ehr#(3, Bool) empty <- mkEhr(True);
Ehr#(3, Bool) full <- mkEhr(False);
Bit#(TLog#(n)) max_index = fromInteger(valueOf(n)-1);
method Bool notFull = !full[1];
method Action enq(t x) if( !full[1] );
data[enqP[0]] <= x;
empty[1] <= False;
let next_enqP = (enqP[0] == max_index) ? 0 : enqP[0] + 1;
enqP[0] <= next_enqP;
if( next_enqP == deqP[1] ) begin
full[1] <= True;
end
endmethod
method Bool notEmpty = !empty[0];
method Action deq if( !empty[0] );
full[0] <= False;
let next_deqP = (deqP[0] == max_index) ? 0 : deqP[0] + 1;
deqP[0] <= next_deqP;
if( next_deqP == enqP[0] ) begin
empty[0] <= True;
end
endmethod
method t first if( !empty[0] );
return data[deqP[0]];
endmethod
method Action clear;
enqP[1] <= 0;
deqP[1] <= 0;
empty[2] <= True;
full[2] <= False;
endmethod
endmodule
// A Bypass implementation of n element FIFO
// notFull < enq < {notEmpty, first} < deq < clear
module mkBypassFifo( Fifo#(n, t) ) provisos (Bits#(t,tSz));
// n is size of fifo
// t is data type of fifo
Vector#(n, Ehr#(2,t)) data <- replicateM(mkEhr(?));
Ehr#(2, Bit#(TLog#(n))) enqP <- mkEhr(0);
Ehr#(2, Bit#(TLog#(n))) deqP <- mkEhr(0);
Ehr#(3, Bool) empty <- mkEhr(True);
Ehr#(3, Bool) full <- mkEhr(False);
Bit#(TLog#(n)) max_index = fromInteger(valueOf(n)-1);
method Bool notFull = !full[0];
method Action enq(t x) if( !full[0] );
data[enqP[0]][0] <= x;
empty[0] <= False;
let next_enqP = (enqP[0] == max_index) ? 0 : enqP[0] + 1;
enqP[0] <= next_enqP;
if( next_enqP == deqP[0] ) begin
full[0] <= True;
end
endmethod
method Bool notEmpty = !empty[1];
method Action deq if( !empty[1] );
full[1] <= False;
let next_deqP = (deqP[0] == max_index) ? 0 : deqP[0] + 1;
deqP[0] <= next_deqP;
if( next_deqP == enqP[1] ) begin
empty[1] <= True;
end
endmethod
method t first if( !empty[1] );
return data[deqP[0]][1];
endmethod
method Action clear;
enqP[1] <= 0;
deqP[1] <= 0;
empty[2] <= True;
full[2] <= False;
endmethod
endmodule
// Searchable FIFO has an extra search method
interface SFifo#(numeric type n, type t, type st);
method Bool notFull;
method Action enq(t x);
method Bool notEmpty;
method Action deq;
method Bool search(st s);
method t first;
method Action clear;
endinterface
// search is conflict-free with {enq, deq, first, notFull, notEmpty}
// search < clear < canonicalize
module mkCFSFifo#(function Bool isFound(t v, st k))(SFifo#(n, t, st)) provisos(Bits#(t, tSz), Add#(n, 1, n1), Log#(n1, sz), Add#(sz, 1, sz1));
Integer ni = valueOf(n);
Bit#(sz1) nb = fromInteger(ni);
Bit#(sz1) n2 = 2*nb;
Vector#(n, Reg#(t)) data <- replicateM(mkRegU);
Ehr#(3, Bit#(sz1)) enqP <- mkEhr(0);
Ehr#(3, Bit#(sz1)) deqP <- mkEhr(0);
Ehr#(3, Bool) enqEn <- mkEhr(True);
Ehr#(3, Bool) deqEn <- mkEhr(False);
Ehr#(2, t) tempData <- mkEhr(?);
Ehr#(2, Maybe#(Bit#(sz1))) tempEnqP <- mkEhr(Invalid);
Ehr#(2, Maybe#(Bit#(sz1))) tempDeqP <- mkEhr(Invalid);
Bit#(sz1) cnt0 = enqP[0] >= deqP[0]? enqP[0] - deqP[0]:
(enqP[0]%nb + nb) - deqP[0]%nb;
Bit#(sz1) cnt2 = enqP[2] >= deqP[2]? enqP[2] - deqP[2]:
(enqP[2]%nb + nb) - deqP[2]%nb;
rule canonicalize;
if(!enqEn[2] && cnt2 != nb) enqEn[2] <= True;
if(!deqEn[2] && cnt2 != 0) deqEn[2] <= True;
if(isValid(tempEnqP[1]))
begin
data[validValue(tempEnqP[1])] <= tempData[1];
tempEnqP[1] <= Invalid;
end
if(isValid(tempDeqP[1]))
begin
deqP[0] <= validValue(tempDeqP[1]);
tempDeqP[1] <= Invalid;
end
endrule
method Bool notFull = enqEn[0];
method Action enq(t x) if(enqEn[0]);
tempData[0] <= x;
tempEnqP[0] <= Valid (enqP[0]%nb);
enqP[0] <= (enqP[0] + 1)%n2;
enqEn[0] <= False;
endmethod
method Bool notEmpty = deqEn[0];
method Action deq if(deqEn[0]);
tempDeqP[0] <= Valid ((deqP[0] + 1)%n2);
deqEn[0] <= False;
endmethod
method t first if(deqEn[0]);
return data[deqP[0]%nb];
endmethod
method Bool search(st s);
Bool ret = False;
for(Bit#(sz1) i = 0; i < nb; i = i + 1)
begin
let ptr = (deqP[0] + i)%nb;
if(isFound(data[ptr], s) && i < cnt0)
ret = True;
end
return ret;
endmethod
method Action clear;
enqP[1] <= 0;
deqP[1] <= 0;
enqEn[1] <= True;
deqEn[1] <= False;
endmethod
endmodule
// {notEmpty, first} < deq < search
// search CF {enq, notFull}
// search < clear
module mkPipelineSFifo#(function Bool isFound(t v, st k))(SFifo#(n, t, st)) provisos(Bits#(t, tSz), Add#(n, 1, n1), Log#(n1, sz), Add#(sz, 1, sz1), Bits#(st, stz));
Integer ni = valueOf(n);
Bit#(sz1) nb = fromInteger(ni);
Bit#(sz1) n2 = 2*nb;
Vector#(n, Reg#(t)) data <- replicateM(mkRegU);
Ehr#(3, Bit#(sz1)) enqP <- mkEhr(0);
Ehr#(2, Bit#(sz1)) deqP <- mkEhr(0);
Bit#(sz1) cnt0 = enqP[0] >= deqP[0]? enqP[0] - deqP[0]:
(enqP[0]%nb + nb) - deqP[0]%nb;
Bit#(sz1) cnt1 = enqP[0] >= deqP[1]? enqP[0] - deqP[1]:
(enqP[0]%nb + nb) - deqP[1]%nb;
method Bool notFull = cnt1 < nb;
method Action enq(t x) if(cnt1 < nb);
enqP[0] <= (enqP[0] + 1)%n2;
data[enqP[0]%nb] <= x;
endmethod
method Bool notEmpty = cnt0 != 0;
method Action deq if(cnt0 != 0);
deqP[0] <= (deqP[0] + 1)%n2;
endmethod
method t first if(cnt0 != 0);
return data[deqP[0]%nb];
endmethod
method Bool search(st s);
Bool ret = False;
for(Bit#(sz1) i = 0; i < nb; i = i + 1)
begin
let ptr = (deqP[1] + i)%nb;
if(isFound(data[ptr], s) && i < cnt1)
ret = True;
end
return ret;
endmethod
method Action clear;
enqP[2] <= 0;
deqP[1] <= 0;
endmethod
endmodule
// Searchable Count FIFO has an extra search method which returns the count of the number of elements found
interface SCountFifo#(numeric type n, type t, type st);
method Bool notFull;
method Action enq(t x);
method Bool notEmpty;
method Action deq;
method Bit#(TLog#(TAdd#(n, 1))) search(st s);
method t first;
method Action clear;
endinterface
// search is conflict-free with {enq, deq, first, notFull, notEmpty}
// search < clear < canonicalize
module mkCFSCountFifo#(function Bool isFound(t v, st k))(SCountFifo#(n, t, st)) provisos(Bits#(t, tSz), Add#(n, 1, n1), Log#(n1, sz), Add#(sz, 1, sz1));
Integer ni = valueOf(n);
Bit#(sz1) nb = fromInteger(ni);
Bit#(sz1) n2 = 2*nb;
Vector#(n, Reg#(t)) data <- replicateM(mkRegU);
Ehr#(3, Bit#(sz1)) enqP <- mkEhr(0);
Ehr#(3, Bit#(sz1)) deqP <- mkEhr(0);
Ehr#(3, Bool) enqEn <- mkEhr(True);
Ehr#(3, Bool) deqEn <- mkEhr(False);
Ehr#(2, t) tempData <- mkEhr(?);
Ehr#(2, Maybe#(Bit#(sz1))) tempEnqP <- mkEhr(Invalid);
Ehr#(2, Maybe#(Bit#(sz1))) tempDeqP <- mkEhr(Invalid);
Bit#(sz1) cnt0 = enqP[0] >= deqP[0]? enqP[0] - deqP[0]:
(enqP[0]%nb + nb) - deqP[0]%nb;
Bit#(sz1) cnt2 = enqP[2] >= deqP[2]? enqP[2] - deqP[2]:
(enqP[2]%nb + nb) - deqP[2]%nb;
rule canonicalize;
if(!enqEn[2] && cnt2 != nb) enqEn[2] <= True;
if(!deqEn[2] && cnt2 != 0) deqEn[2] <= True;
if(isValid(tempEnqP[1]))
begin
data[validValue(tempEnqP[1])] <= tempData[1];
tempEnqP[1] <= Invalid;
end
if(isValid(tempDeqP[1]))
begin
deqP[0] <= validValue(tempDeqP[1]);
tempDeqP[1] <= Invalid;
end
endrule
method Bool notFull = enqEn[0];
method Action enq(t x) if(enqEn[0]);
tempData[0] <= x;
tempEnqP[0] <= Valid (enqP[0]%nb);
enqP[0] <= (enqP[0] + 1)%n2;
enqEn[0] <= False;
endmethod
method Bool notEmpty = deqEn[0];
method Action deq if(deqEn[0]);
tempDeqP[0] <= Valid ((deqP[0] + 1)%n2);
deqEn[0] <= False;
endmethod
method t first if(deqEn[0]);
return data[deqP[0]%nb];
endmethod
method Bit#(TLog#(TAdd#(n, 1))) search(st s);
Bit#(TLog#(TAdd#(n, 1))) ret = 0;
for(Bit#(sz1) i = 0; i < nb; i = i + 1)
begin
let ptr = (deqP[0] + i)%nb;
if(isFound(data[ptr], s) && i < cnt0)
ret = ret + 1;
end
return ret;
endmethod
method Action clear;
enqP[1] <= 0;
deqP[1] <= 0;
enqEn[1] <= True;
deqEn[1] <= False;
endmethod
endmodule

View File

@@ -0,0 +1,892 @@
// Copyright (c) 2016, 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.
`include "ProcConfig.bsv"
// Fpu.bsv
// This file contains modules needed for hardware floating-point arithmetic.
// The FpuExec module abstracts away the ISA implementation simplifying the
// requirements for the hardware FPU units.
import BuildVector::*;
import Types::*;
import ProcTypes::*;
import FIFO::*;
import FIFOF::*;
import ClientServer::*;
import GetPut::*;
import Divide::*;
import SquareRoot::*;
import FloatingPoint::*;
import XilinxFpu::*;
import HasSpecBits::*;
import SpecFifo::*;
import SpecPoisonFifo::*;
export FpuResult(..);
export FpuResp(..);
export FpuExec(..);
export mkFpuExecPipeline;
typedef FloatingPoint::RoundMode FpuRoundMode;
typedef FloatingPoint::Exception FpuException;
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
typedef struct {
Data data;
Bit#(5) fflags;
} FpuResult deriving(Bits, Eq, FShow);
typedef struct {
FpuResult res;
Maybe#(PhyDst) dst;
InstTag tag;
// spec bits is not used in later stage, so not included here
} FpuResp deriving(Bits, Eq, FShow);
interface FpuExec;
// input req
method Action exec(FpuInst fpu_inst, Data rVal1, Data rVal2, Data rVal3,
Maybe#(PhyDst) dst, InstTag tag, SpecBits specBits);
// output
method ActionValue#(FpuResp) simpleResp;
method ActionValue#(FpuResp) fmaResp;
method ActionValue#(FpuResp) divResp;
method ActionValue#(FpuResp) sqrtResp;
// speculation
interface SpeculationUpdate specUpdate;
endinterface
(* synthesize *)
module mkDoubleDiv(Server#(Tuple3#(Double, Double, FpuRoundMode), Tuple2#(Double, FpuException)));
`ifdef USE_XILINX_FPU
let fpu <- mkXilinxFpDiv;
`else
let int_div <- mkDivider(1); // [sizhuo] size in RVFpu: 2
let fpu <- mkFloatingPointDivider(int_div);
`endif
return fpu;
endmodule
(* synthesize *)
module mkDoubleSqrt(Server#(Tuple2#(Double, FpuRoundMode), Tuple2#(Double, FpuException)));
`ifdef USE_XILINX_FPU
let fpu <- mkXilinxFpSqrt;
`else
let int_sqrt <- mkSquareRooter(1); // [sizhuo] size in RVFpu: 3
let fpu <- mkFloatingPointSquareRooter(int_sqrt);
`endif
return fpu;
endmodule
// Bluespec FMA is a + b * c, and we have wrap Xilinx FMA also into this form
(* synthesize *)
module mkDoubleFMA(Server#(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode), Tuple2#(Double, FpuException)));
`ifdef USE_XILINX_FPU
let fpu <- mkXilinxFpFma;
`else
let fpu <- mkFloatingPointFusedMultiplyAccumulate;
`endif
return fpu;
endmodule
// wrap FMA to an adder
function Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) getAddReqToFma(Tuple3#(Double, Double, FpuRoundMode) x);
let in1 = tpl_1(x);
let in2 = tpl_2(x);
let rm = tpl_3(x);
Double one_const = one(False);
return tuple4(tagged Valid in1, in2, one_const, rm);
endfunction
// wrap FMA to a multiplier
function Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) getMulReqToFma(Tuple3#(Double, Double, FpuRoundMode) x);
let in1 = tpl_1(x);
let in2 = tpl_2(x);
let rm = tpl_3(x);
return tuple4(tagged Invalid, in1, in2, rm);
endfunction
// FCVT float -> float functions
function Tuple2#(Double, FpuException) fcvt_d_s (Float in, FpuRoundMode rmode);
return convert(in, rmode, True);
endfunction
function Tuple2#(Float, FpuException) fcvt_s_d (Double in, FpuRoundMode rmode);
return convert(in, rmode, True);
endfunction
// FCVT float -> int functions
function Tuple2#(Bit#(64), FpuException) fcvt_l_f (FloatingPoint#(e,m) in, FpuRoundMode rmode);
return float_to_int(in, False, False, rmode);
endfunction
function Tuple2#(Bit#(64), FpuException) fcvt_lu_f (FloatingPoint#(e,m) in, FpuRoundMode rmode);
return float_to_int(in, False, True, rmode);
endfunction
function Tuple2#(Bit#(64), FpuException) fcvt_w_f (FloatingPoint#(e,m) in, FpuRoundMode rmode);
return float_to_int(in, True, False, rmode);
endfunction
function Tuple2#(Bit#(64), FpuException) fcvt_wu_f (FloatingPoint#(e,m) in, FpuRoundMode rmode);
return float_to_int(in, True, True, rmode);
endfunction
// FCVT int -> float functions
function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_l (Bit#(64) in_bits, FpuRoundMode rmode)
provisos (FixedFloatCVT#(FloatingPoint#(e, m), Int#(64)));
Int#(64) in = unpack(in_bits);
return vFixedToFloat(in, 1'b0, rmode);
endfunction
function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_lu (Bit#(64) in_bits, FpuRoundMode rmode)
provisos (FixedFloatCVT#(FloatingPoint#(e, m), UInt#(64)));
UInt#(64) in = unpack(in_bits);
return vFixedToFloat(in, 1'b0, rmode);
endfunction
function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_w (Bit#(64) in_bits, FpuRoundMode rmode)
provisos (FixedFloatCVT#(FloatingPoint#(e, m), Int#(32)));
Int#(32) in = unpack(truncate(in_bits));
return vFixedToFloat(in, 1'b0, rmode);
endfunction
function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_wu (Bit#(64) in_bits, FpuRoundMode rmode)
provisos (FixedFloatCVT#(FloatingPoint#(e, m), UInt#(32)));
UInt#(32) in = unpack(truncate(in_bits));
return vFixedToFloat(in, 1'b0, rmode);
endfunction
function Tuple2#(Bit#(64), FpuException) fmin_s(Bit#(64) in1, Bit#(64) in2);
Float in1_f = unpack(in1[31:0]);
Float in2_f = unpack(in2[31:0]);
Float nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
e.invalid_op = True;
return tuple2(zeroExtend(pack(nan_f)), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
end else begin
let signLT = (in1_f.sign && !in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absLT = {in1_f.exp, in1_f.sfd} < {in2_f.exp, in2_f.sfd};
if (signLT || (signEQ && (in1_f.sign ? !absLT : absLT))) begin
return tuple2(in1, e);
end else begin
return tuple2(in2, e);
end
end
endfunction
function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
Double in1_f = unpack(in1);
Double in2_f = unpack(in2);
Double nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
e.invalid_op = True;
return tuple2(pack(nan_f), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
end else begin
let signLT = (in1_f.sign && !in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absLT = {in1_f.exp, in1_f.sfd} < {in2_f.exp, in2_f.sfd};
if (signLT || (signEQ && (in1_f.sign ? !absLT : absLT))) begin
return tuple2(in1, e);
end else begin
return tuple2(in2, e);
end
end
endfunction
function Tuple2#(Bit#(64), FpuException) fmax_s(Bit#(64) in1, Bit#(64) in2);
Float in1_f = unpack(in1[31:0]);
Float in2_f = unpack(in2[31:0]);
Float nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
e.invalid_op = True;
return tuple2(zeroExtend(pack(nan_f)), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
end else begin
let signGT = (!in1_f.sign && in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absGT = {in1_f.exp, in1_f.sfd} > {in2_f.exp, in2_f.sfd};
if (signGT || (signEQ && (in1_f.sign ? !absGT : absGT))) begin
return tuple2(in1, e);
end else begin
return tuple2(in2, e);
end
end
endfunction
function Tuple2#(Bit#(64), FpuException) fmax_d(Bit#(64) in1, Bit#(64) in2);
Double in1_f = unpack(in1);
Double in2_f = unpack(in2);
Double nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
e.invalid_op = True;
return tuple2(pack(nan_f), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
end else begin
let signGT = (!in1_f.sign && in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absGT = {in1_f.exp, in1_f.sfd} > {in2_f.exp, in2_f.sfd};
if (signGT || (signEQ && (in1_f.sign ? !absGT : absGT))) begin
return tuple2(in1, e);
end else begin
return tuple2(in2, e);
end
end
endfunction
function Tuple2#(Bit#(64), FpuException) float_to_int(
FloatingPoint#(e, m) in, Bool is_32bit, Bool is_unsigned, FpuRoundMode rmode
);
// 3 cases of exponents:
Bit#(64) out = 0;
Bit#(64) max_val = is_unsigned ? '1 : (is_32bit ? 64'h000000007FFFFFFF : 64'h7FFFFFFFFFFFFFFF);
Bit#(64) min_val = is_unsigned ? 0 : (is_32bit ? 64'hFFFFFFFF80000000 : 64'h8000000000000000);
FpuException exc = unpack(0);
if (isNaN(in)) begin
out = max_val;
exc.invalid_op = True;
end else if (isInfinity(in)) begin
out = in.sign ? min_val : max_val;
exc.invalid_op = True;
end else if (isZero(in)) begin
out = 0;
end else begin
// Now actually do the conversion
Int#(TAdd#(e,TLog#(m))) bias_exp = fromInteger((2**(valueOf(e)-1))-1);
Int#(TAdd#(e,TLog#(m))) in_exp = unpack(zeroExtend(in.exp));
// The bottom two bits of int_val will be fractional data.
// The bottom bit holds information about all bits with lesser
// significance that were shifted out - same for top bit - this is
// necessary for rounding and overflow detection.
Bit#(TAdd#(66,m)) int_val = {64'b1, in.sfd, 2'b0}; // this is 2**m times larger than it should be - we will shift this to correct for that
int_val = saturating_shift_right(int_val, fromInteger(valueOf(m)) + bias_exp - in_exp);
// do rounding
// 00 : exact
// 01 : < 0.5
// 10 : = 0.5
// 11 : > 0.5
Bool round_up = False; // by magnitude (default behavior will be drop bits)
if (int_val[1:0] != 0) begin
exc.inexact = True;
case (rmode)
Rnd_Nearest_Even: round_up = (int_val[1:0] == 2'b11) || ((int_val[1:0] == 2'b10) && (int_val[2] == 1));
Rnd_Nearest_Away_Zero: round_up = (int_val[1] == 1);
Rnd_Plus_Inf: round_up = !in.sign;
Rnd_Minus_Inf: round_up = in.sign;
Rnd_Zero: round_up = False;
endcase
end
// Take the integer part of int_val and round it up if necessary
Bit#(TAdd#(64,m)) int_val_rnd = truncateLSB(int_val) + (round_up ? 1 : 0);
// correct the output sign for negative numbers rounded to 0
Bool out_sign = (int_val_rnd == 0) ? False : in.sign;
// Now check to see if int_val_rnd is in range
Bit#(TAdd#(64,m)) mask_32bit = {0, 32'hFFFFFFFF};
Bit#(TAdd#(64,m)) mask_64bit = {0, 64'hFFFFFFFFFFFFFFFF};
if (is_unsigned) begin
if (out_sign) begin
// negative number - out of range
out = 0;
exc.invalid_op = True;
end else begin
// positive number
if (is_32bit) begin
// WU
if ((int_val_rnd & mask_32bit) == int_val_rnd) begin
Bit#(32) val = truncate(int_val_rnd);
out = signExtend(val);
end else begin
// out of range
out = '1;
exc.invalid_op = True;
end
end else begin
// LU
if ((int_val_rnd & mask_64bit) == int_val_rnd) begin
out = truncate(int_val_rnd);
end else begin
// out of range
out = '1;
exc.invalid_op = True;
end
end
end
end else begin
// signed
if (is_32bit) begin
// W
Bit#(32) max_val = out_sign ? 32'h80000000 : 32'h7FFFFFFF;
if (((int_val_rnd & mask_32bit) == int_val_rnd) && (truncate(int_val_rnd) <= max_val)) begin
Bit#(32) val = truncate(int_val_rnd);
val = out_sign ? ((~val) + 1) : val;
out = signExtend(val);
end else begin
// out of range
out = signExtend(max_val);
exc.invalid_op = True;
end
end else begin
// L
Bit#(64) max_val = out_sign ? 64'h8000000000000000 : 64'h7FFFFFFFFFFFFFFF;
if (((int_val_rnd & mask_64bit) == int_val_rnd) && (truncate(int_val_rnd) <= max_val)) begin
Bit#(64) val = truncate(int_val_rnd);
out = out_sign ? ((~val) + 1) : val;
end else begin
// out of range
out = max_val;
exc.invalid_op = True;
end
end
end
end
if (exc.invalid_op) begin
// by convention
exc.inexact = False;
end
return tuple2(out, exc);
endfunction
function Bit#(n) saturating_shift_right(Bit#(n) in, Int#(m) amt)
provisos (Add#(a__,1,n));
// This function saturates in each direction
Bool amt_sign = msb(amt) == 1;
Bit#(m) amt_abs = amt_sign ? ((~pack(amt))+1) : pack(amt);
Bit#(n) shifted = amt_sign ? (in << amt_abs) : (in >> amt_abs);
Bit#(n) shifted_out_mask = amt_sign ? ~('1 >> amt_abs) : ~('1 << amt_abs);
Bit#(1) saturated_bit = |(in & shifted_out_mask);
shifted = amt_sign ? (shifted | {saturated_bit, 0}) : (shifted | {0, saturated_bit});
return shifted;
endfunction
// exec function for simple operations
(* noinline *)
function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
FpuResult fpu_result = FpuResult{data: 0, fflags: 0};
// Convert the Risc-V RVRoundMode to FloatingPoint::RoundMode
FpuRoundMode fpu_rm = (case (fpu_inst.rm)
RNE: Rnd_Nearest_Even;
RTZ: Rnd_Zero;
RDN: Rnd_Minus_Inf;
RUP: Rnd_Plus_Inf;
RMM: Rnd_Nearest_Away_Zero;
RDyn: Rnd_Nearest_Even;
default: Rnd_Nearest_Even;
endcase);
if (fpu_inst.precision == Single) begin
// single precision
Float in1 = unpack(rVal1[31:0]);
Float in2 = unpack(rVal2[31:0]);
Float dst = unpack(0);
Maybe#(Data) full_dst = Invalid;
FpuException e = unpack(0);
let fpu_f = fpu_inst.func;
// Fpu Decoding
case (fpu_f)
// combinational instructions
FMin: begin
Data x;
{x, e} = fmin_s(rVal1, rVal2);
full_dst = tagged Valid x;
end
FMax: begin
Data x;
{x, e} = fmax_s(rVal1, rVal2);
full_dst = tagged Valid x;
end
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
FLt: begin
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
end
FLe: begin
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
end
// CLASS functions
FClass: begin
Bool exp_0s = (in1.exp == 0);
Bool exp_1s = (in1.exp == '1);
Bool sfd_0s = (in1.sfd == 0);
Bit#(10) res = 0;
res[0] = pack(in1.sign && exp_1s && sfd_0s); // -inf
res[1] = pack(in1.sign && !exp_1s && !exp_0s); // -normal
res[2] = pack(in1.sign && exp_0s && !sfd_0s); // -subnormal
res[3] = pack(in1.sign && exp_0s && sfd_0s); // -0
res[4] = pack(!in1.sign && exp_0s && sfd_0s); // +0
res[5] = pack(!in1.sign && exp_0s && !sfd_0s); // +subnormal
res[6] = pack(!in1.sign && !exp_1s && !exp_0s); // +normal
res[7] = pack(!in1.sign && exp_1s && sfd_0s); // -inf
res[8] = pack(exp_1s && !sfd_0s && (msb(in1.sfd) == 0)); // signaling NaN
res[9] = pack(exp_1s && !sfd_0s && (msb(in1.sfd) == 1)); // quiet NaN
full_dst = tagged Valid zeroExtend(res);
end
// Sign Injection
FSgnj: begin
dst = in1;
dst.sign = in2.sign;
end
FSgnjn: begin
dst = in1;
dst.sign = !in2.sign;
end
FSgnjx: begin
dst = in1;
dst.sign = unpack(pack(in1.sign) ^ pack(in2.sign));
end
// Float -> Bits
FMv_XF: full_dst = tagged Valid signExtend(pack(in1));
// Bits -> Float
FMv_FX: full_dst = tagged Valid zeroExtend(pack(in1));
// Float -> Float
FCvt_FF: begin
Double in1_double = unpack(rVal1);
{dst, e} = fcvt_s_d(in1_double, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
// Float -> Int
FCvt_WF: begin
Data dst_bits;
{dst_bits, e} = fcvt_w_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
FCvt_WUF: begin
Data dst_bits;
{dst_bits, e} = fcvt_wu_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
FCvt_LF: begin
Data dst_bits;
{dst_bits, e} = fcvt_l_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
FCvt_LUF: begin
Data dst_bits;
{dst_bits, e} = fcvt_lu_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
// Int -> Float
FCvt_FW: begin
{dst, e} = fcvt_f_w(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
FCvt_FWU: begin
{dst, e} = fcvt_f_wu(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
FCvt_FL: begin
{dst, e} = fcvt_f_l(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
FCvt_FLU: begin
{dst, e} = fcvt_f_lu(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
endcase
fpu_result.data = (full_dst matches tagged Valid .data ? data : zeroExtend(pack(dst)));
fpu_result.fflags = pack(e);
end else if (fpu_inst.precision == Double) begin
// double precision
Double in1 = unpack(rVal1);
Double in2 = unpack(rVal2);
Double dst = unpack(0);
Maybe#(Data) full_dst = Invalid;
FpuException e = unpack(0);
let fpu_f = fpu_inst.func;
// Fpu Decoding
case (fpu_f)
// combinational instructions
FMin: begin
Data x;
{x, e} = fmin_d(rVal1, rVal2);
full_dst = tagged Valid x;
end
FMax: begin
Data x;
{x, e} = fmax_d(rVal1, rVal2);
full_dst = tagged Valid x;
end
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
FLt: begin
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
end
FLe: begin
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
end
// CLASS functions
FClass: begin
Bool exp_0s = (in1.exp == 0);
Bool exp_1s = (in1.exp == '1);
Bool sfd_0s = (in1.sfd == 0);
Bit#(10) res = 0;
res[0] = pack(in1.sign && exp_1s && sfd_0s); // -inf
res[1] = pack(in1.sign && !exp_1s && !exp_0s); // -normal
res[2] = pack(in1.sign && exp_0s && !sfd_0s); // -subnormal
res[3] = pack(in1.sign && exp_0s && sfd_0s); // -0
res[4] = pack(!in1.sign && exp_0s && sfd_0s); // +0
res[5] = pack(!in1.sign && exp_0s && !sfd_0s); // +subnormal
res[6] = pack(!in1.sign && !exp_1s && !exp_0s); // +normal
res[7] = pack(!in1.sign && exp_1s && sfd_0s); // -inf
res[8] = pack(exp_1s && !sfd_0s && (msb(in1.sfd) == 0)); // signaling NaN
res[9] = pack(exp_1s && !sfd_0s && (msb(in1.sfd) == 1)); // quiet NaN
full_dst = tagged Valid zeroExtend(res);
end
// Sign Injection
FSgnj: begin
dst = in1;
dst.sign = in2.sign;
end
FSgnjn: begin
dst = in1;
dst.sign = !in2.sign;
end
FSgnjx: begin
dst = in1;
dst.sign = unpack(pack(in1.sign) ^ pack(in2.sign));
end
// Float -> Bits
FMv_XF: full_dst = tagged Valid pack(in1);
// Bits -> Float
FMv_FX: full_dst = tagged Valid pack(in1);
// Float -> Float
FCvt_FF: begin
Float in1_float = unpack(rVal1[31:0]);
{dst, e} = fcvt_d_s(in1_float, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
// Float -> Int
FCvt_WF: begin
Data dst_bits;
{dst_bits, e} = fcvt_w_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
FCvt_WUF: begin
Data dst_bits;
{dst_bits, e} = fcvt_wu_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
FCvt_LF: begin
Data dst_bits;
{dst_bits, e} = fcvt_l_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
FCvt_LUF: begin
Data dst_bits;
{dst_bits, e} = fcvt_lu_f(in1, fpu_rm);
full_dst = tagged Valid dst_bits;
end
// Int -> Float
FCvt_FW: begin
{dst, e} = fcvt_f_w(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
FCvt_FWU: begin
{dst, e} = fcvt_f_wu(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
FCvt_FL: begin
{dst, e} = fcvt_f_l(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
FCvt_FLU: begin
{dst, e} = fcvt_f_lu(rVal1, fpu_rm);
if (isNaN(dst)) dst = canonicalNaN;
end
endcase
fpu_result.data = (full_dst matches tagged Valid .data ? data : pack(dst));
fpu_result.fflags = pack(e);
end
return fpu_result;
endfunction
// Spec FIFO in parallel with FP units. Div and Sqrt should not be frequent, so
// we use small FIFOs. FMA may be mroe frequent, so match its latency
typedef enum {Fma, Div, Sqrt} FpuFuncUnit deriving(Bits, Eq, FShow);
typedef struct {
// fpu inst specific
FpuRoundMode roundMode;
FpuPrecision precision;
FpuException exc_conv_in; // exception during convert single input to double
Bool negateResult;
// generic bookkeeping
Maybe#(PhyDst) dst;
InstTag tag;
} FpuExecInfo deriving(Bits, Eq, FShow);
typedef SpecPoisonFifo#(n, FpuExecInfo) FpuExecQ#(numeric type n);
module mkFpuExecQ(FpuExecQ#(n));
let m <- mkSpecPoisonFifo(True); // lazy enq
return m;
endmodule
typedef FpuExecQ#(2) MinimumExecQ;
(* synthesize *)
module mkMinimumExecQ(MinimumExecQ);
let m <- mkFpuExecQ;
return m;
endmodule
typedef FpuExecQ#(`BOOKKEEPING_FP_FMA_SIZE) FmaExecQ;
(* synthesize *)
module mkFmaExecQ(FmaExecQ);
let m <- mkFpuExecQ;
return m;
endmodule
// for simple ops that do not go to func units, we have a respQ for them
typedef SpecFifo_SB_deq_enq_C_deq_enq#(2, FpuResp) SimpleRespQ;
(* synthesize *)
module mkSimpleRespQ(SimpleRespQ);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(True); // lazy enq
return m;
endmodule
// don't synthesize to optimize guard for exec method
module mkFpuExecPipeline(FpuExec);
// simple req that can be done combinationally
let simpleQ <- mkSimpleRespQ;
// spec fifos in parallel with each func unit
let fmaQ <- mkFmaExecQ;
let divQ <- mkMinimumExecQ;
let sqrtQ <- mkMinimumExecQ;
// Pipelined units
let double_fma <- mkDoubleFMA;
let double_div <- mkDoubleDiv;
let double_sqrt <- mkDoubleSqrt;
// post processing of results that come out of function unit
function FpuResp finalizeResp(FpuExecInfo info, Double out, FpuException exc_op);
FpuResult res;
if(info.precision == Single) begin
// convert out back to single
let {out_f, exc_conv_out} = fcvt_s_d(out, info.roundMode);
// negate result if needed
if(info.negateResult) begin
out_f = -out_f;
end
// canonicalize NaN
out_f = isNaN(out_f) ? canonicalNaN : out_f;
res = FpuResult {
data: zeroExtend(pack(out_f)),
fflags: pack(info.exc_conv_in | exc_op | exc_conv_out)
};
end
else begin
// no convert is needed
// negate result if needed
if(info.negateResult) begin
out = -out;
end
// canonicalize NaN
out = isNaN(out) ? canonicalNaN : out;
res = FpuResult {
data: pack(out),
fflags: pack(exc_op) // info.exc_conv_in should be 0
};
end
return FpuResp {
res: res,
dst: info.dst,
tag: info.tag
};
endfunction
// drain poisoned insts
rule deqFmaPoisoned(fmaQ.first_poisoned);
fmaQ.deq;
let x <- double_fma.response.get;
endrule
rule deqDivPoisoned(divQ.first_poisoned);
divQ.deq;
let x <- double_div.response.get;
endrule
rule deqSqrtPoisoned(sqrtQ.first_poisoned);
sqrtQ.deq;
let x <- double_sqrt.response.get;
endrule
method Action exec(FpuInst fpu_inst, Data rVal1, Data rVal2, Data rVal3,
Maybe#(PhyDst) dst, InstTag tag, SpecBits spec_bits);
// Convert the Risc-V RVRoundMode to FloatingPoint::RoundMode
FpuRoundMode fpu_rm = (case (fpu_inst.rm)
RNE: Rnd_Nearest_Even;
RTZ: Rnd_Zero;
RDN: Rnd_Minus_Inf;
RUP: Rnd_Plus_Inf;
RMM: Rnd_Nearest_Away_Zero;
RDyn: Rnd_Nearest_Even;
default: Rnd_Nearest_Even;
endcase);
// convert float inputs to double: may have exceptions
FpuException exc_conv = unpack(0);
Double in1 = unpack(rVal1);
Double in2 = unpack(rVal2);
Double in3 = unpack(rVal3);
if (fpu_inst.precision == Single) begin
// conver single to double
Float f1 = unpack(rVal1[31:0]);
Float f2 = unpack(rVal2[31:0]);
Float f3 = unpack(rVal3[31:0]);
let {d1, exc1} = fcvt_d_s(f1, fpu_rm);
let {d2, exc2} = fcvt_d_s(f2, fpu_rm);
let {d3, exc3} = fcvt_d_s(f3, fpu_rm);
in1 = d1;
in2 = d2;
in3 = d3;
// get exception
case (fpu_inst.func)
FSqrt: begin
exc_conv = exc1;
end
FAdd, FSub, FMul, FDiv: begin
exc_conv = exc1 | exc2;
end
FMAdd, FMSub, FNMSub, FNMAdd: begin
exc_conv = exc1 | exc2 | exc3;
end
endcase
end
// request function unit
case (fpu_inst.func)
FAdd: double_fma.request.put(getAddReqToFma(tuple3(in1, in2, fpu_rm)));
FSub: double_fma.request.put(getAddReqToFma(tuple3(in1, -in2, fpu_rm)));
FMul: double_fma.request.put(getMulReqToFma(tuple3(in1, in2, fpu_rm)));
FDiv: double_div.request.put(tuple3(in1, in2, fpu_rm));
FSqrt: double_sqrt.request.put(tuple2(in1, fpu_rm));
// Bluespec FMA(a, b, c) is a + b * c, RISC-V ISA needs in1 *
// in2 + in3, so we need to shuffle the inputs
FMAdd: double_fma.request.put(tuple4(tagged Valid in3, in1, in2, fpu_rm));
FMSub: double_fma.request.put(tuple4(tagged Valid (-in3), in1, in2, fpu_rm));
FNMSub: double_fma.request.put(tuple4(tagged Valid (-in3), in1, in2, fpu_rm));
FNMAdd: double_fma.request.put(tuple4(tagged Valid in3, in1, in2, fpu_rm));
endcase
// enq to spec fifo
let info = FpuExecInfo {
roundMode: fpu_rm,
precision: fpu_inst.precision,
exc_conv_in: exc_conv,
negateResult: fpu_inst.func == FNMSub || fpu_inst.func == FNMAdd,
dst: dst,
tag: tag
};
case (fpu_inst.func)
FAdd, FSub, FMul, FMAdd, FMSub, FNMSub, FNMAdd: begin
fmaQ.enq(ToSpecFifo {
data: info,
spec_bits: spec_bits
});
end
FDiv: begin
divQ.enq(ToSpecFifo {
data: info,
spec_bits: spec_bits
});
end
FSqrt: begin
sqrtQ.enq(ToSpecFifo{
data: info,
spec_bits: spec_bits
});
end
default: begin
// simple ops that can be directly handled
FpuResult fpu_result = execFpuSimple(fpu_inst, rVal1, rVal2);
simpleQ.enq(ToSpecFifo {
data: FpuResp {
res: fpu_result,
dst: dst,
tag: tag
},
spec_bits: spec_bits
});
end
endcase
endmethod
method ActionValue#(FpuResp) simpleResp;
simpleQ.deq;
return simpleQ.first.data;
endmethod
method ActionValue#(FpuResp) fmaResp if(!fmaQ.first_poisoned);
fmaQ.deq;
let {out, exc} <- double_fma.response.get;
return finalizeResp(fmaQ.first_data.data, out, exc);
endmethod
method ActionValue#(FpuResp) divResp if(!divQ.first_poisoned);
divQ.deq;
let {out, exc} <- double_div.response.get;
return finalizeResp(divQ.first_data.data, out, exc);
endmethod
method ActionValue#(FpuResp) sqrtResp if(!sqrtQ.first_poisoned);
sqrtQ.deq;
let {out, exc} <- double_sqrt.response.get;
return finalizeResp(sqrtQ.first_data.data, out, exc);
endmethod
interface specUpdate = joinSpeculationUpdate(vec(
simpleQ.specUpdate,
fmaQ.specUpdate,
divQ.specUpdate,
sqrtQ.specUpdate
));
endmodule

View File

@@ -0,0 +1,189 @@
// 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.
import Vector::*;
import Ehr::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
typedef struct {
Bool hit;
indexT index; // hit index, for later update LRU info
TlbEntry entry; // hit entry
} FullAssocTlbResp#(type indexT) deriving(Bits, Eq, FShow);
// flush preempts addEntry and (translate + updateRepByHit)
// addEntry C (translate + updateRepByHit)
// This avoids bypass from addEntry to translate, and avoids races on LRU bits
// translate CF updateRepByHit.
// updateRepByHit is separted from translate to avoid making translate an
// actionvalue method which may create potential guard lifting problems.
interface FullAssocTlb#(numeric type n);
method Action flush;
method FullAssocTlbResp#(Bit#(TLog#(n))) translate(Vpn vpn, Asid asid);
method Action updateRepByHit(Bit#(TLog#(n)) index); // update replacement info when hit
method Action addEntry(TlbEntry x);
endinterface
module mkFullAssocTlb#(
Bool randRep // introduce randomness in bit LRU replacement
)(FullAssocTlb#(tlbSz)) provisos(
Add#(1, a__, tlbSz),
Alias#(tlbIdxT, Bit#(TLog#(tlbSz)))
);
Vector#(tlbSz, Reg#(Bool)) validVec <- replicateM(mkReg(False));
Vector#(tlbSz, Reg#(TlbEntry)) entryVec <- replicateM(mkRegU);
// bit-LRU replacement. To reduce cycle time, we update LRU bits in the
// next cycle after we touch an entry. However, when we have two
// consecutive cycles inserting new entries, the latter insertion should
// see the updates to LRU bits made by the former insertion; otherwise the
// latter will insert to the same slot as the former one.
Ehr#(2, Bit#(tlbSz)) lruBit <- mkEhr(0);
Reg#(Bit#(tlbSz)) lruBit_upd = lruBit[0]; // write
Reg#(Bit#(tlbSz)) lruBit_add = lruBit[1]; // read
// reg as 1 cycle delay for update LRU
Ehr#(2, Maybe#(tlbIdxT)) updRepIdx <- mkEhr(Invalid);
Reg#(Maybe#(tlbIdxT)) updRepIdx_deq = updRepIdx[0];
Reg#(Maybe#(tlbIdxT)) updRepIdx_enq = updRepIdx[1];
// randomly choose an LRU idx at replacement time
Reg#(tlbIdxT) randIdx <- mkReg(0);
if(randRep) begin
rule incRandIdx;
randIdx <= randIdx + 1;
endrule
end
// fire signal for flush
PulseWire flushEn <- mkPulseWire;
rule doUpdateRep(!flushEn &&& updRepIdx_deq matches tagged Valid .idx);
updRepIdx_deq <= Invalid;
// update LRU bits
Bit#(tlbSz) val = lruBit_upd;
val[idx] = 1;
if(val == maxBound) begin
val = 0;
val[idx] = 1;
end
lruBit_upd <= val;
endrule
// function to match TLB entry
method Action flush;
writeVReg(validVec, replicate(False));
// also reset LRU bits
lruBit_upd <= 0;
updRepIdx_deq <= Invalid;
// record fire signal
flushEn.send;
endmethod
method FullAssocTlbResp#(tlbIdxT) translate(Vpn vpn, Asid asid) if(!flushEn);
// find the matching entry
function Bool isMatch(tlbIdxT i);
TlbEntry entry = entryVec[i];
Bool asidMatch = entry.asid == asid || entry.pteType.global;
Bool vpnMatch = getMaskedVpn(vpn, entry.level) == entry.vpn;
return validVec[i] && asidMatch && vpnMatch;
endfunction
Vector#(tlbSz, tlbIdxT) idxVec = genWith(fromInteger);
if(find(isMatch, idxVec) matches tagged Valid .idx) begin
// hit a TLB entry, get its content
return FullAssocTlbResp {
hit: True,
index: idx,
entry: entryVec[idx]
};
end
else begin // miss
return FullAssocTlbResp {
hit: False,
index: ?,
entry: ?
};
end
endmethod
method Action updateRepByHit(tlbIdxT index) if(!flushEn && updRepIdx_enq == Invalid);
updRepIdx_enq <= Valid (index);
endmethod
method Action addEntry(TlbEntry x) if(!flushEn && updRepIdx_enq == Invalid);
// check ppn and vpn lower bits are 0 for super pages
doAssert(x.ppn == getMaskedPpn(x.ppn, x.level), "ppn lower bits not 0");
doAssert(x.vpn == getMaskedVpn(x.vpn, x.level), "vpn lower bits not 0");
// first check if the entry already exists in TLB, this can happen
// because we may have multiple misses on the same TLB entry. Since
// lower bits of ppn/vpn of super pages in TLB should have been zeroed,
// we can directly compare the full addr.
function Bool sameEntry(tlbIdxT i);
let en = entryVec[i];
Bool same_page = en.vpn == x.vpn &&
en.level == x.level &&
en.asid == x.asid &&
en.pteType.global == x.pteType.global;
return validVec[i] && same_page;
endfunction
Vector#(tlbSz, tlbIdxT) idxVec = genWith(fromInteger);
if(any(sameEntry, idxVec)) begin
// entry exists, update LRU
//updRepIdx_enq <= Valid (idx);
// FIXME: when I try to use find() to find the index, the
// compilation just explodes...
noAction;
end
else begin
// find a slot for this translation
// Since TLB is read-only cache, we can silently evict
tlbIdxT addIdx;
if(findIndex( \== (False) , readVReg(validVec) ) matches tagged Valid .idx) begin
// get empty slot
addIdx = pack(idx);
end
else begin
// find LRU slot (lruBit[i] = 0 means i is LRU slot)
Vector#(tlbSz, Bool) isLRU = unpack(~lruBit_add);
if(randRep && isLRU[randIdx]) begin
addIdx = randIdx;
end
else if(findIndex(id, isLRU) matches tagged Valid .idx) begin
addIdx = pack(idx);
end
else begin
addIdx = 0; // this is actually impossible
doAssert(False, "must have at least 1 LRU slot");
end
end
// update slot
validVec[addIdx] <= True;
entryVec[addIdx] <= x;
// update LRU bits
updRepIdx_enq <= Valid (addIdx);
end
endmethod
endmodule

View File

@@ -0,0 +1,144 @@
// 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.
import Types::*;
import ProcTypes::*;
import RegFile::*;
import Ehr::*;
import Vector::*;
import GlobalBrHistReg::*;
import BrPred::*;
export GSelectGHistSz;
export GSelectGHist;
export GSelectTrainInfo;
export mkGSelectPred;
// 1KB gselect predictor
typedef 8 GSelectGHistSz;
typedef 4 PCIndexSz;
typedef Bit#(GSelectGHistSz) GSelectGHist;
typedef TAdd#(GSelectGHistSz, PCIndexSz) BhtIndexSz;
typedef Bit#(BhtIndexSz) BhtIndex;
// bookkeeping info a branch should keep for future training
typedef struct {
GSelectGHist gHist;
} GSelectTrainInfo deriving(Bits, Eq, FShow);
// global history
typedef GlobalBrHistReg#(GSelectGHistSz) GSelectGHistReg;
(* synthesize *)
module mkGSelectGHistReg(GSelectGHistReg);
let m <- mkGlobalBrHistReg;
return m;
endmodule
(* synthesize *)
module mkGSelectPred(DirPredictor#(GSelectTrainInfo));
// sat counter table
RegFile#(BhtIndex, Bit#(2)) tab <- mkRegFileWCF(0, maxBound);
// global branch history
GSelectGHistReg globalHist <- mkGSelectGHistReg;
// EHR to record predict results in this cycle
Ehr#(TAdd#(1, SupSize), Bit#(TLog#(TAdd#(SupSize, 1)))) predCnt <- mkEhr(0);
Ehr#(TAdd#(1, SupSize), Bit#(SupSize)) predRes <- mkEhr(0);
function BhtIndex getIndex(Addr pc, GSelectGHist gHist);
Bit#(PCIndexSz) pcIdx = truncate(pc >> 2);
return {gHist, pcIdx};
endfunction
function Bool isTaken(Bit#(2) cnt);
return cnt[1] == 1;
endfunction
function Bit#(2) updateCnt(Bit#(2) cnt, Bool taken);
if(taken) begin
return cnt == maxBound ? maxBound : cnt + 1;
end
else begin
return cnt == 0 ? 0 : cnt - 1;
end
endfunction
GSelectGHist curGHist = globalHist.history; // global history: MSB is the latest branch
Vector#(SupSize, DirPred#(GSelectTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(GSelectTrainInfo)) pred(Addr pc);
// get the global history
// all previous branch in this cycle must be not taken
// otherwise this branch should be on wrong path
// because all inst in same cycle are fetched consecutively
GSelectGHist gHist = curGHist >> predCnt[i];
Bool taken = isTaken(tab.sub(getIndex(pc, gHist)));
// record pred result
predCnt[i] <= predCnt[i] + 1;
Bit#(SupSize) res = predRes[i];
res[predCnt[i]] = pack(taken);
predRes[i] <= res;
// return
return DirPredResult {
taken: taken,
train: GSelectTrainInfo {
gHist: gHist
}
};
endmethod
endinterface);
end
(* fire_when_enabled, no_implicit_conditions *)
rule canonGlobalHist;
globalHist.addHistory(predRes[valueof(SupSize)], predCnt[valueof(SupSize)]);
predRes[valueof(SupSize)] <= 0;
predCnt[valueof(SupSize)] <= 0;
endrule
interface pred = predIfc;
method Action update(Addr pc, Bool taken, GSelectTrainInfo train, Bool mispred);
// update history if mispred
if(mispred) begin
GSelectGHist newHist = truncate({pack(taken), train.gHist} >> 1);
globalHist.redirect(newHist);
end
// update sat cnt
let index = getIndex(pc, train.gHist);
Bit#(2) cnt = tab.sub(index);
tab.upd(index, updateCnt(cnt, taken));
endmethod
method flush = noAction;
method flush_done = True;
endmodule

View File

@@ -0,0 +1,144 @@
// 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.
import Types::*;
import ProcTypes::*;
import RegFile::*;
import Ehr::*;
import Vector::*;
import GlobalBrHistReg::*;
import BrPred::*;
export GShareGHistSz;
export GShareGHist;
export GShareTrainInfo;
export mkGSharePred;
// 16KB gshare predictor (to match BOOM evaluation paper)
typedef 8 GShareGHistSz;
typedef GShareGHistSz PCIndexSz;
typedef Bit#(GShareGHistSz) GShareGHist;
typedef GShareGHistSz BhtIndexSz;
typedef Bit#(BhtIndexSz) BhtIndex;
// bookkeeping info a branch should keep for future training
typedef struct {
GShareGHist gHist;
} GShareTrainInfo deriving(Bits, Eq, FShow);
// global history
typedef GlobalBrHistReg#(GShareGHistSz) GShareGHistReg;
(* synthesize *)
module mkGShareGHistReg(GShareGHistReg);
let m <- mkGlobalBrHistReg;
return m;
endmodule
(* synthesize *)
module mkGSharePred(DirPredictor#(GShareTrainInfo));
// sat counter table
RegFile#(BhtIndex, Bit#(2)) tab <- mkRegFileWCF(0, maxBound);
// global branch history
GShareGHistReg globalHist <- mkGShareGHistReg;
// EHR to record predict results in this cycle
Ehr#(TAdd#(1, SupSize), Bit#(TLog#(TAdd#(SupSize, 1)))) predCnt <- mkEhr(0);
Ehr#(TAdd#(1, SupSize), Bit#(SupSize)) predRes <- mkEhr(0);
function BhtIndex getIndex(Addr pc, GShareGHist gHist);
Bit#(PCIndexSz) pcIdx = truncate(pc >> 2);
return gHist ^ pcIdx;
endfunction
function Bool isTaken(Bit#(2) cnt);
return cnt[1] == 1;
endfunction
function Bit#(2) updateCnt(Bit#(2) cnt, Bool taken);
if(taken) begin
return cnt == maxBound ? maxBound : cnt + 1;
end
else begin
return cnt == 0 ? 0 : cnt - 1;
end
endfunction
GShareGHist curGHist = globalHist.history; // global history: MSB is the latest branch
Vector#(SupSize, DirPred#(GShareTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(GShareTrainInfo)) pred(Addr pc);
// get the global history
// all previous branch in this cycle must be not taken
// otherwise this branch should be on wrong path
// because all inst in same cycle are fetched consecutively
GShareGHist gHist = curGHist >> predCnt[i];
Bool taken = isTaken(tab.sub(getIndex(pc, gHist)));
// record pred result
predCnt[i] <= predCnt[i] + 1;
Bit#(SupSize) res = predRes[i];
res[predCnt[i]] = pack(taken);
predRes[i] <= res;
// return
return DirPredResult {
taken: taken,
train: GShareTrainInfo {
gHist: gHist
}
};
endmethod
endinterface);
end
(* fire_when_enabled, no_implicit_conditions *)
rule canonGlobalHist;
globalHist.addHistory(predRes[valueof(SupSize)], predCnt[valueof(SupSize)]);
predRes[valueof(SupSize)] <= 0;
predCnt[valueof(SupSize)] <= 0;
endrule
interface pred = predIfc;
method Action update(Addr pc, Bool taken, GShareTrainInfo train, Bool mispred);
// update history if mispred
if(mispred) begin
GShareGHist newHist = truncate({pack(taken), train.gHist} >> 1);
globalHist.redirect(newHist);
end
// update sat cnt
let index = getIndex(pc, train.gHist);
Bit#(2) cnt = tab.sub(index);
tab.upd(index, updateCnt(cnt, taken));
endmethod
method flush = noAction;
method flush_done = True;
endmodule

View File

@@ -0,0 +1,75 @@
// 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.
import Types::*;
import ProcTypes::*;
export GlobalBrHistReg(..);
export mkGlobalBrHistReg;
interface GlobalBrHistReg#(numeric type histLen);
method Bit#(histLen) history;
// add new history: taken[0--num-1] are newly added history
// taken[num-1] is the latest branch
method Action addHistory(Bit#(SupSize) taken, Bit#(TLog#(TAdd#(SupSize, 1))) num);
method Action redirect(Bit#(histLen) newHist);
endinterface
typedef struct {
Bit#(SupSize) taken;
Bit#(TLog#(TAdd#(SupSize, 1))) num;
} AddHistory deriving(Bits, Eq, FShow);
module mkGlobalBrHistReg(GlobalBrHistReg#(histLen));
// history reg: MSB is the most recent branch
Reg#(Bit#(histLen)) hist <- mkReg(0);
// wires to change history
RWire#(AddHistory) addHist <- mkRWire; // used by addHistory
RWire#(Bit#(histLen)) redirectHist <- mkRWire; // used by redirect
(* fire_when_enabled, no_implicit_conditions *)
rule canon_redirect(isValid(redirectHist.wget));
hist <= validValue(redirectHist.wget);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule canon_addHistory(!isValid(redirectHist.wget) && isValid(addHist.wget));
let x = validValue(addHist.wget);
// shift into hist from MSB
hist <= truncate({x.taken, hist} >> x.num);
endrule
method history = hist;
method Action addHistory(Bit#(SupSize) taken, Bit#(TLog#(TAdd#(SupSize, 1))) num);
addHist.wset(AddHistory {
taken: taken,
num: num
});
endmethod
method Action redirect(Bit#(histLen) newHist);
redirectHist.wset(newHist);
endmethod
endmodule

View File

@@ -0,0 +1,102 @@
// 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.
import ProcTypes::*;
import HasSpecBits::*;
import GetPut::*;
import Vector::*;
import ReorderBuffer::*;
interface GlobalSpecUpdate#(numeric type correctSpecPortNum, numeric type conflictWrongSpecPortNum);
interface Vector#(correctSpecPortNum, Put#(SpecTag)) correctSpec;
method Action incorrectSpec(Bool kill_all, SpecTag spec_tag, InstTag inst_tag);
// Some rules (e.g. doFinishFpuMulDiv) in Core.bsv may not conflict with wrong spec
// and is ordered before rules that calls incorrectSpec
// this creates cycles in scheduling
// To break the cycle, such rules can call the following interface
// to manually create a conflict with rules that do incorrectSpec
interface Vector#(conflictWrongSpecPortNum, Put#(void)) conflictWrongSpec;
endinterface
module mkGlobalSpecUpdate#(
SpeculationUpdate ifc, ROB_SpeculationUpdate rob
// ROB is treatly separatly due to an optimization on killing
)(
GlobalSpecUpdate#(correctSpecPortNum, conflictWrongSpecPortNum)
);
// record correct spec tags
Vector#(correctSpecPortNum, RWire#(SpecTag)) correctSpecTag <- replicateM(mkRWire);
// make wrong spec conflict with correct spec
Vector#(correctSpecPortNum, RWire#(void)) spec_conflict <- replicateM(mkRWire);
// let the caller of conflictWrongSpec to be conflict with wrong spec
Vector#(conflictWrongSpecPortNum, RWire#(void)) wrongSpec_conflict <- replicateM(mkRWire);
(* fire_when_enabled, no_implicit_conditions *)
rule canon_correct_spec;
SpecBits mask = maxBound;
for(Integer i = 0; i < valueof(correctSpecPortNum); i = i+1) begin
if(correctSpecTag[i].wget matches tagged Valid .tag) begin
mask[tag] = 0;
end
end
ifc.correctSpeculation(mask);
rob.correctSpeculation(mask);
endrule
Vector#(correctSpecPortNum, Put#(SpecTag)) correctVec = ?;
for(Integer i = 0; i < valueof(correctSpecPortNum); i = i+1) begin
correctVec[i] = (interface Put;
method Action put(SpecTag t);
correctSpecTag[i].wset(t);
// conflict with wrong spec
spec_conflict[i].wset(?);
endmethod
endinterface);
end
Vector#(conflictWrongSpecPortNum, Put#(void)) conflictWrongVec = ?;
for(Integer i = 0; i < valueof(conflictWrongSpecPortNum); i = i+1) begin
conflictWrongVec[i] = (interface Put;
method Action put(void x);
wrongSpec_conflict[i].wset(?);
endmethod
endinterface);
end
interface correctSpec = correctVec;
method Action incorrectSpec(Bool kill_all, SpecTag spec_tag, InstTag inst_tag);
ifc.incorrectSpeculation(kill_all, spec_tag);
rob.incorrectSpeculation(kill_all, spec_tag, inst_tag);
// conflict with correct spec
for(Integer i = 0; i < valueof(correctSpecPortNum); i = i+1) begin
spec_conflict[i].wset(?);
end
// conflict with the caller of conflictWrongSpec
for(Integer i = 0; i < valueof(conflictWrongSpecPortNum); i = i+1) begin
wrongSpec_conflict[i].wset(?);
end
endmethod
interface conflictWrongSpec = conflictWrongVec;
endmodule

View File

@@ -0,0 +1,48 @@
// 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.
import ProcTypes::*;
import HList::*; // for Gettable typeclass
import Vector::*;
// actions that can be applied to data structures
interface SpeculationUpdate;
method Action incorrectSpeculation(Bool kill_all, SpecTag kill_tag);
method Action correctSpeculation(SpecBits mask); // mask out corrected spec bits, i.e. sb <= sb & mask
endinterface
function SpeculationUpdate joinSpeculationUpdate( Vector#(n, SpeculationUpdate) vec );
return (interface SpeculationUpdate;
method Action incorrectSpeculation(Bool all, SpecTag tag);
for (Integer i = 0 ; i < valueOf(n) ; i = i+1) begin
vec[i].incorrectSpeculation(all, tag);
end
endmethod
method Action correctSpeculation(SpecBits mask);
for (Integer i = 0 ; i < valueOf(n) ; i = i+1) begin
vec[i].correctSpeculation(mask);
end
endmethod
endinterface);
endfunction

View File

@@ -0,0 +1,373 @@
// 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.
`include "ProcConfig.bsv"
import ClientServer::*;
import DefaultValue::*;
import GetPut::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import Performance::*;
import FullAssocTlb::*;
import ConfigReg::*;
import Fifo::*;
import Cntrs::*;
import SafeCounter::*;
import CacheUtils::*;
import LatencyTimer::*;
// currently blocking
typedef `L1_TLB_SIZE ITlbSize;
typedef struct {
Vpn vpn;
} ITlbRqToP deriving(Bits, Eq, FShow);
typedef struct {
// may get page fault: i.e. hit invalid page or
// get non-leaf page at last-level page table
Maybe#(TlbEntry) entry;
} ITlbRsFromP deriving(Bits, Eq, FShow);
interface ITlbToParent;
interface FifoDeq#(ITlbRqToP) rqToP;
interface FifoEnq#(ITlbRsFromP) rsFromP;
// after ITLB flush itself, it notifies L2, and wait L2 to flush
interface Client#(void, void) flush;
endinterface
interface ITlb;
// system consistency related
method Bool flush_done;
method Action flush;
method Action updateVMInfo(VMInfo vm);
method Bool noPendingReq;
// req/resp with core
interface Server#(Addr, TlbResp) to_proc;
// req/resp with L2 TLB
interface ITlbToParent toParent;
// performance
interface Perf#(L1TlbPerfType) perf;
endinterface
typedef FullAssocTlb#(ITlbSize) ITlbArray;
module mkITlbArray(ITlbArray);
let m <- mkFullAssocTlb(True); // randomness in replacement
return m;
endmodule
(* synthesize *)
module mkITlb(ITlb::ITlb);
Bool verbose = True;
// TLB array
ITlbArray tlb <- mkITlbArray;
// processor init flushing by setting this flag
Reg#(Bool) needFlush <- mkReg(False);
// after flushing ITLB itself, we want parent TLB to flush
Reg#(Bool) waitFlushP <- mkReg(False);
// resp FIFO to proc
Fifo#(2, TlbResp) hitQ <- mkCFFifo;
// current processor VM information
Reg#(VMInfo) vm_info <- mkReg(defaultValue);
// blocking miss
Reg#(Maybe#(Addr)) miss <- mkReg(Invalid);
// req & resp with parent TLB
Fifo#(2, ITlbRqToP) rqToPQ <- mkCFFifo;
Fifo#(2, ITlbRsFromP) rsFromPQ <- mkCFFifo;
// flush req/resp with parent TLB
Fifo#(1, void) flushRqToPQ <- mkCFFifo;
Fifo#(1, void) flushRsFromPQ <- mkCFFifo;
// perf counters
Fifo#(1, L1TlbPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(L1TlbPerfType)) perfRespQ <- mkCFFifo;
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) accessCnt <- mkCount(0);
Count#(Data) missCnt <- mkCount(0);
Count#(Data) missLat <- mkCount(0);
LatencyTimer#(2, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles
rule doPerf;
let t <- toGet(perfReqQ).get;
Data d = (case(t)
L1TlbAccessCnt: (accessCnt);
L1TlbMissParentCnt: (missCnt);
L1TlbMissParentLat: (missLat);
default: (0);
endcase);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
// do flush: only start when all misses resolve
rule doStartFlush(needFlush && !waitFlushP && !isValid(miss));
tlb.flush;
// request parent TLB to flush
flushRqToPQ.enq(?);
waitFlushP <= True;
if(verbose) $display("ITLB %m: flush begin");
endrule
rule doFinishFlush(needFlush && waitFlushP && !isValid(miss));
flushRsFromPQ.deq;
needFlush <= False;
waitFlushP <= False;
if(verbose) $display("ITLB %m: flush done");
endrule
rule doRsFromP(miss matches tagged Valid .vaddr);
rsFromPQ.deq;
let pRs = rsFromPQ.first;
if(pRs.entry matches tagged Valid .en) begin
// TODO when we have multiple misses in future. We first need to
// search TLB to check whether the PTE is already in TLB; this may
// happen for mega/giga pages. We don't want same PTE to occupy >1
// TLB entires.
// check permission
if(hasVMPermission(vm_info,
en.pteType,
en.ppn,
en.level,
InstFetch)) begin
// fill TLB and resp to proc
tlb.addEntry(en);
let trans_addr = translate(vaddr, en.ppn, en.level);
hitQ.enq(tuple2(trans_addr, Invalid));
if(verbose) begin
$display("ITLB %m refill: ", fshow(vaddr),
" ; ", fshow(trans_addr));
end
end
else begin
// page fault
hitQ.enq(tuple2(?, Valid (InstPageFault)));
if(verbose) begin
$display("ITLB %m refill no permission: ", fshow(vaddr));
end
end
end
else begin
// page fault
hitQ.enq(tuple2(?, Valid (InstPageFault)));
if(verbose) $display("ITLB %m refill page fault: ", fshow(vaddr));
end
// miss resolved
miss <= Invalid;
`ifdef PERF_COUNT
let lat <- latTimer.done(0);
if(doStats) begin
missLat.incr(zeroExtend(lat));
end
`endif
endrule
// we check no pending req only at Commit when Fetch1 stage has been
// stalled (setWaitRedirect has been called in previous cycle). Note that
// Commit calls redirect method which is ordered after Fetch1, so checking
// no pending req cannot sequence before Fetch1 which calls iTlb.req.
// Therefore, we use a wire to catch the pending req a the beginning of the
// cycle. This does not matter because Fetch1 stage has been stalled.
Wire#(Bool) no_pending_wire <- mkBypassWire;
(* fire_when_enabled, no_implicit_conditions *)
rule set_no_pending;
no_pending_wire <= !isValid(miss);
endrule
method Action flush if(!needFlush);
needFlush <= True;
waitFlushP <= False;
// this won't interrupt current processing, since
// (1) miss process will continue even if needFlush=True
// (2) flush truly starts when there is no pending req
endmethod
method Bool flush_done = !needFlush;
method Action updateVMInfo(VMInfo vm);
vm_info <= vm;
endmethod
method Bool noPendingReq = no_pending_wire._read;
interface Server to_proc;
interface Put request;
// We do not accept new req when flushing flag is set. We also
// make the guard more restrictive to reduce the time of computing
// guard i.e. guard does not depend on whether TLB hit or miss
method Action put(Addr vaddr) if(
!needFlush && !isValid(miss) && hitQ.notFull && rqToPQ.notFull
);
`ifdef SECURITY
// Security Check
// For non M-mode forbid translating any virtual pc outside of
// the protection domain. Be careful, in M mode we don't take
// into account the same vm_info: changing the evbase and
// evmask in M in en security monitor would lead to failure
// before we have a chance to go back to U: while in M mode
// sanctum_evbase is morally from an other set of CSR, which is
// constant and hardcoded in our implementation.
// TODO TODO TODO TODO WARNING DANGER NO SECURITY HERE we need
// to replace sanctum_evbase and evmask with the actual fixed
// SM region
VMInfo parvm_info = vm_info;
VMInfo eparvm_info = vm_info;
parvm_info.sanctum_evbase = maxBound;
parvm_info.sanctum_evmask = 0;
eparvm_info.sanctum_evbase = maxBound;
eparvm_info.sanctum_evmask = 0;
if ((vm_info.prv == prvM ? (outOfProtectionDomain(parvm_info,vaddr) && outOfProtectionDomain(eparvm_info,vaddr)) : outOfProtectionDomain(vm_info, vaddr))) begin
hitQ.enq(tuple2(?, Valid (InstAccessFault)));
end
`else
// No security check
if (False) begin
noAction;
end
`endif
else if (vm_info.sv39) begin
let vpn = getVpn(vaddr);
let trans_result = tlb.translate(vpn, vm_info.asid);
if (trans_result.hit) begin
// TLB hit
let entry = trans_result.entry;
// check permission
if(hasVMPermission(vm_info,
entry.pteType,
entry.ppn,
entry.level,
InstFetch)) begin
// update replacement info
tlb.updateRepByHit(trans_result.index);
// translate addr
Addr trans_addr = translate(
vaddr, entry.ppn, entry.level
);
hitQ.enq(tuple2(trans_addr, Invalid));
if(verbose) begin
$display("ITLB %m req (hit): ", fshow(vaddr),
" ; ", fshow(trans_result));
end
end
else begin
// page fault
hitQ.enq(tuple2(?, Valid (InstPageFault)));
if(verbose) begin
$display("ITLB %m req no permission: ",
fshow(vaddr));
end
end
end
else begin
// TLB miss, req to parent TLB
miss <= Valid (vaddr);
rqToPQ.enq(ITlbRqToP {vpn: vpn});
if(verbose) begin
$display("ITLB %m req (miss): ", fshow(vaddr));
end
`ifdef PERF_COUNT
latTimer.start(0);
if(doStats) begin
missCnt.incr(1);
end
`endif
end
end
else begin
// bare mode, no translation
hitQ.enq(tuple2(vaddr, Invalid));
if (verbose) $display("ITLB %m req (bare): ", fshow(vaddr));
end
`ifdef PERF_COUNT
if(doStats) begin
accessCnt.incr(1);
end
`endif
endmethod
endinterface
interface Get response = toGet(hitQ);
endinterface
interface ITlbToParent toParent;
interface rqToP = toFifoDeq(rqToPQ);
interface rsFromP = toFifoEnq(rsFromPQ);
interface Client flush;
interface request = toGet(flushRqToPQ);
interface response = toPut(flushRsFromPQ);
endinterface
endinterface
interface Perf perf;
method Action setStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Action req(L1TlbPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(L1TlbPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,434 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import FIFO::*;
import ClientServer::*;
import GetPut::*;
import Assert::*;
import CacheUtils::*;
import Types::*;
import ProcTypes::*;
import Performance::*;
import Fifo::*;
import CCTypes::*;
import L1Pipe::*;
import L1CRqMshr::*;
import L1PRqMshr::*;
import L1Bank::*;
import ICRqMshr::*;
import IPRqMshr::*;
import IBank::*;
import SelfInvL1Pipe::*;
import SelfInvL1Bank::*;
import SelfInvIPipe::*;
import SelfInvIBank::*;
export L1Num;
export LgL1WayNum;
export L1WayNum;
export L1Way;
export DProcReqId;
export L1DCRqStuck(..);
export L1DPRqStuck(..);
export DCoCache(..);
export mkDCoCache;
export ISupSz;
export L1ICRqStuck(..);
export L1IPRqStuck(..);
export ICoCache(..);
export mkICoCache;
// L1$: 1 I$ and 1 D$
typedef TMul#(CoreNum, 2) L1Num;
// Way num is shared among all coherent L1$ (I and D)
typedef `LOG_L1_WAYS LgL1WayNum;
typedef TExp#(LgL1WayNum) L1WayNum;
typedef Bit#(LgL1WayNum) L1Way;
////////
// D$ //
////////
typedef `LOG_L1_LINES LgDLineNum;
typedef 0 LgDBankNum;
typedef TSub#(LgDLineNum, TAdd#(LgDBankNum, LgL1WayNum)) LgDSetNum;
typedef Bit#(LgDBankNum) DBankId;
typedef LgDSetNum DIndexSz;
typedef Bit#(DIndexSz) DIndex;
typedef GetTagSz#(LgDBankNum, LgDSetNum) DTagSz;
typedef Bit#(DTagSz) DTag;
typedef L1WayNum DCRqNum;
typedef 4 DPRqNum; // match cache pipeline latency
typedef Bit#(TLog#(DCRqNum)) DCRqMshrIdx;
typedef Bit#(TLog#(DPRqNum)) DPRqMshrIdx;
typedef Bit#(TMax#(SizeOf#(LdQTag), SizeOf#(SBIndex))) DProcReqId;
(* synthesize *)
module mkDCRqMshrWrapper(
L1CRqMshr#(DCRqNum, L1Way, DTag, ProcRq#(DProcReqId))
);
function Addr getAddrFromReq(ProcRq#(DProcReqId) r);
return r.addr;
endfunction
let m <- mkL1CRqMshr(getAddrFromReq);
return m;
endmodule
(* synthesize *)
module mkDPRqMshrWrapper(
L1PRqMshr#(DPRqNum)
);
let m <- mkL1PRqMshr;
return m;
endmodule
`ifdef SELF_INV_CACHE
typedef `L1D_MAX_HITS DMaxHitNum;
(* synthesize *)
module mkDPipeline(
SelfInvL1Pipe#(LgDBankNum, L1WayNum, DMaxHitNum, DIndex, DTag, DCRqMshrIdx, DPRqMshrIdx)
);
let m <- mkSelfInvL1Pipe;
return m;
endmodule
typedef SelfInvL1Bank#(LgDBankNum, L1WayNum, DIndexSz, DTagSz, DCRqNum, DPRqNum, DMaxHitNum, DProcReqId) DCacheWrapper;
module mkDCacheWrapper#(L1ProcResp#(DProcReqId) procResp)(DCacheWrapper);
let m <- mkSelfInvL1Cache(mkDCRqMshrWrapper, mkDPRqMshrWrapper, mkDPipeline, procResp);
return m;
endmodule
typedef SelfInvL1CRqStuck L1DCRqStuck;
typedef SelfInvL1PRqStuck L1DPRqStuck;
`else // !SELF_INV_CACHE
(* synthesize *)
module mkDPipeline(
L1Pipe#(LgDBankNum, L1WayNum, DIndex, DTag, DCRqMshrIdx, DPRqMshrIdx)
);
let m <- mkL1Pipe;
return m;
endmodule
typedef L1Bank#(LgDBankNum, L1WayNum, DIndexSz, DTagSz, DCRqNum, DPRqNum, DProcReqId) DCacheWrapper;
module mkDCacheWrapper#(L1ProcResp#(DProcReqId) procResp)(DCacheWrapper);
let m <- mkL1Cache(mkDCRqMshrWrapper, mkDPRqMshrWrapper, mkDPipeline, procResp);
return m;
endmodule
typedef L1CRqStuck L1DCRqStuck;
typedef L1PRqStuck L1DPRqStuck;
`endif // SELF_INV_CACHE
interface DCoCache;
interface L1ProcReq#(DProcReqId) procReq;
method Action flush;
method Bool flush_done;
method Action resetLinkAddr;
interface Perf#(L1DPerfType) perf;
interface ChildCacheToParent#(L1Way, void) to_parent;
`ifdef SELF_INV_CACHE
// reconcile
interface Server#(void, void) reconcile;
`endif
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1DCRqStuck) cRqStuck;
interface Get#(L1DPRqStuck) pRqStuck;
endinterface
module mkDCoCache#(L1ProcResp#(DProcReqId) procResp)(DCoCache);
let cache <- mkDCacheWrapper(procResp);
// perf counters
Fifo#(1, L1DPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(L1DPerfType)) perfRespQ <- mkCFFifo;
rule doPerf;
let t <- toGet(perfReqQ).get;
let d = cache.getPerfData(t);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
`ifdef SELF_INV_CACHE
// change the reconcile ifc to a FIFO ifc to avoid scheduling issues
FIFO#(void) reconcileReqQ <- mkFIFO1;
FIFO#(void) reconcileRespQ <- mkFIFO1;
Reg#(Bool) waitReconcile <- mkReg(False);
rule doStartReconcile(!waitReconcile);
reconcileReqQ.deq;
cache.reconcile;
waitReconcile <= True;
endrule
rule doEndReconcile(waitReconcile && cache.reconcile_done);
reconcileRespQ.enq(?);
waitReconcile <= False;
endrule
interface Server reconcile;
interface Put request = toPut(reconcileReqQ);
interface Get response = toGet(reconcileRespQ);
endinterface
`endif
interface procReq = cache.procReq;
method flush = cache.flush;
method flush_done = cache.flush_done;
method Action resetLinkAddr;
cache.resetLinkAddr;
endmethod
interface Perf perf;
method Action setStatus(Bool stats);
cache.setPerfStatus(stats);
endmethod
method Action req(L1DPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(L1DPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
interface to_parent = cache.to_parent;
interface cRqStuck = cache.cRqStuck;
interface pRqStuck = cache.pRqStuck;
endmodule
////////
// I$ //
////////
typedef `LOG_L1_LINES LgILineNum;
typedef 0 LgIBankNum;
typedef TSub#(LgILineNum, TAdd#(LgIBankNum, LgL1WayNum)) LgISetNum;
typedef Bit#(LgIBankNum) IBankId;
typedef LgISetNum IIndexSz;
typedef Bit#(IIndexSz) IIndex;
typedef GetTagSz#(LgIBankNum, LgISetNum) ITagSz;
typedef Bit#(ITagSz) ITag;
typedef L1WayNum ICRqNum;
typedef 4 IPRqNum; // match cache pipeline latency
typedef Bit#(TLog#(ICRqNum)) ICRqMshrIdx;
typedef Bit#(TLog#(IPRqNum)) IPRqMshrIdx;
typedef SupSize ISupSz;
(* synthesize *)
module mkICRqMshrWrapper(
ICRqMshr#(ICRqNum, L1Way, ITag, ProcRqToI, Vector#(ISupSz, Maybe#(Instruction)))
);
function Addr getAddrFromReq(ProcRqToI r);
return r.addr;
endfunction
let m <- mkICRqMshr(getAddrFromReq);
return m;
endmodule
`ifdef SELF_INV_CACHE
(* synthesize *)
module mkIPipeline(
SelfInvIPipe#(LgIBankNum, L1WayNum, IIndex, ITag, ICRqMshrIdx)
);
let m <- mkSelfInvIPipe;
return m;
endmodule
typedef SelfInvIBank#(ISupSz, LgIBankNum, L1WayNum, IIndexSz, ITagSz, ICRqNum) IBankWrapper;
(* synthesize *)
module mkIBankWrapper(IBankWrapper);
let m <- mkSelfInvIBank(0, mkICRqMshrWrapper, mkIPipeline);
return m;
endmodule
typedef SelfInvICRqStuck L1ICRqStuck;
typedef SelfInvIPRqStuck L1IPRqStuck;
`else // !SELF_INV_CACHE
(* synthesize *)
module mkIPRqMshrWrapper(
IPRqMshr#(IPRqNum)
);
let m <- mkIPRqMshr;
return m;
endmodule
(* synthesize *)
module mkIPipeline(
L1Pipe#(LgIBankNum, L1WayNum, IIndex, ITag, ICRqMshrIdx, IPRqMshrIdx)
);
let m <- mkL1Pipe;
return m;
endmodule
typedef IBank#(ISupSz, LgIBankNum, L1WayNum, IIndexSz, ITagSz, ICRqNum, IPRqNum) IBankWrapper;
(* synthesize *)
module mkIBankWrapper(IBankWrapper);
let m <- mkIBank(0, mkICRqMshrWrapper, mkIPRqMshrWrapper, mkIPipeline);
return m;
endmodule
typedef ICRqStuck L1ICRqStuck;
typedef IPRqStuck L1IPRqStuck;
`endif // SELF_INV_CACHE
interface ICoCache;
interface Server#(Addr, Vector#(ISupSz, Maybe#(Instruction))) to_proc;
method Action flush;
method Bool flush_done;
interface Perf#(L1IPerfType) perf;
interface ChildCacheToParent#(L1Way, void) to_parent;
`ifdef SELF_INV_CACHE
// reconcile
method Action reconcile;
method Bool reconcile_done;
`endif
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1ICRqStuck) cRqStuck;
interface Get#(L1IPRqStuck) pRqStuck;
endinterface
(* synthesize *)
module mkICoCache(ICoCache);
`ifdef DEBUG_ICACHE
staticAssert(False, "DEBUG_ICACHE should not be defined");
`endif
let cache <- mkIBankWrapper;
Fifo#(1, L1IPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(L1IPerfType)) perfRespQ <- mkCFFifo;
rule doPerf;
let t <- toGet(perfReqQ).get;
let d = cache.getPerfData(t);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
interface Server to_proc;
interface request = cache.to_proc.req;
interface response = cache.to_proc.resp;
endinterface
method flush = cache.flush;
method flush_done = cache.flush_done;
interface Perf perf;
method Action setStatus(Bool stats);
cache.setPerfStatus(stats);
endmethod
method Action req(L1IPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(L1IPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
interface to_parent = cache.to_parent;
`ifdef SELF_INV_CACHE
method reconcile = cache.reconcile;
method reconcile_done = cache.reconcile_done;
`endif
interface cRqStuck = cache.cRqStuck;
interface pRqStuck = cache.pRqStuck;
endmodule

View File

@@ -0,0 +1,193 @@
// 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.
import Connectable::*;
import Vector::*;
import BuildVector::*;
import GetPut::*;
import CacheUtils::*;
import CCTypes::*;
import L1CoCache::*;
import LLCache::*;
import CrossBar::*;
module mkL1LLConnect#(
ParentCacheToChild#(L1Way, LLChild) llc,
Vector#(L1Num, ChildCacheToParent#(L1Way, void)) l1
)(Empty);
// connect cRq
function XBarDstInfo#(Bit#(0), CRqMsg#(L1Way, LLChild)) getCRqDst(LLChild child, CRqMsg#(L1Way, void) r);
return XBarDstInfo {
idx: 0,
data: CRqMsg {
addr: r.addr,
fromState: r.fromState,
toState: r.toState,
canUpToE: r.canUpToE,
id: r.id,
child: child
}
};
endfunction
function Get#(CRqMsg#(L1Way, void)) cRqGet(ChildCacheToParent#(L1Way, void) ifc) = toGet(ifc.rqToP);
mkXBar(getCRqDst, map(cRqGet, l1), vec(toPut(llc.rqFromC)));
// connect cRs
function XBarDstInfo#(Bit#(0), CRsMsg#(LLChild)) getCRsDst(LLChild child, CRsMsg#(void) r);
return XBarDstInfo {
idx: 0,
data: CRsMsg {
addr: r.addr,
toState: r.toState,
data: r.data,
child: child
}
};
endfunction
function Get#(CRsMsg#(void)) cRsGet(ChildCacheToParent#(L1Way, void) ifc) = toGet(ifc.rsToP);
mkXBar(getCRsDst, map(cRsGet, l1), vec(toPut(llc.rsFromC)));
// connect pRq/pRs
for(Integer i = 0; i < valueof(L1Num); i = i+1) begin
rule sendPRq(llc.toC.first matches tagged PRq .rq &&& rq.child == fromInteger(i));
llc.toC.deq;
l1[i].fromP.enq(PRq (PRqMsg {
addr: rq.addr,
toState: rq.toState,
child: ?
}));
endrule
rule sendPRs(llc.toC.first matches tagged PRs .rs &&& rs.child == fromInteger(i));
llc.toC.deq;
l1[i].fromP.enq(PRs (PRsMsg {
addr: rs.addr,
toState: rs.toState,
child: ?,
data: rs.data,
id: rs.id
}));
endrule
end
endmodule
/*
module mkL1LLConnect#(
ParentCacheToChild#(L1Way, LLChild) llc,
ChildCacheToParent#(L1Way, void) dCache,
ChildCacheToParent#(L1Way, void) iCache
)(Empty);
LLChild dChild = 0;
// D$
// send cRq to P: D$ has priority
rule doRqFromDCToP;
let r <- toGet(dCache.rqToP).get;
llc.rqFromC.enq(CRqMsg {
addr: r.addr,
fromState: r.fromState,
toState: r.toState,
id: r.id,
child: dChild
});
endrule
// send cRs to P: D$ has priority
rule doRsFromDCToP;
let r <- toGet(dCache.rsToP).get;
llc.rsFromC.enq(CRsMsg {
addr: r.addr,
toState: r.toState,
data: r.data,
child: dChild
});
endrule
// send pRs to C
rule doRsFromPToDC(llc.toC.first matches tagged PRs .rs &&& rs.child == dChild);
llc.toC.deq;
dCache.fromP.enq(PRs (PRsMsg {
addr: rs.addr,
toState: rs.toState,
child: ?,
data: rs.data,
id: rs.id
}));
endrule
// send pRq to C
rule doRqFromPToDC(llc.toC.first matches tagged PRq .rq &&& rq.child == dChild);
llc.toC.deq;
dCache.fromP.enq(PRq (PRqMsg {
addr: rq.addr,
toState: rq.toState,
child: ?
}));
endrule
// I$
LLChild iChild = 1;
(* descending_urgency = "doRqFromDCToP, doRqFromICToP" *)
rule doRqFromICToP;
let r <- toGet(iCache.rqToP).get;
llc.rqFromC.enq(CRqMsg {
addr: r.addr,
fromState: r.fromState,
toState: r.toState,
id: r.id,
child: iChild
});
endrule
(* descending_urgency = "doRsFromDCToP, doRsFromICToP" *)
rule doRsFromICToP;
let r <- toGet(iCache.rsToP).get;
llc.rsFromC.enq(CRsMsg {
addr: r.addr,
toState: r.toState,
data: r.data,
child: iChild
});
endrule
rule doRsFromPToIC(llc.toC.first matches tagged PRs .rs &&& rs.child == iChild);
llc.toC.deq;
iCache.fromP.enq(PRs (PRsMsg {
addr: rs.addr,
toState: rs.toState,
child: ?,
data: rs.data,
id: rs.id
}));
endrule
rule doRqFromPToIC(llc.toC.first matches tagged PRq .rq &&& rq.child == iChild);
llc.toC.deq;
iCache.fromP.enq(PRq (PRqMsg {
addr: rq.addr,
toState: rq.toState,
child: ?
}));
endrule
endmodule
*/

View File

@@ -0,0 +1,71 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import Ehr::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import SetAssocTlb::*;
// L2 4KB page set assoc TLB
typedef `LOG_L2_TLB_4KB_WAYS LogL2TlbWayNum;
typedef TExp#(LogL2TlbWayNum) L2TlbWayNum;
typedef Bit#(LogL2TlbWayNum) L2TlbWay;
typedef `LOG_L2_TLB_4KB_SIZE LogL2TlbSize;
typedef TSub#(LogL2TlbSize, LogL2TlbWayNum) LogL2TlbSetNum;
// use true LRU: index 0 --MRU, index way-1 -- LRU
typedef Vector#(L2TlbWayNum, L2TlbWay) L2TlbRepInfo;
L2TlbRepInfo l2TlbRepInfoInitVal = genWith(fromInteger);
function L2TlbWay getL2TlbRepWay(L2TlbRepInfo repInfo, Vector#(L2TlbWayNum, Bool) invalid);
// if there are invalid entries, LRU entry must be invalid
return repInfo[valueof(L2TlbWayNum) - 1];
endfunction
function L2TlbRepInfo updateL2TlbRepInfo(L2TlbRepInfo repInfo, L2TlbWay way);
L2TlbRepInfo newInfo = repInfo;
newInfo[0] = way; // MRU
Bool findWay = False;
for(Integer i = 1; i < valueof(L2TlbWayNum); i = i+1) begin
findWay = findWay || (repInfo[i-1] == way);
if(!findWay) begin
newInfo[i] = repInfo[i-1];
end
end
return newInfo;
endfunction
typedef SetAssocTlbResp#(L2TlbWay) L2SetAssocTlbResp;
typedef SetAssocTlb#(L2TlbWayNum, LogL2TlbSetNum, L2TlbRepInfo) L2SetAssocTlb;
// cannot synthesize becaue a method guard depends on input
module mkL2SetAssocTlb(L2SetAssocTlb);
let m <- mkSetAssocTlb(l2TlbRepInfoInitVal, getL2TlbRepWay, updateL2TlbRepInfo);
return m;
endmodule

View File

@@ -0,0 +1,749 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import DefaultValue::*;
import ClientServer::*;
import GetPut::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import Performance::*;
import FullAssocTlb::*;
import ConfigReg::*;
import Ehr::*;
import Fifo::*;
import Cntrs::*;
import SafeCounter::*;
import CacheUtils::*;
import SetAssocTlb::*;
import L2SetAssocTlb::*;
import TranslationCache::*;
import LatencyTimer::*;
// for SV39 only
// curretly L2 TLB is just a blocking page walker
// interface with memory (LLC)
typedef L2TlbReqIdx TlbMemReqId;
typedef struct {
// this is always a load req
Addr addr;
TlbMemReqId id;
} TlbMemReq deriving(Bits, Eq, FShow);
typedef struct {
Data data;
TlbMemReqId id;
} TlbLdResp deriving(Bits, Eq, FShow);
interface TlbMemClient;
interface FifoDeq#(TlbMemReq) memReq;
interface FifoEnq#(TlbLdResp) respLd;
endinterface
// interface with children (I/D TLB)
typedef union tagged {
void I;
DTlbReqIdx D;
} TlbChild deriving(Bits, Eq, FShow);
typedef struct {
TlbChild child;
Vpn vpn;
} L2TlbRqFromC deriving(Bits, Eq, FShow);
typedef struct {
TlbChild child;
Maybe#(TlbEntry) entry;
} L2TlbRsToC deriving(Bits, Eq, FShow);
interface L2TlbToChildren;
interface Put#(L2TlbRqFromC) rqFromC;
interface FifoDeq#(L2TlbRsToC) rsToC;
// flush with I/D TLB
interface Put#(void) iTlbReqFlush;
interface Put#(void) dTlbReqFlush;
interface Get#(void) flushDone;
endinterface
interface L2Tlb;
// keep update with changes to CSRs
method Action updateVMInfo(VMInfo vmI, VMInfo vmD);
// ifc with ITLb & DTLB
interface L2TlbToChildren toChildren;
// ifc with memory (LLC)
interface TlbMemClient toMem;
// performace
interface Perf#(L2TlbPerfType) perf;
endinterface
typedef FullAssocTlb#(`L2_TLB_HUGE_SIZE) L2FullAssocTlb;
module mkL2FullAssocTlb(L2FullAssocTlb);
let m <- mkFullAssocTlb(True); // randomness in replacement
return m;
endmodule
// a pending tlb req may be in following states
typedef union tagged {
void None;
void WaitMem; // wait for page walk resp from memory
L2TlbReqIdx WaitPeer; // wait for page walk resp from peer req
} L2TlbWait deriving(Bits, Eq, FShow);
// TODO we should raise load access fault if the PTE address is not a DRAM
// address. (trap value is still the virtual address being translated).
(* synthesize *)
module mkL2Tlb(L2Tlb::L2Tlb);
Bool verbose = True;
// set associative TLB for 4KB pages
L2SetAssocTlb tlb4KB <- mkL2SetAssocTlb;
// fully associative TLB for mega and giga pages
L2FullAssocTlb tlbMG <- mkL2FullAssocTlb;
// FIFO in parallel with TLB
Fifo#(1, L2TlbReqIdx) tlbReqQ <- mkPipelineFifo;
// MMU translation cache
TranslationCache transCache <- mkSplitTransCache;
// FIFO in parallel with MMU cache
Fifo#(1, L2TlbReqIdx) transCacheReqQ <- mkPipelineFifo;
// flush
Reg#(Bool) iFlushReq <- mkReg(False);
Reg#(Bool) dFlushReq <- mkReg(False);
Reg#(Bool) waitFlushDone <- mkReg(False);
Bool flushing = iFlushReq && dFlushReq;
Fifo#(1, void) flushDoneQ <- mkCFFifo;
// req/resp with I/D TLBs
Fifo#(1, L2TlbRqFromC) rqFromCQ <- mkBypassFifo;
Fifo#(1, L2TlbRsToC) rsToCQ <- mkBypassFifo;
// pending reqs
// pendWait should be meaningful even when entry is invalid. pendWait =
// WaitMem means this entry is waiting for page walk resp from memory;
// pendWait = WaitPeer means this entry is waiting for a page walk resp
// resp initiated by another req. Thus, pendWait must be None when entry is
// invalid.
Vector#(L2TlbReqNum, Ehr#(2, Bool)) pendValid <- replicateM(mkEhr(False));
Vector#(L2TlbReqNum, Reg#(L2TlbRqFromC)) pendReq <- replicateM(mkRegU);
Vector#(L2TlbReqNum, Ehr#(2, L2TlbWait)) pendWait <- replicateM(mkEhr(None));
Vector#(L2TlbReqNum, Reg#(PageWalkLevel)) pendWalkLevel <- replicateM(mkRegU);
Vector#(L2TlbReqNum, Reg#(Addr)) pendWalkAddr <- replicateM(mkRegU);
`ifdef SECURITY
Vector#(L2TlbReqNum, Reg#(Bool)) pendEnclave <- replicateM(mkRegU);
`endif
// rule ordering:
// - trans cache resp < tlb resp < tlb req
// - trans cache resp mutually exclusive with page walk (trans cache resp
// has precedence). I don't want them to fire together because both rules
// update walk level, and I don't want a bypass path.
// - tlb resp mutually exclusive with page walk (tlb resp has precedence).
// These two rules cannot fire together, because page walk may update trans
// cache and tlb resp may request trans cache. tlb resp takes precednece
// because page walk may update tlb and it needs to wait for tlb resp to
// deq resp.
// - tlb req mutually exclusive with page walk (page walk has precedence).
// They cannot fire together, because page walk may udpate tlb and tlb req
// may request tlb.
let pendValid_transCacheResp = getVEhrPort(pendValid, 0); // assert
let pendValid_tlbResp = getVEhrPort(pendValid, 0);
let pendValid_pageWalk = getVEhrPort(pendValid, 0);
let pendValid_tlbReq = getVEhrPort(pendValid, 1);
let pendWait_transCacheResp = getVEhrPort(pendWait, 0);
let pendWait_pageWalk = getVEhrPort(pendWait, 0);
let pendWait_tlbResp = getVEhrPort(pendWait, 1); // perf
let pendWait_tlbReq = getVEhrPort(pendWait, 1); // assert
// current processor VM information
Reg#(VMInfo) vm_info_I <- mkReg(defaultValue);
Reg#(VMInfo) vm_info_D <- mkReg(defaultValue);
// Memory Queues for page table walks
Fifo#(2, TlbMemReq) memReqQ <- mkCFFifo;
Fifo#(2, TlbLdResp) respLdQ <- mkCFFifo;
// When a mem resp comes, we first process the initiating req, then process
// other reqs that in WaitPeer.
Reg#(Maybe#(L2TlbReqIdx)) respForOtherReq <- mkReg(Invalid);
// FIFO for perf req
Fifo#(1, L2TlbPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(L2TlbPerfType)) perfRespQ <- mkCFFifo;
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) instMissCnt <- mkCount(0);
Count#(Data) instMissLat <- mkCount(0);
Count#(Data) instPageWalks <- mkCount(0);
Count#(Data) instSavedPageWalks <- mkCount(0);
Count#(Data) instHugePageHitCnt <- mkCount(0);
Count#(Data) instHugePageMissCnt <- mkCount(0);
Count#(Data) dataMissCnt <- mkCount(0);
Count#(Data) dataMissLat <- mkCount(0);
Count#(Data) dataPageWalks <- mkCount(0);
Count#(Data) dataSavedPageWalks <- mkCount(0);
Count#(Data) dataHugePageHitCnt <- mkCount(0);
Count#(Data) dataHugePageMissCnt <- mkCount(0);
Count#(Data) hitUnderMissCnt <- mkCount(0);
Count#(Data) allMissCycles <- mkCount(0);
Count#(Data) peerSavedMemReqCnt <- mkCount(0);
LatencyTimer#(L2TlbReqNum, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles
function Action incrMissLat(TlbChild child, L2TlbReqIdx idx);
action
let lat <- latTimer.done(idx);
if(doStats) begin
if(child == I) begin
instMissLat.incr(zeroExtend(lat));
end
else begin
dataMissLat.incr(zeroExtend(lat));
end
end
endaction
endfunction
rule incrAllMissCycles(doStats);
function Bool isMiss(L2TlbWait x) = x != None;
when(all(isMiss, readVEhr(0, pendWait)), allMissCycles.incr(1));
endrule
rule doPerf;
let t <- toGet(perfReqQ).get;
Data d = (case(t)
L2TlbInstMissCnt: (instMissCnt);
L2TlbInstMissLat: (instMissLat);
L2TlbInstPageWalks: (instPageWalks);
L2TlbInstSavedPageWalks: (instSavedPageWalks);
L2TlbInstHugePageHits: (instHugePageHitCnt);
L2TlbInstHugePageMisses: (instHugePageMissCnt);
L2TlbDataMissCnt: (dataMissCnt);
L2TlbDataMissLat: (dataMissLat);
L2TlbDataPageWalks: (dataPageWalks);
L2TlbDataSavedPageWalks: (dataSavedPageWalks);
L2TlbDataHugePageHits: (dataHugePageHitCnt);
L2TlbDataHugePageMisses: (dataHugePageMissCnt);
L2TlbHitUnderMissCnt: (hitUnderMissCnt);
L2TlbAllMissCycles: (allMissCycles);
L2TlbPeerSavedMemReqs: (peerSavedMemReqCnt);
default: (0);
endcase);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
// when flushing is true, since both I and D TLBs have finished flush and
// is waiting for L2 to flush, all I/D TLB req must have been responded.
// Thus, there cannot be any req in pendReq or rqFromCQ.
rule doStartFlush(flushing && !waitFlushDone);
waitFlushDone <= True;
tlb4KB.flush;
tlbMG.flush;
transCache.flush;
// check no req
doAssert(!rqFromCQ.notEmpty, "cannot have new req");
doAssert(readVEhr(0, pendValid) == replicate(False), "cannot have pending req");
endrule
rule doWaitFlush(flushing && waitFlushDone && tlb4KB.flush_done && transCache.flush_done);
waitFlushDone <= False;
flushDoneQ.enq(?);
iFlushReq <= False;
dFlushReq <= False;
endrule
// tlb req rule is preempted by page walk rule, i.e., don't fire when page
// walk resp is avaiable
rule doTlbReq(!flushing && !respLdQ.notEmpty);
// find a slot for the new req
L2TlbReqIdx idx = ?;
if(findIndex( \== (False) , readVReg(pendValid_tlbReq) ) matches tagged Valid .i) begin
idx = pack(i);
end
else begin
when(False, noAction);
end
// get new req
rqFromCQ.deq;
let r = rqFromCQ.first;
// req tlb array
VMInfo vm_info = r.child == I ? vm_info_I : vm_info_D;
tlb4KB.req(Translate (SetAssocTlbTranslateReq {
vpn: r.vpn,
asid: vm_info.asid
}));
tlbReqQ.enq(idx);
// record req
pendValid_tlbReq[idx] <= True;
pendReq[idx] <= r;
doAssert(!pendValid_tlbReq[idx], "entry must be invalid");
doAssert(pendWait_tlbReq[idx] == None, "cannot be waiting");
if(verbose) $display("L2TLB new req: ", fshow(r), "; ", fshow(idx));
endrule
// process resp from 4KB TLB and mega-giga TLB
rule doTlbResp(tlbReqQ.notEmpty);
doAssert(!flushing, "cannot have pending req when flushing");
// get req in tlb
tlbReqQ.deq;
L2TlbReqIdx idx = tlbReqQ.first;
L2TlbRqFromC cRq = pendReq[idx];
doAssert(pendWait_tlbResp[idx] == None, "cannot be waiting");
// get correct VM info
VMInfo vm_info = cRq.child == I ? vm_info_I : vm_info_D;
doAssert(vm_info.sv39, "must be in sv39 mode");
// get resp from 4KB TLB and mega-giga TLB
let resp4KB = tlb4KB.resp;
let respMG = tlbMG.translate(cRq.vpn, vm_info.asid);
if(verbose) begin
$display("L2TLB resp: ", fshow(vm_info), " ; ", fshow(cRq), " ; ",
fshow(resp4KB), " ; ", fshow(respMG));
end
// when page hit, resp to child (4KB array is not dequeued)
function Action pageHit(TlbEntry entry);
action
// resp to child
rsToCQ.enq(L2TlbRsToC {
child: cRq.child,
entry: Valid (entry)
});
// req is done
pendValid_tlbResp[idx] <= False;
`ifdef PERF_COUNT
// perf: hit under miss
function Bool otherMiss(L2TlbReqIdx i);
return pendValid_tlbResp[i] && pendWait_tlbResp[i] != None && i != idx;
endfunction
Vector#(L2TlbReqNum, L2TlbReqIdx) idxVec = genWith(fromInteger);
if(any(otherMiss, idxVec)) begin
hitUnderMissCnt.incr(1);
end
`endif
endaction
endfunction
if(!vm_info.sv39) begin
// not in sv39 -> page fault
// resp with invalid entry
rsToCQ.enq(L2TlbRsToC {
child: cRq.child,
entry: Invalid
});
// deq 4KB TLB array
tlb4KB.deqResp(Invalid);
// req is done
pendValid_tlbResp[idx] <= False;
end
else if(respMG.hit) begin
// hit on a mega or giga page
let entry = respMG.entry;
doAssert(entry.level > 0 && entry.level <= maxPageWalkLevel,
"mega or giga page");
pageHit(entry);
tlb4KB.deqResp(Invalid); // just deq 4KB array
tlbMG.updateRepByHit(respMG.index); // update replacement in MG array
`ifdef PERF_COUNT
if(doStats) begin
if(cRq.child == I) begin
instHugePageHitCnt.incr(1);
end
else begin
dataHugePageHitCnt.incr(1);
end
end
`endif
end
else if(resp4KB.hit) begin
// hit on 4KB page
let entry = resp4KB.entry;
doAssert(entry.level == 0, "must be 4KB page");
pageHit(entry);
// update 4KB array replacement, no need to touch MG array
tlb4KB.deqResp(Valid (resp4KB.way));
end
else begin
// miss, deq resp
tlb4KB.deqResp(Invalid);
// check translation cache
transCache.req(cRq.vpn, vm_info.asid);
transCacheReqQ.enq(idx);
// perf: TLB miss
`ifdef PERF_COUNT
latTimer.start(idx);
if(doStats) begin
if(cRq.child == I) begin
instMissCnt.incr(1);
end
else begin
dataMissCnt.incr(1);
end
end
`endif
end
endrule
// Find other req doing the same page walk. Although it should be ok to
// just compare the VPNs, for safety, we compare the PTE addrs.
function Maybe#(L2TlbReqIdx) otherReqSamePTE(
L2TlbReqIdx idx, Addr pteAddr,
Vector#(L2TlbReqNum, L2TlbWait) waitVec
);
function Bool samePTE(L2TlbReqIdx i);
return i != idx && waitVec[i] == WaitMem && pendWalkAddr[i] == pteAddr;
endfunction
Vector#(L2TlbReqNum, L2TlbReqIdx) idxVec = genWith(fromInteger);
return find(samePTE, idxVec);
endfunction
rule doTranslationCacheResp(transCacheReqQ.notEmpty);
// get req in trans cache
transCacheReqQ.deq;
L2TlbReqIdx idx = transCacheReqQ.first;
L2TlbRqFromC cRq = pendReq[idx];
// get trans cache resp
transCache.deqResp;
let resp = transCache.resp;
// start page walk based on the translation cache resp. Note that if
// the startLevel in resp is max, then we should use the base ppn in vm
// info.
// XXX We believe the PPN stored in translation cache has been
// sanitized before, so we don't need to check again if the PPN is an
// enclave PPN or not. (This should follow the same reasoning that TLB
// hits do not need checks.)
VMInfo vm_info = cRq.child == I ? vm_info_I : vm_info_D;
PageWalkLevel level = resp.startLevel;
Ppn rootPPN = vm_info.basePPN; // root of page table
`ifdef SECURITY
Bool isEnclave = (zeroExtend(cRq.vpn) & vm_info.sanctum_evmask) == vm_info.sanctum_evbase;
if(isEnclave) begin
rootPPN = vm_info.sanctum_ebasePPN; // enclave has its own root of page table
end
`endif
Addr baseAddr = getPTBaseAddr(level < maxPageWalkLevel ? resp.ppn : rootPPN);
Addr pteAddr = getPTEAddr(baseAddr, cRq.vpn, level);
// record page walk info
pendWalkLevel[idx] <= level;
pendWalkAddr[idx] <= pteAddr;
`ifdef SECURITY
pendEnclave[idx] <= isEnclave;
`endif
doAssert(pendWait_transCacheResp[idx] == None, "cannot be waiting");
// don't req memory if someone else is also doing the same page walk.
if(otherReqSamePTE(idx, pteAddr, readVReg(pendWait_transCacheResp)) matches tagged Valid .i) begin
// peer entry has already requested, so don't send
// duplicate req
pendWait_transCacheResp[idx] <= WaitPeer (i);
doAssert(pendValid_transCacheResp[i], "peer must be valid");
`ifdef PERF_COUNT
if(doStats) begin
peerSavedMemReqCnt.incr(1);
end
`endif
end
else begin
// no one has requested before, req memory
memReqQ.enq(TlbMemReq {
addr: pteAddr,
id: idx
});
pendWait_transCacheResp[idx] <= WaitMem;
end
if(verbose) begin
$display("L2TLB start page walk: ", fshow(cRq), "; ",
fshow(vm_info), "; ", fshow(resp), "; ",
fshow(level), "; ", fshow(pteAddr));
end
`ifdef PERF_COUNT
// perf: saved page walks
if(doStats) begin
Data saved = zeroExtend(maxPageWalkLevel - level);
if(cRq.child == I) begin
instSavedPageWalks.incr(saved);
end
else begin
dataSavedPageWalks.incr(saved);
end
end
`endif
endrule
// page walk is preempted by tlb resp rule and trans cache resp rule, i.e.,
// don't fire when tlb resp or trans cache resp are available
rule doPageWalk(respLdQ.notEmpty && !tlbReqQ.notEmpty && !transCacheReqQ.notEmpty);
doAssert(!flushing, "cannot have pending req when flushing");
// get the resp data from memory (LLC); this resp is for the initiating
// req and other req that wait on this one, so don't deq right away
PTESv39 pte = unpack(respLdQ.first.data);
L2TlbReqIdx idx = fromMaybe(respLdQ.first.id, respForOtherReq);
L2TlbRqFromC cRq = pendReq[idx];
// find another req waiting for this resp to process in next cycle
function Bool waitForResp(L2TlbReqIdx i);
return pendWait_pageWalk[i] == WaitPeer (respLdQ.first.id) && i != idx;
endfunction
Vector#(L2TlbReqNum, L2TlbReqIdx) idxVec = genWith(fromInteger);
if(find(waitForResp, idxVec) matches tagged Valid .i) begin
// still have req waiting for this resp, keep processing
respForOtherReq <= Valid (i);
doAssert(pendValid_pageWalk[i], "waiting entry must be valid");
end
else begin
// all req done, deq the mem resp
respForOtherReq <= Invalid;
respLdQ.deq;
end
// handle page fault
function Action pageFault(String reason);
action
// resp with invalid entry
rsToCQ.enq(L2TlbRsToC {
child: cRq.child,
entry: Invalid
});
// req is done
pendValid_pageWalk[idx] <= False;
pendWait_pageWalk[idx] <= None;
`ifdef PERF_COUNT
// incr miss latency
incrMissLat(cRq.child, idx);
`endif
endaction
endfunction
// get correct VM info
VMInfo vm_info = cRq.child == I ? vm_info_I : vm_info_D;
// assume we are continuing page walk, update level and pte addr (it
// causes not harm even if we stop page walk)
PageWalkLevel walkLevel = pendWalkLevel[idx];
PageWalkLevel newWalkLevel = walkLevel - 1;
Addr newPTBase = getPTBaseAddr(pte.ppn);
Addr newPTEAddr = getPTEAddr(newPTBase, cRq.vpn, newWalkLevel);
pendWalkLevel[idx] <= newWalkLevel;
pendWalkAddr[idx] <= newPTEAddr;
// reach leaf PTE
Bool leafPTE = isLeafPTE(pte.pteType);
`ifdef SECURITY
// base/bound/mask to enforce that page walk does not cross enclave
// boundary
Addr parbase = pendEnclave[idx] ? vm_info.sanctum_eparbase : vm_info.sanctum_parbase;
Addr parmask = pendEnclave[idx] ? vm_info.sanctum_eparmask : vm_info.sanctum_parmask;
Addr mrbm = pendEnclave[idx] ? vm_info.sanctum_emrbm : vm_info.sanctum_mrbm;
`endif
if(verbose) begin
$display("L2TLB page walk: ", fshow(vm_info), " ; ",
fshow(idx), " ; ", fshow(cRq), " ; ",
fshow(walkLevel), " ; ", fshow(pte));
end
if(!vm_info.sv39) begin
// no longer in sv39 mode -> page fault
pageFault("Not in sv39");
end
else if(!pte.valid) begin
// invalid pte -> fault
pageFault("invalid page");
end
`ifdef SECURITY
// TODO: raise page fault or access fault?
else if((newPTBase & parmask) == parbase) begin
// access falls into protected address range (that I don't have permission) -> fault
pageFault("SANCTUM protected address range");
end
else if((getAddrRegions(newPTBase, leafPTE, walkLevel) & mrbm) == 0) begin
// access falls outiside regions belong to me -> fault
pageFault("SANCTUM protected address range");
end
`endif
else begin
// page is valid, check leaf or not
if(!leafPTE) begin
// non-leaf page
if(walkLevel == 0) begin
// page walk end with non-leaf page -> fault
pageFault("non-leaf page at end");
end
else begin
// continue page walk, check if other req is doing the same
// walk
if(otherReqSamePTE(idx, newPTEAddr, readVReg(pendWait_pageWalk)) matches tagged Valid .i) begin
pendWait_pageWalk[idx] <= WaitPeer (i);
`ifdef PERF_COUNT
if(doStats) begin
peerSavedMemReqCnt.incr(1);
end
`endif
end
else begin
memReqQ.enq(TlbMemReq {
addr: newPTEAddr,
id: idx
});
pendWait_pageWalk[idx] <= WaitMem;
end
// add to translation cache
transCache.addEntry(cRq.vpn, walkLevel, pte.ppn, vm_info.asid);
end
end
else begin
// leaf page, get new entry
Vpn masked_vpn = getMaskedVpn(cRq.vpn, walkLevel);
Ppn masked_ppn = getMaskedPpn(pte.ppn, walkLevel);
let entry = TlbEntry {
vpn: masked_vpn,
ppn: masked_ppn,
pteType: pte.pteType,
level: walkLevel,
asid: vm_info.asid
};
// resp child
rsToCQ.enq(L2TlbRsToC {
child: cRq.child,
entry: Valid (entry)
});
// update TLB array
if(entry.level > 0) begin
// add to mega/giga page tlb
tlbMG.addEntry(entry);
`ifdef PERF_COUNT
if(doStats) begin
if(cRq.child == I) begin
instHugePageMissCnt.incr(1);
end
else begin
dataHugePageMissCnt.incr(1);
end
end
`endif
end
else begin
// 4KB page, add to 4KB TLB
tlb4KB.req(Refill (entry));
end
// req is done
pendValid_pageWalk[idx] <= False;
pendWait_pageWalk[idx] <= None;
`ifdef PERF_COUNT
// incr miss latency
incrMissLat(cRq.child, idx);
`endif
end
end
`ifdef PERF_COUNT
// perf: page walk done once
if(doStats) begin
if(cRq.child == I) begin
instPageWalks.incr(1);
end
else begin
dataPageWalks.incr(1);
end
end
`endif
endrule
method Action updateVMInfo(VMInfo vmI, VMInfo vmD); //if(!isValid(pendReq));
vm_info_I <= vmI;
vm_info_D <= vmD;
endmethod
interface L2TlbToChildren toChildren;
interface Put rqFromC = toPut(rqFromCQ);
interface rsToC = toFifoDeq(rsToCQ);
interface Put iTlbReqFlush;
method Action put(void x) if(!iFlushReq);
iFlushReq <= True;
endmethod
endinterface
interface Put dTlbReqFlush;
method Action put(void x) if(!dFlushReq);
dFlushReq <= True;
endmethod
endinterface
interface Get flushDone = toGet(flushDoneQ);
endinterface
interface TlbMemClient toMem;
interface FifoDeq memReq = toFifoDeq(memReqQ);
interface FifoEnq respLd = toFifoEnq(respLdQ);
endinterface
interface Perf perf;
method Action setStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Action req(L2TlbPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(L2TlbPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,153 @@
// 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.
import FShow::*;
import GetPut::*;
import Vector::*;
import BuildVector::*;
import FIFO::*;
import Assert::*;
import Types::*;
import ProcTypes::*;
import CacheUtils::*;
import CCTypes::*;
import L2Tlb::*;
import MemLoader::*;
import CrossBar::*;
import MemLoader::*;
typedef struct {
CoreId core;
TlbMemReqId id;
LineDataOffset dataSel;
} TlbDmaReqId deriving(Bits, Eq, FShow);
typedef union tagged {
MemLoaderMemReqId MemLoader;
TlbDmaReqId Tlb;
} LLCDmaReqId deriving(Bits, Eq, FShow);
module mkLLCDmaConnect#(
DmaServer#(LLCDmaReqId) llc,
MemLoaderMemClient memLoader,
Vector#(CoreNum, TlbMemClient) tlb
)(Empty) provisos (
Alias#(dmaRqT, DmaRq#(LLCDmaReqId))
);
Bool verbose = True;
// helper functions for cross bar
function XBarDstInfo#(Bit#(0), Tuple2#(CoreId, TlbMemReq)) getTlbDst(CoreId core, TlbMemReq r);
return XBarDstInfo {idx: 0, data: tuple2(core, r)};
endfunction
function Get#(TlbMemReq) tlbReqGet(TlbMemClient cli) = toGet(cli.memReq);
// cross bar for Tlb
FIFO#(Tuple2#(CoreId, TlbMemReq)) tlbQ <- mkFIFO;
mkXBar(getTlbDst, map(tlbReqGet, tlb), vec(toPut(tlbQ)));
// TLB req is for a whole data
function dmaRqT getTlbDmaReq(CoreId c, TlbMemReq r);
LineDataOffset dataSel = getLineDataOffset(r.addr);
let id = TlbDmaReqId {
core: c,
id: r.id,
dataSel: dataSel
};
return DmaRq {
addr: r.addr,
byteEn: replicate(False), // tlb req is always load
data: ?,
id: Tlb (id)
};
endfunction
// send req to LLC
rule sendMemLoaderReqToLLC;
memLoader.memReq.deq;
let r = memLoader.memReq.first;
dmaRqT req = DmaRq {
addr: r.addr,
byteEn: r.byteEn,
data: r.data,
id: MemLoader (r.id)
};
llc.memReq.enq(req);
if(verbose) begin
$display("[LLCDmaConnnect sendMemLoaderReqToLLC] ",
fshow(r), " ; ", fshow(req));
end
endrule
(* descending_urgency = "sendMemLoaderReqToLLC, sendTlbReqToLLC" *)
rule sendTlbReqToLLC;
let {c, r} <- toGet(tlbQ).get;
let req = getTlbDmaReq(c, r);
llc.memReq.enq(req);
if(verbose) begin
$display(" [LLCDmaConnnect sendTlbReqToLLC] ", fshow(r), " ; ", fshow(req));
end
endrule
// send Ld resp from LLC
rule sendLdRespToMemLoader(llc.respLd.first.id matches tagged MemLoader .id);
llc.respLd.deq;
if(verbose) begin
$display("[LLCDmaConnect sendLdRespToMemLoader] ",
fshow(llc.respLd.first));
end
doAssert(False, "No mem loader ld");
endrule
rule sendLdRespToTlb(llc.respLd.first.id matches tagged Tlb .id);
llc.respLd.deq;
let resp = llc.respLd.first;
let ld = TlbLdResp {
data: resp.data[id.dataSel],
id: id.id
};
tlb[id.core].respLd.enq(ld);
if(verbose) begin
$display(" [LLCDmaConnect sendLdRespToTlb] ", fshow(resp), " ; ", fshow(ld));
end
endrule
// send St resp from LLC
rule sendStRespToMemLoader(llc.respSt.first matches tagged MemLoader .id);
llc.respSt.deq;
memLoader.respSt.enq(id);
if(verbose) begin
$display("[LLCDmaConnect sendStRespToMemLoader] ",
fshow(llc.respSt.first));
end
endrule
rule sendStRespToTlb(llc.respSt.first matches tagged Tlb .id);
llc.respSt.deq;
if(verbose) begin
$display(" [LLCDmaConnect sendStRespToTlb] ", fshow(llc.respSt.first));
end
doAssert(False, "No TLB st");
endrule
endmodule

View File

@@ -0,0 +1,345 @@
// Copyright (c) 2019 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.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
import LLCRqMshr::*;
// SECURITY: this module is a quick and dirty way to model the effect of MSHR
// partition in case of a banked LLC. That is, a single core roughly sees a
// MSHR, which is not fully associative but set associated and indexed by cache
// bank ID. This is purely for **performance modeling**, not a real
// implementation.
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in LLC, the head (h1) of a chain may be linked as the successor of the head (h2) of another chain
// when h2 is replacing the addr of h1
// h1 should be waken up and sent to pipeline when replacement is done (i.e. h2 gets to WaitSt)
interface LLCRqMshrSecureModel#(
numeric type lgBankNum,
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
interface LLCRqMshr#(cRqNum, wayT, tagT, dirPendT, reqT) mshr;
endinterface
//////////////////
// safe version //
//////////////////
module mkLLCRqMshrSecureModel#(
function Addr getAddrFromReq(reqT r),
function Bool needDownReq(dirPendT dirPend),
dirPendT dirPendInitVal
)(
LLCRqMshrSecureModel#(lgBankNum, cRqNum, wayT, tagT, dirPendT, reqT)
) provisos (
Alias#(bankIdT, Bit#(lgBankNum)),
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, LLCRqSlot#(wayT, tagT, dirPendT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(dirPendT, _dirPendSz),
Bits#(reqT, _reqSz),
NumAlias#(bankNum, TExp#(lgBankNum)),
Mul#(bankNum, cRqPerBankNum, cRqNum),
Add#(a__, lgBankNum, AddrSz)
);
slotT slotInitVal = getLLCRqSlotInitVal(dirPendInitVal);
// logical ordering: sendToM < sendRqToC < sendRsToDma/C < mRsDeq < pipelineResp < transfer
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
// EHR ports
Integer sendToM_port = 0; // sendToM is read-only, so use port 0
Integer sendRqToC_port = 0; // read req/state/slot, write slot
Integer sendRsToDmaC_port = 0; // sendRsToDma/C read req/data, write state
Integer mRsDeq_port = 0; // mRsDeq only writes data
Integer pipelineResp_port = 1; // read/write lots of things
Integer transfer_port = 2; // cRqTransfer_xx, mRsTransfer_send, read/write lots of things
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// MSHR entry state
Vector#(cRqNum, Ehr#(3, LLCRqState)) stateVec <- replicateM(mkEhr(Empty));
// summary bit of dirPend in each entry: asserted when some dirPend[i] = ToSend
Vector#(cRqNum, Ehr#(3, Bool)) needReqChildVec <- replicateM(mkEhr(False));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(slotInitVal));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
Vector#(cRqNum, Ehr#(3, Line)) dataVec <- replicateM(mkEhr(?));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) addrSuccValidVec <- replicateM(mkEhr(False));
Vector#(cRqNum, Ehr#(3, Bool)) repSuccValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) addrSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
RegFile#(cRqIndexT, cRqIndexT) repSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
Vector#(bankNum, Fifo#(cRqPerBankNum, cRqIndexT)) emptyEntryQ <- replicateM(mkCFFifo);
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
Reg#(bankIdT) initBank <- mkReg(0);
Reg#(Bit#(TLog#(cRqPerBankNum))) initPerBankCnt <- mkReg(0);
rule initEmptyEntry(!inited);
$display("%t LLCRqMshrSecureModel %m: init empty entry %d to bank %d",
$time, initIdx, initBank);
emptyEntryQ[initBank].enq(initIdx);
// update initIdx
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t LLCRqMshrSecureModel %m: init empty entry done", $time);
end
// update initPerBank and initBank
if(initPerBankCnt == fromInteger(valueof(cRqPerBankNum) - 1)) begin
initPerBankCnt <= 0;
initBank <= initBank + 1;
end
else begin
initPerBankCnt <= initPerBankCnt + 1;
end
endrule
function bankIdT getBankId(Addr a);
return truncate(a >> valueof(LgLineSzBytes));
endfunction
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(LLCRqMshrStuck#(dirPendT, reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(LLCRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP,
dirPend: slotVec[n][0].dirPend
});
end
endrule
`endif
function Action writeSlot(Integer ehrPort, cRqIndexT n, slotT s);
action
slotVec[n][ehrPort] <= s;
// set dirPend summary bit
needReqChildVec[n][ehrPort] <= needDownReq(s.dirPend);
endaction
endfunction
interface LLCRqMshr mshr;
interface LLCRqMshr_transfer transfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][transfer_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][transfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r, Maybe#(Line) d) if(inited);
bankIdT bank = getBankId(getAddrFromReq(r));
emptyEntryQ[bank].deq;
cRqIndexT n = emptyEntryQ[bank].first;
reqVec[n][transfer_port] <= r;
stateVec[n][transfer_port] <= Init;
writeSlot(transfer_port, n, slotInitVal);
dataValidVec[n][transfer_port] <= isValid(d);
dataVec[n][transfer_port] <= validValue(d);
addrSuccValidVec[n][transfer_port] <= False;
repSuccValidVec[n][transfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
method Bool hasEmptyEntry(reqT r);
bankIdT bank = getBankId(getAddrFromReq(r));
return emptyEntryQ[bank].notEmpty;
endmethod
endinterface
interface LLCRqMshr_mRsDeq mRsDeq;
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][mRsDeq_port] <= isValid(d);
dataVec[n][mRsDeq_port] <= fromMaybe(?, d);
endmethod
endinterface
interface LLCRqMshr_sendToM sendToM;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendToM_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendToM_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendToM_port] ? Valid (dataVec[n][sendToM_port]) : Invalid;
endmethod
endinterface
interface LLCRqMshr_sendRsToDmaC sendRsToDmaC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToDmaC_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToDmaC_port] ? Valid (dataVec[n][sendRsToDmaC_port]) : Invalid;
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
bankIdT bank = getBankId(getAddrFromReq(reqVec[n][sendRsToDmaC_port]));
emptyEntryQ[bank].enq(n);
stateVec[n][sendRsToDmaC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
endinterface
interface LLCRqMshr_sendRqToC sendRqToC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToC_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][sendRqToC_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToC_port];
endmethod
method Action setSlot(cRqIndexT n, slotT s);
writeSlot(sendRqToC_port, n, s);
endmethod
method Maybe#(cRqIndexT) searchNeedRqChild(Maybe#(cRqIndexT) suggestIdx);
function Bool isNeedRqChild(cRqIndexT i);
return (stateVec[i][sendRqToC_port] == WaitOldTag || stateVec[i][sendRqToC_port] == WaitSt)
&& needReqChildVec[i][sendRqToC_port];
endfunction
if(suggestIdx matches tagged Valid .idx &&& isNeedRqChild(idx)) begin
return suggestIdx;
end
else begin
Vector#(cRqNum, cRqIndexT) idxVec = genWith(fromInteger);
return searchIndex(isNeedRqChild, idxVec);
end
endmethod
endinterface
interface LLCRqMshr_pipelineResp pipelineResp;
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][pipelineResp_port] ? Valid (dataVec[n][pipelineResp_port]) : Invalid;
endmethod
method Maybe#(cRqIndexT) getAddrSucc(cRqIndexT n);
return addrSuccValidVec[n][pipelineResp_port] ? Valid (addrSuccFile.sub(n)) : Invalid;
endmethod
method Maybe#(cRqIndexT) getRepSucc(cRqIndexT n);
return repSuccValidVec[n][pipelineResp_port] ? Valid (repSuccFile.sub(n)) : Invalid;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][pipelineResp_port] <= isValid(d);
dataVec[n][pipelineResp_port] <= fromMaybe(?, d);
endmethod
method Action setStateSlot(cRqIndexT n, LLCRqState state, slotT slot);
stateVec[n][pipelineResp_port] <= state;
writeSlot(pipelineResp_port, n, slot);
endmethod
method Action setAddrSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
addrSuccValidVec[n][pipelineResp_port] <= isValid(succ);
addrSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Action setRepSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
repSuccValidVec[n][pipelineResp_port] <= isValid(succ);
repSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
let state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noAddrSucc = !addrSuccValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noAddrSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endinterface
endmodule

View File

@@ -0,0 +1,452 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import FIFO::*;
import Connectable::*;
import GetPut::*;
import Assert::*;
import CacheUtils::*;
import Fifo::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import LLPipe::*;
import LLCRqMshr::*;
import LLCRqMshrSecureModel::*;
import LLBank::*;
import SelfInvLLPipe::*;
import SelfInvLLBank::*;
import L1CoCache::*;
import LLCDmaConnect::*;
import Performance::*;
// Last-Level
// whether we model the effect of MSHR partition for security purpose
`ifdef SECURITY
`ifndef DISABLE_SECURE_LLC_MSHR
`define USE_LLC_MSHR_SECURE_MODEL
`endif
`endif
// whether we model the effect of the circular/fair arbiter at the entry point
// of LLC pipeline for security purpose
`ifdef SECURITY
`ifndef DISABLE_SECURE_LLC_ARBITER
`ifndef DISABLE_SECURE_BW
`define USE_LLC_ARBITER_SECURE_MODEL
`endif
`endif
`endif
typedef `LOG_LLC_LINES LgLLLineNum;
typedef `LOG_LLC_WAYS LgLLWayNum;
typedef TExp#(LgLLWayNum) LLWayNum;
typedef 0 LgLLBankNum;
typedef TSub#(LgLLLineNum, TAdd#(LgLLWayNum, LgLLBankNum)) LgLLSetNum;
typedef Bit#(LgLLBankNum) LLBankId;
typedef LgLLSetNum LLIndexSz;
typedef Bit#(LLIndexSz) LLIndex;
typedef GetTagSz#(LgLLBankNum, LgLLSetNum) LLTagSz;
typedef Bit#(LLTagSz) LLTag;
typedef Bit#(TLog#(LLWayNum)) LLWay;
`ifdef USE_LLC_MSHR_SECURE_MODEL
`ifndef DISABLE_SECURE_BW
typedef TDiv#(DramMaxReqs, 2) LLCRqNum; // SECURITY: limit MSHR size <= DRAM bandwidth
`else // DISABLE_SECURE_BW
typedef LLWayNum LLCRqNum; // ignore DRAM bandwidth contention, keep using the old mshr size
`endif // DISABLE_SECURE_BW
`else // !USE_LLC_MSHR_SECURE_MODEL
typedef LLWayNum LLCRqNum;
`endif // USE_LLC_MSHR_SECURE_MODEL
typedef Bit#(TLog#(LLCRqNum)) LLCRqMshrIdx;
// all L1$ are children
typedef L1Num LLChildNum;
typedef Bit#(TLog#(LLChildNum)) LLChild;
typedef L1Way LLCRqId;
`ifdef USE_LLC_MSHR_SECURE_MODEL
module mkLastLvCRqMshr(
LLCRqMshr#(LLChildNum, LLCRqNum, LLWay, LLTag, cRqT)
) provisos(
Alias#(cRqT, LLRq#(LLCRqId, LLCDmaReqId, LLChild))
);
function Addr getAddr(cRqT r) = r.addr;
LLCRqMshrSecureModel#(
`SIM_LOG_LLC_MSHR_BANK_NUM, LLCRqNum, LLWay, LLTag, Vector#(LLChildNum, DirPend), cRqT
) m <- mkLLCRqMshrSecureModel(getAddr, getNeedReqChild, getDirPendInitVal);
return m.mshr;
endmodule
`else // !USE_LLC_MSHR_SECURE_MODEL
`ifdef SELF_INV_CACHE
(* synthesize *)
module mkLastLvCRqMshr(
LLCRqMshr#(LLCRqNum, LLWay, LLTag, SelfInvDirPend#(LLChild), cRqT)
) provisos(
Alias#(cRqT, LLRq#(LLCRqId, LLCDmaReqId, LLChild))
);
function Addr getAddr(cRqT r) = r.addr;
let m <- mkLLCRqMshr(getAddr, getSelfInvNeedReqChild, getSelfInvDirPendInitVal);
return m;
endmodule
`else // !SELF_INV_CACHE
(* synthesize *)
module mkLastLvCRqMshr(
LLCRqMshr#(LLCRqNum, LLWay, LLTag, Vector#(LLChildNum, DirPend), cRqT)
) provisos(
Alias#(cRqT, LLRq#(LLCRqId, LLCDmaReqId, LLChild))
);
function Addr getAddr(cRqT r) = r.addr;
let m <- mkLLCRqMshr(getAddr, getNeedReqChild, getDirPendInitVal);
return m;
endmodule
`endif // SELF_INV_CACHE
`endif // USE_LLC_MSHR_SECURE_MODEL
`ifdef USE_LLC_ARBITER_SECURE_MODEL
`ifdef SIM_LLC_ARBITER_NUM // Model a real arbiter at the pipeline input
typedef `SIM_LLC_ARBITER_NUM SimLLCArbNum;
`else // Only model added latency at the pipline input, no bandwidth loss
typedef `SIM_LLC_ARBITER_LAT SimLLCArbLat;
`endif
(* synthesize *)
module mkLLPipeline(
LLPipe#(LgLLBankNum, LLChildNum, LLWayNum, LLIndex, LLTag, LLCRqMshrIdx)
) provisos (
Alias#(LLPipeIn#(LLChild, LLWay, LLCRqMshrIdx), pipeInT)
);
// pipeline
LLPipe#(LgLLBankNum, LLChildNum, LLWayNum, LLIndex, LLTag, LLCRqMshrIdx) m <- mkLLPipe;
`ifdef BSIM
// print the arbiter num
Reg#(Bool) showArbiter <- mkReg(True);
rule doShowArbiter(showArbiter);
`ifdef SIM_LLC_ARBITER_NUM
$display("[LLPipe] Arbiter size %d", valueof(SimLLCArbNum));
`else
$display("[LLPipe] Arbiter latency %d", valueof(SimLLCArbLat));
`endif
showArbiter <= False;
endrule
`endif // BSIM
`ifdef SIM_LLC_ARBITER_NUM
// round-robin reg: only allow entry to pipeline when turn == 0. This
// models the effect of a circular/fair arbiter.
Reg#(Bit#(TLog#(SimLLCArbNum))) turn <- mkReg(0);
(* fire_when_enabled, no_implicit_conditions *)
rule incrTurn;
turn <= turn == fromInteger(valueof(SimLLCArbNum) - 1) ? 0 : turn + 1;
endrule
method Action send(pipeInT r) if(turn == 0);
m.send(r);
endmethod
`else // !SIM_LLC_ARBITER_NUM
// delay input
Vector#(SimLLCArbLat, FIFO#(pipeInT)) delayQ <- replicateM(mkFIFO);
for(Integer i = 0; i < valueof(SimLLCArbLat) - 1; i = i+1) begin
mkConnection(toGet(delayQ[i]), toPut(delayQ[i + 1]));
end
mkConnection(toGet(delayQ[valueof(SimLLCArbLat) - 1]).get, m.send);
method send = delayQ[0].enq;
`endif // SIM_LLC_ARBITER_NUM
method notEmpty = m.notEmpty;
method first = m.first;
method unguard_first = m.unguard_first;
method deqWrite = m.deqWrite;
endmodule
`else // !USE_LLC_ARBITER_SECURE_MODEL
`ifdef SELF_INV_CACHE
(* synthesize *)
module mkLLPipeline(
SelfInvLLPipe#(LgLLBankNum, LLChildNum, LLWayNum, LLIndex, LLTag, LLCRqMshrIdx)
);
let m <- mkSelfInvLLPipe;
return m;
endmodule
`else // !SELF_INV_CACHE
(* synthesize *)
module mkLLPipeline(
LLPipe#(LgLLBankNum, LLChildNum, LLWayNum, LLIndex, LLTag, LLCRqMshrIdx)
);
let m <- mkLLPipe;
return m;
endmodule
`endif // SELF_INV_CACHE
`endif // USE_LLC_ARBITER_SECURE_MODEL
`ifdef SELF_INV_CACHE
typedef SelfInvLLBank#(LgLLBankNum, LLChildNum, LLWayNum, LLIndexSz, LLTagSz, LLCRqNum, LLCRqId, LLCDmaReqId) LLBankWrapper;
typedef SelfInvLLCRqStuck#(LLChildNum, LLCRqId, LLCDmaReqId) LLCStuck;
`else // !SELF_INV_CACHE
typedef LLBank#(LgLLBankNum, LLChildNum, LLWayNum, LLIndexSz, LLTagSz, LLCRqNum, LLCRqId, LLCDmaReqId) LLBankWrapper;
typedef LLCRqStuck#(LLChildNum, LLCRqId, LLCDmaReqId) LLCStuck;
`endif // SELF_INV_CACHE
interface LLCache;
interface ParentCacheToChild#(LLCRqId, LLChild) to_child;
interface DmaServer#(LLCDmaReqId) dma;
interface MemFifoClient#(LdMemRqId#(LLCRqMshrIdx), void) to_mem;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(LLCStuck) cRqStuck;
// performance
interface Perf#(LLCPerfType) perf;
endinterface
`ifdef SECURITY
// We rotate the addr in/out LLC to achieve set partition
// FIXME This is a hack: we simulate the performance of partitioning a large
// LLC using a smaller LLC with less partitions. So LgLLCPartitionNum should
// NOT be viewed as the number of DRAM regions.
`ifdef SIM_LOG_LLC_PARTITION_NUM
typedef `SIM_LOG_LLC_PARTITION_NUM LgLLCPartitionNum;
`else
typedef `LOG_DRAM_REGION_NUM LgLLCPartitionNum;
`endif
typedef `LOG_DRAM_REGION_SIZE LgDramRegionSz;
typedef TAdd#(TAdd#(LLIndexSz, LgLLBankNum), LgLineSzBytes) LLIndexBankOffsetSz;
function Addr secureRotateAddr(Addr addr) provisos(
// region/partition id cannot be wider than index + bank id
Add#(LgLLCPartitionNum, a__, TAdd#(LLIndexSz, LgLLBankNum))
);
// low bits: index + bank id + line offset without the higher bits which
// will be replaced by region/partition id
Bit#(TSub#(LLIndexBankOffsetSz, LgLLCPartitionNum)) low = truncate(addr);
// swap bits: higher bits of index + bank id to be swapped with region/partition
// id
Bit#(LgLLCPartitionNum) swap = truncate(addr >> (valueof(LLIndexBankOffsetSz) - valueof(LgLLCPartitionNum)));
// middle bits between swap and region
Bit#(TSub#(LgDramRegionSz, LLIndexBankOffsetSz)) mid = truncate(addr >> valueof(LLIndexBankOffsetSz));
// region/partition id
Bit#(LgLLCPartitionNum) region = truncate(addr >> valueof(LgDramRegionSz));
// high bits beyond phy mem boundary
Bit#(TSub#(AddrSz, TAdd#(LgLLCPartitionNum, LgDramRegionSz))) high = truncateLSB(addr);
// exchange swap bits with region bits
return {high, swap, mid, region, low};
endfunction
`endif // SECURITY
(* synthesize *)
module mkLLCache(LLCache);
`ifdef DEBUG_DMA
staticAssert(False, "DEBUG_DMA should not be defined");
`endif
`ifdef SELF_INV_CACHE
LLBankWrapper cache <- mkSelfInvLLBank(mkLastLvCRqMshr, mkLLPipeline);
`else // !SELF_INV_CACHE
`ifdef NO_LOAD_RESP_E
function Bool respLoadWithE(Bool fromMem) = False;
`else
function Bool respLoadWithE(Bool fromMem) = fromMem;
`endif
LLBankWrapper cache <- mkLLBank(mkLastLvCRqMshr, mkLLPipeline, respLoadWithE);
`endif // SELF_INV_CACHE
// perf counters
Fifo#(1, LLCPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(LLCPerfType)) perfRespQ <- mkCFFifo;
rule doPerf;
let t <- toGet(perfReqQ).get;
let d = cache.getPerfData(t);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
`ifdef SECURITY
`ifndef DISABLE_SECURE_LLC
`ifdef BSIM
// print the LLC partition in simulation
Reg#(Bool) showLLCPartition <- mkReg(True);
rule doShowLLCPartition(showLLCPartition);
$fdisplay(stderr, "[LLCache] log LLC partition = %d", valueof(LgLLCPartitionNum));
showLLCPartition <= False;
endrule
`endif // BSIM
// rotate addr to achieve LLC set partition
interface ParentCacheToChild to_child;
interface FifoEnq rsFromC;
method notFull = cache.to_child.rsFromC.notFull;
method Action enq(CRsMsg#(LLChild) x);
let y = x;
y.addr = secureRotateAddr(x.addr);
cache.to_child.rsFromC.enq(y);
endmethod
endinterface
interface FifoEnq rqFromC;
method notFull = cache.to_child.rqFromC.notFull;
method Action enq(CRqMsg#(LLCRqId, LLChild) x);
let y = x;
y.addr = secureRotateAddr(x.addr);
cache.to_child.rqFromC.enq(y);
endmethod
endinterface
interface FifoDeq toC;
method notEmpty = cache.to_child.toC.notEmpty;
method deq = cache.to_child.toC.deq;
method PRqRsMsg#(LLCRqId, LLChild) first;
case(cache.to_child.toC.first) matches
tagged PRq .x: begin
let y = x;
y.addr = secureRotateAddr(x.addr);
return PRq (y);
end
tagged PRs .x: begin
let y = x;
y.addr = secureRotateAddr(x.addr);
return PRs (y);
end
default: return ?;
endcase
endmethod
endinterface
endinterface
interface DmaServer dma;
interface FifoEnq memReq;
method notFull = cache.dma.memReq.notFull;
method Action enq(DmaRq#(LLCDmaReqId) x);
let y = x;
y.addr = secureRotateAddr(x.addr);
cache.dma.memReq.enq(y);
endmethod
endinterface
interface respLd = cache.dma.respLd;
interface respSt = cache.dma.respSt;
endinterface
interface MemFifoClient to_mem;
interface FifoDeq toM;
method notEmpty = cache.to_mem.toM.notEmpty;
method deq = cache.to_mem.toM.deq;
method ToMemMsg#(LdMemRqId#(LLCRqMshrIdx), void) first;
case(cache.to_mem.toM.first) matches
tagged Ld .x: begin
let y = x;
y.addr = secureRotateAddr(x.addr);
return Ld (y);
end
tagged Wb .x: begin
let y = x;
y.addr = secureRotateAddr(x.addr);
return Wb (y);
end
default: return ?;
endcase
endmethod
endinterface
interface rsFromM = cache.to_mem.rsFromM;
endinterface
interface Get cRqStuck;
method ActionValue#(LLCStuck) get;
let x <- cache.cRqStuck.get;
let y = x;
y.addr = secureRotateAddr(x.addr);
return y;
endmethod
endinterface
`else // DISABLE_SECURE_LLC
interface to_child = cache.to_child;
interface dma = cache.dma;
interface to_mem = cache.to_mem;
interface cRqStuck = cache.cRqStuck;
`endif // DISABLE_SECURE_LLC
`else // !SECURITY
interface to_child = cache.to_child;
interface dma = cache.dma;
interface to_mem = cache.to_mem;
interface cRqStuck = cache.cRqStuck;
`endif // SECURITY
interface Perf perf;
method Action setStatus(Bool stats);
cache.setPerfStatus(stats);
endmethod
method Action req(LLCPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(LLCPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,84 @@
// 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.
import Vector::*;
import Types::*;
// a timer to track latency of multiple misses
// To prevent assert failure, start() and done() methods should be called even
// when performance stats is not on
interface LatencyTimer#(numeric type num, numeric type timeWidth);
method Action start(Bit#(TLog#(num)) idx);
method ActionValue#(Bit#(timeWidth)) done(Bit#(TLog#(num)) idx);
endinterface
module mkLatencyTimer(LatencyTimer#(num, timeWidth)) provisos(
//Add#(1, a__, num),
Alias#(idxT, Bit#(TLog#(num))),
Alias#(timeT, Bit#(timeWidth))
);
Reg#(Vector#(num, timeT)) timer <- mkReg(replicate(0));
Reg#(Vector#(num, Bool)) started <- mkReg(replicate(False)); // for checking purposes
RWire#(idxT) startEn <- mkRWire;
RWire#(idxT) doneEn <- mkRWire;
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
Vector#(num, timeT) timerNext = timer;
Vector#(num, Bool) startedNext = started;
// apply done
if(doneEn.wget matches tagged Valid .i) begin
startedNext[i] = False;
doAssert(started[i], "timer must be valid");
end
// incr timer
for(Integer i = 0; i < valueof(num); i = i+1) begin
timeT t = timer[i];
if(t < maxBound) begin
timerNext[i] = t + 1;
end
end
// apply start: new timer should be 1 (counting this cycle)
if(startEn.wget matches tagged Valid .i) begin
timerNext[i] = 1;
startedNext[i] = True;
// do not assert this, because things may be killed
//doAssert(!started[i], "timer must be invalid");
end
// update states
timer <= timerNext;
started <= startedNext;
endrule
method Action start(idxT i);
startEn.wset(i);
endmethod
method ActionValue#(timeT) done(idxT i);
doneEn.wset(i);
return timer[i];
endmethod
endmodule

View File

@@ -0,0 +1,62 @@
// 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.
import Types::*;
import ProcTypes::*;
import CCTypes::*;
// data aligned addr
typedef TSub#(AddrSz, LgDataSzBytes) DataAlignedAddrSz;
typedef Bit#(DataAlignedAddrSz) DataAlignedAddr;
function DataAlignedAddr getDataAlignedAddr(Addr a) = truncateLSB(a);
// base addr for each MMIO reg/device (aligned to Data)
DataAlignedAddr bootRomBaseAddr = getDataAlignedAddr(64'h00001000);
DataAlignedAddr memLoaderBaseAddr = getDataAlignedAddr(64'h01000000);
DataAlignedAddr msipBaseAddr = getDataAlignedAddr(64'h02000000);
DataAlignedAddr mtimecmpBaseAddr = getDataAlignedAddr(64'h02004000);
DataAlignedAddr mtimeBaseAddr = getDataAlignedAddr(64'h0200bff8);
DataAlignedAddr mainMemBaseAddr = getDataAlignedAddr(64'h80000000);
// XXX Each msip reg is 32-bit, while mtime and each mtimecmp are 64-bit. We
// assume Data is 64-bit. We hard code this relation in all MMIO logic.
// Mem loader has 2 64-bit regs
// offset 0: start addr for init mem (write to start mem initialization)
// offset 1: busy (read to determine if initialization is done)
// upper bound addr (bound itself is invalid addr) for each MMIO reg/device
// (aligned to Data)
DataAlignedAddr bootRomBoundAddr = bootRomBaseAddr +
fromInteger(valueof(TExp#(LgBootRomSzData)));
DataAlignedAddr memLoaderBoundAddr = memLoaderBaseAddr + 2;
DataAlignedAddr msipBoundAddr = msipBaseAddr +
fromInteger(valueof(TDiv#(CoreNum, 2)));
DataAlignedAddr mtimecmpBoundAddr = mtimecmpBaseAddr +
fromInteger(valueof(CoreNum));
// offset within each MMIO reg/device (aligned to Data)
typedef Bit#(TLog#(TDiv#(CoreNum, 2))) MSIPDataAlignedOffset;
typedef CoreId MTimCmpDataAlignedOffset;
typedef Bit#(1) MemLoaderAlignedOffset;

View File

@@ -0,0 +1,208 @@
// 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.
import Types::*;
import ConfigReg::*;
import ProcTypes::*;
import MMIOAddrs::*;
import CacheUtils::*;
import Fifo::*;
import Amo::*;
import MMIOInst::*;
// local MMIO logic in each core (MMIOCore)
// Every MMIO req from the core is directly passed to the platform, while this
// logic handles req from the platform when platform is processing core req or
// posting timer interrupt.
interface MMIOCoreToPlatform;
interface FifoDeq#(MMIOCRq) cRq;
interface FifoEnq#(MMIOPRs) pRs;
interface FifoEnq#(MMIOPRq) pRq;
interface FifoDeq#(MMIOCRs) cRs;
// core keeps a copy of the mtime reg. This method allows the platform to
// inform the core to change time CSR.
method Action setTime(Data t);
endinterface
interface MMIOCore;
// is the PHYSICAL mem access addr an MMIO addr
method Bool isMMIOAddr(Addr addr);
// for mem exe pipeline to send/recv MMIO req/resp
method Action dataReq(MMIOCRq r);
method MMIODataPRs dataRespVal;
method Action dataRespDeq;
// set tohost & fromhost addr
method Action setHtifAddrs(Addr toHost, Addr fromHost);
// signal that we have a pending pRq, so some parts of the core (e.g.,
// rename/issue) may yield themselves.
method Bool hasPendingPRq;
// methods to platform
interface MMIOCoreToPlatform toP;
endinterface
interface MMIOCoreInput;
// ifc from inst fetch
interface MMIOInstToCore fetch;
// MMIOCore needs to access MSIP and MTIP CSRs
method Bit#(1) getMSIP;
method Action setMSIP(Bit#(1) v);
method Action setMTIP(Bit#(1) v);
// guard for accessing MSIP or MTIP
method Bool noInflightCSRInstOrInterrupt;
// MMIOCore needs to pass mtime to CSRF
method Action setTime(Data t);
endinterface
module mkMMIOCore#(MMIOCoreInput inIfc)(MMIOCore);
// HTIF mem mapped addrs
Reg#(DataAlignedAddr) toHostAddr <- mkConfigReg(0);
Reg#(DataAlignedAddr) fromHostAddr <- mkConfigReg(0);
// FIFOs connected to memory pipeline
Fifo#(1, MMIOCRq) dataReqQ <- mkCFFifo;
Fifo#(1, MMIODataPRs) dataRespQ <- mkCFFifo;
// we limit to at most 1 data MMIO req by mem pipeline, so that this req
// will not clog requests from other cores
Fifo#(1, void) dataPendQ <- mkCFFifo;
// FIFOs connected to platform
Fifo#(1, MMIOCRq) cRqQ <- mkCFFifo;
Fifo#(1, MMIOPRs) pRsQ <- mkCFFifo;
Fifo#(1, MMIOPRq) pRqQ <- mkCFFifo;
Fifo#(1, MMIOCRs) cRsQ <- mkCFFifo;
// PRq from platform will access MSIP or MTIP. As explained in CSRF,
// accessing these bits may break the atomicity of CSRXXX inst or interrupt
// handling, so we need to check for them. Note that exception handling
// does not touch MSIP/MTIP. Besides, a inst with exception may be after
// the MMIO inst that accesses its own MSIP. Waiting for the clear of
// exception may cause deadlock.
rule handlePRq(inIfc.noInflightCSRInstOrInterrupt);
pRqQ.deq;
MMIOPRq req = pRqQ.first;
MMIOCRs resp = MMIOCRs {data: ?};
case(req.target)
MSIP: begin
if(req.func == St) begin
inIfc.setMSIP(req.data[0]);
end
else begin
// both AMO and Ld need the original data as resp
Bit#(1) msip = inIfc.getMSIP;
resp.data = msip;
// AMO also needs to write
if(req.func matches tagged Amo .amoFunc) begin
let amoInst = AmoInst {
func: amoFunc,
doubleWord: False,
aq: False,
rl: False
};
let newData = amoExec(amoInst, zeroExtend(msip),
zeroExtend(req.data), False);
inIfc.setMSIP(newData[0]);
end
end
end
MTIP: begin
if(req.func == St) begin
inIfc.setMTIP(req.data[0]);
end
else begin
doAssert(False, "platform can only write MTIP");
end
end
default: doAssert(False, "unknown target");
endcase
// resp to platform
cRsQ.enq(resp);
endrule
// arbitrate requests from fetch stage and mem stage; prioritize mem stage
rule sendDataReq;
dataReqQ.deq;
cRqQ.enq(dataReqQ.first);
endrule
(* descending_urgency = "sendDataReq, sendInstReq" *)
rule sendInstReq;
inIfc.fetch.instReq.deq;
let {addr, maxWay} = inIfc.fetch.instReq.first;
cRqQ.enq(MMIOCRq {
addr: addr,
func: Inst (maxWay),
byteEn: ?,
data: ?
});
endrule
// dispatch resp
rule sendDataResp(pRsQ.first matches tagged DataAccess .r);
pRsQ.deq;
dataRespQ.enq(r);
endrule
rule sendInstResp(pRsQ.first matches tagged InstFetch .r);
pRsQ.deq;
inIfc.fetch.instResp.enq(r);
endrule
method Bool isMMIOAddr(Addr addr);
let a = getDataAlignedAddr(addr);
return a < mainMemBaseAddr || a == toHostAddr || a == fromHostAddr;
endmethod
method Action dataReq(MMIOCRq r);
dataReqQ.enq(r);
dataPendQ.enq(?);
endmethod
method MMIODataPRs dataRespVal if(dataPendQ.notEmpty);
return dataRespQ.first;
endmethod
method Action dataRespDeq;
dataPendQ.deq;
dataRespQ.deq;
endmethod
method Action setHtifAddrs(Addr toHost, Addr fromHost);
toHostAddr <= getDataAlignedAddr(toHost);
fromHostAddr <= getDataAlignedAddr(fromHost);
inIfc.fetch.setHtifAddrs(toHost, fromHost);
endmethod
method Bool hasPendingPRq;
return pRqQ.notEmpty;
endmethod
interface MMIOCoreToPlatform toP;
interface cRq = toFifoDeq(cRqQ);
interface pRs = toFifoEnq(pRsQ);
interface pRq = toFifoEnq(pRqQ);
interface cRs = toFifoDeq(cRsQ);
method setTime = inIfc.setTime;
endinterface
endmodule

View File

@@ -0,0 +1,107 @@
// 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.
import Vector::*;
import ConfigReg::*;
import Fifo::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import CacheUtils::*;
import MMIOAddrs::*;
interface MMIOInstToCore;
interface FifoDeq#(Tuple2#(Addr, SupWaySel)) instReq;
interface FifoEnq#(Vector#(SupSize, Maybe#(Instruction))) instResp;
method Action setHtifAddrs(Addr toHost, Addr fromHost);
endinterface
typedef enum {
MainMem,
BootRom,
Fault
} InstFetchTarget deriving(Bits, Eq, FShow);
interface MMIOInst;
// Figure out where to fetch the inst, i.e., from boot rom or main memory.
method InstFetchTarget getFetchTarget(Addr phyPc);
// When req boot rom, need to specify the number of instructions to fetch,
// i.e., maxWay + 1
method Action bootRomReq(Addr phyPc, SupWaySel maxWay);
// The return type is same as I$. An entry is Invalid if it is an access
// fault or not requested before.
method ActionValue#(Vector#(SupSize, Maybe#(Instruction))) bootRomResp;
interface MMIOInstToCore toCore;
endinterface
(* synthesize *)
module mkMMIOInst(MMIOInst);
// record tohost/fromhost addrs to check inst access fault. Since this
// MMIOInst module will be placed inside the fetch stage and we want to
// synthesize fetch stage, we need to add these two regs instead of passing
// in two reg interfaces.
Reg#(DataAlignedAddr) toHostAddr <- mkConfigReg(0);
Reg#(DataAlignedAddr) fromHostAddr <- mkConfigReg(0);
// MMIO requests are handled in a very slow manner at platform, so no need
// to use large FIFO here
Fifo#(1, Tuple2#(Addr, SupWaySel)) reqQ <- mkCFFifo;
Fifo#(1, Vector#(SupSize, Maybe#(Instruction))) respQ <- mkCFFifo;
// To prevent inst fetch requests from clogging the network, we limit to at
// most 1 pending req. The resp for the pending req will be buffered in
// respQ, no affecting other MMIO accesses.
Fifo#(1, void) pendQ <- mkCFFifo;
method InstFetchTarget getFetchTarget(Addr phyPc);
let addr = getDataAlignedAddr(phyPc);
if(addr >= bootRomBaseAddr && addr < bootRomBoundAddr) begin
return BootRom;
end
else if(addr >= mainMemBaseAddr &&
addr != toHostAddr && addr != fromHostAddr) begin
return MainMem;
end
else begin
return Fault;
end
endmethod
method Action bootRomReq(Addr phyPc, SupWaySel maxWay);
reqQ.enq(tuple2(phyPc, maxWay));
pendQ.enq(?);
endmethod
method ActionValue#(Vector#(SupSize, Maybe#(Instruction))) bootRomResp;
pendQ.deq;
respQ.deq;
return respQ.first;
endmethod
interface MMIOInstToCore toCore;
interface instReq = toFifoDeq(reqQ);
interface instResp = toFifoEnq(respQ);
method Action setHtifAddrs(Addr toHost, Addr fromHost);
toHostAddr <= getDataAlignedAddr(toHost);
fromHostAddr <= getDataAlignedAddr(fromHost);
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,292 @@
// 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.
// mem loader: responsible for initializing memory
import Clocks::*;
import Assert::*;
import Connectable::*;
import FShow::*;
import FIFO::*;
import Vector::*;
import Fifo::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import CacheUtils::*;
import MMIOAddrs::*;
import MemLoaderIF::*;
import SyncFifo::*;
// messages from host
typedef struct {
Bool valid;
Addr addr;
} HostWrAddr deriving(Bits, Eq, FShow);
typedef struct {
Data data;
Bit#(DataSzBytes) byteEn;
Bool last;
} HostWrData deriving(Bits, Eq, FShow);
interface MemLoaderIndInv;
method ActionValue#(Addr) start;
method Action wrDone;
endinterface
instance Connectable#(MemLoaderIndInv, MemLoaderIndication);
module mkConnection#(MemLoaderIndInv inv, MemLoaderIndication ind)(Empty);
rule doStart;
let addr <- inv.start;
ind.start(addr);
endrule
rule doWrDone;
inv.wrDone;
ind.wrDone;
endrule
endmodule
endinstance
// messages to LLC
typedef void MemLoaderMemReqId;
typedef DmaRq#(MemLoaderMemReqId) MemLoaderMemReq;
interface MemLoaderMemClient;
interface FifoDeq#(MemLoaderMemReq) memReq;
interface FifoEnq#(MemLoaderMemReqId) respSt;
endinterface
// MMIO
interface MemLoaderMMIO;
method ActionValue#(MMIODataPRs) req(MemLoaderAlignedOffset offset,
ByteEn wrBE, Data wrData);
endinterface
interface MemLoader;
// MMIO ifc
interface MemLoaderMMIO mmio;
// ifc to LLC
interface MemLoaderMemClient to_mem;
// request & indication inverse, under portal clock domain
interface MemLoaderRequest hostReq;
interface MemLoaderIndInv hostIndInv;
endinterface
// this module should be clocked under user domain
(* synthesize *)
module mkMemLoader#(Clock portalClk, Reset portalRst)(MemLoader);
Bool verbose = True;
// MMIO regs
Reg#(Addr) memStartAddr <- mkReg(0);
Reg#(Bool) busy <- mkReg(False);
// states to handle writes from host
// whether we are working on a series of writes
Reg#(Bool) writing <- mkReg(False);
// counter of pending (not acked) stores to LLC
Reg#(Bit#(8)) pendStCnt <- mkReg(0);
// whether we should expect more write data from host for this series
Reg#(Bool) expectWrData <- mkReg(False);
// form req to LLC
Reg#(LineDataOffset) reqSel <- mkRegU;
Reg#(LineAddr) reqAddr <- mkRegU;
Reg#(Line) reqData <- mkRegU;
Reg#(Vector#(LineSzData, Bit#(DataSzBytes))) reqBE <- mkRegU;
// sync FIFOs to cross to portal clk
Clock userClk <- exposeCurrentClock;
Reset userRst <- exposeCurrentReset;
SyncFIFOIfc#(HostWrAddr) hostWrAddrQ <- mkSyncFifo(1, portalClk, portalRst, userClk, userRst);
SyncFIFOIfc#(HostWrData) hostWrDataQ <- mkSyncFifo(1, portalClk, portalRst, userClk, userRst);
SyncFIFOIfc#(Addr) hostStartQ <- mkSyncFifo(1, userClk, userRst, portalClk, portalRst);
SyncFIFOIfc#(void) hostWrDoneQ <- mkSyncFifo(1, userClk, userRst, portalClk, portalRst);
// FIFOs to LLC
Fifo#(1, MemLoaderMemReq) memReqQ <- mkCFFifo;
Fifo#(1, MemLoaderMemReqId) respStQ <- mkCFFifo;
rule doNewWrite(busy && !writing);
hostWrAddrQ.deq;
HostWrAddr req = hostWrAddrQ.first;
if(req.valid) begin
// new write series, setup regs
writing <= True;
pendStCnt <= 0;
expectWrData <= True;
reqSel <= getLineDataOffset(req.addr);
reqAddr <= getLineAddr(req.addr);
reqBE <= replicate(0);
// check addr align to Data
Bit#(LgDataSzBytes) offset = truncate(req.addr);
doAssert(offset == 0, "write addr not aligned to Data");
end
else begin
// all writes done, reset busy
busy <= False;
end
if(verbose) begin
$display("[MemLoader doNewWrite] ", fshow(req));
end
endrule
// check counter overflow in guard
rule doStReq(writing && expectWrData && pendStCnt != maxBound);
// get write data from host
hostWrDataQ.deq;
HostWrData wr = hostWrDataQ.first;
// merge with req data & BE
Line newData = reqData;
Vector#(LineSzData, Bit#(DataSzBytes)) newBE = reqBE;
newData[reqSel] = wr.data;
newBE[reqSel] = wr.byteEn;
// common state update
expectWrData <= !wr.last;
reqData <= newData;
reqSel <= reqSel + 1;
reqAddr <= reqSel == maxBound ? reqAddr + 1 : reqAddr;
// print
if(verbose) begin
$display("[MemLoader doStReq] ", fshow(wr),
" ; reqData ", fshow(reqData), " ; reqBE " ,fshow(reqBE),
" ; reqSel %d ; reqAddr %x", reqSel, reqAddr);
end
// we need to req LLC when: (1) fill up a line OR (2) last host busrt
if(reqSel == maxBound || wr.last) begin
MemLoaderMemReq req = DmaRq {
addr: {reqAddr, 0},
byteEn: unpack(pack(newBE)),
data: newData,
id: ?
};
if(verbose) begin
$display("[MemLoader doStReq] req to LLC ", fshow(req));
end
// reset BE for next fresh LLC req
reqBE <= replicate(0);
// only send real write to LLC, otherwise may spawn orphan read resp
if(req.byteEn != replicate(False)) begin
memReqQ.enq(req);
pendStCnt <= pendStCnt + 1;
end
else begin
doAssert(False, "write req cannot have zero BE");
end
end
else begin
reqBE <= newBE;
end
endrule
rule doStResp(writing);
if(verbose) begin
$display("[MemLoader doStResp] pend st cnt %d, expect wr data %d",
pendStCnt, expectWrData);
end
// get st resp
respStQ.deq;
doAssert(pendStCnt > 0, "pend st cnt underflow");
let pendStCntNext = pendStCnt - 1;
pendStCnt <= pendStCntNext;
if(pendStCntNext == 0 && !expectWrData) begin
// we have finished this write series
hostWrDoneQ.enq(?);
writing <= False;
end
endrule
// We don't resolve rule conflicts here because this module does not need
// to be fast.
interface MemLoaderMMIO mmio;
method ActionValue#(MMIODataPRs) req(MemLoaderAlignedOffset offset,
ByteEn wrBE, Data wrData);
if(wrBE == replicate(False)) begin
return MMIODataPRs {
valid: True,
data: offset == 0 ? memStartAddr : zeroExtend(pack(busy))
};
end
else begin
if(offset == 0) begin
// only write mem start addr when not busy
if(!busy) begin
Vector#(DataSzBytes, Bit#(8)) wrVec = unpack(wrData);
Vector#(DataSzBytes, Bit#(8)) newVec = unpack(memStartAddr);
for(Integer i = 0; i < valueof(DataSzBytes); i = i+1) begin
if(wrBE[i]) begin
newVec[i] = wrVec[i];
end
end
memStartAddr <= pack(newVec);
// become busy and ask host to start transmitting data
busy <= True;
hostStartQ.enq(pack(newVec));
end
return MMIODataPRs {
valid: True,
data: ?
};
end
else begin
return MMIODataPRs {
valid: False,
data: ?
};
end
end
endmethod
endinterface
interface MemLoaderMemClient to_mem;
interface memReq = toFifoDeq(memReqQ);
interface respSt = toFifoEnq(respStQ);
endinterface
interface MemLoaderRequest hostReq;
method Action wrAddr(Bool valid, Addr addr);
hostWrAddrQ.enq(HostWrAddr {
valid: valid,
addr: addr
});
endmethod
method Action wrData(Data data, Bit#(DataSzBytes) byteEn, Bool last);
hostWrDataQ.enq(HostWrData {
data: data,
byteEn: byteEn,
last: last
});
endmethod
endinterface
interface MemLoaderIndInv hostIndInv;
method ActionValue#(Addr) start;
hostStartQ.deq;
return hostStartQ.first;
endmethod
method Action wrDone;
hostWrDoneQ.deq;
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,49 @@
// 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.
// This is the connectal interface for a mem loader backed by connectal
// When the mem loader receives the request from RISC-V processor to initialize
// memory, it will send indication to host. And then host will keep writing
// data to the mem loader, and mem loader should send these writes to the LLC
// of RISC-V processor.
// The interface for host to write is similar to AXI. Write addr should be
// aligned w.r.t 8B.
interface MemLoaderRequest;
// addr channel for host writes. A valid addr is the starting addr for a
// series of sequentail writes. An invalid addr means all writes are done.
method Action wrAddr(Bool valid, Bit#(64) addr);
// data channel for host writes
method Action wrData(Bit#(64) data, Bit#(8) byteEn, Bool last);
endinterface
interface MemLoaderIndication;
// ask host to start write data; memStartAddr is the begin addr to
// initialize memory (for sanity check at host side)
method Action start(Bit#(64) memStartAddr);
// signal that the requested series of writes is done
method Action wrDone;
endinterface

View File

@@ -0,0 +1,43 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import Vector::*;
import ClientServer::*;
// typedefs for mem access performed by processor
// cache line is defined in CacheUtils.bsv
typedef Data MemResp;
typedef enum{Ld, St, Lr, Sc, Amo} MemOp deriving(Eq,Bits,FShow); // add more ops
Bit#(3) memB = 3'b000;
Bit#(3) memH = 3'b001;
Bit#(3) memW = 3'b010;
Bit#(3) memD = 3'b011;
Bit#(3) memBU = 3'b100;
Bit#(3) memHU = 3'b101;
Bit#(3) memWU = 3'b110;

View File

@@ -0,0 +1,122 @@
// 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.
import Fifo::*;
typedef union tagged {
reqT Req;
respT Resp;
} CacheMsg#(type reqT, type respT) deriving(Bits, Eq);
interface MsgFifo#(numeric type n, type reqT, type respT);
// enq port
method Bool reqNotFull;
method Bool respNotFull;
method Action enqReq(reqT r);
method Action enqResp(respT r);
// deq port
method Bool notEmpty;
method Bool reqNotEmpty;
method Bool respNotEmpty;
method Action deq;
method CacheMsg#(reqT, respT) first;
endinterface
interface MsgFifoEnq#(type reqT, type respT);
method Bool reqNotFull;
method Bool respNotFull;
method Action enqReq(reqT r);
method Action enqResp(respT r);
endinterface
function MsgFifoEnq#(reqT, respT) toMsgFifoEnq(MsgFifo#(n, reqT, respT) ifc);
return (interface MsgFifoEnq;
method reqNotFull = ifc.reqNotFull;
method respNotFull = ifc.respNotFull;
method enqReq = ifc.enqReq;
method enqResp = ifc.enqResp;
endinterface);
endfunction
interface MsgFifoDeq#(type reqT, type respT);
method Bool notEmpty;
method Bool reqNotEmpty;
method Bool respNotEmpty;
method Action deq;
method CacheMsg#(reqT, respT) first;
endinterface
function MsgFifoDeq#(reqT, respT) toMsgFifoDeq(MsgFifo#(n, reqT, respT) ifc);
return (interface MsgFifoDeq;
method notEmpty = ifc.notEmpty;
method reqNotEmpty = ifc.reqNotEmpty;
method respNotEmpty = ifc.respNotEmpty;
method deq = ifc.deq;
method first = ifc.first;
endinterface);
endfunction
module mkMsgFifo(MsgFifo#(n, reqT, respT)) provisos(
Bits#(reqT, reqW),
Bits#(respT, respW),
Add#(a__, 1, n)
);
Fifo#(n, reqT) reqQ <- mkCFFifo;
Fifo#(n, respT) respQ <- mkCFFifo;
method reqNotFull = reqQ.notFull;
method respNotFull = respQ.notFull;
method Action enqReq(reqT r);
reqQ.enq(r);
endmethod
method Action enqResp(respT r);
respQ.enq(r);
endmethod
method Bool notEmpty = reqQ.notEmpty || respQ.notEmpty;
method Bool reqNotEmpty = reqQ.notEmpty;
method Bool respNotEmpty = respQ.notEmpty;
method CacheMsg#(reqT, respT) first if(reqQ.notEmpty || respQ.notEmpty);
// resp has higher priority than req
if(respQ.notEmpty) begin
return (Resp (respQ.first));
end
else begin
return (Req (reqQ.first));
end
endmethod
method Action deq if(reqQ.notEmpty || respQ.notEmpty);
// resp has higher priority than req
if(respQ.notEmpty) begin
respQ.deq;
end
else begin
reqQ.deq;
end
endmethod
endmodule

View File

@@ -0,0 +1,219 @@
// 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.
`include "ProcConfig.bsv"
import BuildVector::*;
import Types::*;
import ProcTypes::*;
import Fifo::*;
import FIFO::*;
import XilinxIntMul::*;
import XilinxIntDiv::*;
import HasSpecBits::*;
import SpecFifo::*;
import SpecPoisonFifo::*;
export MulDivResp(..);
export MulDivExec(..);
export mkMulDivExec;
function Bool isMulFunc(MulDivFunc func);
return (case(func)
Mul, Mulh: True;
default: False;
endcase);
endfunction
function Bool isDivFunc(MulDivFunc func);
return (case(func)
Div, Rem: True;
default: False;
endcase);
endfunction
function XilinxIntMulSign getXilinxMulSign(MulDivSign s);
return (case(s)
Signed: (Signed);
Unsigned: (Unsigned);
default: (SignedUnsigned);
endcase);
endfunction
function Bool isDivSigned(MulDivSign s);
return s == Signed; // there is no SignedUnsigned for div
endfunction
// resp type
typedef struct {
Data data;
Maybe#(PhyDst) dst;
InstTag tag;
// spec bits is not used in later stage, so not included here
} MulDivResp deriving(Bits, Eq, FShow);
interface MulDivExec;
// input req
method Action exec(MulDivInst mdInst, Data rVal1, Data rVal2,
Maybe#(PhyDst) dst, InstTag tag, SpecBits spec_bits);
// output
method ActionValue#(MulDivResp) mulResp;
method ActionValue#(MulDivResp) divResp;
// speculation
interface SpeculationUpdate specUpdate;
endinterface
// spec fifos in parallel with func units
typedef struct {
MulDivFunc func;
Bool w; // op32
// generic bookkeepings
Maybe#(PhyDst) dst;
InstTag tag;
} MulDivExecInfo deriving(Bits, Eq, FShow);
typedef SpecPoisonFifo#(`BOOKKEEPING_INT_MUL_SIZE, MulDivExecInfo) MulExecQ;
(* synthesize *)
module mkMulExecQ(MulExecQ);
let m <- mkSpecPoisonFifo(True); // lazy enq
return m;
endmodule
typedef SpecPoisonFifo#(2, MulDivExecInfo) DivExecQ;
(* synthesize *)
module mkDivExecQ(DivExecQ);
let m <- mkSpecPoisonFifo(True); // lazy enq
return m;
endmodule
// don't synthesize to optimize guard of exec
module mkMulDivExec(MulDivExec);
Bool verbose = False;
XilinxIntMul#(void) mulUnit <- mkXilinxIntMul;
XilinxIntDiv#(void) divUnit <- mkXilinxIntDiv;
let mulQ <- mkMulExecQ;
let divQ <- mkDivExecQ;
// drain poisoned resp
rule deqMulPoisoned(mulQ.first_poisoned);
mulQ.deq;
mulUnit.deqResp;
endrule
rule deqDivPoisoned(divQ.first_poisoned);
divQ.deq;
divUnit.deqResp;
endrule
method Action exec(MulDivInst mdInst, Data rVal1, Data rVal2,
Maybe#(PhyDst) dst, InstTag tag, SpecBits spec_bits);
if(verbose) begin
$display("[MulDiv] ", fshow(mdInst), ", ",
fshow(rVal1), ", ", fshow(rVal2));
end
// get the operands, we need to extend correctly in case of OP32. This
// does not matter for MULW but matters for DIVW.
Data a = rVal1;
Data b = rVal2;
if(mdInst.w) begin
a = mdInst.sign == Unsigned ? zeroExtend(rVal1[31:0]) :
signExtend(rVal1[31:0]);
b = mdInst.sign == Signed ? signExtend(rVal2[31:0]) :
zeroExtend(rVal2[31:0]);
end
// issue to func unit & bookkeeping fifo
let info = MulDivExecInfo {
func: mdInst.func,
w: mdInst.w,
dst: dst,
tag: tag
};
if(isMulFunc(mdInst.func)) begin
mulUnit.req(a, b, getXilinxMulSign(mdInst.sign), ?);
mulQ.enq(ToSpecFifo {
data: info,
spec_bits: spec_bits
});
end
else begin
divUnit.req(a, b, isDivSigned(mdInst.sign), ?);
divQ.enq(ToSpecFifo {
data: info,
spec_bits: spec_bits
});
end
endmethod
// output
method ActionValue#(MulDivResp) mulResp if(!mulQ.first_poisoned);
mulUnit.deqResp;
mulQ.deq;
let info = mulQ.first_data.data;
doAssert(isMulFunc(info.func), "must be mul func");
// get result data
Data data = (case(info.func)
Mul : (truncate(mulUnit.product));
Mulh : (truncateLSB(mulUnit.product));
default: ?;
endcase);
// correct for OP32 instructions
if (info.w) begin
data = signExtend(data[31:0]);
end
return MulDivResp {
data: data,
dst: info.dst,
tag: info.tag
};
endmethod
method ActionValue#(MulDivResp) divResp if(!divQ.first_poisoned);
divUnit.deqResp;
divQ.deq;
let info = divQ.first_data.data;
doAssert(isDivFunc(info.func), "must be div func");
// get result data
Data data = (case(info.func)
Div : (divUnit.quotient);
Rem : (divUnit.remainder);
default: ?;
endcase);
// correct for OP32 instructions
if (info.w) begin
data = signExtend(data[31:0]);
end
return MulDivResp {
data: data,
dst: info.dst,
tag: info.tag
};
endmethod
interface specUpdate = joinSpeculationUpdate(vec(
mulQ.specUpdate,
divQ.specUpdate
));
endmodule

View File

@@ -0,0 +1,206 @@
// 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.
import FShow::*;
// performance counter typedefs
// query performance counters in each stage/module
typedef enum {
L1ILdCnt,
L1ILdMissCnt,
L1ILdMissLat,
L1IReconcileCnt
} L1IPerfType deriving(Bits, Eq, FShow);
typedef enum {
L1DLdCnt,
L1DLdMissCnt,
L1DLdMissLat,
L1DStCnt,
L1DStMissCnt,
L1DStMissLat,
L1DAmoCnt,
L1DAmoMissCnt,
L1DAmoMissLat,
L1DSelfInvCnt, // self inv due to max hits
L1DReconcileCnt
} L1DPerfType deriving(Bits, Eq, FShow);
typedef enum {
LLCDmaMemLdCnt,
LLCDmaMemLdLat,
LLCNormalMemLdCnt,
LLCNormalMemLdLat,
LLCMshrBlockCycles, // full MSHR blocks new cRq
LLCDownRespCnt,
LLCDownRespDataCnt,
LLCDownReqCnt,
LLCUpRespCnt,
LLCUpRespDataCnt,
LLCDmaLdReqCnt,
LLCDmaStReqCnt
} LLCPerfType deriving(Bits, Eq, FShow);
typedef enum {
L1TlbAccessCnt,
L1TlbMissParentCnt, // miss and send req to parent TLB
L1TlbMissParentLat,
L1TlbMissPeerCnt, // miss and wait for resp for peer entry
L1TlbMissPeerLat,
L1TlbHitUnderMissCnt,
L1TlbAllMissCycles // all TLB req entries are miss, so TLB is blocked
} L1TlbPerfType deriving(Bits, Eq, FShow);
typedef enum {
L2TlbInstMissCnt,
L2TlbInstMissLat,
L2TlbInstPageWalks,
L2TlbInstSavedPageWalks,
L2TlbInstHugePageHits, // hits on huge page (2MB, 1GB)
L2TlbInstHugePageMisses, // miss (i.e., page walk) to get huge page (2MB, 1GB)
L2TlbDataMissCnt,
L2TlbDataMissLat,
L2TlbDataPageWalks,
L2TlbDataSavedPageWalks,
L2TlbDataHugePageHits,
L2TlbDataHugePageMisses,
L2TlbHitUnderMissCnt,
L2TlbAllMissCycles, // all TLB req entries are doing page walk, so TLB is blocked
L2TlbPeerSavedMemReqs // mem req saved by re-using peer req's page walk
} L2TlbPerfType deriving(Bits, Eq, FShow);
typedef enum {
DecRedirectBr,
DecRedirectJmp,
DecRedirectJr,
DecRedirectOther
} DecStagePerfType deriving(Bits, Eq, FShow);
typedef enum {
SupRenameCnt, // number of cycles that rename correct path inst cnt > 1
SpecNoneCycles,
SpecNonMemCycles,
ExeRedirectBr,
ExeRedirectJr,
ExeRedirectOther,
ExeLdStallByLd,
ExeLdStallBySt,
ExeLdStallBySB,
ExeLdForward,
ExeLdMemLat,
ExeStMemLat,
ExeLdToUseLat,
ExeLdToUseCnt,
ExeTlbExcep,
ExeScSuccessCnt,
ExeLrScAmoAcqCnt,
ExeLrScAmoRelCnt,
ExeFenceAcqCnt,
ExeFenceRelCnt,
ExeFenceCnt,
ExeIntMulCnt,
ExeIntDivCnt,
ExeFpFmaCnt,
ExeFpDivCnt,
ExeFpSqrtCnt
} ExeStagePerfType deriving(Bits, Eq, FShow);
typedef enum {
LdQFullCycles,
StQFullCycles,
ROBFullCycles,
AluRS0FullCycles,
AluRS1FullCycles,
FpuMulDivRSFullCycles,
MemRSFullCycles,
EpochFullCycles,
SpecTagFullCycles
} CoreSizePerfType deriving(Bits, Eq, FShow);
typedef enum {
CycleCnt,
InstCnt,
UserInstCnt,
SupComUserCnt, // number of cycles that commit user inst cnt > 1
ComBrCnt,
ComJmpCnt,
ComJrCnt,
ComLdCnt,
ComStCnt,
ComLrCnt,
ComScCnt,
ComAmoCnt,
ComLdKillByLd,
ComLdKillBySt,
ComLdKillByCache,
ComSysCnt, // system inst count
ExcepCnt,
InterruptCnt,
FlushTlbCnt,
FlushSecurityCnt,
FlushBPCnt,
FlushCacheCnt
} ComStagePerfType deriving(Bits, Eq, FShow);
// PerfReq = XXPerfType
typedef struct {
perfType pType;
Bit#(64) data;
} PerfResp#(type perfType) deriving(Bits, Eq);
interface Perf#(type perfType);
method Action setStatus(Bool doStats); // change whether we collect data
method Action req(perfType r);
method ActionValue#(PerfResp#(perfType)) resp;
method Bool respValid;
endinterface
// query performance counters in the whole processor
typedef Bit#(5) PerfType; // for all XXPerfType
// which stage/module to query
typedef enum {
ICache,
DCache,
ITlb,
DTlb,
L2Tlb,
DecStage,
ExeStage,
ComStage,
CoreSize,
LLC
} PerfLocation deriving(Bits, Eq, FShow);
typedef struct {
PerfLocation loc;
PerfType pType;
} ProcPerfReq deriving(Bits, Eq);
typedef struct {
PerfLocation loc;
PerfType pType;
Bit#(64) data;
} ProcPerfResp deriving(Bits, Eq);

View File

@@ -0,0 +1,111 @@
// 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.
`include "ProcConfig.bsv"
// Correct use of the register file implies that the same index can't be used for simultaneous read and write from different rules. If different indices are used reads and writes are conflict free. If the reads and writes are in the same rule, write updates the file at the end of the rule.
// We have imitated this conflict free behavior using config regs.
// If we had used ordinary registers, then read<write
// In many designs where we needed Bypass register file, the bypassing was implemented outside the register file, explicitly.
import Types::*;
import ProcTypes::*;
import Vector::*;
import Ehr::*;
import ConfigReg::*;
interface RFileWr;
method Action wr( PhyRIndx rindx, Data data );
endinterface
interface RFileRd;
method Data rd1( PhyRIndx rindx );
method Data rd2( PhyRIndx rindx );
method Data rd3( PhyRIndx rindx );
endinterface
interface RFile#(numeric type wrNum, numeric type rdNum);
interface Vector#(wrNum, RFileWr) write;
interface Vector#(rdNum, RFileRd) read;
endinterface
// lazy: read EHR port 0 of the regfile
// this must be used together with lazy reservation station
module mkRFile#(Bool lazy)( RFile#(wrNum, rdNum) ) provisos (
NumAlias#(ehrPortNum, TAdd#(wrNum, 1)) // wr [< rd] (only in case lazy = false)
);
let verbose = False;
// phy reg init val must be 0: because x0 is renamed to phy reg 0,
// which must be 0 at all time
Vector#(NumPhyReg, Ehr#(ehrPortNum, Data)) rfile <- replicateM(mkEhr(0));
Vector#(NumPhyReg, Data) rdData = ?;
if(lazy) begin
// if being lazy, just return port 0 for read
Vector#(NumPhyReg, Wire#(Data)) rdWire <- replicateM(mkBypassWire);
(* fire_when_enabled, no_implicit_conditions *)
rule setWire;
for(Integer i = 0; i < valueof(NumPhyReg); i = i+1) begin
rdWire[i] <= rfile[i][0];
end
endrule
for(Integer i = 0; i < valueof(NumPhyReg); i = i+1) begin
rdData[i] = rdWire[i];
end
end
else begin
Integer rd_port = valueof(wrNum); // read the last ehr port
for(Integer i = 0; i < valueof(NumPhyReg); i = i+1) begin
rdData[i] = rfile[i][rd_port];
end
end
function Data getRead(PhyRIndx rindx);
return rdData[rindx];
endfunction
Vector#(wrNum, RFileWr) wrIfc = ?;
for(Integer i = 0; i < valueof(wrNum); i = i+1) begin
wrIfc[i] = (interface RFileWr;
method Action wr( PhyRIndx rindx, Data data );
if (verbose) $display("[RFile] wr_%d: r %h <= %h", i, rindx, data);
rfile[rindx][i] <= data;
endmethod
endinterface);
end
Vector#(rdNum, RFileRd) rdIfc = ?;
for(Integer i = 0; i < valueof(rdNum); i = i+1) begin
rdIfc[i] = (interface RFileRd;
method Data rd1( PhyRIndx rindx ) = getRead(rindx);
method Data rd2( PhyRIndx rindx ) = getRead(rindx);
method Data rd3( PhyRIndx rindx ) = getRead(rindx);
endinterface);
end
interface write = wrIfc;
interface read = rdIfc;
endmodule

View File

@@ -0,0 +1,941 @@
// 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.
`include "ProcConfig.bsv"
import Vector::*;
import Types::*;
import FShow::*;
import DefaultValue::*;
import MemoryTypes::*;
typedef `NUM_CORES CoreNum;
typedef Bit#(TLog#(CoreNum)) CoreId;
typedef `sizeSup SupSize;
typedef Bit#(TLog#(SupSize)) SupWaySel;
typedef Bit#(TLog#(TAdd#(SupSize, 1))) SupCnt;
typedef `NUM_EPOCHS NumEpochs;
typedef Bit#(TLog#(NumEpochs)) Epoch;
typedef `NUM_SPEC_TAGS NumSpecTags;
typedef Bit#(TLog#(NumSpecTags)) SpecTag;
typedef Bit#(NumSpecTags) SpecBits;
typedef `ROB_SIZE NumInstTags;
typedef TDiv#(NumInstTags, SupSize) SingleScalarSize;
typedef Bit#(TLog#(SingleScalarSize)) SingleScalarPtr;
typedef Bit#(TAdd#(1, TLog#(SingleScalarSize))) SingleScalarLen;
// consider ROB as a FIFO of size 2^log(NumInstTags)
// inst time is the index of the inst in the FIFO
// This indicates older/younger inst
typedef Bit#(TLog#(NumInstTags)) InstTime;
typedef struct {
SupWaySel way; // which way in superscalar
SingleScalarPtr ptr; // pointer within a way
InstTime t; // inst time in ROB (for dispatch in reservation station)
} InstTag deriving(Bits, Eq, FShow);
typedef `SB_SIZE SBSize;
typedef Bit#(TLog#(SBSize)) SBIndex;
//typedef `LDSTQ_SIZE LdStQSize;
//typedef Bit#(TLog#(LdStQSize)) LdStQTag;
typedef `LDQ_SIZE LdQSize;
typedef Bit#(TLog#(LdQSize)) LdQTag;
typedef `STQ_SIZE StQSize;
typedef Bit#(TLog#(StQSize)) StQTag;
typedef union tagged {
LdQTag Ld;
StQTag St;
} LdStQTag deriving(Bits, Eq, FShow);
typedef enum {Ld, St, Cache} LdKilledBy deriving(Bits, Eq, FShow);
typedef `DRAM_MAX_REQS DramMaxReqs;
typedef `DRAM_MAX_READS DramMaxReads;
typedef `DRAM_MAX_WRITES DramMaxWrites;
typedef `DRAM_LATENCY DramLatency;
typedef Bit#(`LOG_DEADLOCK_CYCLES) DeadlockTimer;
typedef struct {
// ISA modes
Bool s;
Bool u;
// standard ISA extensions
Bool m;
Bool a;
Bool f;
Bool d;
} RiscVISASubset deriving (Bits, Eq, FShow);
instance DefaultValue#(RiscVISASubset);
function RiscVISASubset defaultValue = RiscVISASubset {
s: True, u: True,
m: `m , a: `a , f: `f , d: `d
};
endinstance
function Bit#(2) getXLBits = 2'b10; // MXL/SXL/UXL fix to RV64
function Bit#(26) getExtensionBits(RiscVISASubset isa);
// include S and I by default
Bit#(26) ext = 26'b00000001000000000100000000;
if (isa.m) ext = ext | 26'b00000000000001000000000000;
if (isa.a) ext = ext | 26'b00000000000000000000000001;
if (isa.f) ext = ext | 26'b00000000000000000000100000;
if (isa.d) ext = ext | 26'b00000000000000000000001000;
return ext;
endfunction
typedef Bit#(5) GprRIndx;
typedef Bit#(5) FpuRIndx;
typedef union tagged {
GprRIndx Gpr;
FpuRIndx Fpu;
} ArchRIndx deriving (Bits, Eq, FShow, Bounded);
typedef TExp#(SizeOf#(ArchRIndx)) NumArchReg;
typedef TAdd#(NumArchReg, `ROB_SIZE) NumPhyReg;
typedef Bit#(TLog#(NumPhyReg)) PhyRIndx;
typedef struct {
PhyRIndx indx;
Bool isFpuReg; // need to keep track of this for fs
} PhyDst deriving (Bits, Eq, FShow);
typedef struct {
Maybe#(ArchRIndx) src1;
Maybe#(ArchRIndx) src2;
Maybe#(FpuRIndx) src3;
Maybe#(ArchRIndx) dst;
} ArchRegs deriving (Bits, Eq, FShow);
typedef struct {
Maybe#(PhyRIndx) src1;
Maybe#(PhyRIndx) src2;
Maybe#(PhyRIndx) src3;
Maybe#(PhyDst) dst;
} PhyRegs deriving (Bits, Eq, FShow);
typedef struct {
Bool src1;
Bool src2;
Bool src3;
Bool dst;
} RegsReady deriving(Bits, Eq, FShow);
function Bool allRegsReady(RegsReady x);
return x.src1 && x.src2 && x.src3 && x.dst;
endfunction
typedef enum {
Invalid = 7'b0,
Load = 7'b0000011,
LoadFp = 7'b0000111,
MiscMem = 7'b0001111,
OpImm = 7'b0010011,
Auipc = 7'b0010111,
OpImm32 = 7'b0011011,
Store = 7'b0100011,
StoreFp = 7'b0100111,
Amo = 7'b0101111,
Op = 7'b0110011,
Lui = 7'b0110111,
Op32 = 7'b0111011,
Fmadd = 7'b1000011,
Fmsub = 7'b1000111,
Fnmsub = 7'b1001011,
Fnmadd = 7'b1001111,
OpFp = 7'b1010011,
Branch = 7'b1100011,
Jalr = 7'b1100111,
Jal = 7'b1101111,
System = 7'b1110011
} Opcode deriving(Bits, Eq, FShow);
function Opcode unpackOpcode(Bit#(7) x);
return (case(x)
pack(Opcode'(Load )): (Load );
pack(Opcode'(LoadFp )): (LoadFp );
pack(Opcode'(MiscMem)): (MiscMem);
pack(Opcode'(OpImm )): (OpImm );
pack(Opcode'(Auipc )): (Auipc );
pack(Opcode'(OpImm32)): (OpImm32);
pack(Opcode'(Store )): (Store );
pack(Opcode'(StoreFp)): (StoreFp);
pack(Opcode'(Amo )): (Amo );
pack(Opcode'(Op )): (Op );
pack(Opcode'(Lui )): (Lui );
pack(Opcode'(Op32 )): (Op32 );
pack(Opcode'(Fmadd )): (Fmadd );
pack(Opcode'(Fmsub )): (Fmsub );
pack(Opcode'(Fnmsub )): (Fnmsub );
pack(Opcode'(Fnmadd )): (Fnmadd );
pack(Opcode'(OpFp )): (OpFp );
pack(Opcode'(Branch )): (Branch );
pack(Opcode'(Jalr )): (Jalr );
pack(Opcode'(Jal )): (Jal );
pack(Opcode'(System )): (System );
default : (Invalid);
endcase);
endfunction
typedef enum {
// user standard CSRs
CSRfflags = 12'h001,
CSRfrm = 12'h002,
CSRfcsr = 12'h003,
CSRcycle = 12'hc00,
CSRtime = 12'hc01,
CSRinstret = 12'hc02,
// user non-standard CSRs (TODO)
CSRterminate = 12'h800, // terminate (used to exit Linux)
CSRstats = 12'h801, // turn on/off perf counters
// supervisor standard CSRs
CSRsstatus = 12'h100,
// no user trap handler, so no se/ideleg
CSRsie = 12'h104,
CSRstvec = 12'h105,
CSRscounteren = 12'h106,
CSRsscratch = 12'h140,
CSRsepc = 12'h141,
CSRscause = 12'h142,
CSRstval = 12'h143, // it's still called sbadaddr in spike
CSRsip = 12'h144,
CSRsatp = 12'h180, // it's still called sptbr in spike
// machine standard CSRs
CSRmstatus = 12'h300,
CSRmisa = 12'h301,
CSRmedeleg = 12'h302,
CSRmideleg = 12'h303,
CSRmie = 12'h304,
CSRmtvec = 12'h305,
CSRmcounteren = 12'h306,
CSRmscratch = 12'h340,
CSRmepc = 12'h341,
CSRmcause = 12'h342,
CSRmtval = 12'h343, // it's still called mbadaddr in spike
CSRmip = 12'h344,
CSRmcycle = 12'hb00,
CSRminstret = 12'hb02,
CSRmvendorid = 12'hf11,
CSRmarchid = 12'hf12,
CSRmimpid = 12'hf13,
CSRmhartid = 12'hf14,
`ifdef SECURITY
// sanctum machine CSR
CSRmevbase = 12'h7c0,
CSRmevmask = 12'h7c1,
CSRmeatp = 12'h7c2,
CSRmmrbm = 12'h7c3,
CSRmemrbm = 12'h7c4,
CSRmparbase = 12'h7c5,
CSRmparmask = 12'h7c6,
CSRmeparbase = 12'h7c7,
CSRmeparmask = 12'h7c8,
CSRmflush = 12'h7c9, // flush pipeline + cache
CSRmspec = 12'h7ca, // control speculation
// sanctum user CSR
CSRtrng = 12'hcc0, // random number for secure boot
`endif
// CSR that catches all the unimplemented CSRs. To avoid exception on this,
// make it a user non-standard read/write CSR.
CSRnone = 12'h8ff
} CSR deriving(Bits, Eq, FShow);
function CSR unpackCSR(Bit#(12) x);
return (case(x)
pack(CSR'(CSRfflags )): (CSRfflags );
pack(CSR'(CSRfrm )): (CSRfrm );
pack(CSR'(CSRfcsr )): (CSRfcsr );
pack(CSR'(CSRcycle )): (CSRcycle );
pack(CSR'(CSRtime )): (CSRtime );
pack(CSR'(CSRinstret )): (CSRinstret );
pack(CSR'(CSRterminate )): (CSRterminate );
pack(CSR'(CSRstats )): (CSRstats );
pack(CSR'(CSRsstatus )): (CSRsstatus );
pack(CSR'(CSRsie )): (CSRsie );
pack(CSR'(CSRstvec )): (CSRstvec );
pack(CSR'(CSRscounteren)): (CSRscounteren);
pack(CSR'(CSRsscratch )): (CSRsscratch );
pack(CSR'(CSRsepc )): (CSRsepc );
pack(CSR'(CSRscause )): (CSRscause );
pack(CSR'(CSRstval )): (CSRstval );
pack(CSR'(CSRsip )): (CSRsip );
pack(CSR'(CSRsatp )): (CSRsatp );
pack(CSR'(CSRmstatus )): (CSRmstatus );
pack(CSR'(CSRmisa )): (CSRmisa );
pack(CSR'(CSRmedeleg )): (CSRmedeleg );
pack(CSR'(CSRmideleg )): (CSRmideleg );
pack(CSR'(CSRmie )): (CSRmie );
pack(CSR'(CSRmtvec )): (CSRmtvec );
pack(CSR'(CSRmcounteren)): (CSRmcounteren);
pack(CSR'(CSRmscratch )): (CSRmscratch );
pack(CSR'(CSRmepc )): (CSRmepc );
pack(CSR'(CSRmcause )): (CSRmcause );
pack(CSR'(CSRmtval )): (CSRmtval );
pack(CSR'(CSRmip )): (CSRmip );
pack(CSR'(CSRmcycle )): (CSRmcycle );
pack(CSR'(CSRminstret )): (CSRminstret );
pack(CSR'(CSRmvendorid )): (CSRmvendorid );
pack(CSR'(CSRmarchid )): (CSRmarchid );
pack(CSR'(CSRmimpid )): (CSRmimpid );
pack(CSR'(CSRmhartid )): (CSRmhartid );
`ifdef SECURITY
pack(CSR'(CSRmevbase )): (CSRmevbase );
pack(CSR'(CSRmevmask )): (CSRmevmask );
pack(CSR'(CSRmeatp )): (CSRmeatp );
pack(CSR'(CSRmmrbm )): (CSRmmrbm );
pack(CSR'(CSRmemrbm )): (CSRmemrbm );
pack(CSR'(CSRmparbase )): (CSRmparbase );
pack(CSR'(CSRmparmask )): (CSRmparmask );
pack(CSR'(CSRmeparbase )): (CSRmeparbase );
pack(CSR'(CSRmeparmask )): (CSRmeparmask );
pack(CSR'(CSRmflush )): (CSRmflush );
pack(CSR'(CSRmspec )): (CSRmspec );
pack(CSR'(CSRtrng )): (CSRtrng );
`endif
default : (CSRnone );
endcase);
endfunction
// values for CSRmspec
Bit#(2) mSpecAll = 0; // every inst can speculate
Bit#(2) mSpecNonMem = 1; // only non-memory inst can speculate
Bit#(2) mSpecNone = 2; // no inst can speculate
typedef enum {
Unsupported,
Nop,
Amo,
Alu,
Ld, St, Lr, Sc,
J, Jr, Br,
Auipc,
Fpu,
Csr,
Fence,
FenceI, SFence,
Ecall, Ebreak,
Sret, Mret, // do not support URET
Interrupt // we may turn an inst to an interrupt in implementation
} IType deriving(Bits, Eq, FShow);
typedef enum {
Eq, Neq,
Lt, Ltu, Ge, Geu,
AT, NT
} BrFunc deriving(Bits, Eq, FShow);
typedef enum {
Add, Addw, Sub, Subw,
And, Or, Xor,
Slt, Sltu, Sll, Sllw, Sra, Sraw, Srl, Srlw,
Csrw, Csrs, Csrc
} AluFunc deriving(Bits, Eq, FShow);
typedef enum {Mul, Mulh, Div, Rem} MulDivFunc deriving(Bits, Eq, FShow);
typedef enum {
Signed, Unsigned, SignedUnsigned
} MulDivSign deriving(Bits, Eq, FShow);
typedef struct {
MulDivFunc func;
Bool w; // use word, i.e. 32-bit
MulDivSign sign;
} MulDivInst deriving(Bits, Eq, FShow);
typedef enum {
FAdd, FSub, FMul, FDiv, FSqrt,
FSgnj, FSgnjn, FSgnjx,
FMin, FMax,
FCvt_FF,
FCvt_WF, FCvt_WUF, FCvt_LF, FCvt_LUF,
FCvt_FW, FCvt_FWU, FCvt_FL, FCvt_FLU,
FEq, FLt, FLe,
FClass, FMv_XF, FMv_FX,
FMAdd, FMSub, FNMSub, FNMAdd
} FpuFunc deriving(Bits, Eq, FShow);
typedef enum {
Single,
Double
} FpuPrecision deriving(Bits, Eq, FShow);
typedef struct {
FpuFunc func;
RVRoundMode rm;
FpuPrecision precision;
} FpuInst deriving(Bits, Eq, FShow);
// LdStInst and AmoInst are defined in Types.bsv
typedef union tagged {
AluFunc Alu;
BrFunc Br;
MemInst Mem;
MulDivInst MulDiv;
FpuInst Fpu;
void Other;
} ExecFunc deriving(Bits, Eq, FShow);
// Rounding Modes (encoding by risc-v, not general fpu)
typedef enum {
RNE = 3'b000,
RTZ = 3'b001,
RDN = 3'b010,
RUP = 3'b011,
RMM = 3'b100,
RDyn = 3'b111
} RVRoundMode deriving(Bits, Eq, FShow);
typedef enum {
InstAddrMisaligned = 4'd0,
InstAccessFault = 4'd1,
IllegalInst = 4'd2,
Breakpoint = 4'd3,
LoadAddrMisaligned = 4'd4,
LoadAccessFault = 4'd5,
StoreAddrMisaligned = 4'd6,
StoreAccessFault = 4'd7,
EnvCallU = 4'd8,
EnvCallS = 4'd9,
EnvCallM = 4'd11,
InstPageFault = 4'd12,
LoadPageFault = 4'd13,
StorePageFault = 4'd15
} Exception deriving(Bits, Eq, FShow);
typedef enum {
UserSoftware = 4'd0,
SupervisorSoftware = 4'd1,
MachineSoftware = 4'd3,
UserTimer = 4'd4,
SupervisorTimer = 4'd5,
MachineTimer = 4'd7,
UserExternal = 4'd8,
SupervisorExternel = 4'd9,
MachineExternal = 4'd11
} Interrupt deriving(Bits, Eq, FShow);
typedef 12 InterruptNum;
// Traps are either an exception or an interrupt
typedef union tagged {
Exception Exception;
Interrupt Interrupt;
} Trap deriving(Bits, Eq, FShow);
// privilege modes
Bit#(2) prvU = 0;
Bit#(2) prvS = 1;
Bit#(2) prvM = 3;
// VM modes
Bit#(4) vmBare = 0;
Bit#(4) vmSv39 = 9;
typedef struct {
// for decoding floating-point instructions
Bit#(3) frm;
Bool fEnabled;
// for decoding privileged instructions
Bit#(2) prv;
Bool trapVM; // mstatus.tvm: trap on CSRXXX inst on satp or SFENCE.VMA
// executed in S mode
Bool timeoutWait; // mstatus.tw: trap on WFI after waiting N cycles in S
// mode. This is currently ignore since WFI is a NOP.
Bool trapSret; // mstatus.tsr: trap on SRET executed in S mode
// for decoding rdcycle/time/instret
Bool cycleReadableByS; // S mode can do rdcycle
Bool cycleReadableByU; // U mode can do rdcycle
Bool instretReadableByS; // S mode can do rdinstret
Bool instretReadableByU; // U mode can do rdinstret
Bool timeReadableByS; // S mode can do rdtime
Bool timeReadableByU; // U mode can do rdtime
} CsrDecodeInfo deriving (Bits, Eq, FShow);
typedef struct {
Bit#(2) prv; // has taken mstatus.mprv into account
Asid asid; // currently always 0
Bool sv39; // VM mode: has taken prv into account, False means Bare
Bool exeReadable; // mstatus.mxr: can load page with X=1 and R=0
Bool userAccessibleByS; // mstatus.sum: in S mode (after considering
// mstatus.mprv), accessing page with U=1 will NOT
// fault
Bit#(44) basePPN; // ppn of root page table
`ifdef SECURITY
// sanctum page walk check
Bit#(64) sanctum_evbase;
Bit#(64) sanctum_evmask;
Bit#(44) sanctum_ebasePPN;
Bit#(64) sanctum_mrbm;
Bit#(64) sanctum_emrbm;
Bit#(64) sanctum_parbase;
Bit#(64) sanctum_parmask;
Bit#(64) sanctum_eparbase;
Bit#(64) sanctum_eparmask;
// whether an access on shared (i.e., not in my own private protation
// domain) memory is allowed. This should not be allowed if speculation is
// turned on.
Bool sanctum_authShared;
`endif
} VMInfo deriving(Bits, Eq, FShow);
instance DefaultValue#(VMInfo);
function VMInfo defaultValue = VMInfo {
prv: prvM,
asid: 0,
sv39: False,
exeReadable: False,
userAccessibleByS: False,
basePPN: 0
`ifdef SECURITY
, sanctum_evbase: maxBound,
sanctum_evmask: 0,
sanctum_ebasePPN: 0,
sanctum_mrbm: maxBound,
sanctum_emrbm: 0,
sanctum_parbase: maxBound,
sanctum_parmask: 0,
sanctum_eparbase: 0,
sanctum_eparmask: 0,
sanctum_authShared: False
`endif
};
endinstance
typedef struct {
Addr pc;
Addr nextPc;
IType iType;
Bool taken;
Bool mispredict;
} Redirect deriving (Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr nextPc;
Bool taken;
Bool mispredict;
} ControlFlow deriving (Bits, Eq, FShow);
typedef struct {
DecodedInst dInst;
ArchRegs regs;
Bool illegalInst;
} DecodeResult deriving(Bits, Eq, FShow);
typedef Bit#(32) ImmData; // 32-bit decoded immediate data
typedef struct {
IType iType;
ExecFunc execFunc;
Maybe#(CSR) csr;
Maybe#(ImmData) imm;
} DecodedInst deriving(Bits, Eq, FShow);
function Maybe#(Data) getDInstImm(DecodedInst dInst);
return dInst.imm matches tagged Valid .d ? Valid (signExtend(d)) : Invalid;
endfunction
typedef struct {
Data data;
Data csrData;
Addr addr;
ControlFlow controlFlow;
} ExecResult deriving(Bits, Eq, FShow);
// MMIO
typedef union tagged {
// inst fetch: contains the maximum superscaler way to fetch
SupWaySel Inst;
// data access
void Ld;
void St;
AmoFunc Amo;
} MMIOFunc deriving(Bits, Eq, FShow);
// req fom core to platform
typedef struct {
Addr addr; // physical address
MMIOFunc func; // req type
// BE, shifted for 64-bit aligned. LOAD, STORE and AMO all need to specify
// this. We need this for to remove redundant MMIO accesses (for MSIP), and
// to determine AMO access range (upper 32 bits, lower 32 bits, or full 64
// bits). INST FETCH will not specify this field.
ByteEn byteEn;
// For STORE: this is store data shifted to be 64-bit aligned
// For AMO: this is UNshifted data (like normal mem req)
Data data;
} MMIOCRq deriving(Bits, Eq, FShow);
// resp from platform to core
typedef struct {
Bool valid; // if fase, then access fault
// resp data only for LOAD or AMO req.
// For LOAD: this is the aligned 64-bit result that contains the load
// access range (similar to normal cache loads, i.e., the receiver needs to
// shift the result before writting back to reg).
// For AMO: this is the result that can be directly written into reg, i.e.,
// for 32-bit access, the result has been shifted and sign-extended.
Data data;
} MMIODataPRs deriving(Bits, Eq, FShow);
typedef union tagged {
// Resp for INST fetch. A vector entry can be invalid for two reasons: 1)
// that entry is not requested, 2) that entry is access fault.
Vector#(SupSize, Maybe#(Instruction)) InstFetch;
// Resp for DATA access, i.e. LOAD, STORE and AMO
MMIODataPRs DataAccess;
} MMIOPRs deriving(Bits, Eq, FShow);
// req from platform to core, only access MSIP or MTIP bit
typedef enum {MSIP, MTIP} MMIOPRqTarget deriving(Bits, Eq, FShow);
typedef struct {
MMIOPRqTarget target;
MMIOFunc func;
// For STORE: only data[0] matters.
// For AMO: this is unshifted data which is truncated from the data in the
// original cRq. AMO should only access MSIP with 32-bit access range
// because MSIP is viewed as 32-bit wide in MMIO.
Bit#(32) data;
} MMIOPRq deriving(Bits, Eq, FShow); // req from core to platform,
// resp from core to platform
typedef struct {
// For LOAD or AMO: this is the original MSIP value
// For STORE: this is garbage
Bit#(1) data;
} MMIOCRs deriving(Bits, Eq, FShow);
// Boot rom: each block is 64-bit data
typedef `LOG_BOOT_ROM_BYTES LgBootRomBytes;
typedef TSub#(LgBootRomBytes, TLog#(NumBytes)) LgBootRomSzData;
typedef Bit#(LgBootRomSzData) BootRomIndex;
// mtime: we increment mtime by 50 every 5000 cycles, this simulates a
// 10MHz clock for a 1GHz CPU (same as spike)
typedef /*50*/ 1 TicksPerTimeInc;
typedef /*5000*/ 100 CyclesPerTimeInc;
// Op
Bit#(3) fnADD = 3'b000;
Bit#(3) fnSLL = 3'b001;
Bit#(3) fnSLT = 3'b010;
Bit#(3) fnSLTU = 3'b011;
Bit#(3) fnXOR = 3'b100;
Bit#(3) fnSR = 3'b101;
Bit#(3) fnOR = 3'b110;
Bit#(3) fnAND = 3'b111;
Bit#(7) opALU1 = 7'b0000000;
Bit#(7) opALU2 = 7'b0100000;
Bit#(7) opMULDIV = 7'b0000001;
Bit#(3) fnMUL = 3'b000;
Bit#(3) fnMULH = 3'b001;
Bit#(3) fnMULHSU = 3'b010;
Bit#(3) fnMULHU = 3'b011;
Bit#(3) fnDIV = 3'b100;
Bit#(3) fnDIVU = 3'b101;
Bit#(3) fnREM = 3'b110;
Bit#(3) fnREMU = 3'b111;
// Branch
Bit#(3) fnBEQ = 3'b000;
Bit#(3) fnBNE = 3'b001;
Bit#(3) fnBLT = 3'b100;
Bit#(3) fnBGE = 3'b101;
Bit#(3) fnBLTU = 3'b110;
Bit#(3) fnBGEU = 3'b111;
// Load
Bit#(3) fnLB = 3'b000;
Bit#(3) fnLH = 3'b001;
Bit#(3) fnLW = 3'b010;
Bit#(3) fnLD = 3'b011;
Bit#(3) fnLBU = 3'b100;
Bit#(3) fnLHU = 3'b101;
Bit#(3) fnLWU = 3'b110;
// Store
Bit#(3) fnSB = 3'b000;
Bit#(3) fnSH = 3'b001;
Bit#(3) fnSW = 3'b010;
Bit#(3) fnSD = 3'b011;
// Amo
Bit#(5) fnLR = 5'b00010;
Bit#(5) fnSC = 5'b00011;
Bit#(5) fnAMOSWAP = 5'b00001;
Bit#(5) fnAMOADD = 5'b00000;
Bit#(5) fnAMOXOR = 5'b00100;
Bit#(5) fnAMOAND = 5'b01100;
Bit#(5) fnAMOOR = 5'b01000;
Bit#(5) fnAMOMIN = 5'b10000;
Bit#(5) fnAMOMAX = 5'b10100;
Bit#(5) fnAMOMINU = 5'b11000;
Bit#(5) fnAMOMAXU = 5'b11100;
// FPU
Bit#(2) fmtS = 2'b00;
Bit#(2) fmtD = 2'b01;
Bit#(5) opFADD = 5'b00000;
Bit#(5) opFSUB = 5'b00001;
Bit#(5) opFMUL = 5'b00010;
Bit#(5) opFDIV = 5'b00011;
Bit#(5) opFSQRT = 5'b01011;
Bit#(5) opFSGNJ = 5'b00100;
Bit#(5) opFMINMAX = 5'b00101;
Bit#(5) opFCMP = 5'b10100;
Bit#(5) opFMV_XF = 5'b11100; // FCLASS also
Bit#(5) opFMV_FX = 5'b11110;
Bit#(5) opFCVT_FF = 5'b01000;
Bit#(5) opFCVT_WF = 5'b11000;
Bit#(5) opFCVT_FW = 5'b11010;
//MiscMem
Bit#(3) fnFENCE = 3'b000;
Bit#(3) fnFENCEI = 3'b001;
// System
Bit#(3) fnPRIV = 3'b000;
Bit#(3) fnCSRRW = 3'b001;
Bit#(3) fnCSRRS = 3'b010;
Bit#(3) fnCSRRC = 3'b011;
Bit#(3) fnCSRRWI = 3'b101;
Bit#(3) fnCSRRSI = 3'b110;
Bit#(3) fnCSRRCI = 3'b111;
Bit#(12) privECALL = 12'h000;
Bit#(12) privEBREAK = 12'h001;
Bit#(12) privURET = 12'h002;
Bit#(12) privSRET = 12'h102;
Bit#(12) privMRET = 12'h302;
Bit#(12) privWFI = 12'h105;
Bit#(7) privSFENCEVMA = 7'h9;
function Bool isSystem(IType iType) = (
iType == Unsupported || iType == Interrupt ||
iType == Ecall || iType == Ebreak || iType == Csr ||
iType == SFence || iType == FenceI ||
iType == Sret || iType == Mret
);
// instruction requires replaying (i.e. fetch next instruction after current
// instruction commits)
function Bool doReplay(IType iType) = isSystem(iType);
function Bool isFpuInst(IType iType) = (iType == Fpu);
function Bool isMemInst(IType iType) = (
iType == Ld || iType == St || iType == Lr || iType == Sc || iType == Amo
);
function Fmt showInst(Instruction inst);
Fmt ret = fshow("");
Opcode opcode = unpack(inst[ 6 : 0 ]);
let rd = inst[ 11 : 7 ];
let funct3 = inst[ 14 : 12 ];
let rs1 = inst[ 19 : 15 ];
let rs2 = inst[ 24 : 20 ];
let funct7 = inst[ 31 : 25 ];
Bit#(32) immI = signExtend(inst[31:20]);
Bit#(32) immS = signExtend({ inst[31:25], inst[11:7] });
Bit#(32) immB = signExtend({ inst[31], inst[7], inst[30:25], inst[11:8], 1'b0});
Bit#(32) immU = { inst[31:12], 12'b0 };
Bit#(32) immJ = signExtend({ inst[31], inst[19:12], inst[20], inst[30:25], inst[24:21], 1'b0});
case (opcode)
OpImm:
begin
ret = case (funct3)
fnADD: fshow("addi");
fnSLT: fshow("slti");
fnSLTU: fshow("sltiu");
fnAND: fshow("andi");
fnOR: fshow("ori");
fnXOR: fshow("xori");
fnSLL: fshow("slli");
fnSR: (immI[10] == 0 ? fshow("srli") : fshow("srai"));
endcase;
ret = ret + fshow(" ") + fshow(rd) + fshow(" = ") + fshow(rs1) + fshow(" ");
ret = ret + (case (funct3)
fnSLL, fnSR: fshow(immI[5:0]);
default: fshow(immI);
endcase);
end
OpImm32:
begin
ret = case (funct3)
fnADD: fshow("addiw");
fnSLL: fshow("slliw");
fnSR: (immI[10] == 0 ? fshow("srliw") : fshow("sraiw"));
endcase;
ret = ret + fshow(" ") + fshow(rd) + fshow(" = ") + fshow(rs1) + fshow(" ");
ret = ret + (case (funct3)
fnSLL, fnSR: fshow(immI[4:0]);
default: fshow(immI);
endcase);
end
Op:
begin
ret = case (funct3)
fnADD: (immI[10] == 0 ? fshow("add") : fshow("sub"));
fnSLT: fshow("slt");
fnSLTU: fshow("sltu");
fnAND: fshow("and");
fnOR: fshow("or");
fnXOR: fshow("xor");
fnSLL: fshow("sll");
fnSR: (immI[10] == 0 ? fshow("srl") : fshow("sra"));
endcase;
ret = ret + fshow(" ") + fshow(rd) + fshow(" = ") + fshow(rs1) + fshow(" ") + fshow(rs2);
end
Op32:
begin
ret = case (funct3)
fnADD: (immI[10] == 0 ? fshow("addw") : fshow("subw"));
fnSLL: fshow("sllw");
fnSR: (immI[10] == 0 ? fshow("srlw") : fshow("sraw"));
endcase;
ret = ret + fshow(" ") + fshow(rd) + fshow(" = ") + fshow(rs1) + fshow(" ") + fshow(rs2);
end
Lui:
ret = fshow("lui ") + fshow(rd) + fshow(" ") + fshow(immU);
Auipc:
ret = fshow("auipc ") + fshow(rd) + fshow(" ") + fshow(immU);
Jal:
ret = fshow("jal ") + fshow(rd) + fshow(" ") + fshow(immJ);
Jalr:
ret = fshow("jalr ") + fshow(rd) + fshow(" ") + fshow(rs1) + fshow(" ") + fshow(immI);
Branch:
begin
ret = case(funct3)
fnBEQ: fshow("beq");
fnBNE: fshow("bne");
fnBLT: fshow("blt");
fnBLTU: fshow("bltu");
fnBGE: fshow("bge");
fnBGEU: fshow("bgeu");
endcase;
ret = ret + fshow(" ") + fshow(rs1) + fshow(" ") + fshow(rs2) + fshow(" ") + fshow(immB);
end
Load:
begin
ret = case(funct3)
fnLB: fshow("lb");
fnLH: fshow("lh");
fnLW: fshow("lw");
fnLD: fshow("ld");
fnLBU: fshow("lbu");
fnLHU: fshow("lhu");
fnLWU: fshow("lwu");
endcase;
ret = ret + fshow(" ") + fshow(rd) + fshow(" = ") + fshow(rs1) + fshow(" ") + fshow(immI);
end
Store:
begin
ret = case(funct3)
fnSB: fshow("sb");
fnSH: fshow("sh");
fnSW: fshow("sw");
fnSD: fshow("sd");
endcase;
ret = ret + fshow(" ") + fshow(rs1) + fshow(" ") + fshow(rs2) + fshow(" ") + fshow(immS);
end
MiscMem:
begin
ret = case (funct3)
fnFENCE: fshow("fence");
fnFENCEI: fshow("fence.i");
endcase;
end
System:
begin
case (funct3)
fnCSRRW, fnCSRRS, fnCSRRC, fnCSRRWI, fnCSRRSI, fnCSRRCI:
begin
ret = case(funct3)
fnCSRRW: fshow("csrrw");
fnCSRRC: fshow("csrrc");
fnCSRRS: fshow("csrrs");
fnCSRRWI: fshow("csrrwi");
fnCSRRCI: fshow("csrrci");
fnCSRRSI: fshow("csrrsi");
endcase;
ret = ret + fshow(" ") + fshow(rd) + fshow(" ") + fshow(immI) + fshow(" ") + fshow(rs1);
end
fnPRIV:
begin
ret = case (truncate(immI))
privECALL: fshow("ecall");
privEBREAK: fshow("ebreak");
privURET: fshow("uret");
privSRET: fshow("sret");
privMRET: fshow("mret");
privWFI: fshow("wfi");
default: (
funct7 == privSFENCEVMA ?
(fshow("sfence.vma ") + fshow(rs1) + fshow(" ") + fshow(rs2)) :
fshow("SYSTEM not implemented")
);
endcase;
end
default:
ret = fshow("SYSTEM not implemented");
endcase
end
default:
ret = fshow("nop");
endcase
return ret;
endfunction

View File

@@ -0,0 +1,96 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import RegFile::*;
import Vector::*;
import Ehr::*;
interface RAS;
method Addr first;
// first pop, then push
method Action popPush(Bool pop, Maybe#(Addr) pushAddr);
endinterface
interface ReturnAddrStack;
interface Vector#(SupSize, RAS) ras;
method Action flush;
method Bool flush_done;
endinterface
// Local RAS Typedefs SHOULD BE A POWER OF TWO.
typedef 8 RasEntries;
typedef Bit#(TLog#(RasEntries)) RasIndex;
(* synthesize *)
module mkRas(ReturnAddrStack) provisos(NumAlias#(TExp#(TLog#(RasEntries)), RasEntries));
Vector#(RasEntries, Ehr#(TAdd#(SupSize, 1), Addr)) stack <- replicateM(mkEhr(0));
// head points past valid data
// to gracefully overflow, head is allowed to overflow to 0 and overwrite the oldest data
Ehr#(TAdd#(SupSize, 1), RasIndex) head <- mkEhr(0);
`ifdef SECURITY
Reg#(Bool) flushDone <- mkReg(True);
rule doFlush(!flushDone);
writeVReg(getVEhrPort(stack, valueof(SupSize)), replicate(0));
head[valueof(SupSize)] <= 0;
flushDone <= True;
endrule
`endif
Vector#(SupSize, RAS) rasIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
rasIfc[i] = (interface RAS;
method Addr first;
return stack[head[i]][i];
endmethod
method Action popPush(Bool pop, Maybe#(Addr) pushAddr);
// first pop, then push
RasIndex h = head[i];
if(pop) begin
h = h - 1;
end
if(pushAddr matches tagged Valid .addr) begin
h = h + 1;
stack[h][i] <= addr;
end
head[i] <= h;
endmethod
endinterface);
end
interface ras = rasIfc;
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
endmodule

View File

@@ -0,0 +1,36 @@
// 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.
typedef enum {
TrapCommitLackClaim, // trapped inst lacks renamed dst
TrapCommitRedundantClaim, // trapped inst with redundant renamed dst
NonTrapCommitLackClaim // non-trapped inst lacks renamed dst
} RenameError deriving(Bits, Eq, FShow);
interface RenameDebugIndication;
method Action renameErr(
Bit#(8) core, RenameError err, Bit#(64) pc, Bit#(5) iType,
Bool isException, Bool isInterrupt, Bit#(4) trapVal,
Bit#(32) specBits
);
endinterface

View File

@@ -0,0 +1,503 @@
// 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.
// Alternate free list design:
// The FreeList has the same number of slots as #PhyReg - #ArchReg. When the
// ROB is empty, the FreeList is full. When the ROB is full, the FreeList is
// empty. The FreeList deq method takes the old name for the renamed register
// and writes it to the location just dequeued. On step-by-step rollback, the
// old name is taken from the FreeList and moved back to the RenamingTable.
import Vector::*;
import GetPut::*;
import List::*;
import RevertingVirtualReg::*;
import Types::*;
import ProcTypes::*;
import HasSpecBits::*;
import Ehr::*;
typedef struct {
PhyRegs phy_regs;
} RenameResult deriving(Bits, Eq, FShow);
interface RTRename;
// This will rename the architectural registers in r to physical registers.
// XXX This will **ALWAYS** claim a new physical register as a speculative
// in-flight renaming, no matter whether the dst reg is valid or not.
// This simplifies the implementation for superscalar renaming
method RenameResult getRename(ArchRegs r);
method Action claimRename(ArchRegs r, SpecBits sb);
method Bool canRename; // guard of rename
endinterface
interface RTCommit;
// Commits the oldest instructions. This will free the previous physical
// register used to hold the value for the architectural destination
// register.
method Action commit;
method Bool canCommit; // guard of commit
endinterface
interface RegRenamingTable;
interface Vector#(SupSize, RTRename) rename; // rename port
interface Vector#(SupSize, RTCommit) commit; // commit port
// This subinterface contains the methods specifying correct and incorrect
// speculation. If the speculation is correct, the dependencies on that
// SpecTag should be removed from all SpecBits. If the speculation is
// incorrect, then all renamings that depended on the SpecTag should be
// reverted.
interface SpeculationUpdate specUpdate;
// methods: method Action incorrectSpeculation(SpecTag tag);
// method Action correctSpeculation(SpecTag tag);
endinterface
// actions when we claim a new phy reg in rename
typedef struct {
Maybe#(ArchRIndx) arch;
PhyRIndx phy;
SpecBits specBits;
} RenameClaim deriving(Bits, Eq, FShow);
// actions in case of wrongSpec
typedef struct {
Bool killAll;
SpecTag specTag;
} RTWrongSpec deriving(Bits, Eq, FShow);
(* synthesize *)
module mkRegRenamingTable(RegRenamingTable) provisos (
NumAlias#(size, TSub#(NumPhyReg, NumArchReg)),
Alias#(indexT, Bit#(TLog#(size))),
Alias#(vTagT, Bit#(TLog#(TMul#(2, size)))) // virtual tag: 0 -- size*2-1
);
// ordering: commit < rename < correctSpec
// commit < wrongSpec
// wrongSpec C rename
// NOTES:
// rename do not need to see the effect of commit
// rename is conflict with wrongSpec, so don't need to see its effect
// The real actions of commit, wrongSpec and rename will be done in canon rule
// rename is split into two parts in EHR port assignment:
// - get (get the phy reg for src and dst arch regs, in rename method)
// - claim (truly claim a free phy reg for dst arch reg, in canon rule)
// Although the EHR ports of commit should also be split into two parts,
// we don't need to do so, because the method of commit is directly followed by canon rule.
// EHR ports for renaming_table
Integer rt_get_port = 0;
function Integer rt_commit_port(Integer i) = i;
// EHR ports for valid
Integer valid_get_port = 0;
Integer valid_commit_port = 0;
Integer valid_wrongSpec_port = 1;
Integer valid_claim_port = 1;
// EHR ports for spec_bits
Integer sb_wrongSpec_port = 0;
Integer sb_claim_port = 0;
Integer sb_correctSpec_port = 1;
// non-speculative renaming table at commit port
// initially arch reg i --> phy reg i
Vector#(NumArchReg, Ehr#(SupSize, PhyRIndx)) renaming_table <- genWithM(compose(mkEhr, fromInteger));
// A FIFO of
// - in-flight renaming: when valid = True i.e. within [enqP, deqP)
// - free phy reg: when valid = False i.e. outside [enqP, deqP)
// We store the arch reg and phy reg separately to reduce EHR ports
// XXX A valid in-flight renaming may have arch reg being invalid
// This happens in case we claim a phy reg while the dst arch reg is invalid
Vector#(size, Reg#(Maybe#(ArchRIndx))) new_renamings_arch <- replicateM(mkRegU);
function m#(Reg#(PhyRIndx)) genNewRenamingsPhy(Integer i) provisos (IsModule#(m, a__));
return mkReg(fromInteger(i + valueOf(NumArchReg))); // free phy regs initially
endfunction
Vector#(size, Reg#(PhyRIndx)) new_renamings_phy <- genWithM(genNewRenamingsPhy);
Vector#(size, Ehr#(2, Bool)) valid <- replicateM(mkEhr(False));
Vector#(size, Ehr#(2, SpecBits)) spec_bits <- replicateM(mkEhr(0));
Reg#(indexT) enqP <- mkReg(0); // point to claim free phy reg
Reg#(indexT) deqP <- mkReg(0); // point to commit renaming and make phy reg free
// wires/EHRs to record actions
Vector#(SupSize, RWire#(RenameClaim)) claimEn <- replicateM(mkUnsafeRWire);
Vector#(SupSize, PulseWire) commitEn <- replicateM(mkPulseWire);
RWire#(RTWrongSpec) wrongSpecEn <- mkRWire;
// ordering regs
Vector#(SupSize, Reg#(Bool)) commit_SB_rename <- replicateM(mkRevertingVirtualReg(True));
Reg#(Bool) commit_SB_wrongSpec <- mkRevertingVirtualReg(True);
// wrong spec conflict with rename
Vector#(SupSize, RWire#(void)) wrongSpec_rename_conflict <- replicateM(mkRWire);
function indexT getNextIndex(indexT idx);
return idx == fromInteger(valueof(size) - 1) ? 0 : idx + 1;
endfunction
function indexT incrIndex(indexT idx, SupCnt incr);
Bit#(TLog#(TAdd#(size, 1))) newIdx = zeroExtend(idx) + zeroExtend(incr);
if(newIdx >= fromInteger(valueof(size))) begin
newIdx = newIdx - fromInteger(valueof(size));
end
return truncate(newIdx);
endfunction
// get the index to query renaming_table
function Bit#(TLog#(NumArchReg)) getRTIndex(ArchRIndx arch) = pack(arch);
// vector of index to claim free phy regs for each rename port
Vector#(SupSize, indexT) claimIndex;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
claimIndex[i] = incrIndex(enqP, fromInteger(i));
end
// vector of index to commit phy regs for each commit port
Vector#(SupSize, indexT) commitIndex;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
commitIndex[i] = incrIndex(deqP, fromInteger(i));
end
// similar to LSQ, get virtual tag by using enqP as pivot (enqP is changed at end of cycle)
// valid entry i --> i < enqP ? i + size : i
// NOTE that virtual tag only works for **valid** entry
function vTagT getVTag(indexT i);
return i < enqP ? zeroExtend(i) + fromInteger(valueof(size)) : zeroExtend(i);
endfunction
Vector#(size, vTagT) vTags = map(getVTag, genWith(fromInteger));
// find oldest entry using virtual tag (i.e. smallest)
function Maybe#(indexT) findOldest(Vector#(size, Bool) pred);
function indexT getOlder(indexT a, indexT b);
if(!pred[a]) begin
return b;
end
else if(!pred[b]) begin
return a;
end
else begin
return vTags[a] < vTags[b] ? a : b;
end
endfunction
Vector#(size, indexT) idxVec = genWith(fromInteger);
indexT tag = fold(getOlder, idxVec);
return pred[tag] ? Valid (tag) : Invalid;
endfunction
// find youngest entry using virtual tag (i.e. largest)
function Maybe#(indexT) findYoungest(Vector#(size, Bool) pred);
function indexT getOlder(indexT a, indexT b);
if(!pred[a]) begin
return b;
end
else if(!pred[b]) begin
return a;
end
else begin
return vTags[a] < vTags[b] ? b : a;
end
endfunction
Vector#(size, indexT) idxVec = genWith(fromInteger);
indexT tag = fold(getOlder, idxVec);
return pred[tag] ? Valid (tag) : Invalid;
endfunction
// canonicalize deq, wrongSpec/enq
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
Vector#(SupSize, SupWaySel) supIdxVec = genWith(fromInteger);
// apply commit actions
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(commitEn[i]) begin
indexT curDeqP = commitIndex[i]; // deqP for new renamings
PhyRIndx commit_phy_reg = new_renamings_phy[curDeqP];
Maybe#(ArchRIndx) commit_arch_reg = new_renamings_arch[curDeqP];
if(commit_arch_reg matches tagged Valid .arch) begin
let rtIdx = getRTIndex(arch);
// free phy reg being overwritten in the renaming_table (arch reg is don't care)
PhyRIndx freed_phy_reg = renaming_table[rtIdx][rt_commit_port(i)];
new_renamings_phy[curDeqP] <= freed_phy_reg;
valid[curDeqP][valid_commit_port] <= False;
// update renaming_table
renaming_table[rtIdx][rt_commit_port(i)] <= commit_phy_reg;
end
else begin
// free the phy reg claimed by this renaming (arch reg is don't care)
valid[curDeqP][valid_commit_port] <= False;
end
// sanity check
doAssert(valid[curDeqP][valid_commit_port], "committing entry must be valid");
end
end
// move deqP: find the first non-commit port
function Bool notCommit(SupWaySel i) = !commitEn[i];
indexT nextDeqP;
if(find(notCommit, supIdxVec) matches tagged Valid .idx) begin
nextDeqP = commitIndex[idx];
// sanity check: commit is done consecutively
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
doAssert((fromInteger(i) < idx) == commitEn[i], "commit must be consecutive");
end
end
else begin
nextDeqP = incrIndex(deqP, fromInteger(valueof(SupSize)));
end
deqP <= nextDeqP;
// do wrongSpec OR claim free phy reg
if(wrongSpecEn.wget matches tagged Valid .x) begin
Bool killAll = x.killAll;
SpecTag specTag = x.specTag;
Vector#(size, indexT) idxVec = genWith(fromInteger);
// do wrongSpec, first kill entries (make in-flight renaming to free phy reg)
function Bool needKill(indexT i);
return killAll || spec_bits[i][sb_wrongSpec_port][specTag] == 1;
endfunction
Vector#(size, Bool) isKill = map(needKill, idxVec);
function Action kill(indexT i);
action
if(isKill[i]) begin
valid[i][valid_wrongSpec_port] <= False;
end
endaction
endfunction
joinActions(map(kill, idxVec));
// move enqP: find the oldest **valid** entry being killed
Vector#(size, Bool) killValid = zipWith( \&& , isKill , readVEhr(valid_wrongSpec_port, valid) );
indexT nextEnqP = enqP;
if(findOldest(killValid) matches tagged Valid .idx) begin
nextEnqP = idx;
end
enqP <= nextEnqP;
end
else begin
// claim phy reg
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(claimEn[i].wget matches tagged Valid .claim) begin
indexT curEnqP = claimIndex[i];
new_renamings_arch[curEnqP] <= claim.arch; // keep phy reg unchanged
valid[curEnqP][valid_claim_port] <= True;
spec_bits[curEnqP][sb_claim_port] <= claim.specBits;
// sanity check
doAssert(!valid[curEnqP][valid_get_port], "claiming entry must be invalid");
doAssert(claim.phy == new_renamings_phy[curEnqP], "phy reg should match");
end
end
// move enqP: find the first non-claim port
function Bool notClaim(SupWaySel i) = !isValid(claimEn[i].wget);
indexT nextEnqP;
if(find(notClaim, supIdxVec) matches tagged Valid .idx) begin
nextEnqP = claimIndex[idx];
// sanity check: rename is consecutive
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
doAssert((fromInteger(i) < idx) == isValid(claimEn[i].wget), "claim is consecutive");
end
end
else begin
nextEnqP = incrIndex(enqP, fromInteger(valueof(SupSize)));
end
enqP <= nextEnqP;
end
endrule
`ifdef BSIM
// sanity check in simulation
// all valid entry are within [deqP, enqP), outsiders are invalid entries
(* fire_when_enabled, no_implicit_conditions *)
rule sanityCheck;
Bool empty = all( \== (False), readVEhr(0, valid) );
function Bool in_range(indexT i);
// i is within [deqP, enqP)
if(empty) begin
return False;
end
else begin
if(deqP < enqP) begin
return deqP <= i && i < enqP;
end
else begin
return deqP <= i || i < enqP;
end
end
endfunction
for(Integer i = 0; i < valueof(size); i = i+1) begin
doAssert(in_range(fromInteger(i)) == valid[i][0],
"entries inside [deqP, enqP) should be valid, otherwise invalid"
);
end
doAssert(enqP <= fromInteger(valueof(size) - 1), "enqP < size");
doAssert(deqP <= fromInteger(valueof(size) - 1), "deqP < size");
endrule
`endif
// function to search claimed phy regs just in this cycle to
// get phy reg for an arch reg for get_renaming at port getPort
function Maybe#(PhyRIndx) search_claimed_renamings(Integer getPort, ArchRIndx arch_reg);
List#(RWire#(RenameClaim)) claims = List::take(getPort, toList(claimEn));
function Maybe#(PhyRIndx) getHit(RWire#(RenameClaim) clm);
if(clm.wget matches tagged Valid .e) begin
return e.arch == (Valid (arch_reg)) ? Valid (e.phy) : Invalid;
end
else begin
return Invalid;
end
endfunction
List#(Maybe#(PhyRIndx)) hitList = List::map(getHit, claims);
// find the most recently claimed phy reg (i.e. largest list index)
Maybe#(Maybe#(PhyRIndx)) hit = List::find(isValid, List::reverse(hitList));
return fromMaybe(Invalid, hit);
endfunction
// function to search new_renamings to get phy reg for an arch reg
function Maybe#(PhyRIndx) search_new_src_renamings(ArchRIndx arch_reg);
// first get all hitting in-flight renamings
function Bool hit(Integer i);
return valid[i][valid_get_port] && new_renamings_arch[i] == (Valid (arch_reg));
endfunction
Vector#(size, Bool) isHit = map(hit, genVector);
// find the youngest
if(findYoungest(isHit) matches tagged Valid .idx) begin
return Valid (new_renamings_phy[idx]);
end
else begin
return Invalid;
end
endfunction
// function to get phy reg for an arch reg for get_renaming at port getPort
// priority: newly claimed -> new_renamings -> renaming_table
function PhyRIndx get_src_renaming(Integer getPort, ArchRIndx arch_reg);
let claim_phy_reg = search_claimed_renamings(getPort, arch_reg);
let new_phy_reg = search_new_src_renamings(arch_reg);
let existing_phy_reg = renaming_table[getRTIndex(arch_reg)][rt_get_port];
return fromMaybe(fromMaybe(existing_phy_reg, new_phy_reg), claim_phy_reg);
endfunction
// function to find a free phy reg to claim (at port claimPort) for a dst arch reg
function PhyRIndx get_dst_renaming(Integer claimPort);
return new_renamings_phy[claimIndex[claimPort]];
endfunction
function Bool isFpuReg(ArchRIndx arch_reg);
return arch_reg matches tagged Fpu .* ? True : False;
endfunction
Vector#(SupSize, RTRename) renameIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
// XXX we always claim a free phy reg, but only return it if arch dst reg is valid
Bool guard = !valid[claimIndex[i]][valid_get_port];
PhyRIndx claim_phy_reg = get_dst_renaming(i);
renameIfc[i] = (interface RTRename;
method RenameResult getRename(ArchRegs r) if(guard);
// get renamings
PhyRegs phy_regs = PhyRegs {
src1: tagged Invalid,
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid
};
if (r.src1 matches tagged Valid .valid_src1) begin
phy_regs.src1 = Valid (get_src_renaming(i, valid_src1));
end
if (r.src2 matches tagged Valid .valid_src2) begin
phy_regs.src2 = Valid (get_src_renaming(i, valid_src2));
end
if (r.src3 matches tagged Valid .valid_src3) begin
phy_regs.src3 = tagged Valid (get_src_renaming(i, tagged Fpu valid_src3));
end
if (r.dst matches tagged Valid .valid_dst) begin
phy_regs.dst = Valid (PhyDst {
indx: claim_phy_reg,
isFpuReg: isFpuReg(valid_dst)
});
end
return RenameResult {
phy_regs: phy_regs
};
endmethod
method Action claimRename(ArchRegs r, SpecBits sb) if(guard);
// record the claim
claimEn[i].wset(RenameClaim {
arch: r.dst,
phy: claim_phy_reg,
specBits: sb
});
// conflict with wrong spec
wrongSpec_rename_conflict[i].wset(?);
// ordering with commit
commit_SB_rename[i] <= False;
endmethod
method canRename = guard;
endinterface);
end
Vector#(SupSize, RTCommit) commitIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
Bool guard = valid[commitIndex[i]][valid_commit_port] &&
all(id, readVReg(commit_SB_rename)) && // ordering: commit < rename
commit_SB_wrongSpec; // ordering: commit < wrongSpec
commitIfc[i] = (interface RTCommit;
method Action commit if(guard);
commitEn[i].send; // record commit action
endmethod
method canCommit = guard;
endinterface);
end
interface rename = renameIfc;
interface commit = commitIfc;
interface SpeculationUpdate specUpdate;
method Action incorrectSpeculation(Bool killAll, SpecTag specTag);
// record wrongSpec
wrongSpecEn.wset(RTWrongSpec {
killAll: killAll,
specTag: specTag
});
// conflict with rename
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
wrongSpec_rename_conflict[i].wset(?);
end
// order after commit
commit_SB_wrongSpec <= False;
endmethod
method Action correctSpeculation(SpecBits mask);
function Action correctSpec(Integer i);
action
spec_bits[i][sb_correctSpec_port] <= spec_bits[i][sb_correctSpec_port] & mask;
endaction
endfunction
Vector#(size, Integer) idxVec = genVector;
joinActions(map(correctSpec, idxVec));
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,993 @@
// 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.
`include "ProcConfig.bsv"
// ReorderBuffer
import Types::*;
import ProcTypes::*;
import HasSpecBits::*;
import Vector::*;
import Assert::*;
import Ehr::*;
import RevertingVirtualReg::*;
// right after execution, full_result has more up-to-date data (e.g. ppc of mispredicted branch)
// some parts of full_result are for verification
// but some are truly used for execution
// ppc is only used by iType = BR/J/JR
// csrData is only used by iType = Csr
// vaddr is only used by mem inst in page fault
typedef union tagged {
Addr PPC; // at default store ppc
Addr VAddr; // for mem inst, store vaddr
Data CSRData; // for Csr inst, store csr_data
} PPCVAddrCSRData deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
IType iType;
Maybe#(CSR) csr;
Bool claimed_phy_reg; // whether we need to commmit renaming
Maybe#(Trap) trap;
PPCVAddrCSRData ppc_vaddr_csrData;
Bit#(5) fflags;
Bool will_dirty_fpu_state; // True means 2'b11 will be written to FS
RobInstState rob_inst_state; // was executed (i.e. can commit)
LdStQTag lsqTag; // tag for LSQ
Maybe#(LdKilledBy) ldKilled; // mispeculative load + reason for the kill
// Fence and some mem access are only performed at commit time, so ROB
// should notify LSQ that the instrution arrives at commit stage and access
// can start. XXX For fence, this bit is set at rename stage. For mem
// accesses like Lr/Sc/Amo/MMIO, this bit is set by mem exe pipeline.
Bool memAccessAtCommit;
// we have notified LSQ that inst is at commit
Bool lsqAtCommitNotified;
// a successfully translated non-MMIO store needs ROB to notify the commit
// from ROB, so that it can be dequeud from SQ
Bool nonMMIOStDone;
// We detect some traps at rename stage, and increment epoch to kill
// everything from fetch. So we should not increment epoch again when
// committing the trap. Therefore we record the epoch increment at rename.
Bool epochIncremented;
// speculation
SpecBits spec_bits;
} ToReorderBuffer deriving(Bits, Eq, FShow);
typedef enum {
NotDone,
Executed // i.e. ready to commit
} RobInstState deriving (Bits, Eq, FShow);
interface Row_setExecuted_doFinishAlu;
method Action set(Maybe#(Data) csrData, ControlFlow cf);
endinterface
interface Row_setExecuted_doFinishFpuMulDiv;
method Action set(Bit#(5) fflags);
endinterface
interface ReorderBufferRowEhr#(numeric type aluExeNum, numeric type fpuMulDivExeNum);
method Action write_enq(ToReorderBuffer x);
method ToReorderBuffer read_deq;
method Action setLSQAtCommitNotified;
// deqLSQ rules set ROB state: set execeptions, load mispeculation, and becomes Executed
method Action setExecuted_deqLSQ(Maybe#(Exception) cause, Maybe#(LdKilledBy) ld_killed);
// doFinish rules set ROB state for ALU and FPU/MUL/DIV (always become Executed)
interface Vector#(aluExeNum, Row_setExecuted_doFinishAlu) setExecuted_doFinishAlu;
interface Vector#(fpuMulDivExeNum, Row_setExecuted_doFinishFpuMulDiv) setExecuted_doFinishFpuMulDiv;
// mem addr translation done: record virtual addr (for possible faults),
// whether the access needs to be performed at commit stage (NOTE page
// faulting inst cannot have this set, since there is no access to
// perform), and non-MMIO St can become Executed (NOTE faulting
// instructions are not Executed, they are set at deqLSQ time)
method Action setExecuted_doFinishMem(Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done);
`ifdef INORDER_CORE
// in-order core sets LSQ tag after getting out of issue queue
method Action setLSQTag(LdStQTag t, Bool isFence);
`endif
// get original PC/PPC before execution, EHR port 0 will suffice
method Addr getOrigPC;
method Addr getOrigPredPC;
// speculation
method Bool dependsOn_wrongSpec(SpecTag tag);
method Action correctSpeculation(SpecBits mask);
endinterface
module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) provisos(
Add#(1, a__, aluExeNum), Add#(1, b__, fpuMulDivExeNum)
);
Integer trap_deq_port = 0;
Integer trap_deqLSQ_port = 0; // write trap
Integer trap_finishMem_port = 1; // write trap
Integer trap_enq_port = 2; // write trap
Integer pvc_deq_port = 0;
function Integer pvc_finishAlu_port(Integer i) = i; // write ppc_vaddr_csrData
Integer pvc_finishMem_port = valueof(aluExeNum); // write ppc_vaddr_csrData
Integer pvc_enq_port = 1 + valueof(aluExeNum); // write ppc_vaddr_csrData
Integer fflags_deq_port = 0;
function Integer fflags_finishFpuMulDiv_port(Integer i) = i; // write fflags
Integer fflags_enq_port = valueof(fpuMulDivExeNum); // write fflags
Integer ldKill_deq_port = 0;
Integer ldKill_deqLSQ_port = 0; // set ldKilled
Integer ldKill_enq_port = 1; // init ldKilled
Integer accessCom_deq_port = 0;
Integer accessCom_finishMem_port = 0; // set memAccessAtCommit
`ifdef INORDER_CORE
Integer accessCom_setLSQTag_port = 1; // enq to LSQ
Integer accessCom_enq_port = 2; // init
`else
Integer accessCom_enq_port = 1; // init
`endif
Integer lsqNotified_deq_port = 0;
Integer lsqNotified_setNotified_port = 0; // set True
Integer lsqNotified_enq_port = 1;
Integer nonMMIOSt_deq_port = 0;
Integer nonMMIOSt_finishMem_port = 0;
Integer nonMMIOSt_enq_port = 1;
Integer state_deq_port = 0;
function Integer state_finishAlu_port(Integer i) = i; // write state
function Integer state_finishFpuMulDiv_port(Integer i) = valueof(aluExeNum) + i; // write state
Integer state_deqLSQ_port = valueof(fpuMulDivExeNum) + valueof(aluExeNum); // write state
Integer state_finishMem_port = 1 + state_deqLSQ_port; // write state
Integer state_enq_port = 1 + state_finishMem_port; // write state
Integer sb_deq_port = 0;
Integer sb_wrongSpec_port = 0;
Integer sb_enq_port = 1; // write spec_bits
Integer sb_correctSpec_port = 2; // write spec_bits
Reg#(Addr) pc <- mkRegU;
Reg#(IType) iType <- mkRegU;
Reg#(Maybe#(CSR)) csr <- mkRegU;
Reg#(Bool) claimed_phy_reg <- mkRegU;
Ehr#(3, Maybe#(Trap)) trap <- mkEhr(?);
Ehr#(TAdd#(2, aluExeNum), PPCVAddrCSRData) ppc_vaddr_csrData <- mkEhr(?);
Ehr#(TAdd#(1, fpuMulDivExeNum), Bit#(5)) fflags <- mkEhr(?);
Reg#(Bool) will_dirty_fpu_state <- mkRegU;
Ehr#(TAdd#(3, TAdd#(fpuMulDivExeNum, aluExeNum)), RobInstState) rob_inst_state <- mkEhr(?);
Reg#(LdStQTag) lsqTag <- mkRegU;
Ehr#(2, Maybe#(LdKilledBy)) ldKilled <- mkEhr(?);
Ehr#(3, Bool) memAccessAtCommit <- mkEhr(?);
Ehr#(2, Bool) lsqAtCommitNotified <- mkEhr(?);
Ehr#(2, Bool) nonMMIOStDone <- mkEhr(?);
Reg#(Bool) epochIncremented <- mkRegU;
Ehr#(3, SpecBits) spec_bits <- mkEhr(?);
// wires to get stale (EHR port 0) values of PPC
Wire#(Addr) predPcWire <- mkBypassWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setPcWires;
predPcWire <= ppc_vaddr_csrData[0] matches tagged PPC .a ? a : 0;
endrule
Vector#(aluExeNum, Row_setExecuted_doFinishAlu) aluSetExe;
for(Integer i = 0; i < valueof(aluExeNum); i = i+1) begin
aluSetExe[i] = (interface Row_setExecuted_doFinishAlu;
method Action set(Maybe#(Data) csrData, ControlFlow cf);
// inst is done
rob_inst_state[state_finishAlu_port(i)] <= Executed;
// update PPC or csrData (vaddr is always useless for ALU results)
if(csrData matches tagged Valid .d) begin
ppc_vaddr_csrData[pvc_finishAlu_port(i)] <= CSRData (d);
end
else begin
ppc_vaddr_csrData[pvc_finishAlu_port(i)] <= PPC (cf.nextPc);
end
doAssert(isValid(csr) == isValid(csrData), "csr valid should match");
endmethod
endinterface);
end
Vector#(fpuMulDivExeNum, Row_setExecuted_doFinishFpuMulDiv) fpuMulDivExe;
for(Integer i = 0; i < valueof(fpuMulDivExeNum); i = i+1) begin
fpuMulDivExe[i] = (interface Row_setExecuted_doFinishFpuMulDiv;
method Action set(Bit#(5) new_fflags);
// inst is done
rob_inst_state[state_finishFpuMulDiv_port(i)] <= Executed;
// update fflags
fflags[fflags_finishFpuMulDiv_port(i)] <= new_fflags;
endmethod
endinterface);
end
method Addr getOrigPC = pc;
method Addr getOrigPredPC = predPcWire;
interface setExecuted_doFinishAlu = aluSetExe;
interface setExecuted_doFinishFpuMulDiv = fpuMulDivExe;
method Action setExecuted_doFinishMem(Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done);
doAssert(!(access_at_commit && non_mmio_st_done),
"cannot both be true");
// update ROB state
if(non_mmio_st_done) begin
rob_inst_state[state_finishMem_port] <= Executed;
doAssert(iType == St, "must be St");
end
// update VAddr
ppc_vaddr_csrData[pvc_finishMem_port] <= VAddr (vaddr);
// update access at commit
memAccessAtCommit[accessCom_finishMem_port] <= access_at_commit;
// udpate non mmio st
nonMMIOStDone[nonMMIOSt_finishMem_port] <= non_mmio_st_done;
endmethod
`ifdef INORDER_CORE
method Action setLSQTag(LdStQTag t, Bool isFence);
lsqTag <= t;
memAccessAtCommit[accessCom_setLSQTag_port] <= isFence;
doAssert(isFence == (iType == Fence), "fence should match");
endmethod
`endif
method Action write_enq(ToReorderBuffer x);
pc <= x.pc;
iType <= x.iType;
csr <= x.csr;
claimed_phy_reg <= x.claimed_phy_reg;
trap[trap_enq_port] <= x.trap;
ppc_vaddr_csrData[pvc_enq_port] <= x.ppc_vaddr_csrData;
fflags[fflags_enq_port] <= x.fflags;
will_dirty_fpu_state <= x.will_dirty_fpu_state;
rob_inst_state[state_enq_port] <= x.rob_inst_state;
epochIncremented <= x.epochIncremented;
spec_bits[sb_enq_port] <= x.spec_bits;
`ifdef INORDER_CORE
// in-order core enqs to LSQ later, so don't set LSQ tag; and other
// flags should default to false
memAccessAtCommit[accessCom_enq_port] <= False;
`else
lsqTag <= x.lsqTag;
memAccessAtCommit[accessCom_enq_port] <= x.iType == Fence;
`endif
ldKilled[ldKill_enq_port] <= Invalid;
lsqAtCommitNotified[lsqNotified_enq_port] <= False;
nonMMIOStDone[nonMMIOSt_enq_port] <= False;
// check
doAssert(!isValid(x.ldKilled), "ld killed must be false");
doAssert(x.memAccessAtCommit == False, "mem access at commit must be false");
doAssert(!x.lsqAtCommitNotified, "lsq notified must be false");
doAssert(!x.nonMMIOStDone, "non mmio st must be false");
endmethod
method ToReorderBuffer read_deq;
return ToReorderBuffer {
pc: pc,
iType: iType,
csr: csr,
claimed_phy_reg: claimed_phy_reg,
trap: trap[trap_deq_port],
ppc_vaddr_csrData: ppc_vaddr_csrData[pvc_deq_port],
fflags: fflags[fflags_deq_port],
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state[state_deq_port],
lsqTag: lsqTag,
ldKilled: ldKilled[ldKill_deq_port],
memAccessAtCommit: memAccessAtCommit[accessCom_deq_port],
lsqAtCommitNotified: lsqAtCommitNotified[lsqNotified_deq_port],
nonMMIOStDone: nonMMIOStDone[nonMMIOSt_deq_port],
epochIncremented: epochIncremented,
spec_bits: spec_bits[sb_deq_port]
};
endmethod
method Action setLSQAtCommitNotified;
lsqAtCommitNotified[lsqNotified_setNotified_port] <= True;
endmethod
method Action setExecuted_deqLSQ(Maybe#(Exception) cause, Maybe#(LdKilledBy) ld_killed);
// inst becomes Executed
rob_inst_state[state_deqLSQ_port] <= Executed;
// record trap
doAssert(!isValid(trap[trap_deqLSQ_port]), "cannot have trap");
if(cause matches tagged Valid .e) begin
trap[trap_deqLSQ_port] <= Valid (Exception (e));
end
// record ld misspeculation
ldKilled[ldKill_deqLSQ_port] <= ld_killed;
endmethod
method Bool dependsOn_wrongSpec(SpecTag tag);
return spec_bits[sb_wrongSpec_port][tag] == 1;
endmethod
method Action correctSpeculation(SpecBits mask);
SpecBits sb = spec_bits[sb_correctSpec_port];
spec_bits[sb_correctSpec_port] <= sb & mask;
endmethod
endmodule
interface ROB_SpeculationUpdate;
// when killing wrong path inst, we directly move enqP to inst_tag + 1
// assumption is that any entry within (inst_tag, enqP) will be killed
// (i.e. any such entry has spec_bits[spec_tag] == 1, so valid bit is reset)
// notice that inst_tag itself may be killed! (e.g. a Ld killed by older Ld/St)
// also note that inst_tag itself may be already dequeued just in this cycle
method Action incorrectSpeculation(Bool kill_all, SpecTag spec_tag, InstTag inst_tag);
method Action correctSpeculation(SpecBits mask);
endinterface
////////////////////////////////////////////////////////
////////// Superscalar ehrized reorder buffer //////////
////////////////////////////////////////////////////////
interface ROB_EnqPort;
method Bool canEnq;
method Action enq(ToReorderBuffer x);
method InstTag getEnqInstTag;
endinterface
interface ROB_DeqPort;
method Bool canDeq;
method Action deq;
method InstTag getDeqInstTag;
method ToReorderBuffer deq_data;
endinterface
// XXX guards of enq and deq ifc do not check that enq and deq are done consecutively
// This is the responsibility of outside world
// XXX enq and deq becomes a vector of interfaces
// We do not make them a single method with a vector as input,
// because synth boundary will make the guard conservative (i.e. ignoring input)
// thus requiring all ways to be able to enq/deq, which may cause deadlock
// However, synth boundary is needed to keep the atomicity of methods
// Having a vector of interfaces makes compiler unable to detect that different enq/deq
// interfaces are actually accessing different ways
// We have to use wires to perform the real actions in one rule so that compiler will
// not raise false conflicts between the superscalar enq/deq actions
interface ROB_setExecuted_doFinishAlu;
method Action set(InstTag x, Maybe#(Data) csrData, ControlFlow cf);
endinterface
interface ROB_setExecuted_doFinishFpuMulDiv;
method Action set(InstTag x, Bit#(5) fflags);
endinterface
interface ROB_getOrigPC;
method Addr get(InstTag x);
endinterface
interface ROB_getOrigPredPC;
method Addr get(InstTag x);
endinterface
interface SupReorderBuffer#(numeric type aluExeNum, numeric type fpuMulDivExeNum);
interface Vector#(SupSize, ROB_EnqPort) enqPort;
method Bool isEmpty; // empty signal for enq port (for FENCE/System inst etc.)
interface Vector#(SupSize, ROB_DeqPort) deqPort;
// record that we have notified LSQ about inst reaching commit
method Action setLSQAtCommitNotified(InstTag x);
// deqLSQ rules set ROB state
method Action setExecuted_deqLSQ(InstTag x, Maybe#(Exception) cause, Maybe#(LdKilledBy) ld_killed);
// doFinish rules set ROB state in ALU and FPU/MUL/DIV
interface Vector#(aluExeNum, ROB_setExecuted_doFinishAlu) setExecuted_doFinishAlu;
interface Vector#(fpuMulDivExeNum, ROB_setExecuted_doFinishFpuMulDiv) setExecuted_doFinishFpuMulDiv;
// doFinishMem, after addr translation
method Action setExecuted_doFinishMem(InstTag x, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done);
`ifdef INORDER_CORE
// in-order core sets LSQ tag after getting out of issue queue
method Action setLSQTag(InstTag x, LdStQTag t, Bool isFence);
`endif
// get original PC/PPC before execution, EHR port 0 will suffice
interface Vector#(TAdd#(1, aluExeNum), ROB_getOrigPC) getOrigPC;
interface Vector#(aluExeNum, ROB_getOrigPredPC) getOrigPredPC;
// get enq time for reservation station dispatch
method InstTime getEnqTime;
method Bool isEmpty_ehrPort0;
method Bool isFull_ehrPort0;
interface ROB_SpeculationUpdate specUpdate;
endinterface
typedef struct {
Bool killAll;
// below are only meaningful when killAll is False
SpecTag specTag;
InstTag killInstTag;
} ROBWrongSpecInput deriving(Bits, Eq, FShow);
typedef struct {
SupWaySel firstEnqWay;
Vector#(SupSize, SingleScalarPtr) enqP;
InstTime enqTime;
} ROBWrongSpecEnqUpdate deriving(Bits, Eq, FShow);
module mkSupReorderBuffer#(
Bool lazyEnq,
module#(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) mkRobRow
)(SupReorderBuffer#(aluExeNum, fpuMulDivExeNum)) provisos(
Add#(TExp#(TLog#(SupSize)), 0, SupSize), // require SupSize to be power of 2
Add#(1, a__, aluExeNum), Add#(1, b__, fpuMulDivExeNum)
);
// doCommit rule: deq < wrongSpec (overwrite deq in doCommit) < doRenaming rule: enq
Integer valid_deq_port = 0;
Integer valid_wrongSpec_port = 1;
Integer valid_enq_port = 1;
// doFinishXXX, doDeqLSQ_XXX: setExecute_XXX, correctSpeculation
// these are handled in mkReorderBufferRowEhr
// wrong speculation: make wrong speculation conflict with enq
Vector#(SupSize, RWire#(void)) wrongSpec_enq_conflict <- replicateM(mkRWire);
// SupSize number of FIFOs
Vector#(SupSize, Vector#(SingleScalarSize, ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum))) row <- replicateM(replicateM(mkRobRow));
Vector#(SupSize, Vector#(SingleScalarSize, Ehr#(2, Bool))) valid <- replicateM(replicateM(mkEhr(False)));
Vector#(SupSize, Reg#(SingleScalarPtr)) enqP <- replicateM(mkReg(0));
Vector#(SupSize, Ehr#(2, SingleScalarPtr)) deqP_ehr <- replicateM(mkEhr(0));
let deqP = getVEhrPort(deqP_ehr, 0);
let deqP_wrongSpec = getVEhrPort(deqP_ehr, 1); // for overwrite deqP when killing all
// enq/deq port will operate on above FIFOs in a rotating manner
// We distinguish between enq/deq port and FIFO way
// FIFO way is the selection of the static FIFO array
// enq/deq port is the port selection exposed to the outside world
// (e.g. enq/deq port 0 is for oldest inst in program order)
// The mapping of enq/deq ports to FIFO ways changes dynamically (i.e. rotating)
// firstEnq/DeqWay: which FIFO of row, valid, etc. that enq/deq port 0 should use
Reg#(SupWaySel) firstEnqWay <- mkReg(0);
Ehr#(2, SupWaySel) firstDeqWay_ehr <- mkEhr(0);
Reg#(SupWaySel) firstDeqWay = firstDeqWay_ehr[0];
Reg#(SupWaySel) firstDeqWay_wrongSpec = firstDeqWay_ehr[1];
// time of inst: enq & deq ptr as if ROB is just a FIFO of size 2^log(NumInstTag)
Reg#(InstTime) enqTime <- mkReg(0);
Ehr#(2, InstTime) deqTime_ehr <- mkEhr(0);
Reg#(InstTime) deqTime = deqTime_ehr[0];
Reg#(InstTime) deqTime_wrongSpec = deqTime_ehr[1]; // for overwrite deqTime when killing all
// wires for recording actions on enq & deq ports
Vector#(SupSize, RWire#(ToReorderBuffer)) enqEn <- replicateM(mkRWire);
Vector#(SupSize, PulseWire) deqEn <- replicateM(mkPulseWire);
// wire for recording action of wrongSpec
RWire#(ROBWrongSpecInput) wrongSpecEn <- mkRWire;
// ordering regs: deq sequence < setExecuted_XXX is maintained by each row
// BUT setExecuted_XXX, setLSQAtCommitNotified < enq, deq < enq, and deq <
// wrongSpec NEEDs explicit ordering
Reg#(Bool) deq_SB_wrongSpec <- mkRevertingVirtualReg(True);
Vector#(SupSize, Reg#(Bool)) deq_SB_enq <- replicateM(mkRevertingVirtualReg(True));
Vector#(SupSize, Reg#(Bool)) setExeAlu_SB_enq <- replicateM(mkRevertingVirtualReg(True));
Vector#(SupSize, Reg#(Bool)) setExeMem_SB_enq <- replicateM(mkRevertingVirtualReg(True));
Vector#(SupSize, Reg#(Bool)) setExeLSQ_SB_enq <- replicateM(mkRevertingVirtualReg(True));
Vector#(SupSize, Reg#(Bool)) setExeFpuMulDiv_SB_enq <- replicateM(mkRevertingVirtualReg(True));
Vector#(SupSize, Reg#(Bool)) setNotified_SB_enq <- replicateM(mkRevertingVirtualReg(True));
function SingleScalarPtr getNextPtr(SingleScalarPtr p);
return p == fromInteger(valueOf(SingleScalarSize)-1) ? 0 : p + 1;
endfunction
// convert enq/deq port -> fifo way
function SupWaySel getEnqFifoWay(SupWaySel enqPort) = firstEnqWay + enqPort;
function SupWaySel getDeqFifoWay(SupWaySel deqPort) = firstDeqWay + deqPort;
// convert fifo way -> enq/deq port
function SupWaySel getEnqPort(SupWaySel fifoWay) = fifoWay - firstEnqWay;
function SupWaySel getDeqPort(SupWaySel fifoWay) = fifoWay - firstDeqWay;
// XXX above 4 functions require SupSize to be power of 2
// do deq & update firstDeqWay
(* fire_when_enabled, no_implicit_conditions *)
rule canon_deq;
// -- apply effects of deq --
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
// for FIFO way i (looping for FIFO way should save area than looping for deq port)
SupWaySel deqPort = getDeqPort(fromInteger(i));
doAssert(getDeqFifoWay(deqPort) == fromInteger(i), "deq port matches FIFO way");
if(deqEn[deqPort]) begin
doAssert(valid[i][deqP[i]][valid_deq_port], "deq entry must be valid");
// move deqP & reset valid
deqP[i] <= getNextPtr(deqP[i]);
valid[i][deqP[i]][valid_deq_port] <= False;
end
end
// update firstDeqWay: find the first deq port that is not enabled
Vector#(SupSize, SupWaySel) idxVec = genWith(fromInteger);
function Bool notDeq(SupWaySel i);
return !deqEn[i];
endfunction
if(find(notDeq, idxVec) matches tagged Valid .idx) begin
// idx is the first port that does not deq
// update firstWay (XXX we require SupSize to be power of 2)
firstDeqWay <= firstDeqWay + idx;
// update deq day
deqTime <= deqTime + zeroExtend(idx);
// sanity check: deq ports[idx..max] are not enabled
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
doAssert((fromInteger(i) < idx) == deqEn[i], "Deq must be consective");
end
end
else begin
// all ports are dequeued, so firstWay keeps the same
// update deq day
deqTime <= deqTime + fromInteger(valueof(SupSize));
end
endrule
// process wrongSpec: clear valid bits & compute update for enq regs
(* fire_when_enabled, no_implicit_conditions *)
rule canon_wrongSpec(wrongSpecEn.wget matches tagged Valid .x);
if(x.killAll) begin
// kill everything
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
for(Integer i = 0; i < valueof(SingleScalarSize); i = i+1) begin
valid[w][i][valid_wrongSpec_port] <= False;
end
end
// reset all ptrs to 0
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
enqP[w] <= 0;
deqP_wrongSpec[w] <= 0;
end
firstEnqWay <= 0;
firstDeqWay_wrongSpec <= 0;
enqTime <= 0;
deqTime_wrongSpec <= 0;
end
else begin
SpecTag specTag = x.specTag;
InstTag killInstTag = x.killInstTag;
// only update valid, no need to change spec bits
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
for(Integer i = 0; i < valueof(SingleScalarSize); i = i+1) begin
if(row[w][i].dependsOn_wrongSpec(specTag)) begin
valid[w][i][valid_wrongSpec_port] <= False;
end
end
end
// move enqP to be right after (or just) the inst that initiates the kill
// To do this, we need to figure out the number of inst killed in each FIFO way
// Notice that each FIFO way is enq in round-robin order
// Since we know the number of killed inst in the FIFO that contains the kill-initiating inst,
// we can deduce the number of killed inst in other FIFOs
// For simplicity, we map the way select of FIFOs to a virtual way select
// such that firstEnqWay will be 0 in the virtual way select
// i.e. virtual way = way - firstEnqWay XXX requires SupSize to be power of 2
function SupWaySel toVirtualWay(SupWaySel realWay) = realWay - firstEnqWay;
// In terms of virtual ways, if we align the enqP of all FIFOs,
// we know the program order of inst when we move the ptr backward from enqP
// Thus we can deduce how many inst are killed in each FIFO
// First consider the FIFO that contains the kill-initiating entry
// We get virtual way and the distance from kill-initiating entry to enqP
// With this distance, we can deduce how much we should decrement the enqP for all FIFOs
SupWaySel virtualKillWay = toVirtualWay(killInstTag.way);
SingleScalarPtr killEnqP = enqP[killInstTag.way];
SingleScalarLen killDistToEnqP = killInstTag.ptr < killEnqP ? // if >=, then FIFO must wrap around
zeroExtend(killEnqP - killInstTag.ptr) :
zeroExtend(killEnqP) + fromInteger(valueof(SingleScalarSize)) - zeroExtend(killInstTag.ptr);
doAssert(killDistToEnqP > 0, "distance to enqP must be > 0");
// helper function to decr enqP
function SingleScalarPtr decrPtr(SingleScalarPtr ptr, SingleScalarLen len);
if(zeroExtend(ptr) < len) begin
SingleScalarLen extendedPtr = zeroExtend(ptr) + fromInteger(valueof(SingleScalarSize));
return truncate(extendedPtr - len);
end
else begin
return ptr - truncate(len);
end
endfunction
// the kill-initiating inst should not kill itself
// (in fact it must be a branch, Ld mis-speculation is handled by kill all at commit stage)
// so the ROB entry entry right after the kill-initiating entry should be the enq position
// it must be in the way right after the way that contians the kill-initiating entry
SupWaySel firstEnqWayNext = killInstTag.way + 1;
InstTime enqTimeNext = killInstTag.t + 1;
Vector#(SupSize, SingleScalarPtr) enqPNext;
Vector#(SupSize, SingleScalarLen) distToEnqP; // amount to decr enqP to get enqPNext, record for debugging
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
// consider virtual way of FIFO i, and get the distance that enqP should decr
// virtual way > kill virtual way: enqP decr by killDistToEnqP
// virtual way <= kill virtual way: enqP decr by killDistToEnqP - 1
SupWaySel virtualWay = toVirtualWay(fromInteger(i));
distToEnqP[i] = virtualWay > virtualKillWay ? killDistToEnqP : killDistToEnqP - 1;
enqPNext[i] = decrPtr(enqP[i], distToEnqP[i]);
end
// state update
firstEnqWay <= firstEnqWayNext;
enqTime <= enqTimeNext;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
enqP[i] <= enqPNext[i];
end
// check kill-initiating inst not killing itself
Bool killSelf = valid[killInstTag.way][killInstTag.ptr][valid_wrongSpec_port] &&
row[killInstTag.way][killInstTag.ptr].dependsOn_wrongSpec(specTag);
doAssert(!killSelf, "cannot kill itself");
//if(killSelf) begin
// // the kill-initiating inst also kills itself (e.g. a Ld)
// // so the kill-initiating entry becomes the next enq position
// // first enq way will become the way that contains the kill-initialting entry
// firstEnqWayNext = killInstTag.way;
// enqTimeNext = killInstTag.t;
// // get the enq pointers for all FIFOs
// for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
// // consider virtual way of FIFO i, and get the distance that enqP should decr
// // virtual way >= kill virtual way: enqP decr by killDistToEnqP
// // virtual way < kill virtual way: enqP decr by killDistToEnqP - 1
// SupWaySel virtualWay = toVirtualWay(fromInteger(i));
// distToEnqP[i] = virtualWay >= virtualKillWay ? killDistToEnqP : killDistToEnqP - 1;
// enqPNext[i] = decrPtr(enqP[i], distToEnqP[i]);
// end
//end
// wrong spec is conflicting with enq, so enqEn must be all false
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
doAssert(!isValid(enqEn[i].wget), "when wrongSpec, enq cannot fire");
end
`ifdef BSIM
// sanity check in simulation
Vector#(SupSize, SingleScalarPtr) deqPVec = readVReg(deqP_wrongSpec);
Vector#(SupSize, Vector#(SingleScalarSize, Bool)) depVec;
Vector#(SupSize, Vector#(SingleScalarSize, Bool)) validVec;
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
validVec[w] = readVEhr(valid_wrongSpec_port, valid[w]);
function Bool getDepOn(Integer i) = row[w][i].dependsOn_wrongSpec(specTag);
depVec[w] = map(getDepOn, genVector);
end
$display("[ROB incorrectSpec] ",
fshow(specTag), " ; ",
fshow(killInstTag), " ; ",
fshow(firstEnqWay), " ; ",
fshow(firstDeqWay_wrongSpec), " ; ",
fshow(readVReg(enqP)), " ; ",
fshow(deqPVec), " ; ",
fshow(validVec), " ; ",
fshow(depVec), " ; ",
fshow(firstEnqWayNext), " ; ",
fshow(enqPNext), " ; ",
fshow(distToEnqP)
);
// valid entries within [enqPNext, enqP) are killed
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
function Bool in_kill_range(SingleScalarPtr i);
if(distToEnqP[w] == 0) begin
return False;
end
else begin
if(enqPNext[w] < enqP[w]) begin
return enqPNext[w] <= i && i < enqP[w];
end
else begin
return enqPNext[w] <= i || i < enqP[w];
end
end
endfunction
for(Integer i = 0; i < valueof(SingleScalarSize); i = i+1) begin
doAssert(
in_kill_range(fromInteger(i)) ==
(row[w][i].dependsOn_wrongSpec(specTag) && valid[w][i][valid_wrongSpec_port]),
"valid entries inside [enqPNext, enqP) must be killed, outsiders must not"
);
end
end
// kill-initiating entry may be just dequeued
if(!valid[killInstTag.way][killInstTag.ptr][valid_wrongSpec_port]) begin
doAssert(getNextPtr(killInstTag.ptr) == deqPVec[killInstTag.way],
"if the kill-initiating entry is invalid, it must be just dequeued"
);
end
`endif
end
endrule
// Apply enq effects. This rule cannot be merged with canon_deq, because
// many other methods access ROB row contents are sandwiched between
// canon_deq and this rule.
(* fire_when_enabled, no_implicit_conditions *)
rule canon_enq(!isValid(wrongSpecEn.wget));
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
// for FIFO way i (looping for FIFO way should save area than looping for enq port)
SupWaySel enqPort = getEnqPort(fromInteger(i));
doAssert(getEnqFifoWay(enqPort) == fromInteger(i), "enq port matches FIFO way");
if(enqEn[enqPort].wget matches tagged Valid .x) begin
doAssert(!valid[i][enqP[i]][valid_enq_port], "enq entry must be invalid");
// update row, set valid, move enqP
enqP[i] <= getNextPtr(enqP[i]);
row[i][enqP[i]].write_enq(x);
valid[i][enqP[i]][valid_enq_port] <= True;
end
end
// update firstEnqWay: find the first enq port that is not enabled
Vector#(SupSize, SupWaySel) idxVec = genWith(fromInteger);
function Bool notEnq(SupWaySel i);
return !isValid(enqEn[i].wget);
endfunction
if(find(notEnq, idxVec) matches tagged Valid .idx) begin
// idx is the first port that does not enq
// update firstWay (XXX we require SupSize to be power of 2)
firstEnqWay <= firstEnqWay + idx;
// update enq day
enqTime <= enqTime + zeroExtend(idx);
// sanity check: enq ports[idx..max] are not enabled
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
doAssert((fromInteger(i) < idx) == isValid(enqEn[i].wget), "Enq must be consecutive");
end
end
else begin
// all ports enq, so firstWay keeps the same
// update enq day
enqTime <= enqTime + fromInteger(valueof(SupSize));
end
endrule
`ifdef BSIM
// sanity check in simulation
// all valid entry are within [deqP, enqP), outsiders are invalid entries
(* fire_when_enabled, no_implicit_conditions *)
rule sanityCheck;
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
Bool empty = all( \== (False), readVEhr(0, valid[w]) );
function Bool in_range(SingleScalarPtr i);
// i is within [deqP, enqP)
if(empty) begin
return False;
end
else begin
if(deqP[w] < enqP[w]) begin
return deqP[w] <= i && i < enqP[w];
end
else begin
return deqP[w] <= i || i < enqP[w];
end
end
endfunction
for(Integer i = 0; i < valueof(SingleScalarSize); i = i+1) begin
doAssert(in_range(fromInteger(i)) == valid[w][i][0],
"entries inside [deqP, enqP) should be valid, otherwise invalid"
);
end
end
endrule
`endif
// get enq ifc
// we compute can_enq signal for each FIFO[i], and all FIFO empty signal lazily
staticAssert(lazyEnq, "Only support lazy enq");
Vector#(SupSize, Wire#(Bool)) can_enq_fifo <- replicateM(mkBypassWire); // FIFO[i] can enq (enq slot invalid)
Wire#(Bool) empty_for_enq <- mkBypassWire; // all FIFOs empty
(* fire_when_enabled, no_implicit_conditions *)
rule setEnqWires;
Vector#(SupSize, Integer) idxVec = genVector;
// get all empty
function Bool isEmptyFunc(Integer i);
return !valid[i][enqP[i]][0] && enqP[i] == deqP[i];
endfunction
empty_for_enq <= all(isEmptyFunc, idxVec);
// get can enq
function Action setCanEnq(Integer i);
action
can_enq_fifo[i] <= !valid[i][enqP[i]][0];
endaction
endfunction
joinActions(map(setCanEnq, idxVec));
endrule
Vector#(SupSize, ROB_EnqPort) enqIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
SupWaySel way = getEnqFifoWay(fromInteger(i)); // FIFO[way] is used by enq port i
Bool can_enq = can_enq_fifo[way];
enqIfc[i] = (interface ROB_EnqPort;
method Bool canEnq = can_enq;
method Action enq(ToReorderBuffer x) if(can_enq);
doAssert(getEnqPort(way) == fromInteger(i), "enq FIFO way matches enq port");
// record enq action, real action is applied later
enqEn[i].wset(x);
// make it conflict with wrong speculation
wrongSpec_enq_conflict[i].wset(?);
// ordering: sequence after many other methods
deq_SB_enq[i] <= False;
setExeAlu_SB_enq[i] <= False;
setExeMem_SB_enq[i] <= False;
setExeFpuMulDiv_SB_enq[i] <= False;
setExeLSQ_SB_enq[i] <= False;
setNotified_SB_enq[i] <= False;
endmethod
method InstTag getEnqInstTag;
return InstTag {
way: way,
ptr: enqP[way],
t: enqTime + fromInteger(i)
};
endmethod
endinterface);
end
// get deq ifc
// get canDeq & first for each FIFO[i]
function Bool getFifoCanDeq(Integer i) = valid[i][deqP[i]][valid_deq_port];
function ToReorderBuffer getFifoFirst(Integer i) = row[i][deqP[i]].read_deq;
Vector#(SupSize, Bool) can_deq_fifo = map(getFifoCanDeq, genVector);
Vector#(SupSize, ToReorderBuffer) fifo_first = map(getFifoFirst, genVector);
Vector#(SupSize, ROB_DeqPort) deqIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
SupWaySel way = getDeqFifoWay(fromInteger(i)); // FIFO[way] is used by deq port i
Bool can_deq = can_deq_fifo[way] &&
deq_SB_wrongSpec && // ordering: < wrongSpec
all(id, readVReg(deq_SB_enq)); // ordering: < enq
deqIfc[i] = (interface ROB_DeqPort;
method Bool canDeq = can_deq;
method Action deq if(can_deq);
doAssert(getDeqPort(way) == fromInteger(i), "deq FIFO way matches deq port");
deqEn[i].send; // record deq action, real action is applied later
endmethod
method ToReorderBuffer deq_data if(can_deq);
return fifo_first[way];
endmethod
method InstTag getDeqInstTag;
return InstTag {
way: way,
ptr: deqP[way],
t: deqTime + fromInteger(i)
};
endmethod
endinterface);
end
// set alu exe ifc
Vector#(aluExeNum, ROB_setExecuted_doFinishAlu) aluSetExeIfc;
for(Integer i = 0; i < valueof(aluExeNum); i = i+1) begin
aluSetExeIfc[i] = (interface ROB_setExecuted_doFinishAlu;
method Action set(
InstTag x, Maybe#(Data) csrData, ControlFlow cf
) if(
all(id, readVReg(setExeAlu_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_doFinishAlu[i].set(csrData, cf);
endmethod
endinterface);
end
Vector#(fpuMulDivExeNum, ROB_setExecuted_doFinishFpuMulDiv) fpuMulDivSetExeIfc;
for(Integer i = 0; i < valueof(fpuMulDivExeNum); i = i+1) begin
fpuMulDivSetExeIfc[i] = (interface ROB_setExecuted_doFinishFpuMulDiv;
method Action set(
InstTag x, Bit#(5) fflags
) if(
all(id, readVReg(setExeFpuMulDiv_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_doFinishFpuMulDiv[i].set(fflags);
endmethod
endinterface);
end
// get pc/ppc ifc used by alu exe (also one pc for mem exe)
Vector#(TAdd#(1, aluExeNum), ROB_getOrigPC) getOrigPCIfc;
Vector#(aluExeNum, ROB_getOrigPredPC) getOrigPredPCIfc;
for(Integer i = 0; i < valueof(aluExeNum) + 1; i = i+1) begin
getOrigPCIfc[i] = (interface ROB_getOrigPC;
method Addr get(InstTag x) = row[x.way][x.ptr].getOrigPC;
endinterface);
end
for(Integer i = 0; i < valueof(aluExeNum); i = i+1) begin
getOrigPredPCIfc[i] = (interface ROB_getOrigPredPC;
method Addr get(InstTag x) = row[x.way][x.ptr].getOrigPredPC;
endinterface);
end
interface enqPort = enqIfc;
method Bool isEmpty;
return empty_for_enq;
endmethod
interface deqPort = deqIfc;
method Bool isEmpty_ehrPort0;
function Bool isEmptyFunc(Integer i);
return !valid[i][enqP[i]][0] && enqP[i] == deqP[i];
endfunction
Vector#(SupSize, Integer) idxVec = genVector;
return all(isEmptyFunc, idxVec);
endmethod
method Bool isFull_ehrPort0;
function Bool isFullFunc(Integer i);
return valid[i][enqP[i]][0] && enqP[i] == deqP[i];
endfunction
Vector#(SupSize, Integer) idxVec = genVector;
return all(isFullFunc, idxVec);
endmethod
method Action setLSQAtCommitNotified(InstTag x) if(
all(id, readVReg(setNotified_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setLSQAtCommitNotified;
endmethod
method Action setExecuted_deqLSQ(InstTag x, Maybe#(Exception) cause, Maybe#(LdKilledBy) ld_killed) if(
all(id, readVReg(setExeLSQ_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_deqLSQ(cause, ld_killed);
endmethod
interface setExecuted_doFinishAlu = aluSetExeIfc;
interface setExecuted_doFinishFpuMulDiv = fpuMulDivSetExeIfc;
method Action setExecuted_doFinishMem(
InstTag x, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
) if(
all(id, readVReg(setExeMem_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_doFinishMem(vaddr, access_at_commit, non_mmio_st_done);
endmethod
`ifdef INORDER_CORE
method Action setLSQTag(InstTag x, LdStQTag t, Bool isFence);
row[x.way][x.ptr].setLSQTag(t, isFence);
endmethod
`endif
interface getOrigPC = getOrigPCIfc;
interface getOrigPredPC = getOrigPredPCIfc;
method InstTime getEnqTime = enqTime;
interface ROB_SpeculationUpdate specUpdate;
method Action correctSpeculation(SpecBits mask);
for(Integer w = 0; w < valueof(SupSize); w = w+1) begin
for(Integer i = 0; i < valueof(SingleScalarSize); i = i+1) begin
row[w][i].correctSpeculation(mask);
end
end
endmethod
method Action incorrectSpeculation(Bool killAll, SpecTag specTag, InstTag killInstTag);
// record wrongSpec action
wrongSpecEn.wset(ROBWrongSpecInput {
killAll: killAll,
specTag: specTag,
killInstTag: killInstTag
});
// order after deq
deq_SB_wrongSpec <= False;
// make it conflict with enq
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
wrongSpec_enq_conflict[i].wset(?);
end
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,291 @@
// 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.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import Vector::*;
import ConfigReg::*;
import HasSpecBits::*;
import Ehr::*;
import GetPut::*;
import Assert::*;
typedef struct{
a data;
PhyRegs regs;
InstTag tag;
// speculation
SpecBits spec_bits;
Maybe#(SpecTag) spec_tag;
// scheduling
RegsReady regs_ready;
} ToReservationStation#(type a) deriving(Bits, Eq, FShow);
interface ReservationStation#(
numeric type size, numeric type setRegReadyNum, type a
);
method Action enq(ToReservationStation#(a) x);
method Bool canEnq;
method Action setRobEnqTime(InstTime t);
method ToReservationStation#(a) dispatchData;
method Action doDispatch;
interface Vector#(setRegReadyNum, Put#(Maybe#(PhyRIndx))) setRegReady;
// For count-based scheduling when there are multiple reservation stations
// for the same inst type. This method only takes effect when module
// parameter countValid is true.
method Bit#(TLog#(TAdd#(size, 1))) approximateCount;
// performance: count full cycles
method Bool isFull_ehrPort0;
interface SpeculationUpdate specUpdate;
endinterface
typedef Bit#(TAdd#(1, TLog#(NumInstTags))) VirtualInstTime;
module mkReservationStation#(Bool lazySched, Bool lazyEnq, Bool countValid)(
ReservationStation#(size, setRegReadyNum, a)
) provisos (
NumAlias#(regsReadyPortNum, TAdd#(1, setRegReadyNum)),
Alias#(idxT, Bit#(TLog#(size))),
Alias#(countT, Bit#(TLog#(TAdd#(size, 1)))),
Log#(TAdd#(1, size), TLog#(TAdd#(size, 1))),
Bits#(a, aSz), FShow#(a),
Add#(1, b__, size)
);
Integer valid_wrongSpec_port = 0;
Integer valid_dispatch_port = 0; // write valid
Integer valid_enq_port = 1; // write valid
Integer sb_wrongSpec_port = 0;
Integer sb_dispatch_port = 0;
Integer sb_enq_port = 0; // write spec_bits
Integer sb_correctSpec_port = 1; // write spec_bits
function Integer ready_set_port(Integer i) = i; // write regs_ready by each setRegReady ifc
Integer ready_enq_port = valueof(setRegReadyNum); // write regs_ready
Vector#(size, Ehr#(2,Bool)) valid <- replicateM(mkEhr(False));
Vector#(size, Reg#(a)) data <- replicateM(mkRegU);
Vector#(size, Reg#(PhyRegs)) regs <- replicateM(mkRegU);
Vector#(size, Reg#(InstTag)) tag <- replicateM(mkRegU);
Vector#(size, Reg#(Maybe#(SpecTag))) spec_tag <- replicateM(mkRegU);
Vector#(size, Ehr#(2, SpecBits)) spec_bits <- replicateM(mkEhr(?));
Vector#(size, Ehr#(regsReadyPortNum, RegsReady)) regs_ready <- replicateM(mkEhr(?));
// wrong spec conflict with enq and dispatch
RWire#(void) wrongSpec_enq_conflict <- mkRWire;
RWire#(void) wrongSpec_dispatch_conflict <- mkRWire;
// approximate count of valid entries
Reg#(countT) validEntryCount <- mkConfigReg(0);
if(countValid) begin
(* fire_when_enabled, no_implicit_conditions *)
rule countValidEntries;
validEntryCount <= pack(countElem(True, readVEhr(0, valid)));
endrule
end
// enq time in ROB, used as pivot to get virtual inst time (dispatch happens before ROB enq)
Wire#(InstTime) robEnqTime <- mkDWire(0);
// mapping to virtual inst time as follow:
// valid entry time t --> t < robEnqTime ? t + 2^log(NumInstTags) : t
function VirtualInstTime getVTime(InstTag instTag);
InstTime t = instTag.t;
return t < robEnqTime ? zeroExtend(t) + fromInteger(valueof(TExp#(TLog#(NumInstTags)))) : zeroExtend(t);
endfunction
Vector#(size, VirtualInstTime) vTime = map(getVTime, readVReg(tag));
// function to find the oldest entry (smaller virtual time) which satisfy certain constraint
function Maybe#(idxT) findOldest(Vector#(size, Bool) pred);
function idxT getOlder(idxT a, idxT b);
if(!pred[a]) begin
return b;
end
else if(!pred[b]) begin
return a;
end
else begin
return vTime[a] < vTime[b] ? a : b;
end
endfunction
Vector#(size, idxT) idxVec = genWith(fromInteger);
idxT idx = fold(getOlder, idxVec);
return pred[idx] ? Valid (idx) : Invalid;
endfunction
// search for row to dispatch, we do it lazily
// i.e. look at the EHR port 0 of ready bits, so there is no bypassing
staticAssert(lazySched, "Only support lazy schedule now");
Vector#(size, Wire#(Bool)) ready_wire <- replicateM(mkBypassWire);
(* fire_when_enabled, no_implicit_conditions *)
rule setReadyWire;
function Action setReady(Integer i);
action
ready_wire[i] <= allRegsReady(regs_ready[i][0]);
endaction
endfunction
Vector#(size, Integer) idxVec = genVector;
joinActions(map(setReady, idxVec));
endrule
function Bool get_ready(Wire#(Bool) r);
return r;
endfunction
Vector#(size, Bool) can_schedule = zipWith( \&& , readVEhr(valid_dispatch_port, valid), map(get_ready, ready_wire) );
// oldest index to dispatch
let can_schedule_index = findOldest(can_schedule);
// ifc to set reg ready
Vector#(setRegReadyNum, Put#(Maybe#(PhyRIndx))) setRegReadyIfc = ?;
for(Integer k = 0; k < valueof(setRegReadyNum); k = k+1) begin
setRegReadyIfc[k] = (interface Put;
method Action put(Maybe#(PhyRIndx) x);
Integer ehrPort = ready_set_port(k);
function Action setReady(Integer i);
action
// function to set ready for element i
// This compares Maybe types.
// If both are invalid, regs_ready.srcX must be True.
RegsReady new_regs_ready = regs_ready[i][ehrPort];
if (x == regs[i].src1) begin
new_regs_ready.src1 = True;
end
if (x == regs[i].src2) begin
new_regs_ready.src2 = True;
end
if (x == regs[i].src3) begin
new_regs_ready.src3 = True;
end
regs_ready[i][ehrPort] <= new_regs_ready;
endaction
endfunction
Vector#(size, Integer) idxVec = genVector;
joinActions(map(setReady, idxVec));
endmethod
endinterface);
end
// search to find enq slot
Maybe#(UInt#(TLog#(size))) enqP = ?;
function Bool entryInvalid(Bool v) = !v;
if(lazyEnq) begin
Wire#(Maybe#(UInt#(TLog#(size)))) enqP_wire <- mkBypassWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setWireForEnq;
enqP_wire <= findIndex(entryInvalid, readVEhr(0, valid));
endrule
enqP = enqP_wire;
end
else begin
enqP = findIndex(entryInvalid, readVEhr(valid_enq_port, valid));
end
//rule debugRes(!isValid(enqP));
// $fdisplay(stdout, "cannot enqueue in the reservation station this cycle");
//endrule
method Action enq(ToReservationStation#(a) x) if (enqP matches tagged Valid .idx);
$display(" [mkReservationStationRow::_write] ", fshow(x));
valid[idx][valid_enq_port] <= True;
data[idx] <= x.data;
regs[idx] <= x.regs;
tag[idx] <= x.tag;
spec_tag[idx] <= x.spec_tag;
spec_bits[idx][sb_enq_port] <= x.spec_bits;
regs_ready[idx][ready_enq_port] <= x.regs_ready;
// conflict with wrong spec
wrongSpec_enq_conflict.wset(?);
endmethod
method Bool canEnq = isValid(enqP);
method Action setRobEnqTime(InstTime t);
robEnqTime <= t;
endmethod
method ToReservationStation#(a) dispatchData if (can_schedule_index matches tagged Valid .i);
return ToReservationStation{
data: data[i],
regs: regs[i],
tag: tag[i],
spec_bits: spec_bits[i][sb_dispatch_port],
spec_tag: spec_tag[i],
regs_ready: RegsReady { // must be all true
src1: True,
src2: True,
src3: True,
dst: True
}
};
endmethod
method Action doDispatch if (can_schedule_index matches tagged Valid .i);
valid[i][valid_dispatch_port] <= False;
// conflict with wrong spec
wrongSpec_dispatch_conflict.wset(?);
endmethod
method countT approximateCount;
return validEntryCount;
endmethod
interface setRegReady = setRegReadyIfc;
method Bool isFull_ehrPort0;
return readVEhr(0, valid) == replicate(True);
endmethod
interface SpeculationUpdate specUpdate;
method Action incorrectSpeculation(Bool kill_all, SpecTag x);
function Action wrongSpec(Integer i);
action
if(kill_all || spec_bits[i][sb_wrongSpec_port][x] == 1) begin
valid[i][valid_wrongSpec_port] <= False;
end
endaction
endfunction
Vector#(size, Integer) idxVec = genVector;
joinActions(map(wrongSpec, idxVec));
// conflict with enq, dispatch
wrongSpec_enq_conflict.wset(?);
wrongSpec_dispatch_conflict.wset(?);
endmethod
method Action correctSpeculation(SpecBits mask);
function Action correctSpec(Integer i);
action
spec_bits[i][sb_correctSpec_port] <= spec_bits[i][sb_correctSpec_port] & mask;
endaction
endfunction
Vector#(size, Integer) idxVec = genVector;
joinActions(map(correctSpec, idxVec));
endmethod
endinterface
endmodule

Some files were not shown because too many files have changed in this diff Show More