A timing optimisation for the direction predictor.

Add a "nextPc" interface so that lookup can begin in the previous cycle.
Remove the "pc" operands in the vector of lookup interfaces,
but rather derive the PC from lookup from the first PC being looked up
in that cycle. That is, by adding 4*(interface number) to the beginning
lookup PC.  As this might not actually be the PC of the instruction,
pass the index you used with the training info so that you are certain
to train with the same index you looked up.

This has less than 1% overhead in CoreMark.

This is a general improvement for reasonable timing which should be upstreamed.
This commit is contained in:
Jonathan Woodruff
2022-01-18 11:05:09 +00:00
parent dac5c99100
commit e497091e18
7 changed files with 100 additions and 45 deletions

View File

@@ -358,6 +358,10 @@ module mkFetchStage(FetchStage);
Integer pc_fetch3_port = 2;
Integer pc_redirect_port = 3;
Integer pc_final_port = 4;
// To track the next expected PC in Decode for early lookups for prediction.
Ehr#(TAdd#(SupSize, 2), Addr) decode_pc_reg <- mkEhr(?);
Integer decode_pc_redirect_port = valueOf(SupSize);
Integer decode_pc_final_port = valueOf(SupSize) + 1;
// PC compression structure holding an indexed set of PC blocks so that only indexes need be tracked.
IndexedMultiset#(PcIdx, PcMSB, SupSizeX2) pcBlocks <- mkIndexedMultisetQueue;
@@ -659,7 +663,7 @@ module mkFetchStage(FetchStage);
Maybe#(IType) redirectInst = Invalid;
`endif
// Vector functions to generate all PCs, predicted next PCs, decode all instructions,
// and perform direction prediction for all. Taking these out of the following loop
// and perform direction predictions. Taking these out of the following loop
// makes them unconditional, not depending on previous iterations, improving timing.
function t valid (Maybe#(t) v) = v.Valid;
function CapMem getPc(Integer i) = decompressPc(valid(decodeIn[i]).pc);
@@ -675,7 +679,7 @@ module mkFetchStage(FetchStage);
Bool fetch_branch_misprediction = False;
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
if(dInsts[i].iType == Br && !fetch_branch_misprediction) begin
pred_ress[i] <- dirPred.pred[i].pred(getAddr(pcs[i]));
pred_ress[i] <- dirPred.pred[i].pred;
fetch_branch_misprediction = (pred_ress[i].taken != valid(decodeIn[i]).pred_jump);
end
end
@@ -686,14 +690,14 @@ module mkFetchStage(FetchStage);
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
if (decodeIn[i] matches tagged Valid .in) begin
let cause = in.cause;
CapMem pc = pcs[i];//decompressPc(in.pc);
CapMem ppc = ppcs[i];//decompressPc(in.ppc);
CapMem pc = pcs[i];
CapMem ppc = ppcs[i];
pcBlocks.rPort[i].remove(in.pc.idx);
if (verbose)
$display("Decode: %0d in = ", i, fshow (in));
let decode_result = decode_results[i];//decode(in.inst, getFlags(pc)==1); // Decode 32b inst, or 32b expansion of 16b inst
let dInst = dInsts[i];//decode_result.dInst;
let decode_result = decode_results[i]; // Decode 32b inst, or 32b expansion of 16b inst
let dInst = dInsts[i];
let regs = decode_result.regs;
// do decode and branch prediction
@@ -792,6 +796,7 @@ module mkFetchStage(FetchStage);
`endif
end
end // if (!isValid(cause))
decode_pc_reg[i] <= getAddr(ppc);
let out = FromFetchStage{pc: pc,
`ifdef RVFI_DII
dii_pid: in.dii_pid,
@@ -823,8 +828,8 @@ module mkFetchStage(FetchStage);
end // for (Integer i = 0; i < valueof(SupSize); i=i+1)
// update PC and epoch
if(redirectPc matches tagged Valid .nextPc) begin
pc_reg[pc_decode_port] <= nextPc;
if(redirectPc matches tagged Valid .rp) begin
pc_reg[pc_decode_port] <= rp;
end
`ifdef RVFI_DII
doAssert(isValid(redirectPc) == isValid(redirectDiiPid), "PC and DII redirections always happen together");
@@ -850,6 +855,10 @@ module mkFetchStage(FetchStage);
`endif
endrule
rule reportDecodePc;
dirPred.nextPc(decode_pc_reg[decode_pc_final_port]);
endrule
// train next addr pred: we use a wire to catch outputs of napTrainByDecQ.
// This prevents napTrainByDecQ from clogging doDecode rule when
// superscalar size is large
@@ -917,6 +926,7 @@ module mkFetchStage(FetchStage);
dii_pid_reg[pc_redirect_port] <= dii_pid;
if (verbose) $display("%t Redirect: dii_pid_reg %d", $time(), dii_pid);
`endif
decode_pc_reg[decode_pc_redirect_port] <= getAddr(new_pc);
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
// redirect comes, stop stalling for redirect
waitForRedirect[1] <= False;
@@ -958,7 +968,7 @@ module mkFetchStage(FetchStage);
//end
if (iType == Br) begin
// Train the direction predictor for all branches
dirPred.update(getAddr(pc), taken, dpTrain, mispred);
dirPred.update(taken, dpTrain, mispred);
end
// train next addr pred when mispred
if(mispred) begin

View File

@@ -42,18 +42,21 @@ import BrPred::*;
export BhtTrainInfo;
export mkBht;
typedef Bit#(0) BhtTrainInfo; // no training info needs to be remembered
export BhtEntries;
export BhtIndex;
// Local BHT Typedefs
typedef 128 BhtEntries;
typedef Bit#(TLog#(BhtEntries)) BhtIndex;
typedef BhtIndex BhtTrainInfo;
(* synthesize *)
module mkBht(DirPredictor#(BhtTrainInfo));
// Read and Write ordering doesn't matter since this is a predictor
// mkRegFileWCF is the RegFile version of mkConfigReg
RegFile#(BhtIndex, Bit#(2)) hist <- mkRegFileWCF(0,fromInteger(valueOf(BhtEntries)-1));
Reg#(Addr) pc_reg <- mkRegU;
function BhtIndex getIndex(Addr pc);
return truncate(pc >> 2);
@@ -62,22 +65,24 @@ module mkBht(DirPredictor#(BhtTrainInfo));
Vector#(SupSize, DirPred#(BhtTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(BhtTrainInfo)) pred(Addr pc);
let index = getIndex(pc);
method ActionValue#(DirPredResult#(BhtTrainInfo)) pred;
let index = getIndex(offsetPc(pc_reg, i));
Bit#(2) cnt = hist.sub(index);
Bool taken = cnt[1] == 1;
return DirPredResult {
taken: taken,
train: 0
train: index
};
endmethod
endinterface);
end
method nextPc = pc_reg._write;
interface pred = predIfc;
method Action update(Addr pc, Bool taken, BhtTrainInfo train, Bool mispred);
let index = getIndex(pc);
method Action update(Bool taken, BhtTrainInfo train, Bool mispred);
let index = train;
let current_hist = hist.sub(index);
Bit#(2) next_hist;
if(taken) begin

View File

@@ -67,18 +67,22 @@ endfunction
// general types for direction predictor
// Function to offset PC by the probable size of an instruction without a full add delay.
function Addr offsetPc(Addr pc, Integer i) = {truncateLSB(pc), pc[7:0] + (fromInteger(i)*4)};
typedef struct {
Bool taken;
trainInfoT train; // info that a branch must keep for future training
} DirPredResult#(type trainInfoT) deriving(Bits, Eq, FShow);
interface DirPred#(type trainInfoT);
method ActionValue#(DirPredResult#(trainInfoT)) pred(Addr pc);
method ActionValue#(DirPredResult#(trainInfoT)) pred;
endinterface
interface DirPredictor#(type trainInfoT);
method Action nextPc(Addr nextPc);
interface Vector#(SupSize, DirPred#(trainInfoT)) pred;
method Action update(Addr pc, Bool taken, trainInfoT train, Bool mispred);
method Action update(Bool taken, trainInfoT train, Bool mispred);
method Action flush;
method Bool flush_done;
endinterface

View File

@@ -46,6 +46,9 @@ export GSelectGHistSz;
export GSelectGHist;
export GSelectTrainInfo;
export mkGSelectPred;
export PCIndexSz;
export BhtIndexSz;
export BhtIndex;
// 1KB gselect predictor
@@ -60,6 +63,7 @@ typedef Bit#(BhtIndexSz) BhtIndex;
// bookkeeping info a branch should keep for future training
typedef struct {
GSelectGHist gHist;
BhtIndex index;
} GSelectTrainInfo deriving(Bits, Eq, FShow);
// global history
@@ -83,6 +87,9 @@ module mkGSelectPred(DirPredictor#(GSelectTrainInfo));
Ehr#(TAdd#(1, SupSize), Bit#(TLog#(TAdd#(SupSize, 1)))) predCnt <- mkEhr(0);
Ehr#(TAdd#(1, SupSize), Bit#(SupSize)) predRes <- mkEhr(0);
// Lookup PC
Reg#(Addr) pc_reg <- mkRegU;
function BhtIndex getIndex(Addr pc, GSelectGHist gHist);
Bit#(PCIndexSz) pcIdx = truncate(pc >> 2);
return {gHist, pcIdx};
@@ -106,13 +113,14 @@ module mkGSelectPred(DirPredictor#(GSelectTrainInfo));
Vector#(SupSize, DirPred#(GSelectTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(GSelectTrainInfo)) pred(Addr pc);
method ActionValue#(DirPredResult#(GSelectTrainInfo)) pred;
// get the global history
// all previous branch in this cycle must be not taken
// otherwise this branch should be on wrong path
// because all inst in same cycle are fetched consecutively
GSelectGHist gHist = curGHist >> predCnt[i];
Bool taken = isTaken(tab.sub(getIndex(pc, gHist)));
BhtIndex index = getIndex(offsetPc(pc_reg, i), gHist);
Bool taken = isTaken(tab.sub(index));
// record pred result
predCnt[i] <= predCnt[i] + 1;
@@ -124,7 +132,8 @@ module mkGSelectPred(DirPredictor#(GSelectTrainInfo));
return DirPredResult {
taken: taken,
train: GSelectTrainInfo {
gHist: gHist
gHist: gHist,
index: index
}
};
endmethod
@@ -138,16 +147,18 @@ module mkGSelectPred(DirPredictor#(GSelectTrainInfo));
predCnt[valueof(SupSize)] <= 0;
endrule
method nextPc = pc_reg._write;
interface pred = predIfc;
method Action update(Addr pc, Bool taken, GSelectTrainInfo train, Bool mispred);
method Action update(Bool taken, GSelectTrainInfo train, Bool mispred);
// update history if mispred
if(mispred) begin
GSelectGHist newHist = truncate({pack(taken), train.gHist} >> 1);
globalHist.redirect(newHist);
end
// update sat cnt
let index = getIndex(pc, train.gHist);
let index = train.index;
Bit#(2) cnt = tab.sub(index);
tab.upd(index, updateCnt(cnt, taken));
endmethod

View File

@@ -46,6 +46,8 @@ export GShareGHistSz;
export GShareGHist;
export GShareTrainInfo;
export mkGSharePred;
export BhtIndexSz;
export BhtIndex;
// 16KB gshare predictor (to match BOOM evaluation paper)
@@ -60,6 +62,7 @@ typedef Bit#(BhtIndexSz) BhtIndex;
// bookkeeping info a branch should keep for future training
typedef struct {
GShareGHist gHist;
BhtIndex index;
} GShareTrainInfo deriving(Bits, Eq, FShow);
// global history
@@ -83,6 +86,9 @@ module mkGSharePred(DirPredictor#(GShareTrainInfo));
Ehr#(TAdd#(1, SupSize), Bit#(TLog#(TAdd#(SupSize, 1)))) predCnt <- mkEhr(0);
Ehr#(TAdd#(1, SupSize), Bit#(SupSize)) predRes <- mkEhr(0);
// Lookup PC
Reg#(Addr) pc_reg <- mkRegU;
function BhtIndex getIndex(Addr pc, GShareGHist gHist);
Bit#(PCIndexSz) pcIdx = truncate(pc >> 2);
return gHist ^ pcIdx;
@@ -106,13 +112,14 @@ module mkGSharePred(DirPredictor#(GShareTrainInfo));
Vector#(SupSize, DirPred#(GShareTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(GShareTrainInfo)) pred(Addr pc);
method ActionValue#(DirPredResult#(GShareTrainInfo)) pred;
// get the global history
// all previous branch in this cycle must be not taken
// otherwise this branch should be on wrong path
// because all inst in same cycle are fetched consecutively
GShareGHist gHist = curGHist >> predCnt[i];
Bool taken = isTaken(tab.sub(getIndex(pc, gHist)));
BhtIndex index = getIndex(offsetPc(pc_reg, i), gHist);
Bool taken = isTaken(tab.sub(index));
// record pred result
predCnt[i] <= predCnt[i] + 1;
@@ -124,7 +131,8 @@ module mkGSharePred(DirPredictor#(GShareTrainInfo));
return DirPredResult {
taken: taken,
train: GShareTrainInfo {
gHist: gHist
gHist: gHist,
index: index
}
};
endmethod
@@ -138,18 +146,19 @@ module mkGSharePred(DirPredictor#(GShareTrainInfo));
predCnt[valueof(SupSize)] <= 0;
endrule
method nextPc = pc_reg._write;
interface pred = predIfc;
method Action update(Addr pc, Bool taken, GShareTrainInfo train, Bool mispred);
method Action update(Bool taken, GShareTrainInfo train, Bool mispred);
// update history if mispred
if(mispred) begin
GShareGHist newHist = truncate({pack(taken), train.gHist} >> 1);
globalHist.redirect(newHist);
end
// update sat cnt
let index = getIndex(pc, train.gHist);
Bit#(2) cnt = tab.sub(index);
tab.upd(index, updateCnt(cnt, taken));
Bit#(2) cnt = tab.sub(train.index);
tab.upd(train.index, updateCnt(cnt, taken));
endmethod
method flush = noAction;

View File

@@ -50,6 +50,8 @@ export TourTrainInfo(..);
export TourGHistReg(..);
export mkTourGHistReg;
export mkTourPred;
export PCIndexSz;
export PCIndex;
// 4KB tournament predictor
@@ -66,6 +68,7 @@ typedef struct {
TourLocalHist localHist;
Bool globalTaken;
Bool localTaken;
PCIndex pcIndex;
} TourTrainInfo deriving(Bits, Eq, FShow);
// global history reg
@@ -90,6 +93,9 @@ module mkTourPred(DirPredictor#(TourTrainInfo));
// choice sat counters: large (taken) -- use local, small (not taken) -- use global
RegFile#(TourGlobalHist, Bit#(2)) choiceBht <- mkRegFileWCF(0, maxBound);
// Lookup PC
Reg#(Addr) pc_reg <- mkRegU;
// EHR to record predict results in this cycle
Ehr#(TAdd#(1, SupSize), SupCnt) predCnt <- mkEhr(0);
Ehr#(TAdd#(1, SupSize), Bit#(SupSize)) predRes <- mkEhr(0);
@@ -120,9 +126,10 @@ module mkTourPred(DirPredictor#(TourTrainInfo));
Vector#(SupSize, DirPred#(TourTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(TourTrainInfo)) pred(Addr pc);
method ActionValue#(DirPredResult#(TourTrainInfo)) pred;
PCIndex pcIndex = getPCIndex(offsetPc(pc_reg, i));
// get local history & prediction
TourLocalHist localHist = localHistTab.sub(getPCIndex(pc));
TourLocalHist localHist = localHistTab.sub(pcIndex);
Bool localTaken = isTaken(localBht.sub(localHist));
// get the global history
@@ -149,7 +156,8 @@ module mkTourPred(DirPredictor#(TourTrainInfo));
globalHist: curGHist >> predCnt[i],
localHist: localHist,
globalTaken: globalTaken,
localTaken: localTaken
localTaken: localTaken,
pcIndex: pcIndex
}
};
endmethod
@@ -171,16 +179,18 @@ module mkTourPred(DirPredictor#(TourTrainInfo));
predCnt[valueof(SupSize)] <= 0;
endrule
method nextPc = pc_reg._write;
interface pred = predIfc;
method Action update(Addr pc, Bool taken, TourTrainInfo train, Bool mispred);
method Action update(Bool taken, TourTrainInfo train, Bool mispred);
// update history if mispred
if(mispred) begin
TourGlobalHist newHist = truncateLSB({pack(taken), train.globalHist});
gHistReg.redirect(newHist);
end
// update local history (assume only 1 branch for an PC in flight)
localHistTab.upd(getPCIndex(pc), truncateLSB({pack(taken), train.localHist}));
localHistTab.upd(train.pcIndex, truncateLSB({pack(taken), train.localHist}));
// update local sat cnt
let localCnt = localBht.sub(train.localHist);
localBht.upd(train.localHist, updateCnt(localCnt, taken));

View File

@@ -73,7 +73,6 @@ typedef TExp#(LgGlobalVecSz) GlobalVecSz;
typedef Bit#(GlobalVecSz) GlobalVecSelect;
typedef struct {
Addr pc;
Bool taken;
TourTrainInfo train;
Bool mispred;
@@ -93,6 +92,9 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
// choice sat counters: large (taken) -- use local, small (not taken) -- use global
RegFile#(TabIndex, Vector#(GlobalVecSz, Bit#(2))) choiceBht <- mkRegFileWCF(0, maxBound);
// Lookup PC
Reg#(Addr) pc_reg <- mkRegU;
// EHR to record predict results in this cycle
Ehr#(TAdd#(1, SupSize), SupCnt) predCnt <- mkEhr(0);
Ehr#(TAdd#(1, SupSize), Bit#(SupSize)) predRes <- mkEhr(0);
@@ -103,8 +105,9 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
Reg#(Bool) flushDone <- mkReg(True);
Reg#(TabIndex) flushIndex <- mkReg(0);
function Tuple2#(TabIndex, LocalHistVecSelect) getPCIndex(Addr pc);
PCIndex pcIdx = truncate(pc >> 2);
function PCIndex getPCIndex(Addr pc) = truncate(pc >> 2);
function Tuple2#(TabIndex, LocalHistVecSelect) getPCIndices(PCIndex pcIdx);
TabIndex tabIdx = truncateLSB(pcIdx);
LocalHistVecSelect sel = truncate(pcIdx);
return tuple2(tabIdx, sel);
@@ -142,9 +145,10 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
Vector#(SupSize, DirPred#(TourTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(TourTrainInfo)) pred(Addr pc);
method ActionValue#(DirPredResult#(TourTrainInfo)) pred;
// get local history
let {localHistTabIdx, localHistVecSel} = getPCIndex(pc);
PCIndex pcIndex = getPCIndex(offsetPc(pc_reg, i));
let {localHistTabIdx, localHistVecSel} = getPCIndices(pcIndex);
Vector#(LocalHistVecSz, TourLocalHist) localHistVec = localHistTab.sub(localHistTabIdx);
TourLocalHist localHist = localHistVec[localHistVecSel];
// get local prediction
@@ -180,7 +184,8 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
globalHist: globalHist,
localHist: localHist,
globalTaken: globalTaken,
localTaken: localTaken
localTaken: localTaken,
pcIndex: pcIndex
}
};
endmethod
@@ -197,7 +202,6 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
// no flush, accept update
(* fire_when_enabled, no_implicit_conditions *)
rule canonUpdate(flushDone &&& updateEn.wget matches tagged Valid .upd);
let pc = upd.pc;
let taken = upd.taken;
let train = upd.train;
let mispred = upd.mispred;
@@ -209,7 +213,7 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
end
// update local history (assume only 1 branch for an PC in flight)
let {localHistTabIdx, localHistVecSel} = getPCIndex(pc);
let {localHistTabIdx, localHistVecSel} = getPCIndices(train.pcIndex);
Vector#(LocalHistVecSz, TourLocalHist) localHistVec = localHistTab.sub(localHistTabIdx);
localHistVec[localHistVecSel] = truncateLSB({pack(taken), train.localHist});
localHistTab.upd(localHistTabIdx, localHistVec);
@@ -251,10 +255,12 @@ module mkTourPredSecure(DirPredictor#(TourTrainInfo));
end
endrule
method nextPc = pc_reg._write;
interface pred = predIfc;
method Action update(Addr pc, Bool taken, TourTrainInfo train, Bool mispred);
updateEn.wset(TourUpdate {pc: pc, taken: taken, train: train, mispred: mispred});
method Action update(Bool taken, TourTrainInfo train, Bool mispred);
updateEn.wset(TourUpdate {taken: taken, train: train, mispred: mispred});
endmethod
method Action flush if(flushDone);