From b65994c00c0debef32bf55ea62eecd41fa39b6b6 Mon Sep 17 00:00:00 2001 From: Jonathan Woodruff Date: Fri, 2 Feb 2024 11:50:45 +0000 Subject: [PATCH] Include the new single-cycle CCPipe as a seperate implementation, and use it only in the L1 caches so that the L2 cache can have lower resource utilisation and better timing. (Resource utilisation is up about 5% with the new design, and it's not necessary for the L2 cache.) --- src_Core/RISCY_OOO/coherence/src/CCPipe.bsv | 257 +++++++++++++++++++- src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv | 2 +- src_Core/RISCY_OOO/coherence/src/LLPipe.bsv | 4 +- 3 files changed, 247 insertions(+), 16 deletions(-) diff --git a/src_Core/RISCY_OOO/coherence/src/CCPipe.bsv b/src_Core/RISCY_OOO/coherence/src/CCPipe.bsv index 0f98bf7..8aa0c24 100644 --- a/src_Core/RISCY_OOO/coherence/src/CCPipe.bsv +++ b/src_Core/RISCY_OOO/coherence/src/CCPipe.bsv @@ -133,7 +133,7 @@ typedef struct { CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info; repT repInfo; // bypassed or resp line - lineT line; + Maybe#(lineT) line; } Match2Out#( type wayT, type tagT, @@ -176,7 +176,7 @@ 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)), @@ -185,7 +185,7 @@ function dataIndexT getDataRamIndex(wayT w, indexT i) provisos( ); return {w, i}; endfunction -*/ + module mkCCPipe#( ReadOnly#(Bool) initDone, function indexT getIndex(pipeCmdT cmd), @@ -208,7 +208,7 @@ module mkCCPipe#( function ActionValue#(repT) updateRepInfo(repT oldRep, wayT hitWay), Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam, RWBramCore#(indexT, repT) repRam, - Vector#(wayNum, RWBramCore#(dataIndexT, lineT)) dataRam + RWBramCore#(dataIndexT, lineT) dataRam )( CCPipe#(wayNum, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) ) provisos ( @@ -230,6 +230,240 @@ module mkCCPipe#( 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 + +module mkCCPipeSingleCycle#( + 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, + // Must be BRAMs with integrated forwarding; e.g. mkRWBramCoreForwarded + Vector#(wayNum, 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), Alias#(dataIndexT, Bit#(_indexSz)) ); @@ -280,7 +514,7 @@ module mkCCPipe#( pRqMiss: pRqMiss, info: infoVec[way], repInfo: repInfo, - line: fromMaybe(dataVec[way],e2m.respLine) + line: isValid(e2m.respLine) ? e2m.respLine:Valid(dataVec[way]) }; if(e2m.toState matches tagged UpCs .s) begin UpdateByUpCs#(msiT) upd <- updateByUpCs( @@ -295,12 +529,9 @@ module mkCCPipe#( m2o.info.cs = upd.cs; m2o.info.dir = upd.dir; end - indexT index = getIndex(e2m.cmd); - if (e2m.respLine matches tagged Valid .rl) begin - m2o.line = rl; - end else begin - m2o.line = dataVec[way]; - end mat2Out_match <= Valid (m2o); + //indexT index = getIndex(e2m.cmd); + //m2o.line = isValid(e2m.respLine) ? e2m.respLine:Valid(dataVec[way]); + mat2Out_match <= Valid (m2o); // reset enq2mat enq2Mat_match <= Invalid; endrule @@ -314,7 +545,7 @@ module mkCCPipe#( pRqMiss: m2o.pRqMiss, ram: RamData { info: m2o.info, - line: m2o.line + line: m2o.line.Valid }, repInfo: m2o.repInfo }; @@ -379,7 +610,7 @@ module mkCCPipe#( pRqMiss: False, // reset (not valid for swapped in pRq) info: wrRam.info, repInfo: repInfo, - line: wrRam.line + line: Valid(wrRam.line) }); end else begin diff --git a/src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv b/src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv index c929209..2d2a612 100644 --- a/src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv +++ b/src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv @@ -340,7 +340,7 @@ module mkL1Pipe( CCPipe#( wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT - ) pipe <- mkCCPipe( + ) pipe <- mkCCPipeSingleCycle( regToReadOnly(initDone), getIndex, tagMatch, updateByUpCs, updateByDownDir, updateRepInfo, infoRam, repRam, dataRam diff --git a/src_Core/RISCY_OOO/coherence/src/LLPipe.bsv b/src_Core/RISCY_OOO/coherence/src/LLPipe.bsv index b7a4050..45fe88e 100644 --- a/src_Core/RISCY_OOO/coherence/src/LLPipe.bsv +++ b/src_Core/RISCY_OOO/coherence/src/LLPipe.bsv @@ -162,9 +162,9 @@ module mkLLPipe( Bool verbose = False; // RAMs - Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCoreForwarded); + Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore); RWBramCore#(indexT, repT) repRam <- mkRandRepRam; - Vector#(wayNum, RWBramCore#(indexT, Line)) dataRam <- replicateM(mkRWBramCoreForwarded); + RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore; // initialize RAM Reg#(Bool) initDone <- mkReg(False);