Initial load of files
This commit is contained in:
21
src_Core/RISCY_OOO/LICENSE_RISCY-OOO
Normal file
21
src_Core/RISCY_OOO/LICENSE_RISCY-OOO
Normal 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
153
src_Core/RISCY_OOO/Makefile
Normal 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 .
|
||||
408
src_Core/RISCY_OOO/coherence/src/CCPipe.bsv
Normal file
408
src_Core/RISCY_OOO/coherence/src/CCPipe.bsv
Normal 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
|
||||
464
src_Core/RISCY_OOO/coherence/src/CCTypes.bsv
Normal file
464
src_Core/RISCY_OOO/coherence/src/CCTypes.bsv
Normal 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
|
||||
215
src_Core/RISCY_OOO/coherence/src/CrossBar.bsv
Normal file
215
src_Core/RISCY_OOO/coherence/src/CrossBar.bsv
Normal 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
|
||||
|
||||
849
src_Core/RISCY_OOO/coherence/src/IBank.bsv
Normal file
849
src_Core/RISCY_OOO/coherence/src/IBank.bsv
Normal 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
|
||||
|
||||
353
src_Core/RISCY_OOO/coherence/src/ICRqMshr.bsv
Normal file
353
src_Core/RISCY_OOO/coherence/src/ICRqMshr.bsv
Normal 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
|
||||
214
src_Core/RISCY_OOO/coherence/src/IPRqMshr.bsv
Normal file
214
src_Core/RISCY_OOO/coherence/src/IPRqMshr.bsv
Normal 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
|
||||
1260
src_Core/RISCY_OOO/coherence/src/L1Bank.bsv
Normal file
1260
src_Core/RISCY_OOO/coherence/src/L1Bank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
378
src_Core/RISCY_OOO/coherence/src/L1CRqMshr.bsv
Normal file
378
src_Core/RISCY_OOO/coherence/src/L1CRqMshr.bsv
Normal 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
|
||||
231
src_Core/RISCY_OOO/coherence/src/L1PRqMshr.bsv
Normal file
231
src_Core/RISCY_OOO/coherence/src/L1PRqMshr.bsv
Normal 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
|
||||
391
src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv
Normal file
391
src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv
Normal 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
|
||||
1544
src_Core/RISCY_OOO/coherence/src/LLBank.bsv
Normal file
1544
src_Core/RISCY_OOO/coherence/src/LLBank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
447
src_Core/RISCY_OOO/coherence/src/LLCRqMshr.bsv
Normal file
447
src_Core/RISCY_OOO/coherence/src/LLCRqMshr.bsv
Normal 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
|
||||
|
||||
371
src_Core/RISCY_OOO/coherence/src/LLPipe.bsv
Normal file
371
src_Core/RISCY_OOO/coherence/src/LLPipe.bsv
Normal 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
|
||||
83
src_Core/RISCY_OOO/coherence/src/MshrDeadlockChecker.bsv
Normal file
83
src_Core/RISCY_OOO/coherence/src/MshrDeadlockChecker.bsv
Normal 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
|
||||
64
src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv
Normal file
64
src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv
Normal 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
|
||||
90
src_Core/RISCY_OOO/coherence/src/RandomReplace.bsv
Normal file
90
src_Core/RISCY_OOO/coherence/src/RandomReplace.bsv
Normal 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
|
||||
|
||||
629
src_Core/RISCY_OOO/coherence/src/SelfInvIBank.bsv
Normal file
629
src_Core/RISCY_OOO/coherence/src/SelfInvIBank.bsv
Normal 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
|
||||
|
||||
466
src_Core/RISCY_OOO/coherence/src/SelfInvIPipe.bsv
Normal file
466
src_Core/RISCY_OOO/coherence/src/SelfInvIPipe.bsv
Normal 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
|
||||
1234
src_Core/RISCY_OOO/coherence/src/SelfInvL1Bank.bsv
Normal file
1234
src_Core/RISCY_OOO/coherence/src/SelfInvL1Bank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
492
src_Core/RISCY_OOO/coherence/src/SelfInvL1Pipe.bsv
Normal file
492
src_Core/RISCY_OOO/coherence/src/SelfInvL1Pipe.bsv
Normal 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
|
||||
1494
src_Core/RISCY_OOO/coherence/src/SelfInvLLBank.bsv
Normal file
1494
src_Core/RISCY_OOO/coherence/src/SelfInvLLBank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
366
src_Core/RISCY_OOO/coherence/src/SelfInvLLPipe.bsv
Normal file
366
src_Core/RISCY_OOO/coherence/src/SelfInvLLPipe.bsv
Normal 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
|
||||
178
src_Core/RISCY_OOO/connectal/bsv/ConnectalBramFifo.bsv
Normal file
178
src_Core/RISCY_OOO/connectal/bsv/ConnectalBramFifo.bsv
Normal 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
|
||||
130
src_Core/RISCY_OOO/connectal/bsv/ConnectalClocks.bsv
Normal file
130
src_Core/RISCY_OOO/connectal/bsv/ConnectalClocks.bsv
Normal 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
|
||||
42
src_Core/RISCY_OOO/connectal/lib/bsv/Arith.bsv
Normal file
42
src_Core/RISCY_OOO/connectal/lib/bsv/Arith.bsv
Normal 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
|
||||
@@ -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
|
||||
76
src_Core/RISCY_OOO/fpgautils/lib/DramCommon.bsv
Normal file
76
src_Core/RISCY_OOO/fpgautils/lib/DramCommon.bsv
Normal 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
|
||||
38
src_Core/RISCY_OOO/fpgautils/lib/ResetGuard.bsv
Normal file
38
src_Core/RISCY_OOO/fpgautils/lib/ResetGuard.bsv
Normal 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
|
||||
114
src_Core/RISCY_OOO/fpgautils/lib/SyncFifo.bsv
Normal file
114
src_Core/RISCY_OOO/fpgautils/lib/SyncFifo.bsv
Normal 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
|
||||
51
src_Core/RISCY_OOO/fpgautils/lib/WaitAutoReset.bsv
Normal file
51
src_Core/RISCY_OOO/fpgautils/lib/WaitAutoReset.bsv
Normal 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
|
||||
387
src_Core/RISCY_OOO/fpgautils/lib/XilinxFpu.bsv
Normal file
387
src_Core/RISCY_OOO/fpgautils/lib/XilinxFpu.bsv
Normal 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
|
||||
203
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntDiv.bsv
Normal file
203
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntDiv.bsv
Normal 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
|
||||
|
||||
205
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntMul.bsv
Normal file
205
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntMul.bsv
Normal 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
|
||||
|
||||
181
src_Core/RISCY_OOO/fpgautils/lib/XilinxSyncFifo.bsv
Normal file
181
src_Core/RISCY_OOO/fpgautils/lib/XilinxSyncFifo.bsv
Normal 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
|
||||
|
||||
8
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_div_sim.v
Normal file
8
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_div_sim.v
Normal 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
|
||||
9
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_fma_sim.v
Normal file
9
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_fma_sim.v
Normal 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
|
||||
7
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_sqrt_sim.v
Normal file
7
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_sqrt_sim.v
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
module fp_sqrt_sim(
|
||||
input [63:0] A,
|
||||
output [63:0] RES
|
||||
);
|
||||
assign RES = $realtobits($sqrt($bitstoreal(A)));
|
||||
endmodule
|
||||
69
src_Core/RISCY_OOO/fpgautils/xilinx/reset_regs/reset_guard.v
Normal file
69
src_Core/RISCY_OOO/fpgautils/xilinx/reset_regs/reset_guard.v
Normal 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
|
||||
382
src_Core/RISCY_OOO/procs/RV64G_OOO/AluExePipeline.bsv
Normal file
382
src_Core/RISCY_OOO/procs/RV64G_OOO/AluExePipeline.bsv
Normal 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
|
||||
|
||||
756
src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv
Normal file
756
src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv
Normal 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
|
||||
641
src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv
Normal file
641
src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv
Normal 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
|
||||
|
||||
325
src_Core/RISCY_OOO/procs/RV64G_OOO/FpuMulDivExePipeline.bsv
Normal file
325
src_Core/RISCY_OOO/procs/RV64G_OOO/FpuMulDivExePipeline.bsv
Normal 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
|
||||
1289
src_Core/RISCY_OOO/procs/RV64G_OOO/MemExePipeline.bsv
Normal file
1289
src_Core/RISCY_OOO/procs/RV64G_OOO/MemExePipeline.bsv
Normal file
File diff suppressed because it is too large
Load Diff
326
src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv
Normal file
326
src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv
Normal 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 ====
|
||||
//
|
||||
|
||||
|
||||
34
src_Core/RISCY_OOO/procs/RV64G_OOO/RFileSynth.bsv
Normal file
34
src_Core/RISCY_OOO/procs/RV64G_OOO/RFileSynth.bsv
Normal 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
|
||||
963
src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv
Normal file
963
src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv
Normal 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
|
||||
44
src_Core/RISCY_OOO/procs/RV64G_OOO/ReorderBufferSynth.bsv
Normal file
44
src_Core/RISCY_OOO/procs/RV64G_OOO/ReorderBufferSynth.bsv
Normal 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
|
||||
|
||||
43
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationAlu.bsv
Normal file
43
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationAlu.bsv
Normal 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
|
||||
@@ -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
|
||||
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationMem.bsv
Normal file
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationMem.bsv
Normal 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
|
||||
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ScoreboardSynth.bsv
Normal file
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ScoreboardSynth.bsv
Normal 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
|
||||
|
||||
77
src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv
Normal file
77
src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv
Normal 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;
|
||||
84
src_Core/RISCY_OOO/procs/lib/Amo.bsv
Normal file
84
src_Core/RISCY_OOO/procs/lib/Amo.bsv
Normal 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
|
||||
81
src_Core/RISCY_OOO/procs/lib/Bht.bsv
Normal file
81
src_Core/RISCY_OOO/procs/lib/Bht.bsv
Normal 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
|
||||
|
||||
68
src_Core/RISCY_OOO/procs/lib/BrPred.bsv
Normal file
68
src_Core/RISCY_OOO/procs/lib/BrPred.bsv
Normal 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
|
||||
|
||||
121
src_Core/RISCY_OOO/procs/lib/Btb.bsv
Normal file
121
src_Core/RISCY_OOO/procs/lib/Btb.bsv
Normal 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
|
||||
|
||||
75
src_Core/RISCY_OOO/procs/lib/Bypass.bsv
Normal file
75
src_Core/RISCY_OOO/procs/lib/Bypass.bsv
Normal 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
|
||||
|
||||
153
src_Core/RISCY_OOO/procs/lib/CacheUtils.bsv
Normal file
153
src_Core/RISCY_OOO/procs/lib/CacheUtils.bsv
Normal 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
|
||||
|
||||
506
src_Core/RISCY_OOO/procs/lib/ConcatReg.bsv
Normal file
506
src_Core/RISCY_OOO/procs/lib/ConcatReg.bsv
Normal 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
|
||||
|
||||
606
src_Core/RISCY_OOO/procs/lib/DTlb.bsv
Normal file
606
src_Core/RISCY_OOO/procs/lib/DTlb.bsv
Normal 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
|
||||
743
src_Core/RISCY_OOO/procs/lib/Decode.bsv
Normal file
743
src_Core/RISCY_OOO/procs/lib/Decode.bsv
Normal 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
|
||||
85
src_Core/RISCY_OOO/procs/lib/DirPredictor.bsv
Normal file
85
src_Core/RISCY_OOO/procs/lib/DirPredictor.bsv
Normal 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
|
||||
96
src_Core/RISCY_OOO/procs/lib/Ehr.bsv
Normal file
96
src_Core/RISCY_OOO/procs/lib/Ehr.bsv
Normal 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
|
||||
|
||||
133
src_Core/RISCY_OOO/procs/lib/EpochManager.bsv
Normal file
133
src_Core/RISCY_OOO/procs/lib/EpochManager.bsv
Normal 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
|
||||
|
||||
250
src_Core/RISCY_OOO/procs/lib/Exec.bsv
Normal file
250
src_Core/RISCY_OOO/procs/lib/Exec.bsv
Normal 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
|
||||
|
||||
652
src_Core/RISCY_OOO/procs/lib/Fifo.bsv
Normal file
652
src_Core/RISCY_OOO/procs/lib/Fifo.bsv
Normal 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
|
||||
|
||||
892
src_Core/RISCY_OOO/procs/lib/Fpu.bsv
Normal file
892
src_Core/RISCY_OOO/procs/lib/Fpu.bsv
Normal 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
|
||||
|
||||
189
src_Core/RISCY_OOO/procs/lib/FullAssocTlb.bsv
Normal file
189
src_Core/RISCY_OOO/procs/lib/FullAssocTlb.bsv
Normal 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
|
||||
144
src_Core/RISCY_OOO/procs/lib/GSelectPred.bsv
Normal file
144
src_Core/RISCY_OOO/procs/lib/GSelectPred.bsv
Normal 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
|
||||
144
src_Core/RISCY_OOO/procs/lib/GSharePred.bsv
Normal file
144
src_Core/RISCY_OOO/procs/lib/GSharePred.bsv
Normal 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
|
||||
75
src_Core/RISCY_OOO/procs/lib/GlobalBrHistReg.bsv
Normal file
75
src_Core/RISCY_OOO/procs/lib/GlobalBrHistReg.bsv
Normal 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
|
||||
102
src_Core/RISCY_OOO/procs/lib/GlobalSpecUpdate.bsv
Normal file
102
src_Core/RISCY_OOO/procs/lib/GlobalSpecUpdate.bsv
Normal 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
|
||||
48
src_Core/RISCY_OOO/procs/lib/HasSpecBits.bsv
Normal file
48
src_Core/RISCY_OOO/procs/lib/HasSpecBits.bsv
Normal 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
|
||||
|
||||
373
src_Core/RISCY_OOO/procs/lib/ITlb.bsv
Normal file
373
src_Core/RISCY_OOO/procs/lib/ITlb.bsv
Normal 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
|
||||
434
src_Core/RISCY_OOO/procs/lib/L1CoCache.bsv
Normal file
434
src_Core/RISCY_OOO/procs/lib/L1CoCache.bsv
Normal 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
|
||||
193
src_Core/RISCY_OOO/procs/lib/L1LLConnect.bsv
Normal file
193
src_Core/RISCY_OOO/procs/lib/L1LLConnect.bsv
Normal 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
|
||||
*/
|
||||
71
src_Core/RISCY_OOO/procs/lib/L2SetAssocTlb.bsv
Normal file
71
src_Core/RISCY_OOO/procs/lib/L2SetAssocTlb.bsv
Normal 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
|
||||
749
src_Core/RISCY_OOO/procs/lib/L2Tlb.bsv
Normal file
749
src_Core/RISCY_OOO/procs/lib/L2Tlb.bsv
Normal 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
|
||||
153
src_Core/RISCY_OOO/procs/lib/LLCDmaConnect.bsv
Normal file
153
src_Core/RISCY_OOO/procs/lib/LLCDmaConnect.bsv
Normal 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
|
||||
345
src_Core/RISCY_OOO/procs/lib/LLCRqMshrSecureModel.bsv
Normal file
345
src_Core/RISCY_OOO/procs/lib/LLCRqMshrSecureModel.bsv
Normal 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
|
||||
452
src_Core/RISCY_OOO/procs/lib/LLCache.bsv
Normal file
452
src_Core/RISCY_OOO/procs/lib/LLCache.bsv
Normal 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
|
||||
84
src_Core/RISCY_OOO/procs/lib/LatencyTimer.bsv
Normal file
84
src_Core/RISCY_OOO/procs/lib/LatencyTimer.bsv
Normal 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
|
||||
62
src_Core/RISCY_OOO/procs/lib/MMIOAddrs.bsv
Normal file
62
src_Core/RISCY_OOO/procs/lib/MMIOAddrs.bsv
Normal 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;
|
||||
208
src_Core/RISCY_OOO/procs/lib/MMIOCore.bsv
Normal file
208
src_Core/RISCY_OOO/procs/lib/MMIOCore.bsv
Normal 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
|
||||
|
||||
107
src_Core/RISCY_OOO/procs/lib/MMIOInst.bsv
Normal file
107
src_Core/RISCY_OOO/procs/lib/MMIOInst.bsv
Normal 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
|
||||
292
src_Core/RISCY_OOO/procs/lib/MemLoader.bsv
Normal file
292
src_Core/RISCY_OOO/procs/lib/MemLoader.bsv
Normal 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
|
||||
49
src_Core/RISCY_OOO/procs/lib/MemLoaderIF.bsv
Normal file
49
src_Core/RISCY_OOO/procs/lib/MemLoaderIF.bsv
Normal 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
|
||||
43
src_Core/RISCY_OOO/procs/lib/MemoryTypes.bsv
Normal file
43
src_Core/RISCY_OOO/procs/lib/MemoryTypes.bsv
Normal 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;
|
||||
|
||||
122
src_Core/RISCY_OOO/procs/lib/MsgFifo.bsv
Normal file
122
src_Core/RISCY_OOO/procs/lib/MsgFifo.bsv
Normal 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
|
||||
|
||||
219
src_Core/RISCY_OOO/procs/lib/MulDiv.bsv
Normal file
219
src_Core/RISCY_OOO/procs/lib/MulDiv.bsv
Normal 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
|
||||
|
||||
206
src_Core/RISCY_OOO/procs/lib/Performance.bsv
Normal file
206
src_Core/RISCY_OOO/procs/lib/Performance.bsv
Normal 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);
|
||||
111
src_Core/RISCY_OOO/procs/lib/PhysRFile.bsv
Normal file
111
src_Core/RISCY_OOO/procs/lib/PhysRFile.bsv
Normal 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
|
||||
941
src_Core/RISCY_OOO/procs/lib/ProcTypes.bsv
Normal file
941
src_Core/RISCY_OOO/procs/lib/ProcTypes.bsv
Normal 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
|
||||
|
||||
96
src_Core/RISCY_OOO/procs/lib/Ras.bsv
Normal file
96
src_Core/RISCY_OOO/procs/lib/Ras.bsv
Normal 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
|
||||
36
src_Core/RISCY_OOO/procs/lib/RenameDebugIF.bsv
Normal file
36
src_Core/RISCY_OOO/procs/lib/RenameDebugIF.bsv
Normal 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
|
||||
503
src_Core/RISCY_OOO/procs/lib/RenamingTable.bsv
Normal file
503
src_Core/RISCY_OOO/procs/lib/RenamingTable.bsv
Normal 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
|
||||
993
src_Core/RISCY_OOO/procs/lib/ReorderBuffer.bsv
Normal file
993
src_Core/RISCY_OOO/procs/lib/ReorderBuffer.bsv
Normal 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
|
||||
291
src_Core/RISCY_OOO/procs/lib/ReservationStationEhr.bsv
Normal file
291
src_Core/RISCY_OOO/procs/lib/ReservationStationEhr.bsv
Normal 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
Reference in New Issue
Block a user