Implemented a stride prefetcher with BRAM instead of vectors
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user