Initial load of files
This commit is contained in:
408
src_Core/RISCY_OOO/coherence/src/CCPipe.bsv
Normal file
408
src_Core/RISCY_OOO/coherence/src/CCPipe.bsv
Normal file
@@ -0,0 +1,408 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import Vector::*;
|
||||
import RWBramCore::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
|
||||
// general type param ordering: way < index < tag < msi < dir < owner < other < rep < line < pipeCmd
|
||||
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
msiT DownDir; // cRs downgraded toState
|
||||
msiT UpCs; // pRs upgraded toState
|
||||
} RespState#(type msiT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
pipeCmdT cmd;
|
||||
// tag match & ram output
|
||||
wayT way;
|
||||
Bool pRqMiss; // pRq miss, valid only if go through tag match
|
||||
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram;
|
||||
// replace info, actually not needed, just output for debug purposes
|
||||
repT repInfo;
|
||||
} PipeOut#(
|
||||
type wayT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CCPipe#(
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
);
|
||||
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, RespState#(msiT) toState);
|
||||
method Bool notFull;
|
||||
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) first;
|
||||
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) unguard_first;
|
||||
method Bool notEmpty;
|
||||
method Action deqWrite(
|
||||
Maybe#(pipeCmdT) newCmd,
|
||||
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) wrRam,
|
||||
Bool updateRep // update replacement info
|
||||
);
|
||||
// empty signal when we need to flush self-invalidate cache
|
||||
method Bool emptyForFlush;
|
||||
endinterface
|
||||
|
||||
// internal pipeline reg types
|
||||
// three stages
|
||||
// 1: enq
|
||||
// 2: tag match, read data and dir
|
||||
// 3: output
|
||||
|
||||
typedef struct {
|
||||
pipeCmdT cmd;
|
||||
// bypasses
|
||||
Vector#(wayNum, Maybe#(CacheInfo#(tagT, msiT, dirT, ownerT, otherT))) infoVec;
|
||||
Maybe#(repT) repInfo; // replacement info for the whole set
|
||||
// CRs/PRs info
|
||||
Maybe#(lineT) respLine;
|
||||
RespState#(msiT) toState;
|
||||
} Enq2Match#(
|
||||
numeric type wayNum,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
pipeCmdT cmd;
|
||||
// tag match results
|
||||
wayT way;
|
||||
Bool pRqMiss;
|
||||
// RAM outputs
|
||||
// cs is merged with PRs toState
|
||||
// dir is merged with CRs toState
|
||||
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
|
||||
repT repInfo;
|
||||
// bypassed or resp line
|
||||
Maybe#(lineT) line;
|
||||
} Match2Out#(
|
||||
type wayT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
indexT index;
|
||||
wayT way;
|
||||
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram; // data to write into RAM
|
||||
repT repInfo; // replacement info write into RAM
|
||||
} BypassInfo#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
wayT way;
|
||||
Bool pRqMiss;
|
||||
} TagMatchResult#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
msiT cs;
|
||||
} UpdateByUpCs#(type msiT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
msiT cs;
|
||||
dirT dir;
|
||||
} UpdateByDownDir#(type msiT, type dirT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// index to data ram: {way, normal index}
|
||||
function dataIndexT getDataRamIndex(wayT w, indexT i) provisos(
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(indexT, Bit#(_indexSz)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(_waySz, _indexSz)))
|
||||
);
|
||||
return {w, i};
|
||||
endfunction
|
||||
|
||||
module mkCCPipe#(
|
||||
ReadOnly#(Bool) initDone,
|
||||
function indexT getIndex(pipeCmdT cmd),
|
||||
function ActionValue#(TagMatchResult#(wayT)) tagMatch(
|
||||
// actionvalue enable us to do checking inside the function
|
||||
pipeCmdT cmd,
|
||||
// below are current RAM outputs, is merged with ram write from final stage
|
||||
// but is NOT merged with state changes carried in PRs/CRs
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, msiT) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
),
|
||||
function ActionValue#(UpdateByUpCs#(msiT)) updateByUpCs(
|
||||
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs
|
||||
),
|
||||
function ActionValue#(UpdateByDownDir#(msiT, dirT)) updateByDownDir(
|
||||
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs, dirT oldDir
|
||||
),
|
||||
function ActionValue#(repT) updateRepInfo(repT oldRep, wayT hitWay),
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam,
|
||||
RWBramCore#(indexT, repT) repRam,
|
||||
RWBramCore#(dataIndexT, lineT) dataRam
|
||||
)(
|
||||
CCPipe#(wayNum, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)
|
||||
) provisos (
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(indexT, Bit#(_indexSz)),
|
||||
Alias#(infoT, CacheInfo#(tagT, msiT, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, msiT, dirT, ownerT, otherT, lineT)),
|
||||
Alias#(respStateT, RespState#(msiT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
|
||||
Alias#(enq2MatchT, Enq2Match#(wayNum, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
|
||||
Alias#(match2OutT, Match2Out#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
|
||||
Alias#(bypassInfoT, BypassInfo#(wayT, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT)),
|
||||
Bits#(tagT, _tagSz),
|
||||
Bits#(msiT, _msiSz),
|
||||
Bits#(dirT, _dirSz),
|
||||
Bits#(ownerT, _ownerSz),
|
||||
Bits#(otherT, _otherSz),
|
||||
Bits#(repT, _repSz),
|
||||
Bits#(lineT, _lineSz),
|
||||
Bits#(pipeCmdT, _pipeCmdSz),
|
||||
// index to data ram: {way, normal index}
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), _indexSz)))
|
||||
);
|
||||
|
||||
// pipeline regs
|
||||
|
||||
Ehr#(3, Maybe#(enq2MatchT)) enq2Mat <- mkEhr(Invalid);
|
||||
// port 0: bypass
|
||||
Reg#(Maybe#(enq2MatchT)) enq2Mat_bypass = enq2Mat[0];
|
||||
// port 1: tag match
|
||||
Reg#(Maybe#(enq2MatchT)) enq2Mat_match = enq2Mat[1];
|
||||
// port 2: enq
|
||||
Reg#(Maybe#(enq2MatchT)) enq2Mat_enq = enq2Mat[2];
|
||||
|
||||
Ehr#(2, Maybe#(match2OutT)) mat2Out <- mkEhr(Invalid);
|
||||
// port 0: out
|
||||
Reg#(Maybe#(match2OutT)) mat2Out_out = mat2Out[0];
|
||||
// port 1: tag match
|
||||
Reg#(Maybe#(match2OutT)) mat2Out_match = mat2Out[1];
|
||||
|
||||
// bypass write to ram
|
||||
RWire#(bypassInfoT) bypass <- mkRWire;
|
||||
|
||||
// stage 2: first get bypass
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule doMatch_bypass(isValid(bypass.wget) && isValid(enq2Mat_bypass) && initDone);
|
||||
bypassInfoT b = fromMaybe(?, bypass.wget);
|
||||
enq2MatchT e2m = fromMaybe(?, enq2Mat_bypass);
|
||||
if(b.index == getIndex(e2m.cmd)) begin
|
||||
e2m.infoVec[b.way] = Valid (b.ram.info);
|
||||
e2m.repInfo = Valid (b.repInfo);
|
||||
end
|
||||
enq2Mat_bypass <= Valid (e2m);
|
||||
endrule
|
||||
|
||||
rule doTagMatch(isValid(enq2Mat_match) && !isValid(mat2Out_match) && initDone);
|
||||
enq2MatchT e2m = fromMaybe(?, enq2Mat_match);
|
||||
// get cache output & merge with bypass
|
||||
Vector#(wayNum, infoT) infoVec;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].deqRdResp;
|
||||
infoVec[i] = fromMaybe(infoRam[i].rdResp, e2m.infoVec[i]);
|
||||
end
|
||||
repRam.deqRdResp;
|
||||
repT repInfo = fromMaybe(repRam.rdResp, e2m.repInfo);
|
||||
// do tag match to get way to occupy
|
||||
Vector#(wayNum, tagT) tagVec;
|
||||
Vector#(wayNum, msiT) csVec;
|
||||
Vector#(wayNum, ownerT) ownerVec;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
tagVec[i] = infoVec[i].tag;
|
||||
csVec[i] = infoVec[i].cs;
|
||||
ownerVec[i] = infoVec[i].owner;
|
||||
end
|
||||
let tmRes <- tagMatch(e2m.cmd, tagVec, csVec, ownerVec, repInfo);
|
||||
wayT way = tmRes.way;
|
||||
Bool pRqMiss = tmRes.pRqMiss;
|
||||
// read data
|
||||
indexT index = getIndex(e2m.cmd);
|
||||
dataRam.rdReq(getDataRamIndex(way, index));
|
||||
// set mat2out & merge with CRs/PRs & merge with data bypass
|
||||
// resp data has higher priority than data bypass
|
||||
match2OutT m2o = Match2Out {
|
||||
cmd: e2m.cmd,
|
||||
way: way,
|
||||
pRqMiss: pRqMiss,
|
||||
info: infoVec[way],
|
||||
repInfo: repInfo,
|
||||
line: e2m.respLine
|
||||
};
|
||||
if(e2m.toState matches tagged UpCs .s) begin
|
||||
UpdateByUpCs#(msiT) upd <- updateByUpCs(
|
||||
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs
|
||||
);
|
||||
m2o.info.cs = upd.cs;
|
||||
end
|
||||
else if(e2m.toState matches tagged DownDir .s) begin
|
||||
UpdateByDownDir#(msiT, dirT) upd <- updateByDownDir(
|
||||
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs, m2o.info.dir
|
||||
);
|
||||
m2o.info.cs = upd.cs;
|
||||
m2o.info.dir = upd.dir;
|
||||
end
|
||||
if(bypass.wget matches tagged Valid .b &&& b.index == index &&& b.way == way &&& !isValid(m2o.line)) begin
|
||||
// bypass has lower priority than resp data
|
||||
m2o.line = Valid (b.ram.line);
|
||||
end
|
||||
mat2Out_match <= Valid (m2o);
|
||||
// reset enq2mat
|
||||
enq2Mat_match <= Invalid;
|
||||
endrule
|
||||
|
||||
// construct output with bypass/resp data
|
||||
function pipeOutT firstOut;
|
||||
match2OutT m2o = fromMaybe(?, mat2Out_out);
|
||||
return PipeOut {
|
||||
cmd: m2o.cmd,
|
||||
way: m2o.way,
|
||||
pRqMiss: m2o.pRqMiss,
|
||||
ram: RamData {
|
||||
info: m2o.info,
|
||||
line: fromMaybe(dataRam.rdResp, m2o.line)
|
||||
},
|
||||
repInfo: m2o.repInfo
|
||||
};
|
||||
endfunction
|
||||
|
||||
Bool enq_guard = !isValid(enq2Mat_enq) && initDone;
|
||||
|
||||
Bool deq_guard = isValid(mat2Out_out) && initDone;
|
||||
|
||||
// stage 1: enq req to pipeline: access info+rep RAM & bypass
|
||||
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, respStateT toState) if(enq_guard);
|
||||
// read ram
|
||||
indexT index = getIndex(cmd);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].rdReq(index);
|
||||
end
|
||||
repRam.rdReq(index);
|
||||
// write reg & get bypass
|
||||
enq2MatchT e2m = Enq2Match {
|
||||
cmd: cmd,
|
||||
infoVec: replicate(Invalid),
|
||||
repInfo: Invalid,
|
||||
respLine: respLine,
|
||||
toState: toState
|
||||
};
|
||||
if(bypass.wget matches tagged Valid .b &&& b.index == index) begin
|
||||
e2m.infoVec[b.way] = Valid (b.ram.info);
|
||||
e2m.repInfo = Valid (b.repInfo);
|
||||
end
|
||||
enq2Mat_enq <= Valid (e2m);
|
||||
endmethod
|
||||
|
||||
method Bool notFull = enq_guard;
|
||||
|
||||
method pipeOutT first if(deq_guard);
|
||||
return firstOut;
|
||||
endmethod
|
||||
|
||||
method pipeOutT unguard_first;
|
||||
return firstOut;
|
||||
endmethod
|
||||
|
||||
method Bool notEmpty = deq_guard;
|
||||
|
||||
method Action deqWrite(Maybe#(pipeCmdT) newCmd, ramDataT wrRam, Bool updateRep) if(deq_guard);
|
||||
match2OutT m2o = fromMaybe(?, mat2Out_out);
|
||||
wayT way = m2o.way;
|
||||
indexT index = getIndex(m2o.cmd);
|
||||
// update replacement info
|
||||
repT repInfo = m2o.repInfo;
|
||||
if(updateRep) begin
|
||||
repInfo <- updateRepInfo(m2o.repInfo, way);
|
||||
end
|
||||
// write ram
|
||||
infoRam[way].wrReq(index, wrRam.info);
|
||||
repRam.wrReq(index, repInfo);
|
||||
dataRam.wrReq(getDataRamIndex(way, index), wrRam.line);
|
||||
// set bypass to Enq and Match stages
|
||||
bypass.wset(BypassInfo {
|
||||
index: index,
|
||||
way: way,
|
||||
ram: wrRam,
|
||||
repInfo: repInfo
|
||||
});
|
||||
// change pipeline reg
|
||||
if(newCmd matches tagged Valid .cmd) begin
|
||||
// update pipeline reg
|
||||
mat2Out_out <= Valid (Match2Out {
|
||||
cmd: cmd, // swapped in new cmd
|
||||
way: way, // keep way same
|
||||
pRqMiss: False, // reset (not valid for swapped in pRq)
|
||||
info: wrRam.info, // get bypass
|
||||
repInfo: repInfo, // get bypass
|
||||
line: Valid (wrRam.line) // get bypass
|
||||
});
|
||||
end
|
||||
else begin
|
||||
// XXX deq ram resp, I think this should not block
|
||||
dataRam.deqRdResp;
|
||||
// reset pipeline reg
|
||||
mat2Out_out <= Invalid;
|
||||
end
|
||||
endmethod
|
||||
|
||||
method Bool emptyForFlush;
|
||||
return !isValid(mat2Out[0]) && !isValid(enq2Mat[0]);
|
||||
endmethod
|
||||
endmodule
|
||||
464
src_Core/RISCY_OOO/coherence/src/CCTypes.bsv
Normal file
464
src_Core/RISCY_OOO/coherence/src/CCTypes.bsv
Normal file
@@ -0,0 +1,464 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*; // import from RISCY repo
|
||||
import MemoryTypes::*; // import from RISCY repo
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import CacheUtils::*;
|
||||
import Assert::*;
|
||||
import Connectable::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
|
||||
typedef enum {
|
||||
I = 2'd0,
|
||||
S = 2'd1,
|
||||
E = 2'd2,
|
||||
M = 2'd3
|
||||
} MESI deriving(Bits, Eq, FShow);
|
||||
typedef MESI Msi;
|
||||
|
||||
instance Ord#(MESI);
|
||||
function Bool \< ( MESI x, MESI y );
|
||||
return pack(x) < pack(y);
|
||||
endfunction
|
||||
function Bool \<= ( MESI x, MESI y );
|
||||
return pack(x) <= pack(y);
|
||||
endfunction
|
||||
function Bool \> ( MESI x, MESI y );
|
||||
return pack(x) > pack(y);
|
||||
endfunction
|
||||
function Bool \>= ( MESI x, MESI y );
|
||||
return pack(x) >= pack(y);
|
||||
endfunction
|
||||
function Ordering compare( MESI x, MESI y );
|
||||
return compare(pack(x), pack(y));
|
||||
endfunction
|
||||
function MESI min( MESI x, MESI y );
|
||||
return x < y ? x : y;
|
||||
endfunction
|
||||
function MESI max( MESI x, MESI y );
|
||||
return x > y ? x : y;
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
// whether cache state is enough to serice upgrade req, i.e., no
|
||||
// need to req parent
|
||||
function Bool enoughCacheState(Msi cs, Msi to);
|
||||
return cs >= to || cs >= E;
|
||||
endfunction
|
||||
|
||||
// the maximum dir state that a peer can have so that it will not
|
||||
// be downgraded to service upgrade req to state x
|
||||
function Msi toCompat(Msi x);
|
||||
return x == S ? S : I;
|
||||
endfunction
|
||||
|
||||
typedef TDiv#(DataSz, 8) DataSzBytes;
|
||||
typedef TLog#(DataSzBytes) LgDataSzBytes;
|
||||
typedef Bit#(LgDataSzBytes) DataBytesOffset;
|
||||
|
||||
typedef TDiv#(InstSz, 8) InstSzBytes;
|
||||
typedef TLog#(InstSzBytes) LgInstSzBytes;
|
||||
|
||||
// 64B cache line -- XXX same with parameters in CacheUtils.bsv
|
||||
typedef CacheUtils::LogCLineNumData LgLineSzData;
|
||||
typedef CacheUtils::LogCLineNumBytes LgLineSzBytes;
|
||||
typedef CacheUtils::CLineAddrSz LineAddrSz;
|
||||
typedef CacheUtils::CLineAddr LineAddr;
|
||||
|
||||
typedef CacheUtils::CLineNumData LineSzData;
|
||||
typedef CacheUtils::CLineDataSel LineDataOffset;
|
||||
|
||||
typedef CacheUtils::CLineNumBytes LineSzBytes;
|
||||
typedef CacheUtils::CLineByteEn LineByteEn;
|
||||
|
||||
typedef TDiv#(LineSzBytes, InstSzBytes) LineSzInst;
|
||||
typedef Bit#(TLog#(LineSzInst)) LineInstOffset;
|
||||
|
||||
typedef Vector#(LineSzData, Data) Line;
|
||||
|
||||
function LineAddr getLineAddr(Addr addr) = truncateLSB(addr);
|
||||
|
||||
function LineDataOffset getLineDataOffset(Addr a);
|
||||
return truncate(a >> valueOf(LgDataSzBytes));
|
||||
endfunction
|
||||
|
||||
function LineInstOffset getLineInstOffset(Addr a);
|
||||
return truncate(a >> valueof(LgInstSzBytes));
|
||||
endfunction
|
||||
|
||||
function Line getUpdatedLine(Line curLine, LineByteEn wrBE, Line wrLine);
|
||||
Vector#(LineSzBytes, Bit#(8)) curVec = unpack(pack(curLine));
|
||||
Vector#(LineSzBytes, Bit#(8)) wrVec = unpack(pack(wrLine));
|
||||
function Bit#(8) getNewByte(Integer i);
|
||||
return wrBE[i] ? wrVec[i] : curVec[i];
|
||||
endfunction
|
||||
Vector#(LineSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
|
||||
return unpack(pack(newVec));
|
||||
endfunction
|
||||
|
||||
function Data getUpdatedData(Data curData, ByteEn wrBE, Data wrData);
|
||||
Vector#(DataSzBytes, Bit#(8)) curVec = unpack(pack(curData));
|
||||
Vector#(DataSzBytes, Bit#(8)) wrVec = unpack(pack(wrData));
|
||||
function Bit#(8) getNewByte(Integer i);
|
||||
return wrBE[i] ? wrVec[i] : curVec[i];
|
||||
endfunction
|
||||
Vector#(DataSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
|
||||
return pack(newVec);
|
||||
endfunction
|
||||
|
||||
// calculate tag
|
||||
typedef TSub#(AddrSz, TAdd#(LgLineSzBytes, TAdd#(lgBankNum, lgSetNum)))
|
||||
GetTagSz#(numeric type lgBankNum, numeric type lgSetNum);
|
||||
|
||||
// dependency tracking in MSHR
|
||||
typedef union tagged {
|
||||
cRqIdxT CRq;
|
||||
pRqIdxT PRq;
|
||||
} MshrIndex#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// cache owner
|
||||
typedef struct {
|
||||
cRqIdxT mshrIdx;
|
||||
Bool replacing;
|
||||
} CRqOwner#(type cRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
pRqIdxT mshrIdx;
|
||||
Bool hasSucc; // has successor in dependency chain
|
||||
} PRqOwner#(type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
CRqOwner#(cRqIdxT) CRq;
|
||||
PRqOwner#(pRqIdxT) PRq;
|
||||
} CacheOwner#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// cache info: tag, cs, dir, owner, and other
|
||||
typedef struct {
|
||||
tagT tag;
|
||||
msiT cs;
|
||||
dirT dir;
|
||||
ownerT owner;
|
||||
otherT other;
|
||||
} CacheInfo#(
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
// ram output
|
||||
typedef struct {
|
||||
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
|
||||
lineT line;
|
||||
} RamData#(
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type lineT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
// processor req/resp
|
||||
typedef struct {
|
||||
idT id;
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
// below are detailed mem op
|
||||
MemOp op; // Ld, St, Lr, Sc, Amo
|
||||
ByteEn byteEn; // valid when op == Sc
|
||||
Data data; // valid when op == Sc/Amo
|
||||
AmoInst amoInst; // valid when op == Amo
|
||||
} ProcRq#(type idT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface L1ProcReq#(type idT);
|
||||
method Action req(ProcRq#(idT) r);
|
||||
endinterface
|
||||
|
||||
interface L1ProcResp#(type idT);
|
||||
method Action respLd(idT id, Data resp);
|
||||
method Action respLrScAmo(idT id, Data resp);
|
||||
method ActionValue#(Tuple2#(LineByteEn, Line)) respSt(idT id);
|
||||
method Action evict(LineAddr a); // called when cache line is evicted
|
||||
endinterface
|
||||
|
||||
// RISCV-specific store-cond return values
|
||||
typedef 0 ScSuccVal;
|
||||
typedef 1 ScFailVal;
|
||||
|
||||
`ifdef DEBUG_ICACHE
|
||||
typedef struct {
|
||||
Bit#(64) id;
|
||||
Line line;
|
||||
} DebugICacheResp deriving(Bits, Eq, FShow);
|
||||
`endif
|
||||
|
||||
// I$ req/resp
|
||||
interface InstServer#(numeric type supSz);
|
||||
interface Put#(Addr) req;
|
||||
interface Get#(Vector#(supSz, Maybe#(Instruction))) resp;
|
||||
`ifdef DEBUG_ICACHE
|
||||
interface Get#(DebugICacheResp) done; // the id and cache line of the I$ req that truly performs
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
`ifdef DEBUG_ICACHE
|
||||
Bit#(64) id; // incremening id for each incoming I$ req (0,1,...)
|
||||
`endif
|
||||
} ProcRqToI deriving(Bits, Eq, FShow);
|
||||
|
||||
// child/parent req/resp
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi fromState;
|
||||
Msi toState;
|
||||
Bool canUpToE; // meaningful to upgrade to E if toState is S
|
||||
idT id; // slot id in child cache
|
||||
childT child; // from which child
|
||||
} CRqMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
childT child; // from which child
|
||||
} CRsMsg#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
childT child; // to which child
|
||||
} PRqMsg#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
childT child; // to which child
|
||||
Maybe#(Line) data;
|
||||
idT id; // slot id in cache
|
||||
} PRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
PRqMsg#(childT) PRq;
|
||||
PRsMsg#(idT, childT) PRs;
|
||||
} PRqRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface ChildCacheToParent#(type cRqIdT, type childT);
|
||||
interface FifoDeq#(CRsMsg#(childT)) rsToP;
|
||||
interface FifoDeq#(CRqMsg#(cRqIdT, childT)) rqToP;
|
||||
interface FifoEnq#(PRqRsMsg#(cRqIdT, childT)) fromP;
|
||||
endinterface
|
||||
|
||||
interface ParentCacheToChild#(type cRqIdT, type childT);
|
||||
interface FifoEnq#(CRsMsg#(childT)) rsFromC;
|
||||
interface FifoEnq#(CRqMsg#(cRqIdT, childT)) rqFromC;
|
||||
interface FifoDeq#(PRqRsMsg#(cRqIdT, childT)) toC;
|
||||
endinterface
|
||||
|
||||
// unified child req & dma req
|
||||
typedef union tagged {
|
||||
cRqIdT Child;
|
||||
dmaRqIdT Dma;
|
||||
} LLRqId#(type cRqIdT, type dmaRqIdT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// common addr
|
||||
Addr addr;
|
||||
// child req stuff
|
||||
Msi fromState;
|
||||
Msi toState;
|
||||
Bool canUpToE;
|
||||
childT child;
|
||||
// dma req stuff
|
||||
LineByteEn byteEn;
|
||||
// req id: distinguish between child and dma
|
||||
LLRqId#(cRqIdT, dmaRqIdT) id;
|
||||
} LLRq#(type cRqIdT, type dmaRqIdT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// memory msg
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
childT child; // from which LLC/Dir
|
||||
idT id; // ld req id and other info need encoding
|
||||
} LdMemRq#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct { // LdMemRq id with more info encoded to handle DMA req in LLC
|
||||
Bool refill; // the future mem resp will refill LLC cache line
|
||||
// this is False for DMA read req that miss in LLC (i.e. resp won't refill LLC)
|
||||
mshrIdxT mshrIdx; // mshr id
|
||||
} LdMemRqId#(type mshrIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
LineByteEn byteEn;
|
||||
Line data;
|
||||
} WbMemRs deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
LdMemRq#(idT, childT) Ld;
|
||||
WbMemRs Wb;
|
||||
} ToMemMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Line data;
|
||||
childT child; // send to which LLC/Dir
|
||||
idT id; // original Ld req id
|
||||
} MemRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// Dma req/resp
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
LineByteEn byteEn; // all False means read
|
||||
Line data;
|
||||
idT id; // req id (resp may come out of order, may contain routing info)
|
||||
} DmaRq#(type idT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Line data; // meaningless for write
|
||||
idT id;
|
||||
} DmaRs#(type idT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface DmaServer#(type dmaRqIdT);
|
||||
interface FifoEnq#(DmaRq#(dmaRqIdT)) memReq;
|
||||
interface FifoDeq#(DmaRs#(dmaRqIdT)) respLd;
|
||||
interface FifoDeq#(dmaRqIdT) respSt;
|
||||
`ifdef DEBUG_DMA
|
||||
// signal when DMA req really takes effect
|
||||
interface Get#(dmaRqIdT) wrMissResp;
|
||||
interface Get#(dmaRqIdT) wrHitResp;
|
||||
interface Get#(dmaRqIdT) rdMissResp;
|
||||
interface Get#(dmaRqIdT) rdHitResp;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// memory interface
|
||||
interface MemFifoServer#(type idT, type childT);
|
||||
interface FifoEnq#(ToMemMsg#(idT, childT)) fromC;
|
||||
interface FifoDeq#(MemRsMsg#(idT, childT)) rsToC;
|
||||
endinterface
|
||||
|
||||
interface MemFifoClient#(type idT, type childT);
|
||||
interface FifoDeq#(ToMemMsg#(idT, childT)) toM;
|
||||
interface FifoEnq#(MemRsMsg#(idT, childT)) rsFromM;
|
||||
endinterface
|
||||
|
||||
instance Connectable#(MemFifoServer#(idT, childT), MemFifoClient#(idT, childT));
|
||||
module mkConnection#(
|
||||
MemFifoServer#(idT, childT) server,
|
||||
MemFifoClient#(idT, childT) client
|
||||
)(Empty);
|
||||
rule doCToM;
|
||||
client.toM.deq;
|
||||
server.fromC.enq(client.toM.first);
|
||||
endrule
|
||||
rule doMToC;
|
||||
server.rsToC.deq;
|
||||
client.rsFromM.enq(server.rsToC.first);
|
||||
endrule
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
instance Connectable#(MemFifoClient#(idT, childT), MemFifoServer#(idT, childT));
|
||||
module mkConnection#(
|
||||
MemFifoClient#(idT, childT) client,
|
||||
MemFifoServer#(idT, childT) server
|
||||
)(Empty);
|
||||
mkConnection(server, client);
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
// MSHR dir pending bits
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
Msi ToSend; // need to send down req to downgrade to some state
|
||||
Msi Waiting; // waiting for down resp for some state
|
||||
} DirPend deriving(Bits, Eq, FShow);
|
||||
|
||||
function Vector#(childNum, DirPend) getDirPendInitVal;
|
||||
return replicate(Invalid);
|
||||
endfunction
|
||||
|
||||
function Bool getNeedReqChild(Vector#(childNum, DirPend) dirPend);
|
||||
// function to determine whether we need to send req to some children
|
||||
function Bool isToSend(DirPend dp);
|
||||
if(dp matches tagged ToSend .s) begin
|
||||
return True;
|
||||
end
|
||||
else begin
|
||||
return False;
|
||||
end
|
||||
endfunction
|
||||
return any(isToSend, dirPend);
|
||||
endfunction
|
||||
|
||||
// Self-inv cache dir pending bits
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
childT ToSend;
|
||||
childT Waiting;
|
||||
} SelfInvDirPend#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
function SelfInvDirPend#(childT) getSelfInvDirPendInitVal;
|
||||
return Invalid;
|
||||
endfunction
|
||||
|
||||
function Bool getSelfInvNeedReqChild(SelfInvDirPend#(childT) dirPend);
|
||||
return dirPend matches tagged ToSend .c ? True : False;
|
||||
endfunction
|
||||
|
||||
// useful functions
|
||||
function Action check(Bool v);
|
||||
action
|
||||
when(v, noAction);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Maybe#(Bit#(logv)) searchIndex
|
||||
(function Bool pred(element_type x1), Vector#(vsize, element_type) vect)
|
||||
provisos (Log#(vsize, logv));
|
||||
return case (findIndex(pred, vect)) matches
|
||||
tagged Valid .idx: return tagged Valid pack(idx);
|
||||
Invalid: return tagged Invalid;
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
function a readReg(Reg#(a) r) provisos(Bits#(a, aSz)) = r;
|
||||
function Vector#(n, a) readVector(Vector#(n, Reg#(a)) vr) provisos(Bits#(a, aSz)) = map(readReg, vr);
|
||||
|
||||
// doAssert now defined in Types.bsv
|
||||
//function Action doAssert(Bool b, String str) = dynamicAssert(b, "%m: " + str);
|
||||
|
||||
function Get#(t) nullGet;
|
||||
return (interface Get;
|
||||
method ActionValue#(t) get if(False);
|
||||
return ?;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
215
src_Core/RISCY_OOO/coherence/src/CrossBar.bsv
Normal file
215
src_Core/RISCY_OOO/coherence/src/CrossBar.bsv
Normal file
@@ -0,0 +1,215 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import Connectable::*;
|
||||
import CacheUtils::*;
|
||||
import CCTypes::*;
|
||||
import Types::*;
|
||||
import FShow::*;
|
||||
import Fifo::*;
|
||||
import Ehr::*;
|
||||
|
||||
typedef struct {
|
||||
dstIdxT idx;
|
||||
dstDataT data;
|
||||
} XBarDstInfo#(type dstIdxT, type dstDataT) deriving(Bits, Eq);
|
||||
|
||||
module mkXBar#(
|
||||
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
|
||||
Vector#(srcNum, Get#(srcDataT)) srcIfc,
|
||||
Vector#(dstNum, Put#(dstDataT)) dstIfc
|
||||
)(Empty) provisos(
|
||||
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
|
||||
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
|
||||
Bits#(srcDataT, _srcDataSz),
|
||||
Bits#(dstDataT, _dstDataSz),
|
||||
FShow#(dstDataT)
|
||||
);
|
||||
// proposed data transfer by each src
|
||||
Vector#(srcNum, Ehr#(2, Maybe#(dstIdxT))) propDstIdx <- replicateM(mkEhr(Invalid));
|
||||
Vector#(srcNum, Ehr#(2, dstDataT)) propDstData <- replicateM(mkEhr(?));
|
||||
// enq command that should be carried out
|
||||
Vector#(dstNum, Ehr#(2, Maybe#(dstDataT))) enqDst <- replicateM(mkEhr(Invalid));
|
||||
|
||||
// src propose data transfer when src is not empty
|
||||
// and there is no unfinished src proposal
|
||||
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
|
||||
rule srcPropose(!isValid(propDstIdx[i][0]));
|
||||
let d <- srcIfc[i].get;
|
||||
let info = getDstInfo(fromInteger(i), d);
|
||||
propDstIdx[i][0] <= Valid (info.idx);
|
||||
propDstData[i][0] <= info.data;
|
||||
endrule
|
||||
end
|
||||
|
||||
// round-robin select src for each dst
|
||||
Vector#(dstNum, Reg#(srcIdxT)) srcRR <- replicateM(mkReg(0));
|
||||
|
||||
// do selection for each dst & generate enq commands & deq src proposals
|
||||
// dst which has unfinished enq command cannot select src
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule dstSelectSrc;
|
||||
// which src to deq (i.e. select) by each dst
|
||||
Vector#(dstNum, Maybe#(srcIdxT)) deqSrcByDst = replicate(Invalid);
|
||||
// each dst selects
|
||||
for(Integer dst = 0; dst < valueOf(dstNum); dst = dst + 1) begin
|
||||
// only select src to deq when dst has no unfinished enq command
|
||||
if(!isValid(enqDst[dst][0])) begin
|
||||
function Bool isFromSrc(srcIdxT src);
|
||||
// src has proposed data to this dst or not
|
||||
if(propDstIdx[src][1] matches tagged Valid .dstIdx &&& dstIdx == fromInteger(dst)) begin
|
||||
return True;
|
||||
end
|
||||
else begin
|
||||
return False;
|
||||
end
|
||||
endfunction
|
||||
Maybe#(srcIdxT) whichSrc = Invalid; // the src to select
|
||||
if(isFromSrc(srcRR[dst])) begin
|
||||
// first check the src with priority
|
||||
whichSrc = Valid (srcRR[dst]);
|
||||
end
|
||||
else begin
|
||||
// otherwise just get one valid src
|
||||
Vector#(srcNum, srcIdxT) srcIdxVec = genWith(fromInteger);
|
||||
whichSrc = searchIndex(isFromSrc, srcIdxVec);
|
||||
end
|
||||
if(whichSrc matches tagged Valid .src) begin
|
||||
// can do enq & deq
|
||||
deqSrcByDst[dst] = whichSrc;
|
||||
enqDst[dst][0] <= Valid (propDstData[src][1]); // set enq command
|
||||
// change round robin
|
||||
srcRR[dst] <= srcRR[dst] == fromInteger(valueOf(srcNum) - 1) ? 0 : srcRR[dst] + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// deq selected src
|
||||
function Bool isDeqSrc(srcIdxT src);
|
||||
function Bool isMatch(Maybe#(srcIdxT) deqIdx);
|
||||
return deqIdx matches tagged Valid .idx &&& idx == src ? True : False;
|
||||
endfunction
|
||||
return any(isMatch, deqSrcByDst);
|
||||
endfunction
|
||||
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
|
||||
if(isDeqSrc(fromInteger(i))) begin
|
||||
propDstIdx[i][1] <= Invalid;
|
||||
$display("%t XBar %m: deq src %d", $time, i);
|
||||
doAssert(isValid(propDstIdx[i][1]), "src must be proposing");
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
// enq dst & change round robin
|
||||
for(Integer i = 0; i < valueOf(dstNum); i = i+1) begin
|
||||
// XXX since dstIfc.put may conflict with other rules
|
||||
// the following rule should only fire when it can make progress
|
||||
// otherwise it may prevent other rules from firing forever
|
||||
rule doEnq(enqDst[i][1] matches tagged Valid .d);
|
||||
dstIfc[i].put(d);
|
||||
enqDst[i][1] <= Invalid; // reset enq command
|
||||
$display("%t XBAR %m: enq dst %d ; ", $time, i, fshow(d));
|
||||
endrule
|
||||
end
|
||||
endmodule
|
||||
|
||||
// XBar with latency added at src or dst (mainly for model timing)
|
||||
interface XBarDelay#(numeric type srcLat, numeric type dstLat);
|
||||
endinterface
|
||||
|
||||
module mkXBarDelay#(
|
||||
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
|
||||
Vector#(srcNum, Get#(srcDataT)) srcIfc,
|
||||
Vector#(dstNum, Put#(dstDataT)) dstIfc
|
||||
)(XBarDelay#(srcLat, dstLat)) provisos(
|
||||
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
|
||||
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
|
||||
Bits#(srcDataT, _srcDataSz),
|
||||
Bits#(dstDataT, _dstDataSz),
|
||||
FShow#(dstDataT)
|
||||
);
|
||||
// Add latency at src side
|
||||
Vector#(srcNum, Get#(srcDataT)) src = srcIfc;
|
||||
if(valueof(srcLat) > 0) begin
|
||||
for(Integer i = 0; i < valueof(srcNum); i = i+1) begin
|
||||
Vector#(srcLat, Fifo#(2, srcDataT)) delayQ <- replicateM(mkCFFifo);
|
||||
mkConnection(srcIfc[i], toPut(delayQ[0]));
|
||||
for(Integer j = 0; j < valueof(srcLat) - 1; j = j+1) begin
|
||||
mkConnection(toGet(delayQ[j]), toPut(delayQ[j + 1]));
|
||||
end
|
||||
src[i] = toGet(delayQ[valueof(srcLat) - 1]);
|
||||
end
|
||||
end
|
||||
|
||||
// Add latency at dst side
|
||||
Vector#(dstNum, Put#(dstDataT)) dst = dstIfc;
|
||||
if(valueof(dstLat) > 0) begin
|
||||
for(Integer i = 0; i < valueof(dstNum); i = i+1) begin
|
||||
Vector#(dstLat, Fifo#(2, dstDataT)) delayQ <- replicateM(mkCFFifo);
|
||||
mkConnection(toGet(delayQ[0]), dstIfc[i]);
|
||||
for(Integer j = 0; j < valueof(dstLat) - 1; j = j+1) begin
|
||||
mkConnection(toGet(delayQ[j + 1]), toPut(delayQ[j]));
|
||||
end
|
||||
dst[i] = toPut(delayQ[valueof(dstLat) - 1]);
|
||||
end
|
||||
end
|
||||
|
||||
mkXBar(getDstInfo, src, dst);
|
||||
endmodule
|
||||
|
||||
interface CrossBar#(
|
||||
numeric type srcNum,
|
||||
numeric type srcLat,
|
||||
type srcDataT,
|
||||
numeric type dstNum,
|
||||
numeric type dstLat,
|
||||
type dstDataT
|
||||
);
|
||||
interface Vector#(srcNum, FifoEnq#(srcDataT)) srcIfc;
|
||||
interface Vector#(dstNum, FifoDeq#(dstDataT)) dstIfc;
|
||||
endinterface
|
||||
|
||||
module mkCrossBar#(
|
||||
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data)
|
||||
)(
|
||||
CrossBar#(srcNum, srcLat, srcDataT, dstNum, dstLat, dstDataT)
|
||||
) provisos(
|
||||
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
|
||||
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
|
||||
Bits#(srcDataT, _srcDataSz),
|
||||
Bits#(dstDataT, _dstDataSz),
|
||||
FShow#(dstDataT)
|
||||
);
|
||||
|
||||
// in/out FIFOs
|
||||
Vector#(srcNum, Fifo#(2, srcDataT)) srcQ <- replicateM(mkCFFifo);
|
||||
Vector#(dstNum, Fifo#(2, dstDataT)) dstQ <- replicateM(mkCFFifo);
|
||||
|
||||
XBarDelay#(srcLat, dstLat) xbar <- mkXBarDelay(getDstInfo, map(toGet, srcQ), map(toPut, dstQ));
|
||||
|
||||
interface srcIfc = map(toFifoEnq, srcQ);
|
||||
interface dstIfc = map(toFifoDeq, dstQ);
|
||||
endmodule
|
||||
|
||||
849
src_Core/RISCY_OOO/coherence/src/IBank.bsv
Normal file
849
src_Core/RISCY_OOO/coherence/src/IBank.bsv
Normal file
@@ -0,0 +1,849 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Types::*;
|
||||
import MemoryTypes::*;
|
||||
import Amo::*;
|
||||
|
||||
import Cntrs::*;
|
||||
import Vector::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import CCTypes::*;
|
||||
import ICRqMshr::*;
|
||||
import IPRqMshr::*;
|
||||
import CCPipe::*;
|
||||
import L1Pipe ::*;
|
||||
import FShow::*;
|
||||
import DefaultValue::*;
|
||||
import Fifo::*;
|
||||
import CacheUtils::*;
|
||||
import Performance::*;
|
||||
import LatencyTimer::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export ICRqStuck(..);
|
||||
export IPRqStuck(..);
|
||||
export IBank(..);
|
||||
export mkIBank;
|
||||
|
||||
// L1 I$
|
||||
|
||||
// although pRq never appears in dependency chain
|
||||
// we still need pRq MSHR to limit the number of pRq
|
||||
// and thus limit the size of rsToPIndexQ
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
ICRqState state;
|
||||
Bool waitP;
|
||||
} ICRqStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef IPRqMshrStuck IPRqStuck;
|
||||
|
||||
interface IBank#(
|
||||
numeric type supSz, // superscalar size
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
numeric type indexSz,
|
||||
numeric type tagSz,
|
||||
numeric type cRqNum,
|
||||
numeric type pRqNum
|
||||
);
|
||||
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
|
||||
interface InstServer#(supSz) to_proc; // to child, i.e. processor
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(ICRqStuck) cRqStuck;
|
||||
interface Get#(IPRqStuck) pRqStuck;
|
||||
// security: flush
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
// performance
|
||||
method Action setPerfStatus(Bool stats);
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
endinterface
|
||||
|
||||
module mkIBank#(
|
||||
Bit#(lgBankNum) bankId,
|
||||
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
|
||||
module#(IPRqMshr#(pRqNum)) mkIPRqMshrLocal,
|
||||
module#(L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)) mkL1Pipeline
|
||||
)(
|
||||
IBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum, pRqNum)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
|
||||
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
|
||||
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, void)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, void, Line)),
|
||||
Alias#(procRqT, ProcRqToI),
|
||||
Alias#(cRqToPT, CRqMsg#(wayT, void)),
|
||||
Alias#(cRsToPT, CRsMsg#(void)),
|
||||
Alias#(pRqFromPT, PRqMsg#(void)),
|
||||
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
|
||||
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
|
||||
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
|
||||
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, void, RandRepInfo, Line, l1CmdT)),
|
||||
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
|
||||
// requirements
|
||||
FShow#(pipeOutT),
|
||||
Add#(tagSz, a__, AddrSz),
|
||||
// make sure: cRqNum <= wayNum
|
||||
Add#(cRqNum, b__, wayNum),
|
||||
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
|
||||
);
|
||||
|
||||
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
|
||||
|
||||
IPRqMshr#(pRqNum) pRqMshr <- mkIPRqMshrLocal;
|
||||
|
||||
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT) pipeline <- mkL1Pipeline;
|
||||
|
||||
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
|
||||
|
||||
Fifo#(2, cRsToPT) rsToPQ <- mkCFFifo;
|
||||
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
|
||||
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
|
||||
|
||||
FIFO#(MshrIndex#(cRqIdxT, pRqIdxT)) rsToPIndexQ <- mkSizedFIFO(valueOf(TAdd#(cRqNum, pRqNum)));
|
||||
|
||||
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
// temp fifo for pipelineResp & sendRsToP (reduce conflict)
|
||||
FIFO#(cRqIdxT) rqToPIndexQ_pipelineResp <- mkFIFO;
|
||||
FIFO#(cRqIdxT) rqToPIndexQ_sendRsToP <- mkFIFO;
|
||||
|
||||
// index Q to order all in flight cRq for in-order resp
|
||||
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
|
||||
|
||||
`ifdef DEBUG_ICACHE
|
||||
// id for each cRq, incremented when each new req comes
|
||||
Reg#(Bit#(64)) cRqId <- mkReg(0);
|
||||
// FIFO to signal the id of cRq that is performed
|
||||
// FIFO has 0 cycle latency to match L1 D$ resp latency
|
||||
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
|
||||
`endif
|
||||
|
||||
// security flush
|
||||
`ifdef SECURITY
|
||||
Reg#(Bool) flushDone <- mkReg(True);
|
||||
Reg#(Bool) flushReqStart <- mkReg(False);
|
||||
Reg#(Bool) flushReqDone <- mkReg(False);
|
||||
Reg#(Bool) flushRespDone <- mkReg(False);
|
||||
Reg#(indexT) flushIndex <- mkReg(0);
|
||||
Reg#(wayT) flushWay <- mkReg(0);
|
||||
`else
|
||||
Bool flushDone = True;
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
Count#(Data) ldCnt <- mkCount(0);
|
||||
Count#(Data) ldMissCnt <- mkCount(0);
|
||||
Count#(Data) ldMissLat <- mkCount(0);
|
||||
|
||||
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
|
||||
|
||||
function Action incrReqCnt;
|
||||
action
|
||||
if(doStats) begin
|
||||
ldCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action incrMissCnt(cRqIdxT idx);
|
||||
action
|
||||
let lat <- latTimer.done(idx);
|
||||
if(doStats) begin
|
||||
ldMissLat.incr(zeroExtend(lat));
|
||||
ldMissCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
// XXX since I$ may be requested by processor constantly
|
||||
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
|
||||
// otherwise the whole system may deadlock/livelock
|
||||
// we stop accepting cRq when we need to flush for security
|
||||
rule cRqTransfer(flushDone);
|
||||
Addr addr <- toGet(rqFromCQ).get;
|
||||
`ifdef DEBUG_ICACHE
|
||||
procRqT r = ProcRqToI {addr: addr, id: cRqId};
|
||||
cRqId <= cRqId + 1;
|
||||
`else
|
||||
procRqT r = ProcRqToI {addr: addr};
|
||||
`endif
|
||||
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
|
||||
// send to pipeline
|
||||
pipeline.send(CRq (L1PipeRqIn {
|
||||
addr: r.addr,
|
||||
mshrIdx: n
|
||||
}));
|
||||
// enq to indexQ for in order resp
|
||||
cRqIndexQ.enq(n);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: cRq type
|
||||
incrReqCnt;
|
||||
`endif
|
||||
$display("%t I %m cRqTransfer: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(r)
|
||||
);
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
|
||||
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
|
||||
fromPQ.deq;
|
||||
pRqIdxT n <- pRqMshr.getEmptyEntryInit(req);
|
||||
// send to pipeline
|
||||
pipeline.send(PRq (L1PipeRqIn {
|
||||
addr: req.addr,
|
||||
mshrIdx: n
|
||||
}));
|
||||
$display("%t I %m pRqTransfer: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req)
|
||||
);
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
|
||||
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
|
||||
fromPQ.deq;
|
||||
pipeline.send(PRs (L1PipePRsIn {
|
||||
addr: resp.addr,
|
||||
toState: S,
|
||||
data: resp.data,
|
||||
way: resp.id
|
||||
}));
|
||||
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
|
||||
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
// start flush when cRq MSHR is empty
|
||||
rule startFlushReq(!flushDone && !flushReqStart && cRqMshr.emptyForFlush);
|
||||
flushReqStart <= True;
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "pRsTransfer, flushTransfer" *)
|
||||
(* descending_urgency = "pRqTransfer, flushTransfer" *)
|
||||
rule flushTransfer(!flushDone && flushReqStart && !flushReqDone);
|
||||
// We allocate a pRq MSHR entry for 2 reasons:
|
||||
// (1) reuse the pRq logic to send resp to parent
|
||||
// (2) control the number of downgrade resp to avoid stalling the cache
|
||||
// pipeline
|
||||
pRqIdxT n <- pRqMshr.getEmptyEntryInit(PRqMsg {
|
||||
addr: ?,
|
||||
toState: I,
|
||||
child: ?
|
||||
});
|
||||
pipeline.send(Flush (L1PipeFlushIn {
|
||||
index: flushIndex,
|
||||
way: flushWay,
|
||||
mshrIdx: n
|
||||
}));
|
||||
// increment flush index/way
|
||||
if (flushWay < fromInteger(valueof(wayNum) - 1)) begin
|
||||
flushWay <= flushWay + 1;
|
||||
end
|
||||
else begin
|
||||
flushWay <= 0;
|
||||
flushIndex <= flushIndex + 1; // index num should be power of 2
|
||||
if (flushIndex == maxBound) begin
|
||||
flushReqDone <= True;
|
||||
end
|
||||
end
|
||||
$display("%t I %m flushTransfer: ", $time, fshow(n), " ; ",
|
||||
fshow(flushIndex), " ; ", fshow(flushWay));
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule sendRsToP_cRq(rsToPIndexQ.first matches tagged CRq .n);
|
||||
rsToPIndexQ.deq;
|
||||
// get cRq replacement info
|
||||
procRqT req = cRqMshr.sendRsToP_cRq.getRq(n);
|
||||
cRqSlotT slot = cRqMshr.sendRsToP_cRq.getSlot(n);
|
||||
// send resp to parent
|
||||
cRsToPT resp = CRsMsg {
|
||||
addr: {slot.repTag, truncate(req.addr)}, // get bank id & index from req
|
||||
toState: I,
|
||||
data: Invalid, // I$ never downgrade with data to writeback
|
||||
child: ?
|
||||
};
|
||||
rsToPQ.enq(resp);
|
||||
// req parent for upgrade now
|
||||
// (prevent parent resp from coming to release MSHR entry before replace resp is sent)
|
||||
rqToPIndexQ_sendRsToP.enq(n);
|
||||
$display("%t I %m sendRsToP: ", $time,
|
||||
fshow(rsToPIndexQ.first)," ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(slot), " ; ",
|
||||
fshow(resp)
|
||||
);
|
||||
endrule
|
||||
|
||||
rule sendRsToP_pRq(rsToPIndexQ.first matches tagged PRq .n);
|
||||
rsToPIndexQ.deq;
|
||||
// get pRq info & send resp & release MSHR entry
|
||||
pRqFromPT req = pRqMshr.sendRsToP_pRq.getRq(n);
|
||||
cRsToPT resp = CRsMsg {
|
||||
addr: req.addr,
|
||||
toState: I, // I$ must downgrade to I
|
||||
data: Invalid, // I$ never downgrade with data to writeback
|
||||
child: ?
|
||||
};
|
||||
rsToPQ.enq(resp);
|
||||
pRqMshr.sendRsToP_pRq.releaseEntry(n); // mshr entry released
|
||||
$display("%t I %m sendRsToP: ", $time,
|
||||
fshow(rsToPIndexQ.first), " ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(resp)
|
||||
);
|
||||
doAssert(req.toState == I, "I$ only has downgrade req to I");
|
||||
endrule
|
||||
|
||||
rule sendRqToP;
|
||||
rqToPIndexQ.deq;
|
||||
cRqIdxT n = rqToPIndexQ.first;
|
||||
procRqT req = cRqMshr.sendRqToP.getRq(n);
|
||||
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
|
||||
cRqToPT cRqToP = CRqMsg {
|
||||
addr: req.addr,
|
||||
fromState: I, // I$ upgrade from I
|
||||
toState: S, // I$ upgrade to S
|
||||
canUpToE: False,
|
||||
id: slot.way,
|
||||
child: ?
|
||||
};
|
||||
rqToPQ.enq(cRqToP);
|
||||
$display("%t I %m sendRqToP: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(slot), " ; ",
|
||||
fshow(cRqToP)
|
||||
);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: start miss timer
|
||||
latTimer.start(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// last stage of pipeline: process req
|
||||
|
||||
// XXX: in L1, pRq cannot exist in dependency chain
|
||||
// because there are only two ways to include pRq into chain
|
||||
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
|
||||
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
|
||||
// (this cannot happen in I$)
|
||||
// Thus, dependency chain in L1 only contains cRq
|
||||
|
||||
// pipeline outputs
|
||||
pipeOutT pipeOut = pipeline.first;
|
||||
ramDataT ram = pipeOut.ram;
|
||||
// get proc req to select from cRqMshr
|
||||
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
|
||||
case(pipeOut.cmd) matches
|
||||
tagged L1CRq .n: (n);
|
||||
default: (fromMaybe(0, ram.info.owner)); // L1PRs
|
||||
endcase
|
||||
);
|
||||
|
||||
// function to get superscaler inst read result
|
||||
function resultT readInst(Line line, Addr addr);
|
||||
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
|
||||
// the start offset for reading inst
|
||||
LineInstOffset startSel = getLineInstOffset(addr);
|
||||
// calculate the maximum inst count that could be read from line
|
||||
LineInstOffset maxCntMinusOne = maxBound - startSel;
|
||||
// read inst superscalaer
|
||||
resultT val = ?;
|
||||
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
|
||||
if(fromInteger(i) <= maxCntMinusOne) begin
|
||||
LineInstOffset sel = startSel + fromInteger(i);
|
||||
val[i] = Valid (instVec[sel]);
|
||||
end
|
||||
else begin
|
||||
val[i] = Invalid;
|
||||
end
|
||||
end
|
||||
return val;
|
||||
endfunction
|
||||
|
||||
// function to process cRq hit (MSHR slot may have garbage)
|
||||
function Action cRqHit(cRqIdxT n, procRqT req);
|
||||
action
|
||||
$display("%t I %m pipelineResp: Hit func: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req)
|
||||
);
|
||||
// check tag & cs: even this function is called by pRs, tag should match,
|
||||
// because tag is written into cache before sending req to parent
|
||||
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
|
||||
"cRqHit but tag or cs incorrect"
|
||||
);
|
||||
// deq pipeline or swap in successor
|
||||
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
|
||||
pipeline.deqWrite(succ, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(req.addr), // should be the same as original tag
|
||||
cs: ram.info.cs, // use cs in ram
|
||||
dir: ?,
|
||||
owner: succ,
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, True); // hit, so update rep info
|
||||
// process req to get superscalar inst read results
|
||||
// set MSHR entry as Done & save inst results
|
||||
let instResult = readInst(ram.line, req.addr);
|
||||
cRqMshr.pipelineResp.setResult(n, instResult);
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
|
||||
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
|
||||
fshow(succ), " ; ", fshow(instResult)
|
||||
);
|
||||
`ifdef DEBUG_ICACHE
|
||||
// signal that this req is performed
|
||||
cRqDoneQ.enq(DebugICacheResp {
|
||||
id: req.id,
|
||||
line: ram.line
|
||||
});
|
||||
`endif
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
rule pipelineResp_cRq(pipeOut.cmd matches tagged L1CRq .n);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
|
||||
procRqT procRq = pipeOutCRq;
|
||||
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
|
||||
|
||||
// find end of dependency chain
|
||||
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
|
||||
|
||||
// function to process cRq miss without replacement (MSHR slot may have garbage)
|
||||
function Action cRqMissNoReplacement;
|
||||
action
|
||||
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
|
||||
// it is impossible in L1 to have slot.waitP == True in this function
|
||||
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
|
||||
// and this func is only called when cs < S (otherwise will hit)
|
||||
// because L1 has no children to wait for
|
||||
doAssert(!cSlot.waitP && ram.info.cs == I, "waitP must be false and cs must be I");
|
||||
// Thus we must send req to parent
|
||||
// XXX first send to a temp indexQ to avoid conflict, then merge to rqToPIndexQ later
|
||||
rqToPIndexQ_pipelineResp.enq(n);
|
||||
// update mshr
|
||||
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
|
||||
way: pipeOut.way, // use way from pipeline
|
||||
repTag: ?, // no replacement
|
||||
waitP: True // must fetch from parent
|
||||
});
|
||||
// deq pipeline & set owner, tag
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(procRq.addr), // tag may be garbage if cs == I
|
||||
cs: ram.info.cs,
|
||||
dir: ?,
|
||||
owner: Valid (n), // owner is req itself
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// function to do replacement for cRq
|
||||
function Action cRqReplacement;
|
||||
action
|
||||
// deq pipeline
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(procRq.addr), // set to req tag (old tag is replaced right now)
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Valid (n), // owner is req itself
|
||||
other: ?
|
||||
},
|
||||
line: ? // data is no longer used
|
||||
}, False);
|
||||
doAssert(ram.info.cs == S, "I$ replacement only replace S line");
|
||||
// update MSHR to save replaced tag
|
||||
// although we send req to parent later (when resp to parent is sent)
|
||||
// we set state to WaitSt now, since the req to parent is already on schedule
|
||||
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
|
||||
way: pipeOut.way, // use way from pipeline
|
||||
repTag: ram.info.tag, // tag being replaced for sending rs to parent
|
||||
waitP: True
|
||||
});
|
||||
// send replacement resp to parent
|
||||
rsToPIndexQ.enq(CRq (n));
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// function to set cRq to Depend, and make no further change to cache
|
||||
function Action cRqSetDepNoCacheChange;
|
||||
action
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
|
||||
pipeline.deqWrite(Invalid, pipeOut.ram, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
if(cOwner != n) begin
|
||||
// owner is another cRq, so must just go through tag match
|
||||
// tag match must be hit (because replacement algo won't give a way with owner)
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"cRq should hit in tag match"
|
||||
);
|
||||
// should be added to a cRq in dependency chain & deq from pipeline
|
||||
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
|
||||
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
|
||||
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
|
||||
);
|
||||
end
|
||||
else begin
|
||||
// owner is myself, so must be swapped in
|
||||
// tag should match, since always swapped in by cRq, cs = S
|
||||
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
|
||||
"cRq swapped in by previous cRq, tag must match & cs = S"
|
||||
);
|
||||
// Hit
|
||||
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// cache has no owner, cRq must just go through tag match
|
||||
// check for cRqEOC to append to dependency chain
|
||||
if(cRqEOC matches tagged Valid .k) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
|
||||
cRqMshr.pipelineResp.setSucc(k, Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
end
|
||||
else if(ram.info.cs == I || ram.info.tag == getTag(procRq.addr)) begin
|
||||
// No Replacement necessary
|
||||
if(ram.info.cs > I) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
|
||||
cRqMissNoReplacement;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, replace", $time);
|
||||
cRqReplacement;
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
rule pipelineResp_pRs(pipeOut.cmd == L1PRs);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
$display("%t I %m pipelineResp: pRs: ", $time);
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
procRqT procRq = pipeOutCRq;
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"pRs must be a hit"
|
||||
);
|
||||
cRqHit(cOwner, procRq);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: miss cRq
|
||||
incrMissCnt(cOwner);
|
||||
`endif
|
||||
end
|
||||
else begin
|
||||
doAssert(False, ("pRs owner must match some cRq"));
|
||||
end
|
||||
endrule
|
||||
|
||||
rule pipelineResp_pRq(pipeOut.cmd matches tagged L1PRq .n);
|
||||
pRqFromPT pRq = pRqMshr.pipelineResp.getRq(n);
|
||||
$display("%t I %m pipelineResp: pRq: ", $time, fshow(n), " ; ", fshow(pRq));
|
||||
|
||||
doAssert(pRq.toState == I, "I$ pRq only downgrade to I");
|
||||
|
||||
// pRq is never in dependency chain, so it is never swapped in
|
||||
// pRq must go through tag match, which either returns a tag matched way or asserts pRqMiss
|
||||
// In I$ a tag matched way should always be processed since pRq always downgrades to I
|
||||
// pRq is always directly handled: either dropped or Done
|
||||
|
||||
if(pipeOut.pRqMiss) begin
|
||||
$display("%t I %m pipelineResp: pRq: drop", $time);
|
||||
// pRq can be directly dropped, no successor (since just go through pipeline)
|
||||
pRqMshr.pipelineResp.releaseEntry(n);
|
||||
pipeline.deqWrite(Invalid, pipeOut.ram, False);
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: pRq: valid process", $time);
|
||||
// should process pRq
|
||||
doAssert(ram.info.cs == S && pRq.toState == I && ram.info.tag == getTag(pRq.addr),
|
||||
"pRq should be processed"
|
||||
);
|
||||
// line cannot be owned, because
|
||||
// (1) pRq never own cache line
|
||||
// (2) if owned by cRq, cRq would have hit and released ownership
|
||||
doAssert(ram.info.owner == Invalid, "pRq cannot hit on line owned by anyone");
|
||||
// write ram: set cs to I
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: ram.info.tag,
|
||||
cs: I, // I$ is always downgraded by pRq to I
|
||||
dir: ?,
|
||||
owner: Invalid, // no successor
|
||||
other: ?
|
||||
},
|
||||
line: ? // line is not useful
|
||||
}, False);
|
||||
// pRq is done
|
||||
pRqMshr.pipelineResp.setDone(n);
|
||||
// send resp to parent
|
||||
rsToPIndexQ.enq(PRq (n));
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
rule pipelineResp_flush(
|
||||
!flushDone &&& !flushRespDone &&&
|
||||
pipeOut.cmd matches tagged L1Flush .flush
|
||||
);
|
||||
pRqIdxT n = flush.mshrIdx;
|
||||
$display("%t I %m pipelineResp: flush: ", $time, fshow(flush));
|
||||
|
||||
// During flush, cRq MSHR is empty, so cache line cannot have owner
|
||||
doAssert(ram.info.owner == Invalid, "flushing line cannot have owner");
|
||||
|
||||
// flush always goes through cache pipeline, and is directly handled
|
||||
// here: either dropped or Done
|
||||
if(ram.info.cs == I) begin
|
||||
$display("%t I %m pipelineResp: flush: drop", $time);
|
||||
// flush can be directly dropped
|
||||
pRqMshr.pipelineResp.releaseEntry(n);
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: flush: valid process", $time);
|
||||
pRqMshr.pipelineResp.setDone(n);
|
||||
rsToPIndexQ.enq(PRq (n));
|
||||
// record the flushed addr in MSHR so that sendRsToP rule knows
|
||||
// which addr is invalidated
|
||||
Bit#(LgLineSzBytes) offset = 0;
|
||||
Addr addr = {ram.info.tag, flush.index, bankId, offset};
|
||||
pRqMshr.pipelineResp.setFlushAddr(n, addr);
|
||||
end
|
||||
|
||||
// always clear the cache line
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: ?,
|
||||
cs: I, // downgraded to I
|
||||
dir: ?,
|
||||
owner: Invalid, // no successor
|
||||
other: ?
|
||||
},
|
||||
line: ?
|
||||
}, False);
|
||||
|
||||
// check if we have finished all flush
|
||||
if (flush.index == maxBound &&
|
||||
pipeOut.way == fromInteger(valueof(wayNum) - 1)) begin
|
||||
flushRespDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
rule completeFlush(!flushDone && flushReqStart && flushReqDone && flushRespDone);
|
||||
flushDone <= True;
|
||||
flushReqStart <= False;
|
||||
flushReqDone <= False;
|
||||
flushRespDone <= False;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// merge rq to parent index into indexQ
|
||||
rule rqIndexFromPipelineResp;
|
||||
let n <- toGet(rqToPIndexQ_pipelineResp).get;
|
||||
rqToPIndexQ.enq(n);
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "rqIndexFromPipelineResp, rqIndexFromSendRsToP" *)
|
||||
rule rqIndexFromSendRsToP;
|
||||
let n <- toGet(rqToPIndexQ_sendRsToP).get;
|
||||
rqToPIndexQ.enq(n);
|
||||
endrule
|
||||
|
||||
interface ChildCacheToParent to_parent;
|
||||
interface rsToP = toFifoDeq(rsToPQ);
|
||||
interface rqToP = toFifoDeq(rqToPQ);
|
||||
interface fromP = toFifoEnq(fromPQ);
|
||||
endinterface
|
||||
|
||||
interface InstServer to_proc;
|
||||
interface Put req;
|
||||
method Action put(Addr addr);
|
||||
rqFromCQ.enq(addr);
|
||||
endmethod
|
||||
endinterface
|
||||
interface Get resp;
|
||||
method ActionValue#(resultT) get if(
|
||||
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
|
||||
);
|
||||
cRqIndexQ.deq;
|
||||
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
|
||||
$display("%t I %m sendRsToC: ", $time,
|
||||
fshow(cRqIndexQ.first), " ; ",
|
||||
fshow(inst)
|
||||
);
|
||||
return inst;
|
||||
endmethod
|
||||
endinterface
|
||||
`ifdef DEBUG_ICACHE
|
||||
interface done = toGet(cRqDoneQ);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface Get cRqStuck;
|
||||
method ActionValue#(ICRqStuck) get;
|
||||
let s <- cRqMshr.stuck.get;
|
||||
return ICRqStuck {
|
||||
addr: s.req.addr,
|
||||
state: s.state,
|
||||
waitP: s.waitP
|
||||
};
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface pRqStuck = pRqMshr.stuck;
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action flush if(flushDone);
|
||||
flushDone <= False;
|
||||
endmethod
|
||||
method flush_done = flushDone._read;
|
||||
`else
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
`endif
|
||||
|
||||
method Action setPerfStatus(Bool stats);
|
||||
`ifdef PERF_COUNT
|
||||
doStats <= stats;
|
||||
`else
|
||||
noAction;
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
L1ILdCnt: ldCnt;
|
||||
L1ILdMissCnt: ldMissCnt;
|
||||
L1ILdMissLat: ldMissLat;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
|
||||
// Scheduling note
|
||||
|
||||
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
|
||||
|
||||
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
|
||||
|
||||
// pRsTransfer: -
|
||||
|
||||
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
|
||||
|
||||
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
|
||||
|
||||
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
|
||||
|
||||
// sendRqToP: read cRq MSHR req/slot that is requesting parent
|
||||
|
||||
// pipelineResp_cRq:
|
||||
// -- read cRq MSHR req/state/slot currently processed
|
||||
// -- write cRq MSHR state/slot/result currently processed
|
||||
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
|
||||
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
|
||||
// -- not affected by write in cRqTransfer (state change is Empty->Init)
|
||||
// -- not affected by write in sendRsC (state change is Done->Empty)
|
||||
|
||||
// pipelineResp_pRs:
|
||||
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
|
||||
|
||||
// pipelineResp_pRq:
|
||||
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
|
||||
|
||||
// ---- conflict analysis ----
|
||||
|
||||
// XXXTransfer is conflict with each other
|
||||
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
|
||||
// XXXTransfer should operate on different cRq/pRq from other rules
|
||||
|
||||
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
|
||||
|
||||
// sendRqToP and sendRsToP_cRq are read only
|
||||
|
||||
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
|
||||
|
||||
// ---- conclusion ----
|
||||
|
||||
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
|
||||
|
||||
// we have 5 ports from cRq MSHR
|
||||
// 1. cRqTransfer
|
||||
// 2. sendRsToC
|
||||
// 3. sendRsToP_cRq
|
||||
// 4. sendRqToP
|
||||
// 5. pipelineResp
|
||||
|
||||
// we have 3 ports from pRq MSHR
|
||||
// 1. pRqTransfer
|
||||
// 2. sendRsToP_pRq
|
||||
// 3. pipelineResp
|
||||
|
||||
// safe version: use EHR ports
|
||||
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
|
||||
// pipelineResp < sendRsToP_pRq < pRqTransfer
|
||||
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
|
||||
|
||||
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
|
||||
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
|
||||
// we maintain the logical ordering in safe version
|
||||
|
||||
353
src_Core/RISCY_OOO/coherence/src/ICRqMshr.bsv
Normal file
353
src_Core/RISCY_OOO/coherence/src/ICRqMshr.bsv
Normal file
@@ -0,0 +1,353 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
|
||||
|
||||
// CRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
WaitSt, // wait pRs/cRs to come
|
||||
Done, // resp is in index FIFO
|
||||
Depend
|
||||
} ICRqState deriving(Bits, Eq, FShow);
|
||||
|
||||
// CRq info returned to outside
|
||||
typedef struct {
|
||||
wayT way; // the way to occupy
|
||||
tagT repTag; // tag being replaced
|
||||
Bool waitP; // waiting for parent resp
|
||||
} ICRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
|
||||
|
||||
instance DefaultValue#(ICRqSlot#(wayT, tagT));
|
||||
defaultValue = ICRqSlot {
|
||||
way: ?,
|
||||
repTag: ?,
|
||||
waitP: False
|
||||
};
|
||||
endinstance
|
||||
|
||||
typedef struct {
|
||||
reqT req;
|
||||
ICRqState state;
|
||||
Bool waitP;
|
||||
} ICRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// MSHR data is purely for replacement resp to parent
|
||||
// (resp to processor is done immediately, no data buffering needed)
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface ICRqMshr_sendRsToP_cRq#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// port for sendRqToP
|
||||
interface ICRqMshr_sendRqToP#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// port for pipelineResp
|
||||
interface ICRqMshr_pipelineResp#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT,
|
||||
type resultT
|
||||
);
|
||||
method ICRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method Action setResult(Bit#(TLog#(cRqNum)) n, resultT r); // set a valid result
|
||||
method Action setStateSlot(
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
ICRqState state,
|
||||
ICRqSlot#(wayT, tagT) slot
|
||||
);
|
||||
// can only change state to NON-Empty state
|
||||
// cannot be used to release MSHR entry (use releaseSlot instead)
|
||||
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
|
||||
// index in setSucc is usually different from other getXXX methods
|
||||
|
||||
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
|
||||
// i.e. search the end of dependency chain
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_sendRsToC#(
|
||||
numeric type cRqNum,
|
||||
type resultT
|
||||
);
|
||||
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT, // child req type
|
||||
type resultT // inst result type
|
||||
);
|
||||
// port for cRqTransfer, initialization is done inside method
|
||||
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
|
||||
|
||||
// port for sendRsToC
|
||||
interface ICRqMshr_sendRsToC#(cRqNum, resultT) sendRsToC;
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface ICRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
|
||||
|
||||
// port for sendRqToP
|
||||
interface ICRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
|
||||
|
||||
// port for pipelineResp
|
||||
interface ICRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT, resultT) pipelineResp;
|
||||
|
||||
// port for security flush
|
||||
method Bool emptyForFlush;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(ICRqMshrStuck#(reqT)) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkICRqMshrSafe#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
|
||||
) provisos (
|
||||
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(slotT, ICRqSlot#(wayT, tagT)),
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz),
|
||||
Bits#(resultT, _resultTSz)
|
||||
);
|
||||
// EHR ports
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer cRqTransfer_port = 2;
|
||||
Integer sendRsToC_port = 1; // create a bypass behavior from pipelineResp to sendRsToC (to save a cycle)
|
||||
Integer pipelineResp_port = 0;
|
||||
Integer sendRqToP_port = 0; // sendRqToP is read only
|
||||
Integer sendRsToP_cRq_port = 0; // sendRsToP_cRq is read only
|
||||
Integer flush_port = 0; // flush port is read only
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(cRqNum, Ehr#(3, ICRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
// cRq req contents
|
||||
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
|
||||
// cRq mshr slots
|
||||
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
|
||||
// result
|
||||
Vector#(cRqNum, Ehr#(3, Maybe#(resultT))) resultVec <- replicateM(mkEhr(Invalid));
|
||||
// successor valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
|
||||
// successor MSHR index
|
||||
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(cRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t ICRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(ICRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(ICRqMshrStuck {
|
||||
req: reqVec[n][0],
|
||||
state: stateVec[n][0],
|
||||
waitP: slotVec[n][0].waitP
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
cRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][cRqTransfer_port] <= Init;
|
||||
slotVec[n][cRqTransfer_port] <= defaultValue;
|
||||
resultVec[n][cRqTransfer_port] <= Invalid;
|
||||
succValidVec[n][cRqTransfer_port] <= False;
|
||||
reqVec[n][cRqTransfer_port] <= r;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
interface ICRqMshr_sendRsToC sendRsToC;
|
||||
method Action releaseEntry(cRqIndexT n) if(inited);
|
||||
emptyEntryQ.enq(n);
|
||||
stateVec[n][sendRsToC_port] <= Empty;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
|
||||
return resultVec[n][sendRsToC_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_sendRsToP_cRq sendRsToP_cRq;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_sendRqToP sendRqToP;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_pipelineResp pipelineResp;
|
||||
method ICRqState getState(cRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action setStateSlot(cRqIndexT n, ICRqState state, slotT slot);
|
||||
doAssert(state != Empty, "use releaseEntry to set state to Empty");
|
||||
stateVec[n][pipelineResp_port] <= state;
|
||||
slotVec[n][pipelineResp_port] <= slot;
|
||||
endmethod
|
||||
|
||||
method Action setResult(cRqIndexT n, resultT r);
|
||||
resultVec[n][pipelineResp_port] <= Valid (r);
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
|
||||
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
succValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
succFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
|
||||
function Bool isEndOfChain(Integer i);
|
||||
// check entry i is end of chain or not
|
||||
ICRqState state = stateVec[i][pipelineResp_port];
|
||||
Bool notDone = state != Done;
|
||||
Bool processedOnce = state != Empty && state != Init;
|
||||
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
|
||||
Bool noSucc = !succValidVec[i][pipelineResp_port];
|
||||
return notDone && processedOnce && addrMatch && noSucc;
|
||||
endfunction
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return searchIndex(isEndOfChain, idxVec);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method Bool emptyForFlush;
|
||||
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return all(isEmpty, idxVec);
|
||||
endmethod
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// exported version
|
||||
module mkICRqMshr#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
|
||||
) provisos (
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz),
|
||||
Bits#(resultT, _resultTSz)
|
||||
);
|
||||
let m <- mkICRqMshrSafe(getAddrFromReq);
|
||||
return m;
|
||||
endmodule
|
||||
214
src_Core/RISCY_OOO/coherence/src/IPRqMshr.bsv
Normal file
214
src_Core/RISCY_OOO/coherence/src/IPRqMshr.bsv
Normal file
@@ -0,0 +1,214 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import GetPut::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always directly handled at the end of pipeline
|
||||
|
||||
// PRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
Done
|
||||
} IPRqState deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
IPRqState state;
|
||||
} IPRqMshrStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
interface IPRqMshr_sendRsToP_pRq#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
endinterface
|
||||
|
||||
interface IPRqMshr_pipelineResp#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
method Action setDone(Bit#(TLog#(pRqNum)) n);
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface IPRqMshr#(numeric type pRqNum);
|
||||
// port for pRqTransfer
|
||||
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
|
||||
|
||||
// port for sendRsToP_pRq
|
||||
interface IPRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
|
||||
|
||||
// port for pipelineResp
|
||||
interface IPRqMshr_pipelineResp#(pRqNum) pipelineResp;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(IPRqMshrStuck) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkIPRqMshrSafe(
|
||||
IPRqMshr#(pRqNum)
|
||||
) provisos(
|
||||
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
|
||||
);
|
||||
// EHR port
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer pRqTransfer_port = 2;
|
||||
Integer sendRsToP_pRq_port = 1;
|
||||
Integer pipelineResp_port = 0;
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(pRqNum, Ehr#(3, IPRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
|
||||
// empty entry FIFO
|
||||
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(pRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
// released entry index fifos
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t IPRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(IPRqMshrStuck) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(IPRqMshrStuck {
|
||||
addr: reqVec[n][0].addr,
|
||||
toState: reqVec[n][0].toState,
|
||||
state: stateVec[n][0]
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule doReleaseEntry_sendRsToP_pRq(inited);
|
||||
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
|
||||
rule doReleaseEntry_pipelineResp(inited);
|
||||
let n <- toGet(releaseEntryQ_pipelineResp).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
pRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][pRqTransfer_port] <= Init;
|
||||
reqVec[n][pRqTransfer_port] <= r;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
interface IPRqMshr_sendRsToP_pRq sendRsToP_pRq;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][sendRsToP_pRq_port];
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_sendRsToP_pRq.enq(n);
|
||||
stateVec[n][sendRsToP_pRq_port] <= Empty;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface IPRqMshr_pipelineResp pipelineResp;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action setDone(pRqIndexT n);
|
||||
stateVec[n][pipelineResp_port] <= Done;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_pipelineResp.enq(n);
|
||||
stateVec[n][pipelineResp_port] <= Empty;
|
||||
endmethod
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
reqVec[n][pipelineResp_port] <= PRqMsg {
|
||||
addr: a,
|
||||
toState: I,
|
||||
child: ?
|
||||
};
|
||||
endmethod
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
|
||||
// exportd version
|
||||
module mkIPRqMshr(IPRqMshr#(pRqNum));
|
||||
let m <- mkIPRqMshrSafe;
|
||||
return m;
|
||||
endmodule
|
||||
1260
src_Core/RISCY_OOO/coherence/src/L1Bank.bsv
Normal file
1260
src_Core/RISCY_OOO/coherence/src/L1Bank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
378
src_Core/RISCY_OOO/coherence/src/L1CRqMshr.bsv
Normal file
378
src_Core/RISCY_OOO/coherence/src/L1CRqMshr.bsv
Normal file
@@ -0,0 +1,378 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
|
||||
|
||||
// CRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
WaitNewTag, // waiting replacement resp to send (but tag in RAM is already updated)
|
||||
WaitSt, // wait pRs/cRs to come
|
||||
Done, // resp is in index FIFO
|
||||
Depend
|
||||
} L1CRqState deriving(Bits, Eq, FShow);
|
||||
|
||||
// CRq info returned to outside
|
||||
typedef struct {
|
||||
wayT way; // the way to occupy
|
||||
Msi cs; // current cache MSI, used in sending upgrade req to parent
|
||||
tagT repTag; // tag being replaced: only valid in WaitOld/NewTag states
|
||||
Bool waitP; // waiting for parent resp
|
||||
} L1CRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
|
||||
|
||||
instance DefaultValue#(L1CRqSlot#(wayT, tagT));
|
||||
defaultValue = L1CRqSlot {
|
||||
way: ?,
|
||||
cs: ?,
|
||||
repTag: ?,
|
||||
waitP: False
|
||||
};
|
||||
endinstance
|
||||
|
||||
typedef struct {
|
||||
reqT req;
|
||||
L1CRqState state;
|
||||
Msi slotCs;
|
||||
Bool waitP;
|
||||
} L1CRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// MSHR data is purely for replacement resp to parent
|
||||
// (resp to processor is done immediately, no data buffering needed)
|
||||
|
||||
// port for cRqTransfer and retry
|
||||
interface L1CRqMshr_transfer#(
|
||||
numeric type cRqNum,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
|
||||
endinterface
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface L1CRqMshr_sendRsToP_cRq#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method Action setWaitSt_setSlot_clearData(
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
L1CRqSlot#(wayT, tagT) slot
|
||||
);
|
||||
// data is set to invalid, state is set to WaitSt here
|
||||
endinterface
|
||||
|
||||
// port for sendRqToP
|
||||
interface L1CRqMshr_sendRqToP#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// port for pipelineResp
|
||||
interface L1CRqMshr_pipelineResp#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
|
||||
method Action setStateSlot(
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
L1CRqState state,
|
||||
L1CRqSlot#(wayT, tagT) slot
|
||||
);
|
||||
// can only change state to NON-Empty state
|
||||
// cannot be used to release MSHR entry (use releaseSlot instead)
|
||||
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
|
||||
// index in setSucc is usually different from other getXXX methods
|
||||
|
||||
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
|
||||
// i.e. search the end of dependency chain
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT // child req type
|
||||
);
|
||||
// port for cRqTransfer and retry
|
||||
interface L1CRqMshr_transfer#(cRqNum, reqT) cRqTransfer;
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface L1CRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
|
||||
|
||||
// port for sendRqToP
|
||||
interface L1CRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
|
||||
|
||||
// port for pipelineResp
|
||||
interface L1CRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT) pipelineResp;
|
||||
|
||||
// port for security flush
|
||||
method Bool emptyForFlush;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(L1CRqMshrStuck#(reqT)) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkL1CRqMshrSafe#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
|
||||
) provisos (
|
||||
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(slotT, L1CRqSlot#(wayT, tagT)),
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz)
|
||||
);
|
||||
// EHR ports
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer flush_port = 0; // flush port is read only
|
||||
Integer sendRqToP_port = 0; // sendRqToP is read only
|
||||
Integer sendRsToP_cRq_port = 0;
|
||||
Integer pipelineResp_port = 1;
|
||||
Integer cRqTransfer_port = 2;
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(cRqNum, Ehr#(3, L1CRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
// cRq req contents
|
||||
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
|
||||
// cRq mshr slots
|
||||
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
|
||||
// data valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
|
||||
// data values
|
||||
RegFile#(cRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// successor valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
|
||||
// successor MSHR index
|
||||
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(cRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t L1CRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(L1CRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(L1CRqMshrStuck {
|
||||
req: reqVec[n][0],
|
||||
state: stateVec[n][0],
|
||||
slotCs: slotVec[n][0].cs,
|
||||
waitP: slotVec[n][0].waitP
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
interface L1CRqMshr_transfer cRqTransfer;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][cRqTransfer_port];
|
||||
endmethod
|
||||
|
||||
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
cRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][cRqTransfer_port] <= Init;
|
||||
slotVec[n][cRqTransfer_port] <= defaultValue;
|
||||
dataValidVec[n][cRqTransfer_port] <= False;
|
||||
succValidVec[n][cRqTransfer_port] <= False;
|
||||
reqVec[n][cRqTransfer_port] <= r;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr_sendRsToP_cRq sendRsToP_cRq;
|
||||
method L1CRqState getState(cRqIndexT n);
|
||||
return stateVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][sendRsToP_cRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setWaitSt_setSlot_clearData(cRqIndexT n, slotT s);
|
||||
stateVec[n][sendRsToP_cRq_port] <= WaitSt;
|
||||
slotVec[n][sendRsToP_cRq_port] <= s;
|
||||
dataValidVec[n][sendRsToP_cRq_port] <= False;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr_sendRqToP sendRqToP;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr_pipelineResp pipelineResp;
|
||||
method L1CRqState getState(cRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(cRqIndexT n) if(inited);
|
||||
emptyEntryQ.enq(n);
|
||||
stateVec[n][pipelineResp_port] <= Empty;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Action setStateSlot(cRqIndexT n, L1CRqState state, slotT slot);
|
||||
doAssert(state != Empty, "use releaseEntry to set state to Empty");
|
||||
stateVec[n][pipelineResp_port] <= state;
|
||||
slotVec[n][pipelineResp_port] <= slot;
|
||||
endmethod
|
||||
|
||||
method Action setData(cRqIndexT n, Maybe#(Line) line);
|
||||
dataValidVec[n][pipelineResp_port] <= isValid(line);
|
||||
dataFile.upd(n, fromMaybe(?, line));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
|
||||
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
succValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
succFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
|
||||
function Bool isEndOfChain(Integer i);
|
||||
// check entry i is end of chain or not
|
||||
L1CRqState state = stateVec[i][pipelineResp_port];
|
||||
Bool notDone = state != Done;
|
||||
Bool processedOnce = state != Empty && state != Init;
|
||||
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
|
||||
Bool noSucc = !succValidVec[i][pipelineResp_port];
|
||||
return notDone && processedOnce && addrMatch && noSucc;
|
||||
endfunction
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return searchIndex(isEndOfChain, idxVec);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method Bool emptyForFlush;
|
||||
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return all(isEmpty, idxVec);
|
||||
endmethod
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// exported version
|
||||
module mkL1CRqMshr#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
|
||||
) provisos (
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz)
|
||||
);
|
||||
let m <- mkL1CRqMshrSafe(getAddrFromReq);
|
||||
return m;
|
||||
endmodule
|
||||
231
src_Core/RISCY_OOO/coherence/src/L1PRqMshr.bsv
Normal file
231
src_Core/RISCY_OOO/coherence/src/L1PRqMshr.bsv
Normal file
@@ -0,0 +1,231 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import GetPut::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always directly handled at the end of pipeline
|
||||
|
||||
// PRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
Done
|
||||
} L1PRqState deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
L1PRqState state;
|
||||
} L1PRqMshrStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
interface L1PRqMshr_sendRsToP_pRq#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(pRqNum)) n);
|
||||
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
endinterface
|
||||
|
||||
interface L1PRqMshr_pipelineResp#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method L1PRqState getState(Bit#(TLog#(pRqNum)) n);
|
||||
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
method Action setDone_setData(Bit#(TLog#(pRqNum)) n, Maybe#(Line) d);
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface L1PRqMshr#(numeric type pRqNum);
|
||||
// port for pRqTransfer
|
||||
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
|
||||
|
||||
// port for sendRsToP_pRq
|
||||
interface L1PRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
|
||||
|
||||
// port for pipelineResp
|
||||
interface L1PRqMshr_pipelineResp#(pRqNum) pipelineResp;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(L1PRqMshrStuck) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkL1PRqMshrSafe(
|
||||
L1PRqMshr#(pRqNum)
|
||||
) provisos(
|
||||
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
|
||||
);
|
||||
// EHR port
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer sendRsToP_pRq_port = 0;
|
||||
Integer pipelineResp_port = 1;
|
||||
Integer pRqTransfer_port = 2;
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(pRqNum, Ehr#(3, L1PRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
|
||||
// data valid bits
|
||||
Vector#(pRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
|
||||
// data values
|
||||
RegFile#(pRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(pRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(pRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
// released entry index fifos
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t L1PRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(L1PRqMshrStuck) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(L1PRqMshrStuck {
|
||||
addr: reqVec[n][0].addr,
|
||||
toState: reqVec[n][0].toState,
|
||||
state: stateVec[n][0]
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule doReleaseEntry_sendRsToP_pRq(inited);
|
||||
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
|
||||
rule doReleaseEntry_pipelineResp(inited);
|
||||
let n <- toGet(releaseEntryQ_pipelineResp).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
pRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][pRqTransfer_port] <= Init;
|
||||
reqVec[n][pRqTransfer_port] <= r;
|
||||
dataValidVec[n][pRqTransfer_port] <= False;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
interface L1PRqMshr_sendRsToP_pRq sendRsToP_pRq;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][sendRsToP_pRq_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(pRqIndexT n);
|
||||
return dataValidVec[n][sendRsToP_pRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_sendRsToP_pRq.enq(n);
|
||||
stateVec[n][sendRsToP_pRq_port] <= Empty;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1PRqMshr_pipelineResp pipelineResp;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method L1PRqState getState(pRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action setDone_setData(pRqIndexT n, Maybe#(Line) line);
|
||||
stateVec[n][pipelineResp_port] <= Done;
|
||||
dataValidVec[n][pipelineResp_port] <= isValid(line);
|
||||
dataFile.upd(n, fromMaybe(?, line));
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_pipelineResp.enq(n);
|
||||
stateVec[n][pipelineResp_port] <= Empty;
|
||||
endmethod
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
reqVec[n][pipelineResp_port] <= PRqMsg {
|
||||
addr: a,
|
||||
toState: I,
|
||||
child: ?
|
||||
};
|
||||
endmethod
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// exportd version
|
||||
module mkL1PRqMshr(L1PRqMshr#(pRqNum));
|
||||
let m <- mkL1PRqMshrSafe;
|
||||
return m;
|
||||
endmodule
|
||||
391
src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv
Normal file
391
src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv
Normal file
@@ -0,0 +1,391 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export L1PipeRqIn(..);
|
||||
export L1PipePRsIn(..);
|
||||
export L1PipeFlushIn(..);
|
||||
export L1PipeIn(..);
|
||||
export L1FlushCmd(..);
|
||||
export L1Cmd(..);
|
||||
export L1Pipe(..);
|
||||
export mkL1Pipe;
|
||||
|
||||
// type param ordering: bank < way < index < tag < cRq < pRq
|
||||
|
||||
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
|
||||
// replacement is always done immediately (never have replacing line)
|
||||
// so cache owner type is simply Maybe#(cRqIdxT)
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
rqIdxT mshrIdx;
|
||||
} L1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
wayT way;
|
||||
} L1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
indexT index;
|
||||
wayT way;
|
||||
pRqIdxT mshrIdx;
|
||||
} L1PipeFlushIn#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type pRqIdxT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
L1PipeRqIn#(cRqIdxT) CRq;
|
||||
L1PipeRqIn#(pRqIdxT) PRq;
|
||||
L1PipePRsIn#(wayT) PRs;
|
||||
`ifdef SECURITY
|
||||
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
|
||||
`endif
|
||||
} L1PipeIn#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in L1$
|
||||
typedef struct {
|
||||
indexT index;
|
||||
pRqIdxT mshrIdx;
|
||||
} L1FlushCmd#(type indexT, type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
cRqIdxT L1CRq;
|
||||
pRqIdxT L1PRq;
|
||||
void L1PRs;
|
||||
`ifdef SECURITY
|
||||
L1FlushCmd#(indexT, pRqIdxT) L1Flush;
|
||||
`endif
|
||||
} L1Cmd#(
|
||||
type indexT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
interface L1Pipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
);
|
||||
method Action send(L1PipeIn#(Bit#(TLog#(wayNum)), indexT, cRqIdxT, pRqIdxT) r);
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, void, // no dir
|
||||
Maybe#(cRqIdxT), void, RandRepInfo, // no other
|
||||
Line, L1Cmd#(indexT, cRqIdxT, pRqIdxT)
|
||||
) first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} L1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
L1PipeRqIn#(cRqIdxT) CRq;
|
||||
L1PipeRqIn#(pRqIdxT) PRq;
|
||||
L1PipePRsCmd#(wayT) PRs;
|
||||
`ifdef SECURITY
|
||||
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
|
||||
`endif
|
||||
} L1PipeCmd#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
module mkL1Pipe(
|
||||
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, void), // no directory
|
||||
Alias#(ownerT, Maybe#(cRqIdxT)),
|
||||
Alias#(otherT, void), // no other cache info
|
||||
Alias#(repT, RandRepInfo), // use random replace
|
||||
Alias#(pipeInT, L1PipeIn#(wayT, indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeCmdT, L1PipeCmd#(wayT, indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
|
||||
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
|
||||
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// RAMs
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged PRq .r: r.addr;
|
||||
tagged PRs .r: r.addr;
|
||||
`ifdef SECURITY
|
||||
// fake an address for flush req that has the same index
|
||||
tagged Flush .r: (zeroExtend(r.index) << (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
`endif
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t L1 %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))),
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec), " ; "
|
||||
);
|
||||
if(cmd matches tagged PRs .rs) begin
|
||||
// PRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
`ifdef SECURITY
|
||||
else if(cmd matches tagged Flush .flush) begin
|
||||
// flush directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: flush.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
`endif
|
||||
else begin
|
||||
// CRq/PRq: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (nothing is being replaced)
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else if(cmd matches tagged PRq .rq) begin
|
||||
// pRq miss
|
||||
return TagMatchResult {
|
||||
way: 0, // default to 0
|
||||
pRqMiss: True
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
if(!isValid(repWay)) begin
|
||||
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
|
||||
$finish;
|
||||
end
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByUpCsT) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert((oldCs == I) == dataV, "valid resp data for upgrade from I");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByDownDirT) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
doAssert(False, "L1 does not have dir");
|
||||
return UpdateByDownDir {cs: oldCs, dir: oldDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(
|
||||
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
|
||||
) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
method Action send(pipeInT req);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRq .rq: begin
|
||||
pipe.enq(PRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRs .rs: begin
|
||||
pipe.enq(PRs (L1PipePRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), rs.data, UpCs (rs.toState));
|
||||
end
|
||||
`ifdef SECURITY
|
||||
tagged Flush .flush: begin
|
||||
pipe.enq(Flush (flush), Invalid, Invalid);
|
||||
end
|
||||
`endif
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
let pout = pipe.first;
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: L1CRq (rq.mshrIdx);
|
||||
tagged PRq .rq: L1PRq (rq.mshrIdx);
|
||||
tagged PRs .rs: L1PRs;
|
||||
`ifdef SECURITY
|
||||
tagged Flush .flush: L1Flush (L1FlushCmd {
|
||||
index: flush.index,
|
||||
mshrIdx: flush.mshrIdx
|
||||
});
|
||||
`endif
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endmethod
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin // swap in cRq
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
newCmd = Valid (CRq (L1PipeRqIn {addr: addr, mshrIdx: idx}));
|
||||
`ifdef SECURITY
|
||||
doAssert(pipe.first.cmd matches tagged Flush .f ? False : True,
|
||||
"Cannot swap after a flush req");
|
||||
`endif
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
endmethod
|
||||
endmodule
|
||||
1544
src_Core/RISCY_OOO/coherence/src/LLBank.bsv
Normal file
1544
src_Core/RISCY_OOO/coherence/src/LLBank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
447
src_Core/RISCY_OOO/coherence/src/LLCRqMshr.bsv
Normal file
447
src_Core/RISCY_OOO/coherence/src/LLCRqMshr.bsv
Normal file
@@ -0,0 +1,447 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in LLC, the head (h1) of a chain may be linked as the successor of the head (h2) of another chain
|
||||
// when h2 is replacing the addr of h1
|
||||
// h1 should be waken up and sent to pipeline when replacement is done (i.e. h2 gets to WaitSt)
|
||||
|
||||
// CRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
WaitOldTag,
|
||||
WaitSt,
|
||||
Done,
|
||||
Depend
|
||||
} LLCRqState deriving(Bits, Eq, FShow);
|
||||
|
||||
// we split data from slot info
|
||||
// because data may be used to buffer mem resp data
|
||||
typedef struct {
|
||||
wayT way; // the way to occupy
|
||||
tagT repTag; // tag being replaced, used in sending down req to children
|
||||
Bool waitP; // wait parent resp
|
||||
dirPendT dirPend; // pending child downgrade
|
||||
} LLCRqSlot#(type wayT, type tagT, type dirPendT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
reqT req;
|
||||
LLCRqState state;
|
||||
Bool waitP;
|
||||
dirPendT dirPend;
|
||||
} LLCRqMshrStuck#(type dirPendT, type reqT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// interface for cRq/mRs/cRsTransfer
|
||||
interface LLCRqMshr_transfer#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r, Maybe#(Line) d);
|
||||
// check if any empty MSHR entry is available in order to get MSHR blocking
|
||||
// stats. The argument is not really used here, just in case some other
|
||||
// MSHR implementations may bank entries based on addr.
|
||||
method Bool hasEmptyEntry(reqT r);
|
||||
endinterface
|
||||
|
||||
// interface for mRsDeq
|
||||
interface LLCRqMshr_mRsDeq#(numeric type cRqNum);
|
||||
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
|
||||
endinterface
|
||||
|
||||
// interface for sendToM
|
||||
interface LLCRqMshr_sendToM#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// interface for sendRsToDma and sendRsToC
|
||||
interface LLCRqMshr_sendRsToDmaC#(
|
||||
numeric type cRqNum,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// interface for sendRqToC
|
||||
interface LLCRqMshr_sendRqToC#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setSlot(Bit#(TLog#(cRqNum)) n, LLCRqSlot#(wayT, tagT, dirPendT) s);
|
||||
// find cRq that needs to send req to child to downgrade
|
||||
// (either replacement, or incompatible children states)
|
||||
// we can pass in a suggested req idx (which will have priority)
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchNeedRqChild(Maybe#(Bit#(TLog#(cRqNum))) suggestIdx);
|
||||
endinterface
|
||||
|
||||
// interface for pipelineResp_xxx
|
||||
interface LLCRqMshr_pipelineResp#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getAddrSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getRepSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
|
||||
method Action setStateSlot(
|
||||
Bit#(TLog#(cRqNum)) n, LLCRqState state,
|
||||
LLCRqSlot#(wayT, tagT, dirPendT) slot
|
||||
);
|
||||
method Action setAddrSucc( // same address successor
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
Maybe#(Bit#(TLog#(cRqNum))) succ
|
||||
);
|
||||
method Action setRepSucc( // successor due to replacement
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
Maybe#(Bit#(TLog#(cRqNum))) succ
|
||||
);
|
||||
// find existing cRq which has gone through pipeline, but not in Done state, and has no addr successor
|
||||
// (it could have rep successor)
|
||||
// i.e. search the end of dependency chain for req to the same addr
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
interface LLCRqMshr_transfer#(cRqNum, wayT, tagT, dirPendT, reqT) transfer;
|
||||
interface LLCRqMshr_mRsDeq#(cRqNum) mRsDeq;
|
||||
interface LLCRqMshr_sendToM#(cRqNum, wayT, tagT, dirPendT, reqT) sendToM;
|
||||
interface LLCRqMshr_sendRsToDmaC#(cRqNum, reqT) sendRsToDmaC;
|
||||
interface LLCRqMshr_sendRqToC#(cRqNum, wayT, tagT, dirPendT, reqT) sendRqToC;
|
||||
interface LLCRqMshr_pipelineResp#(cRqNum, wayT, tagT, dirPendT, reqT) pipelineResp;
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(LLCRqMshrStuck#(dirPendT, reqT)) stuck;
|
||||
endinterface
|
||||
|
||||
function LLCRqSlot#(wayT, tagT, dirPendT) getLLCRqSlotInitVal(dirPendT dirPendInitVal);
|
||||
return LLCRqSlot {
|
||||
way: ?,
|
||||
repTag: ?,
|
||||
waitP: False,
|
||||
dirPend: dirPendInitVal
|
||||
};
|
||||
endfunction
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkLLCRqMshr#(
|
||||
function Addr getAddrFromReq(reqT r),
|
||||
function Bool needDownReq(dirPendT dirPend),
|
||||
dirPendT dirPendInitVal
|
||||
)(
|
||||
LLCRqMshr#(cRqNum, wayT, tagT, dirPendT, reqT)
|
||||
) provisos (
|
||||
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(slotT, LLCRqSlot#(wayT, tagT, dirPendT)),
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(dirPendT, _dirPendSz),
|
||||
Bits#(reqT, _reqSz)
|
||||
);
|
||||
slotT slotInitVal = getLLCRqSlotInitVal(dirPendInitVal);
|
||||
|
||||
// logical ordering: sendToM < sendRqToC < sendRsToDma/C < mRsDeq < pipelineResp < transfer
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
// EHR ports
|
||||
Integer sendToM_port = 0; // sendToM is read-only, so use port 0
|
||||
Integer sendRqToC_port = 0; // read req/state/slot, write slot
|
||||
Integer sendRsToDmaC_port = 0; // sendRsToDma/C read req/data, write state
|
||||
Integer mRsDeq_port = 0; // mRsDeq only writes data
|
||||
Integer pipelineResp_port = 1; // read/write lots of things
|
||||
Integer transfer_port = 2; // cRqTransfer_xx, mRsTransfer_send, read/write lots of things
|
||||
|
||||
// cRq req contents
|
||||
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
|
||||
// MSHR entry state
|
||||
Vector#(cRqNum, Ehr#(3, LLCRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
// summary bit of dirPend in each entry: asserted when some dirPend[i] = ToSend
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) needReqChildVec <- replicateM(mkEhr(False));
|
||||
// cRq mshr slots
|
||||
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(slotInitVal));
|
||||
// data valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
|
||||
// data values
|
||||
Vector#(cRqNum, Ehr#(3, Line)) dataVec <- replicateM(mkEhr(?));
|
||||
// successor valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) addrSuccValidVec <- replicateM(mkEhr(False));
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) repSuccValidVec <- replicateM(mkEhr(False));
|
||||
// successor MSHR index
|
||||
RegFile#(cRqIndexT, cRqIndexT) addrSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
RegFile#(cRqIndexT, cRqIndexT) repSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
Fifo#(cRqNum, cRqIndexT) emptyEntryQ <- mkCFFifo;
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(cRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t LLCRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(LLCRqMshrStuck#(dirPendT, reqT)) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(LLCRqMshrStuck {
|
||||
req: reqVec[n][0],
|
||||
state: stateVec[n][0],
|
||||
waitP: slotVec[n][0].waitP,
|
||||
dirPend: slotVec[n][0].dirPend
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
function Action writeSlot(Integer ehrPort, cRqIndexT n, slotT s);
|
||||
action
|
||||
slotVec[n][ehrPort] <= s;
|
||||
// set dirPend summary bit
|
||||
needReqChildVec[n][ehrPort] <= needDownReq(s.dirPend);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
interface LLCRqMshr_transfer transfer;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][transfer_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][transfer_port];
|
||||
endmethod
|
||||
|
||||
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r, Maybe#(Line) d) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
cRqIndexT n = emptyEntryQ.first;
|
||||
reqVec[n][transfer_port] <= r;
|
||||
stateVec[n][transfer_port] <= Init;
|
||||
writeSlot(transfer_port, n, slotInitVal);
|
||||
dataValidVec[n][transfer_port] <= isValid(d);
|
||||
dataVec[n][transfer_port] <= validValue(d);
|
||||
addrSuccValidVec[n][transfer_port] <= False;
|
||||
repSuccValidVec[n][transfer_port] <= False;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
method Bool hasEmptyEntry(reqT r);
|
||||
return emptyEntryQ.notEmpty;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_mRsDeq mRsDeq;
|
||||
method Action setData(cRqIndexT n, Maybe#(Line) d);
|
||||
dataValidVec[n][mRsDeq_port] <= isValid(d);
|
||||
dataVec[n][mRsDeq_port] <= fromMaybe(?, d);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_sendToM sendToM;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendToM_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendToM_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][sendToM_port] ? Valid (dataVec[n][sendToM_port]) : Invalid;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_sendRsToDmaC sendRsToDmaC;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRsToDmaC_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][sendRsToDmaC_port] ? Valid (dataVec[n][sendRsToDmaC_port]) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(cRqIndexT n) if(inited);
|
||||
emptyEntryQ.enq(n);
|
||||
stateVec[n][sendRsToDmaC_port] <= Empty;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_sendRqToC sendRqToC;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRqToC_port];
|
||||
endmethod
|
||||
|
||||
method LLCRqState getState(cRqIndexT n);
|
||||
return stateVec[n][sendRqToC_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRqToC_port];
|
||||
endmethod
|
||||
|
||||
method Action setSlot(cRqIndexT n, slotT s);
|
||||
writeSlot(sendRqToC_port, n, s);
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchNeedRqChild(Maybe#(cRqIndexT) suggestIdx);
|
||||
function Bool isNeedRqChild(cRqIndexT i);
|
||||
return (stateVec[i][sendRqToC_port] == WaitOldTag || stateVec[i][sendRqToC_port] == WaitSt)
|
||||
&& needReqChildVec[i][sendRqToC_port];
|
||||
endfunction
|
||||
if(suggestIdx matches tagged Valid .idx &&& isNeedRqChild(idx)) begin
|
||||
return suggestIdx;
|
||||
end
|
||||
else begin
|
||||
Vector#(cRqNum, cRqIndexT) idxVec = genWith(fromInteger);
|
||||
return searchIndex(isNeedRqChild, idxVec);
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_pipelineResp pipelineResp;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method LLCRqState getState(cRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][pipelineResp_port] ? Valid (dataVec[n][pipelineResp_port]) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getAddrSucc(cRqIndexT n);
|
||||
return addrSuccValidVec[n][pipelineResp_port] ? Valid (addrSuccFile.sub(n)) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getRepSucc(cRqIndexT n);
|
||||
return repSuccValidVec[n][pipelineResp_port] ? Valid (repSuccFile.sub(n)) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setData(cRqIndexT n, Maybe#(Line) d);
|
||||
dataValidVec[n][pipelineResp_port] <= isValid(d);
|
||||
dataVec[n][pipelineResp_port] <= fromMaybe(?, d);
|
||||
endmethod
|
||||
|
||||
method Action setStateSlot(cRqIndexT n, LLCRqState state, slotT slot);
|
||||
stateVec[n][pipelineResp_port] <= state;
|
||||
writeSlot(pipelineResp_port, n, slot);
|
||||
endmethod
|
||||
|
||||
method Action setAddrSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
addrSuccValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
addrSuccFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Action setRepSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
repSuccValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
repSuccFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
|
||||
function Bool isEndOfChain(Integer i);
|
||||
// check entry i is end of chain or not
|
||||
let state = stateVec[i][pipelineResp_port];
|
||||
Bool notDone = state != Done;
|
||||
Bool processedOnce = state != Empty && state != Init;
|
||||
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
|
||||
Bool noAddrSucc = !addrSuccValidVec[i][pipelineResp_port];
|
||||
return notDone && processedOnce && addrMatch && noAddrSucc;
|
||||
endfunction
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return searchIndex(isEndOfChain, idxVec);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
371
src_Core/RISCY_OOO/coherence/src/LLPipe.bsv
Normal file
371
src_Core/RISCY_OOO/coherence/src/LLPipe.bsv
Normal file
@@ -0,0 +1,371 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export LLPipeCRqIn(..);
|
||||
export LLPipeMRsIn(..);
|
||||
export LLPipeIn(..);
|
||||
export LLCmd(..);
|
||||
export LLPipe(..);
|
||||
export mkLLPipe;
|
||||
|
||||
// type param ordering: bank < child < way < index < tag < cRq
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
cRqIdxT mshrIdx;
|
||||
} LLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState; // come from req in MSHR (E or M)
|
||||
Line data; // come from memory must be valid
|
||||
wayT way; // come from MSHR
|
||||
} LLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
LLPipeCRqIn#(cRqIdxT) CRq;
|
||||
CRsMsg#(childT) CRs;
|
||||
LLPipeMRsIn#(wayT) MRs;
|
||||
} LLPipeIn#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in LLC
|
||||
typedef union tagged {
|
||||
cRqIdxT LLCRq; // mshr idx of the cRq
|
||||
childT LLCRs; // which child is downgrading
|
||||
void LLMRs;
|
||||
} LLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
|
||||
|
||||
interface LLPipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type childNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT
|
||||
);
|
||||
method Action send(LLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
|
||||
method Bool notEmpty;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, Vector#(childNum, Msi),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) first;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, Vector#(childNum, Msi),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) unguard_first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, Vector#(childNum, Msi), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
childT child;
|
||||
} LLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} LLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
LLPipeCRqIn#(cRqIdxT) CRq;
|
||||
LLPipeCRsCmd#(childT) CRs;
|
||||
LLPipeMRsCmd#(wayT) MRs;
|
||||
} LLPipeCmd#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
module mkLLPipe(
|
||||
LLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
|
||||
) provisos(
|
||||
Alias#(childT, Bit#(TLog#(childNum))),
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, Vector#(childNum, Msi)),
|
||||
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
|
||||
Alias#(otherT, void), // no other cache info
|
||||
Alias#(repT, RandRepInfo), // use random replace
|
||||
Alias#(pipeInT, LLPipeIn#(childT, wayT, cRqIdxT)),
|
||||
Alias#(pipeCmdT, LLPipeCmd#(childT, wayT, cRqIdxT)),
|
||||
Alias#(llCmdT, LLCmd#(childT, cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
|
||||
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// RAMs
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: replicate(I),
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged CRs .r: r.addr;
|
||||
tagged MRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t LL %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec)
|
||||
);
|
||||
if(cmd matches tagged MRs .rs) begin
|
||||
// MRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// CRq/CRs: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (we do not check replacing bit in LLC)
|
||||
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// cRs must hit, so only cRq cannot enter here
|
||||
doAssert(cmd matches tagged CRq ._rq ? True : False,
|
||||
"only cRq can tag match miss"
|
||||
);
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
doAssert(isValid(repWay), "should always find a way to replace");
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByUpCsT) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByDownDirT) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
// update dir
|
||||
dirT newDir = oldDir;
|
||||
if(cmd matches tagged CRs .cRs) begin
|
||||
if(dataV) begin
|
||||
doAssert(oldDir[cRs.child] >= E, "cRs with data, dir must >= E");
|
||||
end
|
||||
else begin
|
||||
doAssert(oldDir[cRs.child] < M, "cRs without data, dir must < M");
|
||||
end
|
||||
if(oldDir[cRs.child] == M) begin
|
||||
doAssert(dataV, "must have data");
|
||||
end
|
||||
newDir[cRs.child] = toState;
|
||||
end
|
||||
else begin
|
||||
// should not happen
|
||||
doAssert(False, "only cRs updates dir");
|
||||
end
|
||||
// update cs
|
||||
// XXX since child can upgrade from E to M silently, use data valid
|
||||
// to determine if we need to upgrade to M. Note that the data
|
||||
// valid field has not been overwritten by bypass in CCPipe.
|
||||
Msi newCs = oldCs;
|
||||
if(dataV) begin
|
||||
doAssert(oldCs >= E, "cRs has data, cs must >= E");
|
||||
newCs = M;
|
||||
end
|
||||
return UpdateByDownDir {cs: newCs, dir: newDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// get first output from CCPipe output
|
||||
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: LLCRq (rq.mshrIdx);
|
||||
tagged CRs .rs: LLCRs (rs.child);
|
||||
tagged MRs .rs: LLMRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endfunction
|
||||
|
||||
method Action send(pipeInT req);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged CRs .rs: begin
|
||||
pipe.enq(CRs (LLPipeCRsCmd {
|
||||
addr: rs.addr,
|
||||
child: rs.child
|
||||
}), rs.data, DownDir (rs.toState));
|
||||
end
|
||||
tagged MRs .rs: begin
|
||||
pipe.enq(MRs (LLPipeMRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), Valid (rs.data), UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
return getFirst(pipe.first); // guarded version
|
||||
endmethod
|
||||
|
||||
method pipeOutT unguard_first;
|
||||
return getFirst(pipe.unguard_first); // unguarded version
|
||||
endmethod
|
||||
|
||||
method notEmpty = pipe.notEmpty;
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin
|
||||
newCmd = Valid (CRq (LLPipeCRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
endmethod
|
||||
endmodule
|
||||
83
src_Core/RISCY_OOO/coherence/src/MshrDeadlockChecker.bsv
Normal file
83
src_Core/RISCY_OOO/coherence/src/MshrDeadlockChecker.bsv
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Ehr::*;
|
||||
import CCTypes::*;
|
||||
import Vector::*;
|
||||
import ProcTypes::*;
|
||||
|
||||
interface MshrDeadlockChecker#(numeric type num);
|
||||
method ActionValue#(Maybe#(Bit#(TLog#(num)))) getStuckIdx; // get deadlock MSHR idx
|
||||
method Action initEntry(Bit#(TLog#(num)) n); // new MSHR entry allocated
|
||||
method Action releaseEntry(Bit#(TLog#(num)) n); // existing MSHR entry released
|
||||
endinterface
|
||||
|
||||
module mkMshrDeadlockChecker(MshrDeadlockChecker#(num)) provisos(
|
||||
Alias#(idxT, Bit#(TLog#(num)))
|
||||
);
|
||||
Integer check_port = 0;
|
||||
Integer incr_port = 1;
|
||||
// timer for each entry to detect deadlock: being processed for 64M cycles
|
||||
Vector#(num, Ehr#(2, Maybe#(DeadlockTimer))) timer <- replicateM(mkEhr(Invalid));
|
||||
// when new entry is allocated, init timer
|
||||
Vector#(num, PulseWire) init <- replicateM(mkPulseWire);
|
||||
// when existing entry is released, end the timer
|
||||
Vector#(num, PulseWire) done <- replicateM(mkPulseWire);
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incrTimer;
|
||||
for(Integer i = 0; i < valueof(num); i = i+1) begin
|
||||
if(init[i]) begin
|
||||
timer[i][incr_port] <= Valid (0);
|
||||
end
|
||||
else if(done[i]) begin
|
||||
timer[i][incr_port] <= Invalid;
|
||||
end
|
||||
else if(timer[i][incr_port] matches tagged Valid .t &&& t != maxBound) begin
|
||||
timer[i][incr_port] <= Valid (t + 1);
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
method ActionValue#(Maybe#(idxT)) getStuckIdx;
|
||||
function Bool isDeadlock(Integer i);
|
||||
return timer[i][check_port] == Valid (maxBound);
|
||||
endfunction
|
||||
Vector#(num, Integer) idxVec = genVector;
|
||||
if(searchIndex(isDeadlock, idxVec) matches tagged Valid .n) begin
|
||||
timer[n][check_port] <= Valid (0);
|
||||
return Valid (n);
|
||||
end
|
||||
else begin
|
||||
return Invalid;
|
||||
end
|
||||
endmethod
|
||||
|
||||
method Action initEntry(idxT n);
|
||||
init[n].send;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(idxT n);
|
||||
done[n].send;
|
||||
endmethod
|
||||
endmodule
|
||||
64
src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv
Normal file
64
src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import BRAMCore::*;
|
||||
import Fifo::*;
|
||||
|
||||
interface RWBramCore#(type addrT, type dataT);
|
||||
method Action wrReq(addrT a, dataT d);
|
||||
method Action rdReq(addrT a);
|
||||
method dataT rdResp;
|
||||
method Bool rdRespValid;
|
||||
method Action deqRdResp;
|
||||
endinterface
|
||||
|
||||
module mkRWBramCore(RWBramCore#(addrT, dataT)) provisos(
|
||||
Bits#(addrT, addrSz), Bits#(dataT, dataSz)
|
||||
);
|
||||
BRAM_DUAL_PORT#(addrT, dataT) bram <- mkBRAMCore2(valueOf(TExp#(addrSz)), False);
|
||||
BRAM_PORT#(addrT, dataT) wrPort = bram.a;
|
||||
BRAM_PORT#(addrT, dataT) rdPort = bram.b;
|
||||
// 1 elem pipeline fifo to add guard for read req/resp
|
||||
// must be 1 elem to make sure rdResp is not corrupted
|
||||
// BRAMCore should not change output if no req is made
|
||||
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
|
||||
|
||||
method Action wrReq(addrT a, dataT d);
|
||||
wrPort.put(True, a, d);
|
||||
endmethod
|
||||
|
||||
method Action rdReq(addrT a);
|
||||
rdReqQ.enq(?);
|
||||
rdPort.put(False, a, ?);
|
||||
endmethod
|
||||
|
||||
method dataT rdResp if(rdReqQ.notEmpty);
|
||||
return rdPort.read;
|
||||
endmethod
|
||||
|
||||
method rdRespValid = rdReqQ.notEmpty;
|
||||
|
||||
method Action deqRdResp;
|
||||
rdReqQ.deq;
|
||||
endmethod
|
||||
endmodule
|
||||
90
src_Core/RISCY_OOO/coherence/src/RandomReplace.bsv
Normal file
90
src_Core/RISCY_OOO/coherence/src/RandomReplace.bsv
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import Fifo::*;
|
||||
import CCTypes::*;
|
||||
import RWBramCore::*;
|
||||
|
||||
// random replace does not need any bookkeeping
|
||||
typedef void RandRepInfo;
|
||||
|
||||
function RandRepInfo randRepInitInfo;
|
||||
return ?;
|
||||
endfunction
|
||||
|
||||
module mkRandRepRam(RWBramCore#(indexT, RandRepInfo));
|
||||
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
|
||||
|
||||
method Action wrReq(indexT a, RandRepInfo d);
|
||||
noAction;
|
||||
endmethod
|
||||
method Action rdReq(indexT a);
|
||||
rdReqQ.enq(?);
|
||||
endmethod
|
||||
method RandRepInfo rdResp if(rdReqQ.notEmpty);
|
||||
return ?;
|
||||
endmethod
|
||||
method rdRespValid = rdReqQ.notEmpty;
|
||||
method deqRdResp = rdReqQ.deq;
|
||||
endmodule
|
||||
|
||||
interface RandomReplace#(numeric type wayNum);
|
||||
// find a way to replace, which is not locked
|
||||
// and Invalid way has priority
|
||||
method Maybe#(Bit#(TLog#(wayNum))) getReplaceWay(
|
||||
Vector#(wayNum, Bool) unlocked,
|
||||
Vector#(wayNum, Bool) invalid
|
||||
);
|
||||
endinterface
|
||||
|
||||
module mkRandomReplace(RandomReplace#(wayNum)) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum)))
|
||||
);
|
||||
Reg#(wayT) randWay <- mkReg(0);
|
||||
|
||||
rule tick;
|
||||
randWay <= randWay == fromInteger(valueOf(wayNum) - 1) ? 0 : randWay + 1;
|
||||
endrule
|
||||
|
||||
method Maybe#(wayT) getReplaceWay(Vector#(wayNum, Bool) unlocked, Vector#(wayNum, Bool) invalid);
|
||||
// first search for invalid & unlocked way
|
||||
function Bool isInvUnlock(Integer i);
|
||||
return unlocked[i] && invalid[i];
|
||||
endfunction
|
||||
Vector#(wayNum, Integer) idxVec = genVector;
|
||||
Maybe#(wayT) repWay = searchIndex(isInvUnlock, idxVec);
|
||||
if(!isValid(repWay)) begin
|
||||
// check whether random way is unlocked
|
||||
if(unlocked[randWay]) begin
|
||||
repWay = Valid (randWay);
|
||||
end
|
||||
else begin
|
||||
// just find a unlocked way
|
||||
repWay = searchIndex(id, unlocked);
|
||||
end
|
||||
end
|
||||
return repWay;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
629
src_Core/RISCY_OOO/coherence/src/SelfInvIBank.bsv
Normal file
629
src_Core/RISCY_OOO/coherence/src/SelfInvIBank.bsv
Normal file
@@ -0,0 +1,629 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Types::*;
|
||||
import MemoryTypes::*;
|
||||
import Amo::*;
|
||||
|
||||
import Cntrs::*;
|
||||
import Vector::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import CCTypes::*;
|
||||
import ICRqMshr::*;
|
||||
import CCPipe::*;
|
||||
import SelfInvIPipe ::*;
|
||||
import FShow::*;
|
||||
import DefaultValue::*;
|
||||
import Fifo::*;
|
||||
import CacheUtils::*;
|
||||
import Performance::*;
|
||||
import LatencyTimer::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export SelfInvICRqStuck(..);
|
||||
export SelfInvIPRqStuck(..);
|
||||
export SelfInvIBank(..);
|
||||
export mkSelfInvIBank;
|
||||
|
||||
// L1 I$, no pRq
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
ICRqState state;
|
||||
Bool waitP;
|
||||
} SelfInvICRqStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef void SelfInvIPRqStuck; // not used
|
||||
|
||||
interface SelfInvIBank#(
|
||||
numeric type supSz, // superscalar size
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
numeric type indexSz,
|
||||
numeric type tagSz,
|
||||
numeric type cRqNum
|
||||
);
|
||||
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
|
||||
interface InstServer#(supSz) to_proc; // to child, i.e. processor
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(SelfInvICRqStuck) cRqStuck;
|
||||
interface Get#(SelfInvIPRqStuck) pRqStuck;
|
||||
// security: flush (not implemented)
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
// reconcile
|
||||
method Action reconcile;
|
||||
method Bool reconcile_done;
|
||||
// performance
|
||||
method Action setPerfStatus(Bool stats);
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
endinterface
|
||||
|
||||
module mkSelfInvIBank#(
|
||||
Bit#(lgBankNum) bankId,
|
||||
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
|
||||
module#(SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)) mkIPipeline
|
||||
)(
|
||||
SelfInvIBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
|
||||
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
|
||||
Alias#(otherT, void),
|
||||
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, otherT, Line)),
|
||||
Alias#(procRqT, ProcRqToI),
|
||||
Alias#(cRqToPT, CRqMsg#(wayT, void)),
|
||||
Alias#(cRsToPT, CRsMsg#(void)),
|
||||
Alias#(pRqFromPT, PRqMsg#(void)),
|
||||
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
|
||||
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
|
||||
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
|
||||
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, otherT, RandRepInfo, Line, iCmdT)),
|
||||
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
|
||||
// requirements
|
||||
FShow#(pipeOutT),
|
||||
Add#(tagSz, a__, AddrSz),
|
||||
// make sure: cRqNum <= wayNum
|
||||
Add#(cRqNum, b__, wayNum),
|
||||
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
|
||||
);
|
||||
|
||||
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
|
||||
|
||||
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT) pipeline <- mkIPipeline;
|
||||
|
||||
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
|
||||
|
||||
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
|
||||
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
|
||||
|
||||
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
|
||||
// index Q to order all in flight cRq for in-order resp
|
||||
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
|
||||
|
||||
// Reconcile states
|
||||
Reg#(Bool) needReconcile <- mkReg(False);
|
||||
Reg#(Bool) waitReconcileDone <- mkReg(False);
|
||||
|
||||
`ifdef DEBUG_ICACHE
|
||||
// id for each cRq, incremented when each new req comes
|
||||
Reg#(Bit#(64)) cRqId <- mkReg(0);
|
||||
// FIFO to signal the id of cRq that is performed
|
||||
// FIFO has 0 cycle latency to match L1 D$ resp latency
|
||||
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
Count#(Data) ldCnt <- mkCount(0);
|
||||
Count#(Data) ldMissCnt <- mkCount(0);
|
||||
Count#(Data) ldMissLat <- mkCount(0);
|
||||
Count#(Data) reconcileCnt <- mkCount(0);
|
||||
|
||||
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
|
||||
|
||||
function Action incrReqCnt;
|
||||
action
|
||||
if(doStats) begin
|
||||
ldCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action incrMissCnt(cRqIdxT idx);
|
||||
action
|
||||
let lat <- latTimer.done(idx);
|
||||
if(doStats) begin
|
||||
ldMissLat.incr(zeroExtend(lat));
|
||||
ldMissCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
// XXX since I$ may be requested by processor constantly
|
||||
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
|
||||
// otherwise the whole system may deadlock/livelock
|
||||
// we stop accepting cRq when we need to reconcile
|
||||
rule cRqTransfer(!needReconcile);
|
||||
Addr addr <- toGet(rqFromCQ).get;
|
||||
`ifdef DEBUG_ICACHE
|
||||
procRqT r = ProcRqToI {addr: addr, id: cRqId};
|
||||
cRqId <= cRqId + 1;
|
||||
`else
|
||||
procRqT r = ProcRqToI {addr: addr};
|
||||
`endif
|
||||
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
|
||||
// send to pipeline
|
||||
pipeline.send(CRq (SelfInvIPipeRqIn {
|
||||
addr: r.addr,
|
||||
mshrIdx: n
|
||||
}));
|
||||
// enq to indexQ for in order resp
|
||||
cRqIndexQ.enq(n);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: cRq type
|
||||
incrReqCnt;
|
||||
`endif
|
||||
$display("%t I %m cRqTransfer: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(r)
|
||||
);
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
// pRq cannot happen, because I$ is never exclusive
|
||||
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
|
||||
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
|
||||
fromPQ.deq;
|
||||
$display("%t I %m pRqTransfer: ", $time, fshow(req));
|
||||
doAssert(False, "should not have pRq");
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
|
||||
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
|
||||
fromPQ.deq;
|
||||
pipeline.send(PRs (SelfInvIPipePRsIn {
|
||||
addr: resp.addr,
|
||||
toState: S,
|
||||
data: resp.data,
|
||||
way: resp.id
|
||||
}));
|
||||
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
|
||||
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
|
||||
endrule
|
||||
|
||||
rule sendRqToP;
|
||||
rqToPIndexQ.deq;
|
||||
cRqIdxT n = rqToPIndexQ.first;
|
||||
procRqT req = cRqMshr.sendRqToP.getRq(n);
|
||||
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
|
||||
cRqToPT cRqToP = CRqMsg {
|
||||
addr: req.addr,
|
||||
fromState: I, // I$ upgrade from I
|
||||
toState: S, // I$ upgrade to S
|
||||
canUpToE: False,
|
||||
id: slot.way,
|
||||
child: ?
|
||||
};
|
||||
rqToPQ.enq(cRqToP);
|
||||
$display("%t I %m sendRqToP: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(slot), " ; ",
|
||||
fshow(cRqToP)
|
||||
);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: start miss timer
|
||||
latTimer.start(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// last stage of pipeline: process req
|
||||
|
||||
// XXX: in L1, pRq cannot exist in dependency chain
|
||||
// because there are only two ways to include pRq into chain
|
||||
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
|
||||
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
|
||||
// (this cannot happen in I$)
|
||||
// Thus, dependency chain in L1 only contains cRq
|
||||
|
||||
// pipeline outputs
|
||||
pipeOutT pipeOut = pipeline.first;
|
||||
ramDataT ram = pipeOut.ram;
|
||||
// get proc req to select from cRqMshr
|
||||
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
|
||||
case(pipeOut.cmd) matches
|
||||
tagged ICRq .n: (n);
|
||||
default: (fromMaybe(0, ram.info.owner)); // L1PRs
|
||||
endcase
|
||||
);
|
||||
|
||||
// function to get superscaler inst read result
|
||||
function resultT readInst(Line line, Addr addr);
|
||||
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
|
||||
// the start offset for reading inst
|
||||
LineInstOffset startSel = getLineInstOffset(addr);
|
||||
// calculate the maximum inst count that could be read from line
|
||||
LineInstOffset maxCntMinusOne = maxBound - startSel;
|
||||
// read inst superscalaer
|
||||
resultT val = ?;
|
||||
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
|
||||
if(fromInteger(i) <= maxCntMinusOne) begin
|
||||
LineInstOffset sel = startSel + fromInteger(i);
|
||||
val[i] = Valid (instVec[sel]);
|
||||
end
|
||||
else begin
|
||||
val[i] = Invalid;
|
||||
end
|
||||
end
|
||||
return val;
|
||||
endfunction
|
||||
|
||||
// function to process cRq hit (MSHR slot may have garbage)
|
||||
function Action cRqHit(cRqIdxT n, procRqT req);
|
||||
action
|
||||
$display("%t I %m pipelineResp: Hit func: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req)
|
||||
);
|
||||
// check tag & cs: even this function is called by pRs, tag should match,
|
||||
// because tag is written into cache before sending req to parent
|
||||
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
|
||||
"cRqHit but tag or cs incorrect"
|
||||
);
|
||||
// deq pipeline or swap in successor
|
||||
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
|
||||
pipeline.deqWrite(succ, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(req.addr), // should be the same as original tag
|
||||
cs: ram.info.cs, // use cs in ram
|
||||
dir: ?,
|
||||
owner: succ,
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, True); // hit, so update rep info
|
||||
// process req to get superscalar inst read results
|
||||
// set MSHR entry as Done & save inst results
|
||||
let instResult = readInst(ram.line, req.addr);
|
||||
cRqMshr.pipelineResp.setResult(n, instResult);
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
|
||||
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
|
||||
fshow(succ), " ; ", fshow(instResult)
|
||||
);
|
||||
`ifdef DEBUG_ICACHE
|
||||
// signal that this req is performed
|
||||
cRqDoneQ.enq(DebugICacheResp {
|
||||
id: req.id,
|
||||
line: ram.line
|
||||
});
|
||||
`endif
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
rule pipelineResp_cRq(pipeOut.cmd matches tagged ICRq .n);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
|
||||
procRqT procRq = pipeOutCRq;
|
||||
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
|
||||
|
||||
// find end of dependency chain
|
||||
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
|
||||
|
||||
// function to process cRq miss without replacement (MSHR slot may have garbage)
|
||||
// We never replace in self-inv I$, because all S lines can be replaced silently
|
||||
function Action cRqMissNoReplacement;
|
||||
action
|
||||
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
|
||||
// it is impossible in L1 to have slot.waitP == True in this function
|
||||
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
|
||||
doAssert(!cSlot.waitP, "waitP must be false");
|
||||
// No exclusive in I$
|
||||
doAssert(ram.info.cs <= S, "no exclusive in I$");
|
||||
// This cannot be a hit
|
||||
doAssert(ram.info.cs == I || ram.info.tag != getTag(procRq.addr), "cannot hit");
|
||||
// Thus we must send req to parent
|
||||
rqToPIndexQ.enq(n);
|
||||
// update mshr
|
||||
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
|
||||
way: pipeOut.way, // use way from pipeline
|
||||
repTag: ?, // no replacement
|
||||
waitP: True // must fetch from parent
|
||||
});
|
||||
// deq pipeline & set owner, tag
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(procRq.addr), // tag may be garbage if cs == I or silent replace
|
||||
cs: I, // line must be invalid
|
||||
dir: ?,
|
||||
owner: Valid (n), // owner is req itself
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// function to set cRq to Depend, and make no further change to cache
|
||||
function Action cRqSetDepNoCacheChange;
|
||||
action
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
|
||||
pipeline.deqWrite(Invalid, pipeOut.ram, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
if(cOwner != n) begin
|
||||
// owner is another cRq, so must just go through tag match
|
||||
// tag match must be hit (because replacement algo won't give a way with owner)
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"cRq should hit in tag match"
|
||||
);
|
||||
// should be added to a cRq in dependency chain & deq from pipeline
|
||||
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
|
||||
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
|
||||
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
|
||||
);
|
||||
end
|
||||
else begin
|
||||
// owner is myself, so must be swapped in
|
||||
// tag should match, since always swapped in by cRq, cs = S
|
||||
// Reconcile happens only when no cRq in MSHR, so it won't affect swap
|
||||
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
|
||||
"cRq swapped in by previous cRq, tag must match & cs = S"
|
||||
);
|
||||
// Hit
|
||||
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// cache has no owner, cRq must just go through tag match
|
||||
// check for cRqEOC to append to dependency chain
|
||||
if(cRqEOC matches tagged Valid .k) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
|
||||
cRqMshr.pipelineResp.setSucc(k, Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
end
|
||||
else if(ram.info.cs > I && ram.info.tag == getTag(procRq.addr)) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
else begin
|
||||
// can always sliently replace
|
||||
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
|
||||
cRqMissNoReplacement;
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
rule pipelineResp_pRs(pipeOut.cmd == IPRs);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
$display("%t I %m pipelineResp: pRs: ", $time);
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
procRqT procRq = pipeOutCRq;
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"pRs must be a hit"
|
||||
);
|
||||
cRqHit(cOwner, procRq);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: miss cRq
|
||||
incrMissCnt(cOwner);
|
||||
`endif
|
||||
end
|
||||
else begin
|
||||
doAssert(False, ("pRs owner must match some cRq"));
|
||||
end
|
||||
endrule
|
||||
|
||||
// Reconcile lines in S state: start after cRq MSHR is empty
|
||||
// Since cRqTransfer rule cannot fire when needReconcile is true, we use a
|
||||
// wire to catch cRqMshr.empty to avoid scheduling cycles
|
||||
PulseWire cRqMshrEmpty <- mkPulseWire;
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setCRqMshrEmpty(cRqMshr.emptyForFlush);
|
||||
cRqMshrEmpty.send;
|
||||
endrule
|
||||
rule startReconcile(needReconcile && !waitReconcileDone && cRqMshrEmpty);
|
||||
pipeline.reconcile;
|
||||
waitReconcileDone <= True;
|
||||
$display("%t I %m startReconcile", $time);
|
||||
`ifdef PERF_COUNT
|
||||
if(doStats) begin
|
||||
reconcileCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
rule completeReconcile(needReconcile && waitReconcileDone && pipeline.reconcile_done);
|
||||
needReconcile <= False;
|
||||
waitReconcileDone <= False;
|
||||
$display("%t I %m completeReconcile", $time);
|
||||
endrule
|
||||
|
||||
interface ChildCacheToParent to_parent;
|
||||
interface rsToP = nullFifoDeq;
|
||||
interface rqToP = toFifoDeq(rqToPQ);
|
||||
interface fromP = toFifoEnq(fromPQ);
|
||||
endinterface
|
||||
|
||||
interface InstServer to_proc;
|
||||
interface Put req;
|
||||
method Action put(Addr addr);
|
||||
rqFromCQ.enq(addr);
|
||||
endmethod
|
||||
endinterface
|
||||
interface Get resp;
|
||||
method ActionValue#(resultT) get if(
|
||||
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
|
||||
);
|
||||
cRqIndexQ.deq;
|
||||
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
|
||||
$display("%t I %m sendRsToC: ", $time,
|
||||
fshow(cRqIndexQ.first), " ; ",
|
||||
fshow(inst)
|
||||
);
|
||||
return inst;
|
||||
endmethod
|
||||
endinterface
|
||||
`ifdef DEBUG_ICACHE
|
||||
interface done = toGet(cRqDoneQ);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface Get cRqStuck;
|
||||
method ActionValue#(SelfInvICRqStuck) get;
|
||||
let s <- cRqMshr.stuck.get;
|
||||
return SelfInvICRqStuck {
|
||||
addr: s.req.addr,
|
||||
state: s.state,
|
||||
waitP: s.waitP
|
||||
};
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface pRqStuck = nullGet;
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action flush if(flushDone);
|
||||
flushDone <= False;
|
||||
endmethod
|
||||
method flush_done = flushDone._read;
|
||||
`else
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
`endif
|
||||
|
||||
method Action reconcile if(!needReconcile);
|
||||
needReconcile <= True;
|
||||
endmethod
|
||||
method Bool reconcile_done;
|
||||
return !needReconcile;
|
||||
endmethod
|
||||
|
||||
method Action setPerfStatus(Bool stats);
|
||||
`ifdef PERF_COUNT
|
||||
doStats <= stats;
|
||||
`else
|
||||
noAction;
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
L1ILdCnt: ldCnt;
|
||||
L1ILdMissCnt: ldMissCnt;
|
||||
L1ILdMissLat: ldMissLat;
|
||||
L1IReconcileCnt: reconcileCnt;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
|
||||
// Scheduling note
|
||||
|
||||
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
|
||||
|
||||
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
|
||||
|
||||
// pRsTransfer: -
|
||||
|
||||
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
|
||||
|
||||
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
|
||||
|
||||
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
|
||||
|
||||
// sendRqToP: read cRq MSHR req/slot that is requesting parent
|
||||
|
||||
// pipelineResp_cRq:
|
||||
// -- read cRq MSHR req/state/slot currently processed
|
||||
// -- write cRq MSHR state/slot/result currently processed
|
||||
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
|
||||
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
|
||||
// -- not affected by write in cRqTransfer (state change is Empty->Init)
|
||||
// -- not affected by write in sendRsC (state change is Done->Empty)
|
||||
|
||||
// pipelineResp_pRs:
|
||||
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
|
||||
|
||||
// pipelineResp_pRq:
|
||||
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
|
||||
|
||||
// ---- conflict analysis ----
|
||||
|
||||
// XXXTransfer is conflict with each other
|
||||
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
|
||||
// XXXTransfer should operate on different cRq/pRq from other rules
|
||||
|
||||
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
|
||||
|
||||
// sendRqToP and sendRsToP_cRq are read only
|
||||
|
||||
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
|
||||
|
||||
// ---- conclusion ----
|
||||
|
||||
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
|
||||
|
||||
// we have 5 ports from cRq MSHR
|
||||
// 1. cRqTransfer
|
||||
// 2. sendRsToC
|
||||
// 3. sendRsToP_cRq
|
||||
// 4. sendRqToP
|
||||
// 5. pipelineResp
|
||||
|
||||
// we have 3 ports from pRq MSHR
|
||||
// 1. pRqTransfer
|
||||
// 2. sendRsToP_pRq
|
||||
// 3. pipelineResp
|
||||
|
||||
// safe version: use EHR ports
|
||||
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
|
||||
// pipelineResp < sendRsToP_pRq < pRqTransfer
|
||||
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
|
||||
|
||||
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
|
||||
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
|
||||
// we maintain the logical ordering in safe version
|
||||
|
||||
466
src_Core/RISCY_OOO/coherence/src/SelfInvIPipe.bsv
Normal file
466
src_Core/RISCY_OOO/coherence/src/SelfInvIPipe.bsv
Normal file
@@ -0,0 +1,466 @@
|
||||
|
||||
// Copyright (c) 2019 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Assert::*;
|
||||
import ConfigReg::*;
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import Fifo::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export SelfInvIPipeRqIn(..);
|
||||
export SelfInvIPipePRsIn(..);
|
||||
export SelfInvIPipeIn(..);
|
||||
export SelfInvICmd(..);
|
||||
export SelfInvIPipe(..);
|
||||
export mkSelfInvIPipe;
|
||||
|
||||
// type param ordering: bank < way < index < tag < cRq
|
||||
|
||||
// In I cache, only cRq can occupy cache line, there is no pRq or explicity
|
||||
// replacement, so cache owner type is simply Maybe#(cRqIdxT)
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
rqIdxT mshrIdx;
|
||||
} SelfInvIPipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
wayT way;
|
||||
} SelfInvIPipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvIPipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvIPipePRsIn#(wayT) PRs;
|
||||
} SelfInvIPipeIn#(
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in I$
|
||||
typedef union tagged {
|
||||
cRqIdxT ICRq;
|
||||
void IPRs;
|
||||
} SelfInvICmd#(
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
interface SelfInvIPipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT
|
||||
);
|
||||
method Action send(SelfInvIPipeIn#(Bit#(TLog#(wayNum)), cRqIdxT) r);
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, void, // no dir
|
||||
Maybe#(cRqIdxT), void, RandRepInfo, // no other
|
||||
Line, SelfInvICmd#(cRqIdxT)
|
||||
) first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
// drop stale clean cache lines
|
||||
method Action reconcile;
|
||||
method Bool reconcile_done;
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} SelfInvIPipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvIPipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvIPipePRsCmd#(wayT) PRs;
|
||||
} SelfInvIPipeCmd#(
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// cache state array with reconcile port
|
||||
// ram sub interface has same scheduling as RWBramCore:
|
||||
// - wrReq CF rdReq (read cannot see write)
|
||||
// - deqRdResp < rdReq
|
||||
interface CacheStateArray#(type indexT);
|
||||
interface RWBramCore#(indexT, Msi) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz))
|
||||
);
|
||||
staticAssert(pack(Msi'(I)) == 2'd0, "I = 0");
|
||||
staticAssert(pack(Msi'(S)) == 2'd1, "S = 0");
|
||||
|
||||
Vector#(size, Reg#(Bit#(1))) state <- replicateM(mkConfigReg(0));
|
||||
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, Msi s);
|
||||
doAssert(s == I || s == S, "only I or S");
|
||||
state[idx] <= truncate(pack(s));
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
rdRespQ.enq(unpack(zeroExtend(state[idx])));
|
||||
endmethod
|
||||
method rdResp = rdRespQ.first;
|
||||
method rdRespValid = rdRespQ.notEmpty;
|
||||
method deqRdResp = rdRespQ.deq;
|
||||
endinterface
|
||||
|
||||
method Action reconcile;
|
||||
function Action resetS(Reg#(Bit#(1)) s);
|
||||
action
|
||||
s <= 0;
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(resetS, state));
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// Put cache state array and tag+owner ram together to form an array
|
||||
typedef struct {
|
||||
tagT tag;
|
||||
ownerT owner;
|
||||
otherT other;
|
||||
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
|
||||
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz)),
|
||||
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
|
||||
Bits#(tagT, _tagSz),
|
||||
Bits#(ownerT, _ownerSz),
|
||||
Bits#(otherT, _otherSz)
|
||||
);
|
||||
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
|
||||
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, infoT x);
|
||||
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
|
||||
tag: x.tag,
|
||||
owner: x.owner,
|
||||
other: x.other
|
||||
});
|
||||
csArray.ram.wrReq(idx, x.cs);
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
tagOwnerOtherRam.rdReq(idx);
|
||||
csArray.ram.rdReq(idx);
|
||||
endmethod
|
||||
method infoT rdResp;
|
||||
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
|
||||
Msi cs = csArray.ram.rdResp;
|
||||
return CacheInfo {
|
||||
tag: tagOwnerOther.tag,
|
||||
cs: cs,
|
||||
dir: ?,
|
||||
owner: tagOwnerOther.owner,
|
||||
other: tagOwnerOther.other
|
||||
};
|
||||
endmethod
|
||||
method Bool rdRespValid;
|
||||
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
|
||||
endmethod
|
||||
method Action deqRdResp;
|
||||
tagOwnerOtherRam.deqRdResp;
|
||||
csArray.ram.deqRdResp;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method reconcile = csArray.reconcile;
|
||||
endmodule
|
||||
|
||||
module mkSelfInvIPipe(
|
||||
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, void), // no directory
|
||||
Alias#(ownerT, Maybe#(cRqIdxT)),
|
||||
Alias#(otherT, void),
|
||||
Alias#(repT, RandRepInfo),
|
||||
Alias#(pipeInT, SelfInvIPipeIn#(wayT, cRqIdxT)),
|
||||
Alias#(pipeCmdT, SelfInvIPipeCmd#(wayT, cRqIdxT)),
|
||||
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, iCmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// info RAM
|
||||
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
|
||||
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
|
||||
// rep RAM (dummy)
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
// data RAM
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged PRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t L1 %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))),
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec), " ; "
|
||||
);
|
||||
if(cmd matches tagged PRs .rs) begin
|
||||
// PRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
doAssert(cmd matches tagged CRq .rq ? True : False, "must be cRq");
|
||||
// CRq: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (nothing is being replaced)
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
if(!isValid(repWay)) begin
|
||||
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
|
||||
$finish;
|
||||
end
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState == S && oldCs == I, "upgrade cs I->S");
|
||||
doAssert(dataV, "self inv L1 always needs data resp");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
doAssert(False, "L1 should not have cRs");
|
||||
return UpdateByDownDir {cs: oldCs, dir: oldDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(
|
||||
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
|
||||
) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// reconcile: wait until pipeline empty and drop all S states. Stall
|
||||
// pipeline enq while we are waiting. Make the reconcile rule conflict with
|
||||
// pipeline deq to remove any possible race (the guard of reconcile rule
|
||||
// actually should have done the job).
|
||||
// Since send method will not fire when needReconcile, we can use a wire to
|
||||
// catch pipeline empty signal to avoid scheduling issue
|
||||
Reg#(Bool) needReconcile <- mkReg(False);
|
||||
|
||||
RWire#(void) conflict_reconcile_deq <- mkRWire;
|
||||
|
||||
PulseWire pipeEmpty <- mkPulseWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setPipeEmpty(pipe.emptyForFlush);
|
||||
pipeEmpty.send;
|
||||
endrule
|
||||
|
||||
rule doReconcile(initDone && needReconcile && pipeEmpty);
|
||||
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
|
||||
action
|
||||
ifc.reconcile;
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(flush, infoArray));
|
||||
// reconcile is done
|
||||
needReconcile <= False;
|
||||
// conflict with deq
|
||||
conflict_reconcile_deq.wset(?);
|
||||
$display("%t I %m doReconcile", $time);
|
||||
endrule
|
||||
|
||||
// stall enq for reconcile
|
||||
method Action send(pipeInT req) if(!needReconcile);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRs .rs: begin
|
||||
pipe.enq(PRs (SelfInvIPipePRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), rs.data, UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
let pout = pipe.first;
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: ICRq (rq.mshrIdx);
|
||||
tagged PRs .rs: IPRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endmethod
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin // swap in cRq
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
newCmd = Valid (CRq (SelfInvIPipeRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
// conflict with reconcile
|
||||
conflict_reconcile_deq.wset(?);
|
||||
endmethod
|
||||
|
||||
method Action reconcile if(!needReconcile);
|
||||
needReconcile <= True;
|
||||
endmethod
|
||||
|
||||
method Bool reconcile_done;
|
||||
return !needReconcile;
|
||||
endmethod
|
||||
endmodule
|
||||
1234
src_Core/RISCY_OOO/coherence/src/SelfInvL1Bank.bsv
Normal file
1234
src_Core/RISCY_OOO/coherence/src/SelfInvL1Bank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
492
src_Core/RISCY_OOO/coherence/src/SelfInvL1Pipe.bsv
Normal file
492
src_Core/RISCY_OOO/coherence/src/SelfInvL1Pipe.bsv
Normal file
@@ -0,0 +1,492 @@
|
||||
|
||||
// Copyright (c) 2019 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Assert::*;
|
||||
import ConfigReg::*;
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Fifo::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export SelfInvL1PipeRqIn(..);
|
||||
export SelfInvL1PipePRsIn(..);
|
||||
export SelfInvL1PipeIn(..);
|
||||
export SelfInvL1Cmd(..);
|
||||
export SelfInvL1Hits(..);
|
||||
export SelfInvL1Pipe(..);
|
||||
export mkSelfInvL1Pipe;
|
||||
|
||||
// type param ordering: bank < way < index < tag < cRq < pRq
|
||||
|
||||
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
|
||||
// replacement is always done immediately (never have replacing line)
|
||||
// so cache owner type is simply Maybe#(cRqIdxT)
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
rqIdxT mshrIdx;
|
||||
} SelfInvL1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
wayT way;
|
||||
} SelfInvL1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
|
||||
SelfInvL1PipePRsIn#(wayT) PRs;
|
||||
} SelfInvL1PipeIn#(
|
||||
type wayT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in L1$
|
||||
typedef union tagged {
|
||||
cRqIdxT L1CRq;
|
||||
pRqIdxT L1PRq;
|
||||
void L1PRs;
|
||||
} SelfInvL1Cmd#(
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// The "other" field in CacheInfo tracks the number of hits on the cache line
|
||||
// before self inv
|
||||
typedef struct {
|
||||
Bit#(TLog#(maxHitNum)) hits;
|
||||
} SelfInvL1Hits#(numeric type maxHitNum) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface SelfInvL1Pipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
numeric type maxHitNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
);
|
||||
method Action send(SelfInvL1PipeIn#(Bit#(TLog#(wayNum)), cRqIdxT, pRqIdxT) r);
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, void, // no dir
|
||||
Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), RandRepInfo,
|
||||
Line, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)
|
||||
) first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
// drop stale clean cache lines
|
||||
method Action reconcile;
|
||||
method Bool reconcile_done;
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} SelfInvL1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
|
||||
SelfInvL1PipePRsCmd#(wayT) PRs;
|
||||
} SelfInvL1PipeCmd#(
|
||||
type wayT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// cache state array with reconcile port
|
||||
// ram sub interface has same scheduling as RWBramCore:
|
||||
// - wrReq CF rdReq (read cannot see write)
|
||||
// - deqRdResp < rdReq
|
||||
interface CacheStateArray#(type indexT);
|
||||
interface RWBramCore#(indexT, Msi) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz))
|
||||
);
|
||||
Vector#(size, Reg#(Msi)) state <- replicateM(mkConfigReg(I));
|
||||
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, Msi s);
|
||||
state[idx] <= s;
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
rdRespQ.enq(state[idx]);
|
||||
endmethod
|
||||
method rdResp = rdRespQ.first;
|
||||
method rdRespValid = rdRespQ.notEmpty;
|
||||
method deqRdResp = rdRespQ.deq;
|
||||
endinterface
|
||||
|
||||
method Action reconcile;
|
||||
function Action resetS(Reg#(Msi) s);
|
||||
action
|
||||
if(s == S) begin
|
||||
s <= I;
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(resetS, state));
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// Put cache state array and tag+owner ram together to form an array
|
||||
typedef struct {
|
||||
tagT tag;
|
||||
ownerT owner;
|
||||
otherT other;
|
||||
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
|
||||
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz)),
|
||||
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
|
||||
Bits#(tagT, _tagSz),
|
||||
Bits#(ownerT, _ownerSz),
|
||||
Bits#(otherT, _otherSz)
|
||||
);
|
||||
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
|
||||
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, infoT x);
|
||||
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
|
||||
tag: x.tag,
|
||||
owner: x.owner,
|
||||
other: x.other
|
||||
});
|
||||
csArray.ram.wrReq(idx, x.cs);
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
tagOwnerOtherRam.rdReq(idx);
|
||||
csArray.ram.rdReq(idx);
|
||||
endmethod
|
||||
method infoT rdResp;
|
||||
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
|
||||
Msi cs = csArray.ram.rdResp;
|
||||
return CacheInfo {
|
||||
tag: tagOwnerOther.tag,
|
||||
cs: cs,
|
||||
dir: ?,
|
||||
owner: tagOwnerOther.owner,
|
||||
other: tagOwnerOther.other
|
||||
};
|
||||
endmethod
|
||||
method Bool rdRespValid;
|
||||
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
|
||||
endmethod
|
||||
method Action deqRdResp;
|
||||
tagOwnerOtherRam.deqRdResp;
|
||||
csArray.ram.deqRdResp;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method reconcile = csArray.reconcile;
|
||||
endmodule
|
||||
|
||||
module mkSelfInvL1Pipe(
|
||||
SelfInvL1Pipe#(lgBankNum, wayNum, maxHitNum, indexT, tagT, cRqIdxT, pRqIdxT)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, void), // no directory
|
||||
Alias#(ownerT, Maybe#(cRqIdxT)),
|
||||
Alias#(otherT, SelfInvL1Hits#(maxHitNum)),
|
||||
Alias#(repT, RandRepInfo),
|
||||
Alias#(pipeInT, SelfInvL1PipeIn#(wayT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeCmdT, SelfInvL1PipeCmd#(wayT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(l1CmdT, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
|
||||
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// info RAM
|
||||
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
|
||||
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
|
||||
// rep RAM (dummy)
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
// data RAM
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Invalid,
|
||||
other: SelfInvL1Hits {hits: 0}
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged PRq .r: r.addr;
|
||||
tagged PRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t L1 %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))),
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec), " ; "
|
||||
);
|
||||
if(cmd matches tagged PRs .rs) begin
|
||||
// PRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// CRq/PRq: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (nothing is being replaced)
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else if(cmd matches tagged PRq .rq) begin
|
||||
// pRq miss
|
||||
return TagMatchResult {
|
||||
way: 0, // default to 0
|
||||
pRqMiss: True
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
if(!isValid(repWay)) begin
|
||||
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
|
||||
$finish;
|
||||
end
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert(dataV, "self inv L1 always needs data resp");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
doAssert(False, "L1 should not have cRs");
|
||||
return UpdateByDownDir {cs: oldCs, dir: oldDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(
|
||||
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
|
||||
) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// reconcile: wait until pipeline empty and drop all S states. Stall
|
||||
// pipeline enq while we are waiting. Make the reconcile rule conflict with
|
||||
// pipeline deq to remove any possible race (the guard of reconcile rule
|
||||
// actually should have done the job).
|
||||
// Since send method will not fire when needReconcile, we can use a wire to
|
||||
// catch pipeline empty signal to avoid scheduling issue
|
||||
Reg#(Bool) needReconcile <- mkReg(False);
|
||||
|
||||
RWire#(void) conflict_reconcile_deq <- mkRWire;
|
||||
|
||||
PulseWire pipeEmpty <- mkPulseWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setPipeEmpty(pipe.emptyForFlush);
|
||||
pipeEmpty.send;
|
||||
endrule
|
||||
|
||||
rule doReconcile(initDone && needReconcile && pipeEmpty);
|
||||
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
|
||||
action
|
||||
ifc.reconcile;
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(flush, infoArray));
|
||||
// reconcile is done
|
||||
needReconcile <= False;
|
||||
// conflict with deq
|
||||
conflict_reconcile_deq.wset(?);
|
||||
$display("%t L1 %m doReconcile", $time);
|
||||
endrule
|
||||
|
||||
// stall enq for reconcile
|
||||
method Action send(pipeInT req) if(!needReconcile);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRq .rq: begin
|
||||
pipe.enq(PRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRs .rs: begin
|
||||
pipe.enq(PRs (SelfInvL1PipePRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), rs.data, UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
let pout = pipe.first;
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: L1CRq (rq.mshrIdx);
|
||||
tagged PRq .rq: L1PRq (rq.mshrIdx);
|
||||
tagged PRs .rs: L1PRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endmethod
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin // swap in cRq
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
newCmd = Valid (CRq (SelfInvL1PipeRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
// conflict with reconcile
|
||||
conflict_reconcile_deq.wset(?);
|
||||
endmethod
|
||||
|
||||
method Action reconcile if(!needReconcile);
|
||||
needReconcile <= True;
|
||||
endmethod
|
||||
|
||||
method Bool reconcile_done;
|
||||
return !needReconcile;
|
||||
endmethod
|
||||
endmodule
|
||||
1494
src_Core/RISCY_OOO/coherence/src/SelfInvLLBank.bsv
Normal file
1494
src_Core/RISCY_OOO/coherence/src/SelfInvLLBank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
366
src_Core/RISCY_OOO/coherence/src/SelfInvLLPipe.bsv
Normal file
366
src_Core/RISCY_OOO/coherence/src/SelfInvLLPipe.bsv
Normal file
@@ -0,0 +1,366 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
// type param ordering: bank < child < way < index < tag < cRq
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
cRqIdxT mshrIdx;
|
||||
} SelfInvLLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState; // come from req in MSHR (E or M)
|
||||
Line data; // come from memory must be valid
|
||||
wayT way; // come from MSHR
|
||||
} SelfInvLLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
|
||||
CRsMsg#(childT) CRs;
|
||||
SelfInvLLPipeMRsIn#(wayT) MRs;
|
||||
} SelfInvLLPipeIn#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in LLC
|
||||
typedef union tagged {
|
||||
cRqIdxT LLCRq; // mshr idx of the cRq
|
||||
childT LLCRs; // which child is downgrading
|
||||
void LLMRs;
|
||||
} SelfInvLLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
|
||||
|
||||
// Track only exclusive child in directory
|
||||
typedef struct {
|
||||
childT exChild;
|
||||
Msi state; // I/E/M, we don't track S
|
||||
} SelfInvDir#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface SelfInvLLPipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type childNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT
|
||||
);
|
||||
method Action send(SelfInvLLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
|
||||
method Bool notEmpty;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) first;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) unguard_first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
childT child;
|
||||
} SelfInvLLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} SelfInvLLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
|
||||
SelfInvLLPipeCRsCmd#(childT) CRs;
|
||||
SelfInvLLPipeMRsCmd#(wayT) MRs;
|
||||
} SelfInvLLPipeCmd#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
module mkSelfInvLLPipe(
|
||||
SelfInvLLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
|
||||
) provisos(
|
||||
Alias#(childT, Bit#(TLog#(childNum))),
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, SelfInvDir#(childT)),
|
||||
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
|
||||
Alias#(otherT, void), // no other cache info
|
||||
Alias#(repT, RandRepInfo), // use random replace
|
||||
Alias#(pipeInT, SelfInvLLPipeIn#(childT, wayT, cRqIdxT)),
|
||||
Alias#(pipeCmdT, SelfInvLLPipeCmd#(childT, wayT, cRqIdxT)),
|
||||
Alias#(llCmdT, SelfInvLLCmd#(childT, cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
|
||||
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// RAMs
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: SelfInvDir {exChild: ?, state: I},
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged CRs .r: r.addr;
|
||||
tagged MRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t LL %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec)
|
||||
);
|
||||
if(cmd matches tagged MRs .rs) begin
|
||||
// MRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// CRq/CRs: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (we do not check replacing bit in LLC)
|
||||
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// cRs must hit, so only cRq cannot enter here
|
||||
doAssert(cmd matches tagged CRq ._rq ? True : False,
|
||||
"only cRq can tag match miss"
|
||||
);
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
doAssert(isValid(repWay), "should always find a way to replace");
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByUpCsT) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByDownDirT) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
// update dir
|
||||
// The exclusive child is downgraded. Since we don't track S, just make
|
||||
// dir invalid, child field is useless for I
|
||||
dirT newDir = SelfInvDir {exChild: ?, state: I};
|
||||
doAssert(oldDir.state >= E && toState <= S, "no more exclusive child");
|
||||
doAssert(cmd matches tagged CRs .cRs &&& oldDir.exChild == cRs.child ? True : False,
|
||||
"cRs child should match");
|
||||
if(!dataV) begin
|
||||
doAssert(oldDir.state < M, "cRs without data, dir must < M");
|
||||
end
|
||||
if(oldDir.state == M) begin
|
||||
doAssert(dataV, "downgrade from M must have data");
|
||||
doAssert(oldCs == M, "cs must be M");
|
||||
end
|
||||
// update cs
|
||||
// XXX since child can upgrade from E to M silently, use data valid
|
||||
// to determine if we need to upgrade to M. Note that the data
|
||||
// valid field has not been overwritten by bypass in CCPipe.
|
||||
Msi newCs = oldCs;
|
||||
if(dataV) begin
|
||||
doAssert(oldCs >= E, "cRs has data, cs must >= E");
|
||||
newCs = M;
|
||||
end
|
||||
return UpdateByDownDir {cs: newCs, dir: newDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// get first output from CCPipe output
|
||||
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: LLCRq (rq.mshrIdx);
|
||||
tagged CRs .rs: LLCRs (rs.child);
|
||||
tagged MRs .rs: LLMRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endfunction
|
||||
|
||||
method Action send(pipeInT req);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged CRs .rs: begin
|
||||
pipe.enq(CRs (SelfInvLLPipeCRsCmd {
|
||||
addr: rs.addr,
|
||||
child: rs.child
|
||||
}), rs.data, DownDir (rs.toState));
|
||||
end
|
||||
tagged MRs .rs: begin
|
||||
pipe.enq(MRs (SelfInvLLPipeMRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), Valid (rs.data), UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
return getFirst(pipe.first); // guarded version
|
||||
endmethod
|
||||
|
||||
method pipeOutT unguard_first;
|
||||
return getFirst(pipe.unguard_first); // unguarded version
|
||||
endmethod
|
||||
|
||||
method notEmpty = pipe.notEmpty;
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin
|
||||
newCmd = Valid (CRq (SelfInvLLPipeCRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
endmethod
|
||||
endmodule
|
||||
Reference in New Issue
Block a user