Initial load of files

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

View File

@@ -0,0 +1,408 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Ehr::*;
import Fifo::*;
import Vector::*;
import RWBramCore::*;
import FShow::*;
import Types::*;
import CCTypes::*;
// general type param ordering: way < index < tag < msi < dir < owner < other < rep < line < pipeCmd
typedef union tagged {
void Invalid;
msiT DownDir; // cRs downgraded toState
msiT UpCs; // pRs upgraded toState
} RespState#(type msiT) deriving(Bits, Eq, FShow);
typedef struct {
pipeCmdT cmd;
// tag match & ram output
wayT way;
Bool pRqMiss; // pRq miss, valid only if go through tag match
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram;
// replace info, actually not needed, just output for debug purposes
repT repInfo;
} PipeOut#(
type wayT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
interface CCPipe#(
numeric type wayNum,
type indexT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
);
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, RespState#(msiT) toState);
method Bool notFull;
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) first;
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) unguard_first;
method Bool notEmpty;
method Action deqWrite(
Maybe#(pipeCmdT) newCmd,
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) wrRam,
Bool updateRep // update replacement info
);
// empty signal when we need to flush self-invalidate cache
method Bool emptyForFlush;
endinterface
// internal pipeline reg types
// three stages
// 1: enq
// 2: tag match, read data and dir
// 3: output
typedef struct {
pipeCmdT cmd;
// bypasses
Vector#(wayNum, Maybe#(CacheInfo#(tagT, msiT, dirT, ownerT, otherT))) infoVec;
Maybe#(repT) repInfo; // replacement info for the whole set
// CRs/PRs info
Maybe#(lineT) respLine;
RespState#(msiT) toState;
} Enq2Match#(
numeric type wayNum,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
typedef struct {
pipeCmdT cmd;
// tag match results
wayT way;
Bool pRqMiss;
// RAM outputs
// cs is merged with PRs toState
// dir is merged with CRs toState
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
repT repInfo;
// bypassed or resp line
Maybe#(lineT) line;
} Match2Out#(
type wayT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
typedef struct {
indexT index;
wayT way;
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram; // data to write into RAM
repT repInfo; // replacement info write into RAM
} BypassInfo#(
type wayT,
type indexT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT
) deriving(Bits, Eq, FShow);
typedef struct {
wayT way;
Bool pRqMiss;
} TagMatchResult#(type wayT) deriving(Bits, Eq, FShow);
typedef struct {
msiT cs;
} UpdateByUpCs#(type msiT) deriving(Bits, Eq, FShow);
typedef struct {
msiT cs;
dirT dir;
} UpdateByDownDir#(type msiT, type dirT) deriving(Bits, Eq, FShow);
// index to data ram: {way, normal index}
function dataIndexT getDataRamIndex(wayT w, indexT i) provisos(
Alias#(wayT, Bit#(_waySz)),
Alias#(indexT, Bit#(_indexSz)),
Alias#(dataIndexT, Bit#(TAdd#(_waySz, _indexSz)))
);
return {w, i};
endfunction
module mkCCPipe#(
ReadOnly#(Bool) initDone,
function indexT getIndex(pipeCmdT cmd),
function ActionValue#(TagMatchResult#(wayT)) tagMatch(
// actionvalue enable us to do checking inside the function
pipeCmdT cmd,
// below are current RAM outputs, is merged with ram write from final stage
// but is NOT merged with state changes carried in PRs/CRs
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, msiT) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
),
function ActionValue#(UpdateByUpCs#(msiT)) updateByUpCs(
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs
),
function ActionValue#(UpdateByDownDir#(msiT, dirT)) updateByDownDir(
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs, dirT oldDir
),
function ActionValue#(repT) updateRepInfo(repT oldRep, wayT hitWay),
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam,
RWBramCore#(indexT, repT) repRam,
RWBramCore#(dataIndexT, lineT) dataRam
)(
CCPipe#(wayNum, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)
) provisos (
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(_indexSz)),
Alias#(infoT, CacheInfo#(tagT, msiT, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, msiT, dirT, ownerT, otherT, lineT)),
Alias#(respStateT, RespState#(msiT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(enq2MatchT, Enq2Match#(wayNum, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(match2OutT, Match2Out#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(bypassInfoT, BypassInfo#(wayT, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT)),
Bits#(tagT, _tagSz),
Bits#(msiT, _msiSz),
Bits#(dirT, _dirSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz),
Bits#(repT, _repSz),
Bits#(lineT, _lineSz),
Bits#(pipeCmdT, _pipeCmdSz),
// index to data ram: {way, normal index}
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), _indexSz)))
);
// pipeline regs
Ehr#(3, Maybe#(enq2MatchT)) enq2Mat <- mkEhr(Invalid);
// port 0: bypass
Reg#(Maybe#(enq2MatchT)) enq2Mat_bypass = enq2Mat[0];
// port 1: tag match
Reg#(Maybe#(enq2MatchT)) enq2Mat_match = enq2Mat[1];
// port 2: enq
Reg#(Maybe#(enq2MatchT)) enq2Mat_enq = enq2Mat[2];
Ehr#(2, Maybe#(match2OutT)) mat2Out <- mkEhr(Invalid);
// port 0: out
Reg#(Maybe#(match2OutT)) mat2Out_out = mat2Out[0];
// port 1: tag match
Reg#(Maybe#(match2OutT)) mat2Out_match = mat2Out[1];
// bypass write to ram
RWire#(bypassInfoT) bypass <- mkRWire;
// stage 2: first get bypass
(* fire_when_enabled, no_implicit_conditions *)
rule doMatch_bypass(isValid(bypass.wget) && isValid(enq2Mat_bypass) && initDone);
bypassInfoT b = fromMaybe(?, bypass.wget);
enq2MatchT e2m = fromMaybe(?, enq2Mat_bypass);
if(b.index == getIndex(e2m.cmd)) begin
e2m.infoVec[b.way] = Valid (b.ram.info);
e2m.repInfo = Valid (b.repInfo);
end
enq2Mat_bypass <= Valid (e2m);
endrule
rule doTagMatch(isValid(enq2Mat_match) && !isValid(mat2Out_match) && initDone);
enq2MatchT e2m = fromMaybe(?, enq2Mat_match);
// get cache output & merge with bypass
Vector#(wayNum, infoT) infoVec;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].deqRdResp;
infoVec[i] = fromMaybe(infoRam[i].rdResp, e2m.infoVec[i]);
end
repRam.deqRdResp;
repT repInfo = fromMaybe(repRam.rdResp, e2m.repInfo);
// do tag match to get way to occupy
Vector#(wayNum, tagT) tagVec;
Vector#(wayNum, msiT) csVec;
Vector#(wayNum, ownerT) ownerVec;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
tagVec[i] = infoVec[i].tag;
csVec[i] = infoVec[i].cs;
ownerVec[i] = infoVec[i].owner;
end
let tmRes <- tagMatch(e2m.cmd, tagVec, csVec, ownerVec, repInfo);
wayT way = tmRes.way;
Bool pRqMiss = tmRes.pRqMiss;
// read data
indexT index = getIndex(e2m.cmd);
dataRam.rdReq(getDataRamIndex(way, index));
// set mat2out & merge with CRs/PRs & merge with data bypass
// resp data has higher priority than data bypass
match2OutT m2o = Match2Out {
cmd: e2m.cmd,
way: way,
pRqMiss: pRqMiss,
info: infoVec[way],
repInfo: repInfo,
line: e2m.respLine
};
if(e2m.toState matches tagged UpCs .s) begin
UpdateByUpCs#(msiT) upd <- updateByUpCs(
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs
);
m2o.info.cs = upd.cs;
end
else if(e2m.toState matches tagged DownDir .s) begin
UpdateByDownDir#(msiT, dirT) upd <- updateByDownDir(
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs, m2o.info.dir
);
m2o.info.cs = upd.cs;
m2o.info.dir = upd.dir;
end
if(bypass.wget matches tagged Valid .b &&& b.index == index &&& b.way == way &&& !isValid(m2o.line)) begin
// bypass has lower priority than resp data
m2o.line = Valid (b.ram.line);
end
mat2Out_match <= Valid (m2o);
// reset enq2mat
enq2Mat_match <= Invalid;
endrule
// construct output with bypass/resp data
function pipeOutT firstOut;
match2OutT m2o = fromMaybe(?, mat2Out_out);
return PipeOut {
cmd: m2o.cmd,
way: m2o.way,
pRqMiss: m2o.pRqMiss,
ram: RamData {
info: m2o.info,
line: fromMaybe(dataRam.rdResp, m2o.line)
},
repInfo: m2o.repInfo
};
endfunction
Bool enq_guard = !isValid(enq2Mat_enq) && initDone;
Bool deq_guard = isValid(mat2Out_out) && initDone;
// stage 1: enq req to pipeline: access info+rep RAM & bypass
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, respStateT toState) if(enq_guard);
// read ram
indexT index = getIndex(cmd);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].rdReq(index);
end
repRam.rdReq(index);
// write reg & get bypass
enq2MatchT e2m = Enq2Match {
cmd: cmd,
infoVec: replicate(Invalid),
repInfo: Invalid,
respLine: respLine,
toState: toState
};
if(bypass.wget matches tagged Valid .b &&& b.index == index) begin
e2m.infoVec[b.way] = Valid (b.ram.info);
e2m.repInfo = Valid (b.repInfo);
end
enq2Mat_enq <= Valid (e2m);
endmethod
method Bool notFull = enq_guard;
method pipeOutT first if(deq_guard);
return firstOut;
endmethod
method pipeOutT unguard_first;
return firstOut;
endmethod
method Bool notEmpty = deq_guard;
method Action deqWrite(Maybe#(pipeCmdT) newCmd, ramDataT wrRam, Bool updateRep) if(deq_guard);
match2OutT m2o = fromMaybe(?, mat2Out_out);
wayT way = m2o.way;
indexT index = getIndex(m2o.cmd);
// update replacement info
repT repInfo = m2o.repInfo;
if(updateRep) begin
repInfo <- updateRepInfo(m2o.repInfo, way);
end
// write ram
infoRam[way].wrReq(index, wrRam.info);
repRam.wrReq(index, repInfo);
dataRam.wrReq(getDataRamIndex(way, index), wrRam.line);
// set bypass to Enq and Match stages
bypass.wset(BypassInfo {
index: index,
way: way,
ram: wrRam,
repInfo: repInfo
});
// change pipeline reg
if(newCmd matches tagged Valid .cmd) begin
// update pipeline reg
mat2Out_out <= Valid (Match2Out {
cmd: cmd, // swapped in new cmd
way: way, // keep way same
pRqMiss: False, // reset (not valid for swapped in pRq)
info: wrRam.info, // get bypass
repInfo: repInfo, // get bypass
line: Valid (wrRam.line) // get bypass
});
end
else begin
// XXX deq ram resp, I think this should not block
dataRam.deqRdResp;
// reset pipeline reg
mat2Out_out <= Invalid;
end
endmethod
method Bool emptyForFlush;
return !isValid(mat2Out[0]) && !isValid(enq2Mat[0]);
endmethod
endmodule

View File

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

View File

@@ -0,0 +1,215 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import Connectable::*;
import CacheUtils::*;
import CCTypes::*;
import Types::*;
import FShow::*;
import Fifo::*;
import Ehr::*;
typedef struct {
dstIdxT idx;
dstDataT data;
} XBarDstInfo#(type dstIdxT, type dstDataT) deriving(Bits, Eq);
module mkXBar#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
Vector#(srcNum, Get#(srcDataT)) srcIfc,
Vector#(dstNum, Put#(dstDataT)) dstIfc
)(Empty) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// proposed data transfer by each src
Vector#(srcNum, Ehr#(2, Maybe#(dstIdxT))) propDstIdx <- replicateM(mkEhr(Invalid));
Vector#(srcNum, Ehr#(2, dstDataT)) propDstData <- replicateM(mkEhr(?));
// enq command that should be carried out
Vector#(dstNum, Ehr#(2, Maybe#(dstDataT))) enqDst <- replicateM(mkEhr(Invalid));
// src propose data transfer when src is not empty
// and there is no unfinished src proposal
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
rule srcPropose(!isValid(propDstIdx[i][0]));
let d <- srcIfc[i].get;
let info = getDstInfo(fromInteger(i), d);
propDstIdx[i][0] <= Valid (info.idx);
propDstData[i][0] <= info.data;
endrule
end
// round-robin select src for each dst
Vector#(dstNum, Reg#(srcIdxT)) srcRR <- replicateM(mkReg(0));
// do selection for each dst & generate enq commands & deq src proposals
// dst which has unfinished enq command cannot select src
(* fire_when_enabled, no_implicit_conditions *)
rule dstSelectSrc;
// which src to deq (i.e. select) by each dst
Vector#(dstNum, Maybe#(srcIdxT)) deqSrcByDst = replicate(Invalid);
// each dst selects
for(Integer dst = 0; dst < valueOf(dstNum); dst = dst + 1) begin
// only select src to deq when dst has no unfinished enq command
if(!isValid(enqDst[dst][0])) begin
function Bool isFromSrc(srcIdxT src);
// src has proposed data to this dst or not
if(propDstIdx[src][1] matches tagged Valid .dstIdx &&& dstIdx == fromInteger(dst)) begin
return True;
end
else begin
return False;
end
endfunction
Maybe#(srcIdxT) whichSrc = Invalid; // the src to select
if(isFromSrc(srcRR[dst])) begin
// first check the src with priority
whichSrc = Valid (srcRR[dst]);
end
else begin
// otherwise just get one valid src
Vector#(srcNum, srcIdxT) srcIdxVec = genWith(fromInteger);
whichSrc = searchIndex(isFromSrc, srcIdxVec);
end
if(whichSrc matches tagged Valid .src) begin
// can do enq & deq
deqSrcByDst[dst] = whichSrc;
enqDst[dst][0] <= Valid (propDstData[src][1]); // set enq command
// change round robin
srcRR[dst] <= srcRR[dst] == fromInteger(valueOf(srcNum) - 1) ? 0 : srcRR[dst] + 1;
end
end
end
// deq selected src
function Bool isDeqSrc(srcIdxT src);
function Bool isMatch(Maybe#(srcIdxT) deqIdx);
return deqIdx matches tagged Valid .idx &&& idx == src ? True : False;
endfunction
return any(isMatch, deqSrcByDst);
endfunction
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
if(isDeqSrc(fromInteger(i))) begin
propDstIdx[i][1] <= Invalid;
$display("%t XBar %m: deq src %d", $time, i);
doAssert(isValid(propDstIdx[i][1]), "src must be proposing");
end
end
endrule
// enq dst & change round robin
for(Integer i = 0; i < valueOf(dstNum); i = i+1) begin
// XXX since dstIfc.put may conflict with other rules
// the following rule should only fire when it can make progress
// otherwise it may prevent other rules from firing forever
rule doEnq(enqDst[i][1] matches tagged Valid .d);
dstIfc[i].put(d);
enqDst[i][1] <= Invalid; // reset enq command
$display("%t XBAR %m: enq dst %d ; ", $time, i, fshow(d));
endrule
end
endmodule
// XBar with latency added at src or dst (mainly for model timing)
interface XBarDelay#(numeric type srcLat, numeric type dstLat);
endinterface
module mkXBarDelay#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
Vector#(srcNum, Get#(srcDataT)) srcIfc,
Vector#(dstNum, Put#(dstDataT)) dstIfc
)(XBarDelay#(srcLat, dstLat)) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// Add latency at src side
Vector#(srcNum, Get#(srcDataT)) src = srcIfc;
if(valueof(srcLat) > 0) begin
for(Integer i = 0; i < valueof(srcNum); i = i+1) begin
Vector#(srcLat, Fifo#(2, srcDataT)) delayQ <- replicateM(mkCFFifo);
mkConnection(srcIfc[i], toPut(delayQ[0]));
for(Integer j = 0; j < valueof(srcLat) - 1; j = j+1) begin
mkConnection(toGet(delayQ[j]), toPut(delayQ[j + 1]));
end
src[i] = toGet(delayQ[valueof(srcLat) - 1]);
end
end
// Add latency at dst side
Vector#(dstNum, Put#(dstDataT)) dst = dstIfc;
if(valueof(dstLat) > 0) begin
for(Integer i = 0; i < valueof(dstNum); i = i+1) begin
Vector#(dstLat, Fifo#(2, dstDataT)) delayQ <- replicateM(mkCFFifo);
mkConnection(toGet(delayQ[0]), dstIfc[i]);
for(Integer j = 0; j < valueof(dstLat) - 1; j = j+1) begin
mkConnection(toGet(delayQ[j + 1]), toPut(delayQ[j]));
end
dst[i] = toPut(delayQ[valueof(dstLat) - 1]);
end
end
mkXBar(getDstInfo, src, dst);
endmodule
interface CrossBar#(
numeric type srcNum,
numeric type srcLat,
type srcDataT,
numeric type dstNum,
numeric type dstLat,
type dstDataT
);
interface Vector#(srcNum, FifoEnq#(srcDataT)) srcIfc;
interface Vector#(dstNum, FifoDeq#(dstDataT)) dstIfc;
endinterface
module mkCrossBar#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data)
)(
CrossBar#(srcNum, srcLat, srcDataT, dstNum, dstLat, dstDataT)
) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// in/out FIFOs
Vector#(srcNum, Fifo#(2, srcDataT)) srcQ <- replicateM(mkCFFifo);
Vector#(dstNum, Fifo#(2, dstDataT)) dstQ <- replicateM(mkCFFifo);
XBarDelay#(srcLat, dstLat) xbar <- mkXBarDelay(getDstInfo, map(toGet, srcQ), map(toPut, dstQ));
interface srcIfc = map(toFifoEnq, srcQ);
interface dstIfc = map(toFifoDeq, dstQ);
endmodule

View File

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

View File

@@ -0,0 +1,353 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitSt, // wait pRs/cRs to come
Done, // resp is in index FIFO
Depend
} ICRqState deriving(Bits, Eq, FShow);
// CRq info returned to outside
typedef struct {
wayT way; // the way to occupy
tagT repTag; // tag being replaced
Bool waitP; // waiting for parent resp
} ICRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
instance DefaultValue#(ICRqSlot#(wayT, tagT));
defaultValue = ICRqSlot {
way: ?,
repTag: ?,
waitP: False
};
endinstance
typedef struct {
reqT req;
ICRqState state;
Bool waitP;
} ICRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
// MSHR data is purely for replacement resp to parent
// (resp to processor is done immediately, no data buffering needed)
// port for sendRsToP_cRq
interface ICRqMshr_sendRsToP_cRq#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for sendRqToP
interface ICRqMshr_sendRqToP#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for pipelineResp
interface ICRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT,
type resultT
);
method ICRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setResult(Bit#(TLog#(cRqNum)) n, resultT r); // set a valid result
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n,
ICRqState state,
ICRqSlot#(wayT, tagT) slot
);
// can only change state to NON-Empty state
// cannot be used to release MSHR entry (use releaseSlot instead)
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
// index in setSucc is usually different from other getXXX methods
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
// i.e. search the end of dependency chain
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface ICRqMshr_sendRsToC#(
numeric type cRqNum,
type resultT
);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
endinterface
interface ICRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT, // child req type
type resultT // inst result type
);
// port for cRqTransfer, initialization is done inside method
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
// port for sendRsToC
interface ICRqMshr_sendRsToC#(cRqNum, resultT) sendRsToC;
// port for sendRsToP_cRq
interface ICRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
// port for sendRqToP
interface ICRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
// port for pipelineResp
interface ICRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT, resultT) pipelineResp;
// port for security flush
method Bool emptyForFlush;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(ICRqMshrStuck#(reqT)) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkICRqMshrSafe#(
function Addr getAddrFromReq(reqT r)
)(
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, ICRqSlot#(wayT, tagT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz),
Bits#(resultT, _resultTSz)
);
// EHR ports
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer cRqTransfer_port = 2;
Integer sendRsToC_port = 1; // create a bypass behavior from pipelineResp to sendRsToC (to save a cycle)
Integer pipelineResp_port = 0;
Integer sendRqToP_port = 0; // sendRqToP is read only
Integer sendRsToP_cRq_port = 0; // sendRsToP_cRq is read only
Integer flush_port = 0; // flush port is read only
// MSHR entry state
Vector#(cRqNum, Ehr#(3, ICRqState)) stateVec <- replicateM(mkEhr(Empty));
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
// result
Vector#(cRqNum, Ehr#(3, Maybe#(resultT))) resultVec <- replicateM(mkEhr(Invalid));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t ICRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(ICRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(ICRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP
});
end
endrule
`endif
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
stateVec[n][cRqTransfer_port] <= Init;
slotVec[n][cRqTransfer_port] <= defaultValue;
resultVec[n][cRqTransfer_port] <= Invalid;
succValidVec[n][cRqTransfer_port] <= False;
reqVec[n][cRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface ICRqMshr_sendRsToC sendRsToC;
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][sendRsToC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
return resultVec[n][sendRsToC_port];
endmethod
endinterface
interface ICRqMshr_sendRsToP_cRq sendRsToP_cRq;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToP_cRq_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRsToP_cRq_port];
endmethod
endinterface
interface ICRqMshr_sendRqToP sendRqToP;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToP_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToP_port];
endmethod
endinterface
interface ICRqMshr_pipelineResp pipelineResp;
method ICRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Action setStateSlot(cRqIndexT n, ICRqState state, slotT slot);
doAssert(state != Empty, "use releaseEntry to set state to Empty");
stateVec[n][pipelineResp_port] <= state;
slotVec[n][pipelineResp_port] <= slot;
endmethod
method Action setResult(cRqIndexT n, resultT r);
resultVec[n][pipelineResp_port] <= Valid (r);
endmethod
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
endmethod
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
succValidVec[n][pipelineResp_port] <= isValid(succ);
succFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
ICRqState state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noSucc = !succValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
method Bool emptyForFlush;
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
Vector#(cRqNum, Integer) idxVec = genVector;
return all(isEmpty, idxVec);
endmethod
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exported version
module mkICRqMshr#(
function Addr getAddrFromReq(reqT r)
)(
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
) provisos (
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz),
Bits#(resultT, _resultTSz)
);
let m <- mkICRqMshrSafe(getAddrFromReq);
return m;
endmodule

View File

@@ -0,0 +1,214 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import GetPut::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always directly handled at the end of pipeline
// PRq MSHR entry state
typedef enum {
Empty,
Init,
Done
} IPRqState deriving (Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
IPRqState state;
} IPRqMshrStuck deriving(Bits, Eq, FShow);
interface IPRqMshr_sendRsToP_pRq#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
endinterface
interface IPRqMshr_pipelineResp#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
method Action setDone(Bit#(TLog#(pRqNum)) n);
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
`endif
endinterface
interface IPRqMshr#(numeric type pRqNum);
// port for pRqTransfer
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
// port for sendRsToP_pRq
interface IPRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
// port for pipelineResp
interface IPRqMshr_pipelineResp#(pRqNum) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(IPRqMshrStuck) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkIPRqMshrSafe(
IPRqMshr#(pRqNum)
) provisos(
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
);
// EHR port
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer pRqTransfer_port = 2;
Integer sendRsToP_pRq_port = 1;
Integer pipelineResp_port = 0;
// MSHR entry state
Vector#(pRqNum, Ehr#(3, IPRqState)) stateVec <- replicateM(mkEhr(Empty));
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
// empty entry FIFO
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(pRqIndexT) initIdx <- mkReg(0);
// released entry index fifos
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
inited <= True;
$display("%t IPRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(IPRqMshrStuck) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(IPRqMshrStuck {
addr: reqVec[n][0].addr,
toState: reqVec[n][0].toState,
state: stateVec[n][0]
});
end
endrule
`endif
rule doReleaseEntry_sendRsToP_pRq(inited);
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
rule doReleaseEntry_pipelineResp(inited);
let n <- toGet(releaseEntryQ_pipelineResp).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
emptyEntryQ.deq;
pRqIndexT n = emptyEntryQ.first;
stateVec[n][pRqTransfer_port] <= Init;
reqVec[n][pRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface IPRqMshr_sendRsToP_pRq sendRsToP_pRq;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][sendRsToP_pRq_port];
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_sendRsToP_pRq.enq(n);
stateVec[n][sendRsToP_pRq_port] <= Empty;
endmethod
endinterface
interface IPRqMshr_pipelineResp pipelineResp;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method Action setDone(pRqIndexT n);
stateVec[n][pipelineResp_port] <= Done;
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_pipelineResp.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
endmethod
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
reqVec[n][pipelineResp_port] <= PRqMsg {
addr: a,
toState: I,
child: ?
};
endmethod
`endif
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exportd version
module mkIPRqMshr(IPRqMshr#(pRqNum));
let m <- mkIPRqMshrSafe;
return m;
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,378 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitNewTag, // waiting replacement resp to send (but tag in RAM is already updated)
WaitSt, // wait pRs/cRs to come
Done, // resp is in index FIFO
Depend
} L1CRqState deriving(Bits, Eq, FShow);
// CRq info returned to outside
typedef struct {
wayT way; // the way to occupy
Msi cs; // current cache MSI, used in sending upgrade req to parent
tagT repTag; // tag being replaced: only valid in WaitOld/NewTag states
Bool waitP; // waiting for parent resp
} L1CRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
instance DefaultValue#(L1CRqSlot#(wayT, tagT));
defaultValue = L1CRqSlot {
way: ?,
cs: ?,
repTag: ?,
waitP: False
};
endinstance
typedef struct {
reqT req;
L1CRqState state;
Msi slotCs;
Bool waitP;
} L1CRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
// MSHR data is purely for replacement resp to parent
// (resp to processor is done immediately, no data buffering needed)
// port for cRqTransfer and retry
interface L1CRqMshr_transfer#(
numeric type cRqNum,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
endinterface
// port for sendRsToP_cRq
interface L1CRqMshr_sendRsToP_cRq#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Action setWaitSt_setSlot_clearData(
Bit#(TLog#(cRqNum)) n,
L1CRqSlot#(wayT, tagT) slot
);
// data is set to invalid, state is set to WaitSt here
endinterface
// port for sendRqToP
interface L1CRqMshr_sendRqToP#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for pipelineResp
interface L1CRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n,
L1CRqState state,
L1CRqSlot#(wayT, tagT) slot
);
// can only change state to NON-Empty state
// cannot be used to release MSHR entry (use releaseSlot instead)
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
// index in setSucc is usually different from other getXXX methods
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
// i.e. search the end of dependency chain
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface L1CRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT // child req type
);
// port for cRqTransfer and retry
interface L1CRqMshr_transfer#(cRqNum, reqT) cRqTransfer;
// port for sendRsToP_cRq
interface L1CRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
// port for sendRqToP
interface L1CRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
// port for pipelineResp
interface L1CRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT) pipelineResp;
// port for security flush
method Bool emptyForFlush;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1CRqMshrStuck#(reqT)) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkL1CRqMshrSafe#(
function Addr getAddrFromReq(reqT r)
)(
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, L1CRqSlot#(wayT, tagT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz)
);
// EHR ports
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer flush_port = 0; // flush port is read only
Integer sendRqToP_port = 0; // sendRqToP is read only
Integer sendRsToP_cRq_port = 0;
Integer pipelineResp_port = 1;
Integer cRqTransfer_port = 2;
// MSHR entry state
Vector#(cRqNum, Ehr#(3, L1CRqState)) stateVec <- replicateM(mkEhr(Empty));
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
RegFile#(cRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t L1CRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(L1CRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(L1CRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
slotCs: slotVec[n][0].cs,
waitP: slotVec[n][0].waitP
});
end
endrule
`endif
interface L1CRqMshr_transfer cRqTransfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][cRqTransfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
stateVec[n][cRqTransfer_port] <= Init;
slotVec[n][cRqTransfer_port] <= defaultValue;
dataValidVec[n][cRqTransfer_port] <= False;
succValidVec[n][cRqTransfer_port] <= False;
reqVec[n][cRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
endinterface
interface L1CRqMshr_sendRsToP_cRq sendRsToP_cRq;
method L1CRqState getState(cRqIndexT n);
return stateVec[n][sendRsToP_cRq_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToP_cRq_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRsToP_cRq_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToP_cRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
endmethod
method Action setWaitSt_setSlot_clearData(cRqIndexT n, slotT s);
stateVec[n][sendRsToP_cRq_port] <= WaitSt;
slotVec[n][sendRsToP_cRq_port] <= s;
dataValidVec[n][sendRsToP_cRq_port] <= False;
endmethod
endinterface
interface L1CRqMshr_sendRqToP sendRqToP;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToP_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToP_port];
endmethod
endinterface
interface L1CRqMshr_pipelineResp pipelineResp;
method L1CRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
method Action setStateSlot(cRqIndexT n, L1CRqState state, slotT slot);
doAssert(state != Empty, "use releaseEntry to set state to Empty");
stateVec[n][pipelineResp_port] <= state;
slotVec[n][pipelineResp_port] <= slot;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) line);
dataValidVec[n][pipelineResp_port] <= isValid(line);
dataFile.upd(n, fromMaybe(?, line));
endmethod
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
endmethod
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
succValidVec[n][pipelineResp_port] <= isValid(succ);
succFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
L1CRqState state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noSucc = !succValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
method Bool emptyForFlush;
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
Vector#(cRqNum, Integer) idxVec = genVector;
return all(isEmpty, idxVec);
endmethod
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exported version
module mkL1CRqMshr#(
function Addr getAddrFromReq(reqT r)
)(
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
) provisos (
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz)
);
let m <- mkL1CRqMshrSafe(getAddrFromReq);
return m;
endmodule

View File

@@ -0,0 +1,231 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import GetPut::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always directly handled at the end of pipeline
// PRq MSHR entry state
typedef enum {
Empty,
Init,
Done
} L1PRqState deriving (Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
L1PRqState state;
} L1PRqMshrStuck deriving(Bits, Eq, FShow);
interface L1PRqMshr_sendRsToP_pRq#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
endinterface
interface L1PRqMshr_pipelineResp#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method L1PRqState getState(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
method Action setDone_setData(Bit#(TLog#(pRqNum)) n, Maybe#(Line) d);
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
`endif
endinterface
interface L1PRqMshr#(numeric type pRqNum);
// port for pRqTransfer
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
// port for sendRsToP_pRq
interface L1PRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
// port for pipelineResp
interface L1PRqMshr_pipelineResp#(pRqNum) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1PRqMshrStuck) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkL1PRqMshrSafe(
L1PRqMshr#(pRqNum)
) provisos(
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
);
// EHR port
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer sendRsToP_pRq_port = 0;
Integer pipelineResp_port = 1;
Integer pRqTransfer_port = 2;
// MSHR entry state
Vector#(pRqNum, Ehr#(3, L1PRqState)) stateVec <- replicateM(mkEhr(Empty));
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
// data valid bits
Vector#(pRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
RegFile#(pRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(pRqNum) - 1));
// empty entry FIFO
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(pRqIndexT) initIdx <- mkReg(0);
// released entry index fifos
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
inited <= True;
$display("%t L1PRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(L1PRqMshrStuck) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(L1PRqMshrStuck {
addr: reqVec[n][0].addr,
toState: reqVec[n][0].toState,
state: stateVec[n][0]
});
end
endrule
`endif
rule doReleaseEntry_sendRsToP_pRq(inited);
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
rule doReleaseEntry_pipelineResp(inited);
let n <- toGet(releaseEntryQ_pipelineResp).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
emptyEntryQ.deq;
pRqIndexT n = emptyEntryQ.first;
stateVec[n][pRqTransfer_port] <= Init;
reqVec[n][pRqTransfer_port] <= r;
dataValidVec[n][pRqTransfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface L1PRqMshr_sendRsToP_pRq sendRsToP_pRq;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][sendRsToP_pRq_port];
endmethod
method Maybe#(Line) getData(pRqIndexT n);
return dataValidVec[n][sendRsToP_pRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_sendRsToP_pRq.enq(n);
stateVec[n][sendRsToP_pRq_port] <= Empty;
endmethod
endinterface
interface L1PRqMshr_pipelineResp pipelineResp;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method L1PRqState getState(pRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method Action setDone_setData(pRqIndexT n, Maybe#(Line) line);
stateVec[n][pipelineResp_port] <= Done;
dataValidVec[n][pipelineResp_port] <= isValid(line);
dataFile.upd(n, fromMaybe(?, line));
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_pipelineResp.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
endmethod
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
reqVec[n][pipelineResp_port] <= PRqMsg {
addr: a,
toState: I,
child: ?
};
endmethod
`endif
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exportd version
module mkL1PRqMshr(L1PRqMshr#(pRqNum));
let m <- mkL1PRqMshrSafe;
return m;
endmodule

View File

@@ -0,0 +1,391 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export L1PipeRqIn(..);
export L1PipePRsIn(..);
export L1PipeFlushIn(..);
export L1PipeIn(..);
export L1FlushCmd(..);
export L1Cmd(..);
export L1Pipe(..);
export mkL1Pipe;
// type param ordering: bank < way < index < tag < cRq < pRq
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
// replacement is always done immediately (never have replacing line)
// so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} L1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} L1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef struct {
indexT index;
wayT way;
pRqIdxT mshrIdx;
} L1PipeFlushIn#(
type wayT,
type indexT,
type pRqIdxT
) deriving(Bits, Eq, FShow);
typedef union tagged {
L1PipeRqIn#(cRqIdxT) CRq;
L1PipeRqIn#(pRqIdxT) PRq;
L1PipePRsIn#(wayT) PRs;
`ifdef SECURITY
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
`endif
} L1PipeIn#(
type wayT,
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in L1$
typedef struct {
indexT index;
pRqIdxT mshrIdx;
} L1FlushCmd#(type indexT, type pRqIdxT) deriving(Bits, Eq, FShow);
typedef union tagged {
cRqIdxT L1CRq;
pRqIdxT L1PRq;
void L1PRs;
`ifdef SECURITY
L1FlushCmd#(indexT, pRqIdxT) L1Flush;
`endif
} L1Cmd#(
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
interface L1Pipe#(
numeric type lgBankNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT,
type pRqIdxT
);
method Action send(L1PipeIn#(Bit#(TLog#(wayNum)), indexT, cRqIdxT, pRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), void, RandRepInfo, // no other
Line, L1Cmd#(indexT, cRqIdxT, pRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} L1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
L1PipeRqIn#(cRqIdxT) CRq;
L1PipeRqIn#(pRqIdxT) PRq;
L1PipePRsCmd#(wayT) PRs;
`ifdef SECURITY
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
`endif
} L1PipeCmd#(
type wayT,
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
module mkL1Pipe(
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, L1PipeIn#(wayT, indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeCmdT, L1PipeCmd#(wayT, indexT, cRqIdxT, pRqIdxT)),
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRq .r: r.addr;
tagged PRs .r: r.addr;
`ifdef SECURITY
// fake an address for flush req that has the same index
tagged Flush .r: (zeroExtend(r.index) << (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
`endif
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
`ifdef SECURITY
else if(cmd matches tagged Flush .flush) begin
// flush directly read from cmd
return TagMatchResult {
way: flush.way,
pRqMiss: False
};
end
`endif
else begin
// CRq/PRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else if(cmd matches tagged PRq .rq) begin
// pRq miss
return TagMatchResult {
way: 0, // default to 0
pRqMiss: True
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) == dataV, "valid resp data for upgrade from I");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 does not have dir");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRq .rq: begin
pipe.enq(PRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (L1PipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
`ifdef SECURITY
tagged Flush .flush: begin
pipe.enq(Flush (flush), Invalid, Invalid);
end
`endif
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: L1CRq (rq.mshrIdx);
tagged PRq .rq: L1PRq (rq.mshrIdx);
tagged PRs .rs: L1PRs;
`ifdef SECURITY
tagged Flush .flush: L1Flush (L1FlushCmd {
index: flush.index,
mshrIdx: flush.mshrIdx
});
`endif
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (L1PipeRqIn {addr: addr, mshrIdx: idx}));
`ifdef SECURITY
doAssert(pipe.first.cmd matches tagged Flush .f ? False : True,
"Cannot swap after a flush req");
`endif
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,447 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in LLC, the head (h1) of a chain may be linked as the successor of the head (h2) of another chain
// when h2 is replacing the addr of h1
// h1 should be waken up and sent to pipeline when replacement is done (i.e. h2 gets to WaitSt)
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitOldTag,
WaitSt,
Done,
Depend
} LLCRqState deriving(Bits, Eq, FShow);
// we split data from slot info
// because data may be used to buffer mem resp data
typedef struct {
wayT way; // the way to occupy
tagT repTag; // tag being replaced, used in sending down req to children
Bool waitP; // wait parent resp
dirPendT dirPend; // pending child downgrade
} LLCRqSlot#(type wayT, type tagT, type dirPendT) deriving(Bits, Eq, FShow);
typedef struct {
reqT req;
LLCRqState state;
Bool waitP;
dirPendT dirPend;
} LLCRqMshrStuck#(type dirPendT, type reqT) deriving(Bits, Eq, FShow);
// interface for cRq/mRs/cRsTransfer
interface LLCRqMshr_transfer#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r, Maybe#(Line) d);
// check if any empty MSHR entry is available in order to get MSHR blocking
// stats. The argument is not really used here, just in case some other
// MSHR implementations may bank entries based on addr.
method Bool hasEmptyEntry(reqT r);
endinterface
// interface for mRsDeq
interface LLCRqMshr_mRsDeq#(numeric type cRqNum);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
endinterface
// interface for sendToM
interface LLCRqMshr_sendToM#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
endinterface
// interface for sendRsToDma and sendRsToC
interface LLCRqMshr_sendRsToDmaC#(
numeric type cRqNum,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
endinterface
// interface for sendRqToC
interface LLCRqMshr_sendRqToC#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setSlot(Bit#(TLog#(cRqNum)) n, LLCRqSlot#(wayT, tagT, dirPendT) s);
// find cRq that needs to send req to child to downgrade
// (either replacement, or incompatible children states)
// we can pass in a suggested req idx (which will have priority)
method Maybe#(Bit#(TLog#(cRqNum))) searchNeedRqChild(Maybe#(Bit#(TLog#(cRqNum))) suggestIdx);
endinterface
// interface for pipelineResp_xxx
interface LLCRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Maybe#(Bit#(TLog#(cRqNum))) getAddrSucc(Bit#(TLog#(cRqNum)) n);
method Maybe#(Bit#(TLog#(cRqNum))) getRepSucc(Bit#(TLog#(cRqNum)) n);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n, LLCRqState state,
LLCRqSlot#(wayT, tagT, dirPendT) slot
);
method Action setAddrSucc( // same address successor
Bit#(TLog#(cRqNum)) n,
Maybe#(Bit#(TLog#(cRqNum))) succ
);
method Action setRepSucc( // successor due to replacement
Bit#(TLog#(cRqNum)) n,
Maybe#(Bit#(TLog#(cRqNum))) succ
);
// find existing cRq which has gone through pipeline, but not in Done state, and has no addr successor
// (it could have rep successor)
// i.e. search the end of dependency chain for req to the same addr
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface LLCRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
interface LLCRqMshr_transfer#(cRqNum, wayT, tagT, dirPendT, reqT) transfer;
interface LLCRqMshr_mRsDeq#(cRqNum) mRsDeq;
interface LLCRqMshr_sendToM#(cRqNum, wayT, tagT, dirPendT, reqT) sendToM;
interface LLCRqMshr_sendRsToDmaC#(cRqNum, reqT) sendRsToDmaC;
interface LLCRqMshr_sendRqToC#(cRqNum, wayT, tagT, dirPendT, reqT) sendRqToC;
interface LLCRqMshr_pipelineResp#(cRqNum, wayT, tagT, dirPendT, reqT) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(LLCRqMshrStuck#(dirPendT, reqT)) stuck;
endinterface
function LLCRqSlot#(wayT, tagT, dirPendT) getLLCRqSlotInitVal(dirPendT dirPendInitVal);
return LLCRqSlot {
way: ?,
repTag: ?,
waitP: False,
dirPend: dirPendInitVal
};
endfunction
//////////////////
// safe version //
//////////////////
module mkLLCRqMshr#(
function Addr getAddrFromReq(reqT r),
function Bool needDownReq(dirPendT dirPend),
dirPendT dirPendInitVal
)(
LLCRqMshr#(cRqNum, wayT, tagT, dirPendT, reqT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, LLCRqSlot#(wayT, tagT, dirPendT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(dirPendT, _dirPendSz),
Bits#(reqT, _reqSz)
);
slotT slotInitVal = getLLCRqSlotInitVal(dirPendInitVal);
// logical ordering: sendToM < sendRqToC < sendRsToDma/C < mRsDeq < pipelineResp < transfer
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
// EHR ports
Integer sendToM_port = 0; // sendToM is read-only, so use port 0
Integer sendRqToC_port = 0; // read req/state/slot, write slot
Integer sendRsToDmaC_port = 0; // sendRsToDma/C read req/data, write state
Integer mRsDeq_port = 0; // mRsDeq only writes data
Integer pipelineResp_port = 1; // read/write lots of things
Integer transfer_port = 2; // cRqTransfer_xx, mRsTransfer_send, read/write lots of things
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// MSHR entry state
Vector#(cRqNum, Ehr#(3, LLCRqState)) stateVec <- replicateM(mkEhr(Empty));
// summary bit of dirPend in each entry: asserted when some dirPend[i] = ToSend
Vector#(cRqNum, Ehr#(3, Bool)) needReqChildVec <- replicateM(mkEhr(False));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(slotInitVal));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
Vector#(cRqNum, Ehr#(3, Line)) dataVec <- replicateM(mkEhr(?));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) addrSuccValidVec <- replicateM(mkEhr(False));
Vector#(cRqNum, Ehr#(3, Bool)) repSuccValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) addrSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
RegFile#(cRqIndexT, cRqIndexT) repSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
Fifo#(cRqNum, cRqIndexT) emptyEntryQ <- mkCFFifo;
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t LLCRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(LLCRqMshrStuck#(dirPendT, reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(LLCRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP,
dirPend: slotVec[n][0].dirPend
});
end
endrule
`endif
function Action writeSlot(Integer ehrPort, cRqIndexT n, slotT s);
action
slotVec[n][ehrPort] <= s;
// set dirPend summary bit
needReqChildVec[n][ehrPort] <= needDownReq(s.dirPend);
endaction
endfunction
interface LLCRqMshr_transfer transfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][transfer_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][transfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r, Maybe#(Line) d) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
reqVec[n][transfer_port] <= r;
stateVec[n][transfer_port] <= Init;
writeSlot(transfer_port, n, slotInitVal);
dataValidVec[n][transfer_port] <= isValid(d);
dataVec[n][transfer_port] <= validValue(d);
addrSuccValidVec[n][transfer_port] <= False;
repSuccValidVec[n][transfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
method Bool hasEmptyEntry(reqT r);
return emptyEntryQ.notEmpty;
endmethod
endinterface
interface LLCRqMshr_mRsDeq mRsDeq;
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][mRsDeq_port] <= isValid(d);
dataVec[n][mRsDeq_port] <= fromMaybe(?, d);
endmethod
endinterface
interface LLCRqMshr_sendToM sendToM;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendToM_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendToM_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendToM_port] ? Valid (dataVec[n][sendToM_port]) : Invalid;
endmethod
endinterface
interface LLCRqMshr_sendRsToDmaC sendRsToDmaC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToDmaC_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToDmaC_port] ? Valid (dataVec[n][sendRsToDmaC_port]) : Invalid;
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][sendRsToDmaC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
endinterface
interface LLCRqMshr_sendRqToC sendRqToC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToC_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][sendRqToC_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToC_port];
endmethod
method Action setSlot(cRqIndexT n, slotT s);
writeSlot(sendRqToC_port, n, s);
endmethod
method Maybe#(cRqIndexT) searchNeedRqChild(Maybe#(cRqIndexT) suggestIdx);
function Bool isNeedRqChild(cRqIndexT i);
return (stateVec[i][sendRqToC_port] == WaitOldTag || stateVec[i][sendRqToC_port] == WaitSt)
&& needReqChildVec[i][sendRqToC_port];
endfunction
if(suggestIdx matches tagged Valid .idx &&& isNeedRqChild(idx)) begin
return suggestIdx;
end
else begin
Vector#(cRqNum, cRqIndexT) idxVec = genWith(fromInteger);
return searchIndex(isNeedRqChild, idxVec);
end
endmethod
endinterface
interface LLCRqMshr_pipelineResp pipelineResp;
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][pipelineResp_port] ? Valid (dataVec[n][pipelineResp_port]) : Invalid;
endmethod
method Maybe#(cRqIndexT) getAddrSucc(cRqIndexT n);
return addrSuccValidVec[n][pipelineResp_port] ? Valid (addrSuccFile.sub(n)) : Invalid;
endmethod
method Maybe#(cRqIndexT) getRepSucc(cRqIndexT n);
return repSuccValidVec[n][pipelineResp_port] ? Valid (repSuccFile.sub(n)) : Invalid;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][pipelineResp_port] <= isValid(d);
dataVec[n][pipelineResp_port] <= fromMaybe(?, d);
endmethod
method Action setStateSlot(cRqIndexT n, LLCRqState state, slotT slot);
stateVec[n][pipelineResp_port] <= state;
writeSlot(pipelineResp_port, n, slot);
endmethod
method Action setAddrSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
addrSuccValidVec[n][pipelineResp_port] <= isValid(succ);
addrSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Action setRepSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
repSuccValidVec[n][pipelineResp_port] <= isValid(succ);
repSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
let state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noAddrSucc = !addrSuccValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noAddrSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule

View File

@@ -0,0 +1,371 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export LLPipeCRqIn(..);
export LLPipeMRsIn(..);
export LLPipeIn(..);
export LLCmd(..);
export LLPipe(..);
export mkLLPipe;
// type param ordering: bank < child < way < index < tag < cRq
// input types
typedef struct {
Addr addr;
cRqIdxT mshrIdx;
} LLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState; // come from req in MSHR (E or M)
Line data; // come from memory must be valid
wayT way; // come from MSHR
} LLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
LLPipeCRqIn#(cRqIdxT) CRq;
CRsMsg#(childT) CRs;
LLPipeMRsIn#(wayT) MRs;
} LLPipeIn#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in LLC
typedef union tagged {
cRqIdxT LLCRq; // mshr idx of the cRq
childT LLCRs; // which child is downgrading
void LLMRs;
} LLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
interface LLPipe#(
numeric type lgBankNum,
numeric type childNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(LLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
method Bool notEmpty;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, Vector#(childNum, Msi),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) first;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, Vector#(childNum, Msi),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) unguard_first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, Vector#(childNum, Msi), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
childT child;
} LLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
wayT way;
} LLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
LLPipeCRqIn#(cRqIdxT) CRq;
LLPipeCRsCmd#(childT) CRs;
LLPipeMRsCmd#(wayT) MRs;
} LLPipeCmd#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
module mkLLPipe(
LLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(childT, Bit#(TLog#(childNum))),
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, Vector#(childNum, Msi)),
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, LLPipeIn#(childT, wayT, cRqIdxT)),
Alias#(pipeCmdT, LLPipeCmd#(childT, wayT, cRqIdxT)),
Alias#(llCmdT, LLCmd#(childT, cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: replicate(I),
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged CRs .r: r.addr;
tagged MRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t LL %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec)
);
if(cmd matches tagged MRs .rs) begin
// MRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/CRs: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (we do not check replacing bit in LLC)
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// cRs must hit, so only cRq cannot enter here
doAssert(cmd matches tagged CRq ._rq ? True : False,
"only cRq can tag match miss"
);
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
doAssert(isValid(repWay), "should always find a way to replace");
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
// update dir
dirT newDir = oldDir;
if(cmd matches tagged CRs .cRs) begin
if(dataV) begin
doAssert(oldDir[cRs.child] >= E, "cRs with data, dir must >= E");
end
else begin
doAssert(oldDir[cRs.child] < M, "cRs without data, dir must < M");
end
if(oldDir[cRs.child] == M) begin
doAssert(dataV, "must have data");
end
newDir[cRs.child] = toState;
end
else begin
// should not happen
doAssert(False, "only cRs updates dir");
end
// update cs
// XXX since child can upgrade from E to M silently, use data valid
// to determine if we need to upgrade to M. Note that the data
// valid field has not been overwritten by bypass in CCPipe.
Msi newCs = oldCs;
if(dataV) begin
doAssert(oldCs >= E, "cRs has data, cs must >= E");
newCs = M;
end
return UpdateByDownDir {cs: newCs, dir: newDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// get first output from CCPipe output
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: LLCRq (rq.mshrIdx);
tagged CRs .rs: LLCRs (rs.child);
tagged MRs .rs: LLMRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endfunction
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged CRs .rs: begin
pipe.enq(CRs (LLPipeCRsCmd {
addr: rs.addr,
child: rs.child
}), rs.data, DownDir (rs.toState));
end
tagged MRs .rs: begin
pipe.enq(MRs (LLPipeMRsCmd {
addr: rs.addr,
way: rs.way
}), Valid (rs.data), UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
return getFirst(pipe.first); // guarded version
endmethod
method pipeOutT unguard_first;
return getFirst(pipe.unguard_first); // unguarded version
endmethod
method notEmpty = pipe.notEmpty;
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin
newCmd = Valid (CRq (LLPipeCRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

View File

@@ -0,0 +1,83 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Ehr::*;
import CCTypes::*;
import Vector::*;
import ProcTypes::*;
interface MshrDeadlockChecker#(numeric type num);
method ActionValue#(Maybe#(Bit#(TLog#(num)))) getStuckIdx; // get deadlock MSHR idx
method Action initEntry(Bit#(TLog#(num)) n); // new MSHR entry allocated
method Action releaseEntry(Bit#(TLog#(num)) n); // existing MSHR entry released
endinterface
module mkMshrDeadlockChecker(MshrDeadlockChecker#(num)) provisos(
Alias#(idxT, Bit#(TLog#(num)))
);
Integer check_port = 0;
Integer incr_port = 1;
// timer for each entry to detect deadlock: being processed for 64M cycles
Vector#(num, Ehr#(2, Maybe#(DeadlockTimer))) timer <- replicateM(mkEhr(Invalid));
// when new entry is allocated, init timer
Vector#(num, PulseWire) init <- replicateM(mkPulseWire);
// when existing entry is released, end the timer
Vector#(num, PulseWire) done <- replicateM(mkPulseWire);
(* fire_when_enabled, no_implicit_conditions *)
rule incrTimer;
for(Integer i = 0; i < valueof(num); i = i+1) begin
if(init[i]) begin
timer[i][incr_port] <= Valid (0);
end
else if(done[i]) begin
timer[i][incr_port] <= Invalid;
end
else if(timer[i][incr_port] matches tagged Valid .t &&& t != maxBound) begin
timer[i][incr_port] <= Valid (t + 1);
end
end
endrule
method ActionValue#(Maybe#(idxT)) getStuckIdx;
function Bool isDeadlock(Integer i);
return timer[i][check_port] == Valid (maxBound);
endfunction
Vector#(num, Integer) idxVec = genVector;
if(searchIndex(isDeadlock, idxVec) matches tagged Valid .n) begin
timer[n][check_port] <= Valid (0);
return Valid (n);
end
else begin
return Invalid;
end
endmethod
method Action initEntry(idxT n);
init[n].send;
endmethod
method Action releaseEntry(idxT n);
done[n].send;
endmethod
endmodule

View File

@@ -0,0 +1,64 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import BRAMCore::*;
import Fifo::*;
interface RWBramCore#(type addrT, type dataT);
method Action wrReq(addrT a, dataT d);
method Action rdReq(addrT a);
method dataT rdResp;
method Bool rdRespValid;
method Action deqRdResp;
endinterface
module mkRWBramCore(RWBramCore#(addrT, dataT)) provisos(
Bits#(addrT, addrSz), Bits#(dataT, dataSz)
);
BRAM_DUAL_PORT#(addrT, dataT) bram <- mkBRAMCore2(valueOf(TExp#(addrSz)), False);
BRAM_PORT#(addrT, dataT) wrPort = bram.a;
BRAM_PORT#(addrT, dataT) rdPort = bram.b;
// 1 elem pipeline fifo to add guard for read req/resp
// must be 1 elem to make sure rdResp is not corrupted
// BRAMCore should not change output if no req is made
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
method Action wrReq(addrT a, dataT d);
wrPort.put(True, a, d);
endmethod
method Action rdReq(addrT a);
rdReqQ.enq(?);
rdPort.put(False, a, ?);
endmethod
method dataT rdResp if(rdReqQ.notEmpty);
return rdPort.read;
endmethod
method rdRespValid = rdReqQ.notEmpty;
method Action deqRdResp;
rdReqQ.deq;
endmethod
endmodule

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import Fifo::*;
import CCTypes::*;
import RWBramCore::*;
// random replace does not need any bookkeeping
typedef void RandRepInfo;
function RandRepInfo randRepInitInfo;
return ?;
endfunction
module mkRandRepRam(RWBramCore#(indexT, RandRepInfo));
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
method Action wrReq(indexT a, RandRepInfo d);
noAction;
endmethod
method Action rdReq(indexT a);
rdReqQ.enq(?);
endmethod
method RandRepInfo rdResp if(rdReqQ.notEmpty);
return ?;
endmethod
method rdRespValid = rdReqQ.notEmpty;
method deqRdResp = rdReqQ.deq;
endmodule
interface RandomReplace#(numeric type wayNum);
// find a way to replace, which is not locked
// and Invalid way has priority
method Maybe#(Bit#(TLog#(wayNum))) getReplaceWay(
Vector#(wayNum, Bool) unlocked,
Vector#(wayNum, Bool) invalid
);
endinterface
module mkRandomReplace(RandomReplace#(wayNum)) provisos(
Alias#(wayT, Bit#(TLog#(wayNum)))
);
Reg#(wayT) randWay <- mkReg(0);
rule tick;
randWay <= randWay == fromInteger(valueOf(wayNum) - 1) ? 0 : randWay + 1;
endrule
method Maybe#(wayT) getReplaceWay(Vector#(wayNum, Bool) unlocked, Vector#(wayNum, Bool) invalid);
// first search for invalid & unlocked way
function Bool isInvUnlock(Integer i);
return unlocked[i] && invalid[i];
endfunction
Vector#(wayNum, Integer) idxVec = genVector;
Maybe#(wayT) repWay = searchIndex(isInvUnlock, idxVec);
if(!isValid(repWay)) begin
// check whether random way is unlocked
if(unlocked[randWay]) begin
repWay = Valid (randWay);
end
else begin
// just find a unlocked way
repWay = searchIndex(id, unlocked);
end
end
return repWay;
endmethod
endmodule

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,366 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
// type param ordering: bank < child < way < index < tag < cRq
// input types
typedef struct {
Addr addr;
cRqIdxT mshrIdx;
} SelfInvLLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState; // come from req in MSHR (E or M)
Line data; // come from memory must be valid
wayT way; // come from MSHR
} SelfInvLLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
CRsMsg#(childT) CRs;
SelfInvLLPipeMRsIn#(wayT) MRs;
} SelfInvLLPipeIn#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in LLC
typedef union tagged {
cRqIdxT LLCRq; // mshr idx of the cRq
childT LLCRs; // which child is downgrading
void LLMRs;
} SelfInvLLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
// Track only exclusive child in directory
typedef struct {
childT exChild;
Msi state; // I/E/M, we don't track S
} SelfInvDir#(type childT) deriving(Bits, Eq, FShow);
interface SelfInvLLPipe#(
numeric type lgBankNum,
numeric type childNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(SelfInvLLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
method Bool notEmpty;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) first;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) unguard_first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
childT child;
} SelfInvLLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
wayT way;
} SelfInvLLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
SelfInvLLPipeCRsCmd#(childT) CRs;
SelfInvLLPipeMRsCmd#(wayT) MRs;
} SelfInvLLPipeCmd#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
module mkSelfInvLLPipe(
SelfInvLLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(childT, Bit#(TLog#(childNum))),
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, SelfInvDir#(childT)),
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, SelfInvLLPipeIn#(childT, wayT, cRqIdxT)),
Alias#(pipeCmdT, SelfInvLLPipeCmd#(childT, wayT, cRqIdxT)),
Alias#(llCmdT, SelfInvLLCmd#(childT, cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: SelfInvDir {exChild: ?, state: I},
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged CRs .r: r.addr;
tagged MRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t LL %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec)
);
if(cmd matches tagged MRs .rs) begin
// MRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/CRs: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (we do not check replacing bit in LLC)
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// cRs must hit, so only cRq cannot enter here
doAssert(cmd matches tagged CRq ._rq ? True : False,
"only cRq can tag match miss"
);
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
doAssert(isValid(repWay), "should always find a way to replace");
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
// update dir
// The exclusive child is downgraded. Since we don't track S, just make
// dir invalid, child field is useless for I
dirT newDir = SelfInvDir {exChild: ?, state: I};
doAssert(oldDir.state >= E && toState <= S, "no more exclusive child");
doAssert(cmd matches tagged CRs .cRs &&& oldDir.exChild == cRs.child ? True : False,
"cRs child should match");
if(!dataV) begin
doAssert(oldDir.state < M, "cRs without data, dir must < M");
end
if(oldDir.state == M) begin
doAssert(dataV, "downgrade from M must have data");
doAssert(oldCs == M, "cs must be M");
end
// update cs
// XXX since child can upgrade from E to M silently, use data valid
// to determine if we need to upgrade to M. Note that the data
// valid field has not been overwritten by bypass in CCPipe.
Msi newCs = oldCs;
if(dataV) begin
doAssert(oldCs >= E, "cRs has data, cs must >= E");
newCs = M;
end
return UpdateByDownDir {cs: newCs, dir: newDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// get first output from CCPipe output
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: LLCRq (rq.mshrIdx);
tagged CRs .rs: LLCRs (rs.child);
tagged MRs .rs: LLMRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endfunction
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged CRs .rs: begin
pipe.enq(CRs (SelfInvLLPipeCRsCmd {
addr: rs.addr,
child: rs.child
}), rs.data, DownDir (rs.toState));
end
tagged MRs .rs: begin
pipe.enq(MRs (SelfInvLLPipeMRsCmd {
addr: rs.addr,
way: rs.way
}), Valid (rs.data), UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
return getFirst(pipe.first); // guarded version
endmethod
method pipeOutT unguard_first;
return getFirst(pipe.unguard_first); // unguarded version
endmethod
method notEmpty = pipe.notEmpty;
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin
newCmd = Valid (CRq (SelfInvLLPipeCRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule