diff --git a/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv b/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv index 6db9163..760f234 100644 --- a/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv +++ b/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv @@ -1,4 +1,8 @@ import ISA_Decls :: *; +import RWBramCore::*; +import FIFO::*; +import FIFOF::*; +import SpecialFIFOs :: *; import Ehr::*; import CacheUtils::*; import CCTypes::*; @@ -809,6 +813,148 @@ provisos( endmodule +module mkBRAMStridePCPrefetcher(PCPrefetcher) +provisos( + NumAlias#(strideTableSize, 64), + NumAlias#(cLinesAheadToPrefetch, 3), // TODO fetch more if have repeatedly hit an entry, and if stride big + Alias#(strideTableIndexT, Bit#(TLog#(strideTableSize))) + ); + //Vector#(strideTableSize, Reg#(StrideEntry)) strideTable <- replicateM(mkReg(unpack(0))); + RWBramCore#(strideTableIndexT, StrideEntry) strideTable <- mkRWBramCoreForwarded; + FIFOF#(Tuple3#(Addr, Bit#(16), HitOrMiss)) memAccesses <- mkSizedBypassFIFOF(8); + Reg#(Tuple3#(Addr, Bit#(16), HitOrMiss)) rdRespEntry <- mkReg(?); + + FIFO#(Addr) addrToPrefetch <- mkSizedFIFO(8); + FIFO#(Tuple3#(StrideEntry, Addr, Bit#(16))) strideEntryForPrefetch <- mkBypassFIFO(); + Reg#(Maybe#(Bit#(4))) cLinesPrefetchedLatest <- mkReg(?); + PulseWire holdReadReq <- mkPulseWire; + + rule sendReadReq if (!holdReadReq); + match {.addr, .pcHash, .hitMiss} = memAccesses.first; + $display("%t Sending read req for %h!", $time, pcHash); + strideTable.rdReq(truncate(pcHash)); + rdRespEntry <= memAccesses.first; + memAccesses.deq; + endrule + + + rule updateStrideEntry; + //Find slot in vector + //if miss and slot empty + //if slot init, put address, stride and move to transit + //if slot transit or steady, verify stride, and move to steady + // also put last_prefetched + //if stride wrong, move to transit + match {.addr, .pcHash, .hitMiss} = rdRespEntry; + strideTableIndexT index = truncate(pcHash); + StrideEntry se = strideTable.rdResp; + strideTable.deqRdResp; + StrideEntry seNext = se; + Bit#(13) observedStride = {1'b0, addr[11:0]} - {1'b0, se.lastAddr}; + $writeh("%t Stride Prefetcher updateStrideEntry ", $time, + fshow(hitMiss), " ", addr, + ". Entry ", index, " state is ", fshow(se.state)); + if (se.state == EMPTY) begin + if (hitMiss == MISS) begin + seNext.lastAddr = truncate(addr); + seNext.state = INIT; + $display(", allocate entry"); + end + else begin + $display(", ignore"); + end + end + else if (se.state == INIT && observedStride != 0) begin + seNext.stride = observedStride; + seNext.state = TRANSIENT; + seNext.lastAddr = truncate(addr); + $display(", set stride to %h", seNext.stride); + end + else if ((se.state == TRANSIENT || se.state == STEADY) && observedStride != 0) begin + if (observedStride == se.stride) begin + if (se.state == TRANSIENT) begin + //Here we transition from TRANSIENT to STEADY, so init this field + seNext.cLinesPrefetched = 0; + end + else begin + //state == STEADY + if (se.lastAddr[11:6] != addr[11:6]) begin + //This means we have crossed a cache line since last access + seNext.cLinesPrefetched = + (se.cLinesPrefetched == 0) ? 0 : se.cLinesPrefetched - 1; + end + end + seNext.state = STEADY; + seNext.lastAddr = truncate(addr); + $display(", stride %h is confirmed!", seNext.stride); + end + else begin + seNext.state = TRANSIENT; + seNext.stride = observedStride; + seNext.lastAddr = truncate(addr); + $display(", old stride is broken! New stride: %h", seNext.stride); + end + end + else + $display(""); + + strideEntryForPrefetch.enq(tuple3(seNext, addr, pcHash)); + endrule + + rule createPrefetchRequests; + match {.se, .addr, .pcHash} = strideEntryForPrefetch.first; + //If this rule is looping, then we'll have a valid cLinesPrefetchedLatest + Bit#(4) cLinesPrefetched = fromMaybe(se.cLinesPrefetched, cLinesPrefetchedLatest); + + if (se.state == STEADY && + cLinesPrefetched != + fromInteger(valueof(cLinesAheadToPrefetch))) begin + //can prefetch + + Bit#(13) strideToUse; + Bit#(13) cLineSize = fromInteger(valueof(DataSz)); + if (se.stride[12] == 1 && se.stride > -cLineSize) begin + //stride is negative and jumps less than one cline + strideToUse = -cLineSize; + end + else if (se.stride[12] == 0 && se.stride < cLineSize) begin + //stride is positive and jumps less than one cline + strideToUse = cLineSize; + end + else begin + strideToUse = se.stride; + end + + let reqAddr = addr + + (signExtend(strideToUse) * zeroExtend(cLinesPrefetched + 1)); + + addrToPrefetch.enq(reqAddr); + // We will still be processing this StrideEntry next cycle, + // so hold off any potential read requests until we do a writeback + holdReadReq.send(); + cLinesPrefetchedLatest <= Valid(cLinesPrefetched + 1); + $display("%t Stride Prefetcher getNextPrefetchAddr requesting %h for entry %h", $time, reqAddr, pcHash[7:0]); + end + else begin + //cant prefetch + $display("%t Stride Prefetcher no possible prefetch for entry %h", $time, strideTableIndexT'(truncate(pcHash))); + strideEntryForPrefetch.deq; + se.cLinesPrefetched = cLinesPrefetched; + cLinesPrefetchedLatest <= Invalid; + strideTable.wrReq(truncate(pcHash), se); + end + endrule + + method Action reportAccess(Addr addr, Bit#(16) pcHash, HitOrMiss hitMiss); + memAccesses.enq(tuple3 (addr, pcHash, hitMiss)); + endmethod + + method ActionValue#(Addr) getNextPrefetchAddr; + addrToPrefetch.deq; + return addrToPrefetch.first; + endmethod + +endmodule module mkL1IPrefetcher(Prefetcher); //let m <- mkNextLinePrefetcher; //let m <- mkMultiWindowPrefetcher; diff --git a/src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv b/src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv index 414f1a4..9826548 100644 --- a/src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv +++ b/src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv @@ -76,6 +76,52 @@ module mkRWBramCore(RWBramCore#(addrT, dataT)) provisos( endmethod endmodule +module mkRWBramCoreForwarded(RWBramCore#(addrT, dataT)) provisos( + Bits#(addrT, addrSz), Bits#(dataT, dataSz), Eq#(addrT) +); + 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, Maybe#(dataT)) rdReqQ <- mkPipelineFifo; + RWire#(addrT) currentWriteAddr <- mkRWire; + RWire#(dataT) currentWriteData <- mkRWire; + + method Action wrReq(addrT a, dataT d); + wrPort.put(True, a, d); + currentWriteAddr.wset(a); //Forward data, if read happens on same cycle + currentWriteData.wset(d); + endmethod + + method Action rdReq(addrT a); + if (currentWriteAddr.wget matches tagged Valid .writeAddr &&& writeAddr == a) begin + $display ("%t Write same addr as read -- forwarding data!", $time); + rdReqQ.enq(Valid(fromMaybe(?, currentWriteData.wget))); + end + else begin + rdReqQ.enq(Invalid); + end + rdPort.put(False, a, ?); + endmethod + + method dataT rdResp if(rdReqQ.notEmpty); + if (rdReqQ.first matches tagged Valid .data) begin + return data; + end + else begin + return rdPort.read; + end + endmethod + + method rdRespValid = rdReqQ.notEmpty; + + method Action deqRdResp; + rdReqQ.deq; + endmethod +endmodule + module mkRWBramCoreUG(RWBramCore#(addrT, dataT)) provisos( Bits#(addrT, addrSz), Bits#(dataT, dataSz) ); diff --git a/src_Core/RISCY_OOO/coherence/src/test/Prefetcher_test.bsv b/src_Core/RISCY_OOO/coherence/src/test/Prefetcher_test.bsv index 7855b28..82af515 100644 --- a/src_Core/RISCY_OOO/coherence/src/test/Prefetcher_test.bsv +++ b/src_Core/RISCY_OOO/coherence/src/test/Prefetcher_test.bsv @@ -407,6 +407,80 @@ module mkStridePCPrefetcherTest(Empty); ); endmodule +module mkBRAMStridePCPrefetcherTest(Empty); + let p <- mkBRAMStridePCPrefetcher; + mkAutoFSM( + seq + // ----- Send misses and stuff to one window ----- + action $display("%t", $time); p.reportAccess('h80000040, 'h0069, MISS); endaction + action p.reportAccess('h80000080, 'h0069, HIT); endaction + action p.reportAccess('h800000a0, 'h0069, HIT); endaction + action p.reportAccess('h800000c0, 'h0069, MISS); endaction + action p.reportAccess('h800000e0, 'h0069, MISS); endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h80000100, "test fail!"); + endaction + action p.reportAccess('h80000100, 'h0069, HIT); endaction + action p.reportAccess('h80000120, 'h0069, HIT); endaction + action p.reportAccess('h80000140, 'h0069, HIT); endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h80000140, "test fail!"); + endaction + action endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h80000180, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h800001c0, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h80000200, "test fail!"); + endaction + action p.reportAccess('h900001f0, 'h006a, MISS); endaction + action p.reportAccess('h900001c0, 'h006a, HIT); endaction + action p.reportAccess('h90000190, 'h006a, HIT); endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h90000150, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h90000110, "test fail!"); + endaction + action p.reportAccess('h90000100, 'h006b, MISS); endaction + action p.reportAccess('h90000200, 'h006b, MISS); endaction + action p.reportAccess('h90000300, 'h006b, MISS); endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h900000d0, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h90000400, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h90000500, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h90000600, "test fail!"); + endaction + action p.reportAccess('h90000160, 'h006a, HIT); endaction + // -- use older entry + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h900000a0, "test fail!"); + endaction + endseq + ); +endmodule + module mkMarkovPrefetcherTest(Empty); //let p <- mkMultipleWindowPrefetcher; //TODO pass in value of cachelinesinrange