From 643afd81e2840217b44fff543f42b086105647c9 Mon Sep 17 00:00:00 2001 From: Karlis Susters Date: Wed, 22 Mar 2023 22:12:59 +0000 Subject: [PATCH] Implementation and config for L1D StrideV2-2 prefetcher --- builds/Resources/Include_RISCY_Config.mk | 2 +- .../RISCY_OOO/coherence/src/Prefetcher.bsv | 181 +++++++++++++++++- .../coherence/src/test/Prefetcher_test.bsv | 75 ++++++++ 3 files changed, 256 insertions(+), 2 deletions(-) diff --git a/builds/Resources/Include_RISCY_Config.mk b/builds/Resources/Include_RISCY_Config.mk index 9646934..fa3edb1 100644 --- a/builds/Resources/Include_RISCY_Config.mk +++ b/builds/Resources/Include_RISCY_Config.mk @@ -48,7 +48,7 @@ RENAME_DEBUG ?= false INSTR_PREFETCHER_LOCATION ?= NONE INSTR_PREFETCHER_TYPE ?= SINGLE_WINDOW DATA_PREFETCHER_LOCATION ?= L1 -DATA_PREFETCHER_TYPE ?= MARKOV_ON_HIT +DATA_PREFETCHER_TYPE ?= STRIDE # clk frequency depends on core size ifneq (,$(filter $(CORE_SIZE),TINY SMALL BOOM MEDIUM)) diff --git a/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv b/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv index 0eefb22..d2a9c0d 100644 --- a/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv +++ b/src_Core/RISCY_OOO/coherence/src/Prefetcher.bsv @@ -1064,6 +1064,184 @@ provisos( endmodule +typedef enum { + EMPTY = 3'd0, INIT = 3'd1, TRANSIENT = 3'd2, STEADY = 3'd3, NO_PRED = 3'd4 +} StrideState2 deriving (Bits, Eq, FShow); + +typedef struct { + Bit#(12) lastAddr; + Bit#(13) stride; + Bit#(4) cLinesPrefetched; //Stores how many cache lines have been prefetched for this instruction + StrideState2 state; +} StrideEntry2 deriving (Bits, Eq, FShow); + +module mkStride2PCPrefetcher(PCPrefetcher) +provisos( + NumAlias#(strideTableSize, 512), + NumAlias#(cLinesAheadToPrefetch, 2), // TODO fetch more if have repeatedly hit an entry, and if stride big + Alias#(strideTableIndexT, Bit#(TLog#(strideTableSize))) + ); + RWBramCore#(strideTableIndexT, StrideEntry2) strideTable <- mkRWBramCoreForwarded; + FIFOF#(Tuple3#(Addr, Bit#(16), HitOrMiss)) memAccesses <- mkSizedBypassFIFOF(8); + Reg#(Tuple3#(Addr, Bit#(16), HitOrMiss)) rdRespEntry <- mkReg(?); + + Fifo#(8, Addr) addrToPrefetch <- mkOverflowPipelineFifo; + FIFO#(Tuple3#(StrideEntry2, 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); + StrideEntry2 se = strideTable.rdResp; + strideTable.deqRdResp; + StrideEntry2 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 + if (se.stride == observedStride) begin + //fast track to steady + seNext.state = STEADY; + $display(", stride matches so fast track back to STEADY"); + end + else begin + seNext.stride = observedStride; + seNext.state = TRANSIENT; + $display(", stride doesn't match, so set to %h", seNext.stride); + end + seNext.lastAddr = truncate(addr); + end + else if (se.state == TRANSIENT && observedStride != 0) begin + if (observedStride == se.stride) begin + //stride confimed, move to steady + seNext.cLinesPrefetched = 0; + seNext.state = STEADY; + $display(", stride %h is confirmed!", seNext.stride); + end + else begin + //We're seeing random accesses, go to no pred + seNext.state = NO_PRED; + seNext.stride = observedStride; + $display(", we have a random stride (%h), go to NO_PRED", seNext.stride); + end + seNext.lastAddr = truncate(addr); + end + else if (se.state == STEADY && observedStride != 0) begin + if (observedStride == se.stride) begin + 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 + $display(", stride %h stays confirmed!", seNext.stride); + end + else begin + //We jump to some other random location, so reset number of lines prefetched + seNext.cLinesPrefetched = 0; + seNext.state = INIT; + $display(", random jump! Move to INIT, don't reset stride"); + end + seNext.lastAddr = truncate(addr); + end + else if (se.state == NO_PRED && observedStride != 0) begin + if (observedStride == se.stride) begin + seNext.state = TRANSIENT; + $display(", have repeated stride: %h, move to TRANSIENT", seNext.stride); + end + else begin + seNext.stride = observedStride; + $display(", have random stride: %h", seNext.stride); + end + seNext.lastAddr = truncate(addr); + 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 + typedef enum { EMPTY = 3'd0, INIT = 3'd1, TRANSIENT = 3'd2, STEADY1 = 3'd3, STEADY2 = 3'd4, STEADY3 = 3'd5, STEADY4 = 4'd6, STEADYLAST = 3'd7 @@ -1349,7 +1527,8 @@ module mkL1DPrefetcher(PCPrefetcher); `ifdef DATA_PREFETCHER_BLOCK let m <- mkPCPrefetcherAdapter(mkBlockPrefetcher); `elsif DATA_PREFETCHER_STRIDE - let m <- mkBRAMStridePCPrefetcher; + //let m <- mkBRAMStridePCPrefetcher; + let m <- mkStride2PCPrefetcher; `elsif DATA_PREFETCHER_STRIDE_ADAPTIVE let m <- mkBRAMStrideAdaptivePCPrefetcher; `elsif DATA_PREFETCHER_MARKOV 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 7c93a0c..e7b06ff 100644 --- a/src_Core/RISCY_OOO/coherence/src/test/Prefetcher_test.bsv +++ b/src_Core/RISCY_OOO/coherence/src/test/Prefetcher_test.bsv @@ -596,4 +596,79 @@ module mkOverflowPipelineFifoTest(Empty); endaction endseq ); +endmodule + +module mkStride2PCPrefetcherTest(Empty); + //paremeter - 2 ahead + let p <- mkStride2PCPrefetcher; + 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 == 'h80000120, "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 == 'h80000160, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h80000180, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h800001c0, "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 p.reportAccess('h90000160, 'h006a, HIT); endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h90000120, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h900000e0, "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 p.reportAccess('h90000400, 'h006b, MISS); 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('ha0000420, 'h006b, MISS); endaction + action p.reportAccess('ha0000520, 'h006b, MISS); endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'ha0000620, "test fail!"); + endaction + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'ha0000720, "test fail!"); + endaction + + action p.reportAccess('h90000130, 'h006a, HIT); endaction + // -- use older entry + action + let x <- p.getNextPrefetchAddr; + doAssert(x == 'h900000b0, "test fail!"); + endaction + endseq + ); endmodule \ No newline at end of file