Inteligently delay Decode (for up to 8 cycles) if we encounter a pop

while waiting for a push.
This commit is contained in:
Jonathan Woodruff
2022-03-07 17:52:11 +00:00
parent 6d65de5c88
commit 064cd22114
3 changed files with 107 additions and 48 deletions

View File

@@ -240,6 +240,31 @@ typedef struct {
Bool mispred_first_half;
} InstrFromFetch3 deriving(Bits, Eq, FShow);
function Bool popInst(DecodeResult dr);
let dInst = dr.dInst;
Bool doPop = False;
Bool dst_link = linkedR(dr.regs.dst);
Bool src1_link = linkedR(dr.regs.src1);
if (dr.dInst.iType == Jr || dr.dInst.iType == CJALR) begin // jalr TODO CCALL could be push
// pop or nop (if to trampoline)
// Add hint to architecture?
if (!dst_link && src1_link) begin
// rd is link while rs1 is not: pop
doPop = True;
end
else if (!src1_link && dst_link) begin
doPop = doPop; // Nothing
end
else if (dst_link && src1_link) begin
// both rd and rs1 are links
if (dr.regs.src1 != dr.regs.dst) begin
doPop = True;
end
end
end
return doPop;
endfunction
function InstrFromFetch3 fetch3_2_instC(Fetch3ToDecode in, Instruction inst, Bit#(32) orig_inst) =
InstrFromFetch3 {
pc: in.pc,
@@ -610,12 +635,6 @@ module mkFetchStage(FetchStage);
end
endrule: doFetch3
Bool delay_epoch = False;
`ifdef NO_SPEC_REDIRECT
// Delay decode if the head of this buffer matches the current epoch;
// this means the source of this epoch is still speculative.
delay_epoch = (main_epoch_spec.first.spec_bits != 0);
`endif
function Bool isCurrent(Fetch3ToDecode in) = (main_epoch_spec.notEmpty &&
in.main_epoch == f_main_epoch &&
in.decode_epoch == decode_epoch[0]);
@@ -633,44 +652,63 @@ module mkFetchStage(FetchStage);
main_epoch_spec.first.spec_bits, main_epoch_spec.notEmpty, isCurrent(f32d.deqS[0].first), f_main_epoch, f32d.deqS[0].first.main_epoch);
endrule
rule doDecode(f32d.deqS[0].canDeq && isCurrent(f32d.deqS[0].first) && !delay_epoch);
Vector#(SupSize, Maybe#(InstrFromFetch3)) decodeIn = replicate(Invalid);
// Express the incoming fragments as a vector of maybes.
Vector#(SupSizeX2, Maybe#(Fetch3ToDecode)) frags;
for (Integer i = 0; i < valueOf(SupSizeX2); i = i + 1)
frags[i] = (f32d.deqS[i].canDeq) ? Valid (f32d.deqS[i].first) : Invalid;
// Pick as up to SupSize instructions from the f32d SupFifo.
// Stop picking when we have SupSize instructions or when we have exhausted the ports on the instruction fragment FIFO.
Maybe#(Bit#(TLog#(SupSizeX2))) m_used_frag_count = Invalid;
Bit#(TLog#(SupSize)) pick_count = 0;
Bool prev_frag_available = False;
for (Integer i = 0; i < valueOf(SupSizeX2) && !isValid(decodeIn[valueOf(SupSize) - 1]); i = i + 1) begin
Maybe#(InstrFromFetch3) new_pick = Invalid;
if (frags[i] matches tagged Valid .frag) begin
Fetch3ToDecode prev_frag = (i != 0) ? validValue(frags[i-1]) : ?;
if (prev_frag_available &&& !is_16b_inst(prev_frag.inst_frag)) begin // 2nd half of 32-bit instruction
new_pick = tagged Valid fetch3s_2_inst(frag, prev_frag);
if (!validValue(new_pick).mispred_first_half) begin
doAssert(getAddr(decompressPc(prev_frag.pc))+2 == getAddr(decompressPc(frag.pc)), "Attached fragments with non-contigious PCs");
`ifdef RVFI_DII
doAssert(prev_frag.dii_pid+1 == frag.dii_pid, "Attached fragments with non-contigious DII IDs");
`endif
end
end else if (is_16b_inst(frag.inst_frag) || isValid(frag.cause)) begin // 16-bit instruction
new_pick = tagged Valid fetch3_2_instC(frag,
fv_decode_C (misa, misa_mxl_64, getFlags(decompressPc(frag.pc))==1, frag.inst_frag),
zeroExtend(frag.inst_frag));
end
Vector#(SupSize, Maybe#(InstrFromFetch3)) decodeIn = replicate(Invalid);
// Express the incoming fragments as a vector of maybes.
Vector#(SupSizeX2, Maybe#(Fetch3ToDecode)) frags;
for (Integer i = 0; i < valueOf(SupSizeX2); i = i + 1)
frags[i] = (f32d.deqS[i].canDeq) ? Valid (f32d.deqS[i].first) : Invalid;
// Pick as up to SupSize instructions from the f32d SupFifo.
// Stop picking when we have SupSize instructions or when we have exhausted the ports on the instruction fragment FIFO.
Maybe#(Bit#(TLog#(SupSizeX2))) m_used_frag_count = Invalid;
Bit#(TLog#(SupSize)) pick_count = 0;
Bool prev_frag_available = False;
for (Integer i = 0; i < valueOf(SupSizeX2) && !isValid(decodeIn[valueOf(SupSize) - 1]); i = i + 1) begin
Maybe#(InstrFromFetch3) new_pick = Invalid;
if (frags[i] matches tagged Valid .frag) begin
Fetch3ToDecode prev_frag = (i != 0) ? validValue(frags[i-1]) : ?;
if (prev_frag_available &&& !is_16b_inst(prev_frag.inst_frag)) begin // 2nd half of 32-bit instruction
new_pick = tagged Valid fetch3s_2_inst(frag, prev_frag);
/*if (!validValue(new_pick).mispred_first_half) begin
doAssert(getAddr(decompressPc(prev_frag.pc))+2 == getAddr(decompressPc(frag.pc)), "Attached fragments with non-contigious PCs");
`ifdef RVFI_DII
doAssert(prev_frag.dii_pid+1 == frag.dii_pid, "Attached fragments with non-contigious DII IDs");
`endif
end*/
end else if (is_16b_inst(frag.inst_frag) || isValid(frag.cause)) begin // 16-bit instruction
new_pick = tagged Valid fetch3_2_instC(frag,
fv_decode_C (misa, misa_mxl_64, getFlags(decompressPc(frag.pc))==1, frag.inst_frag),
zeroExtend(frag.inst_frag));
end
decodeIn[pick_count] = new_pick;
if (isValid(new_pick)) begin
if (verbose)
$display("Decode: picked instruction %d, next frag %d :", pick_count, i, fshow(decodeIn[pick_count]));
pick_count = pick_count + truncate(8'b1);
m_used_frag_count = tagged Valid fromInteger(i);
prev_frag_available = False;
end else prev_frag_available = isValid(frags[i]);
end
decodeIn[pick_count] = new_pick;
if (isValid(new_pick)) begin
//if (verbose)
// $display("Decode: picked instruction %d, next frag %d :", pick_count, i, fshow(decodeIn[pick_count]));
pick_count = pick_count + truncate(8'b1);
m_used_frag_count = tagged Valid fromInteger(i);
prev_frag_available = False;
end else prev_frag_available = isValid(frags[i]);
end
Bool anyReturns = False;
Vector#(SupSize, DecodeResult) decodeResults = ?;
for (Integer i = 0; i < valueOf(SupSize); i = i + 1) begin
CapMem pc = decompressPc(validValue(decodeIn[i]).pc);
decodeResults[i] = decode(validValue(decodeIn[i]).inst, getFlags(pc)==1); // Decode 32b inst, or 32b expansion of 16b inst
if (popInst(decodeResults[i])) anyReturns = True;
end
Bool delay_epoch = False;
`ifdef NO_SPEC_REDIRECT
// Delay decode if the head of this buffer matches the current epoch;
// this means the source of this epoch is still speculative.
delay_epoch = (main_epoch_spec.first.spec_bits != 0);
`endif
`ifdef NO_SPEC_RSB_PUSH
Bool delayForPop = ras.pendingPush && anyReturns;
delay_epoch = delay_epoch || delayForPop;
`endif
rule doDecode(f32d.deqS[0].canDeq && isCurrent(f32d.deqS[0].first) && !delay_epoch);
if (m_used_frag_count matches tagged Valid .used_frag_count) begin
for (Integer i = 0; i < valueOf(SupSizeX2) && fromInteger(i) <= used_frag_count; i = i + 1) f32d.deqS[i].deq;
if (verbose)
@@ -693,12 +731,12 @@ module mkFetchStage(FetchStage);
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
CapMem pc = decompressPc(validValue(decodeIn[i]).pc);
CapMem ppc = decompressPc(validValue(decodeIn[i]).ppc);
let decode_result = decode(validValue(decodeIn[i]).inst, getFlags(pc)==1); // Decode 32b inst, or 32b expansion of 16b inst
let decode_result = decodeResults[i]; // Decode 32b inst, or 32b expansion of 16b inst
let dInst = decode_result.dInst;
let regs = decode_result.regs;
PredTrainInfo trainInfo = ?; // dir pred training bookkeeping
DirPredResult#(DirPredTrainInfo) pred_res = DirPredResult{taken: False, train: ?};
if(decode_result.dInst.iType == Br && !likely_epoch_change) begin
if(dInst.iType == Br && !likely_epoch_change) begin
pred_res <- dirPred.pred[i].pred;
trainInfo.dir = pred_res.train;
likely_epoch_change = (pred_res.taken != validValue(decodeIn[i]).pred_jump);
@@ -836,8 +874,11 @@ module mkFetchStage(FetchStage);
end // if (decodeIn[i] matches tagged Valid .in)
end // for (Integer i = 0; i < valueof(SupSize); i=i+1)
`ifndef NO_SPEC_RSB_PUSH
if (m_push_addr matches tagged Valid .pc) ras.push(pc);
if (m_push_addr matches tagged Valid .pc)
`ifdef NO_SPEC_RSB_PUSH
ras.willPush;
`else
ras.push(pc);
`endif
// update PC and epoch

View File

@@ -55,6 +55,8 @@ typedef RasIndex RasPredTrainInfo;
interface ReturnAddrStack;
interface Vector#(SupSize, RAS) ras;
method Action willPush;
method Bool pendingPush;
method Action push(CapMem pushAddr);
method Action setHead(RasIndex h);
method Action flush;
@@ -67,6 +69,15 @@ module mkRas(ReturnAddrStack) provisos(NumAlias#(TExp#(TLog#(RasEntries)), RasEn
// head points past valid data
// to gracefully overflow, head is allowed to overflow to 0 and overwrite the oldest data
Ehr#(TAdd#(SupSize, 3), RasIndex) head <- mkEhr(0);
Ehr#(3, RasIndex) pendingPushReg <- mkEhr(0);
Reg#(Bit#(6)) pendingPushDelay <- mkReg(0);
rule updatePendingPushDelay(pendingPushReg[2] != 0);
if (pendingPushDelay > 8) begin
pendingPushReg[2] <= 0;
pendingPushDelay <= 0;
end else pendingPushDelay <= pendingPushDelay + 1;
endrule
`ifdef SECURITY
Reg#(Bool) flushDone <- mkReg(True);
@@ -95,11 +106,18 @@ module mkRas(ReturnAddrStack) provisos(NumAlias#(TExp#(TLog#(RasEntries)), RasEn
endinterface);
end
method Action willPush;
pendingPushReg[0]._write(pendingPushReg[0] + 1);
$display("RAS willPushReg<-%d", pendingPushReg[0] + 1);
endmethod
method Bool pendingPush = (pendingPushReg[0] != 0);
method Action push(CapMem pushAddr);
Reg#(RasIndex) h = head[valueof(SupSize) + 2];
stack[h+1][1] <= pushAddr;
$display("RAS push head<-%d, val:%x", h+1, pushAddr);
$display("RAS push head<-%d, val:%x, willPushReg<-%d", h+1, pushAddr, pendingPushReg[1] - 1);
h <= h+1;
if (pendingPushReg[1] != 0) pendingPushReg[1] <= pendingPushReg[1] - 1;
endmethod
method Action setHead(RasIndex h);

View File

@@ -39,7 +39,7 @@ BSC_COMPILATION_FLAGS += \
-D MEM64 \
-D RISCV \
-D TSO_MM \
-D NO_SPEC_TRAINING -D NO_SPEC_REDIRECT -D NO_SPEC_STRAIGHT_PATH -D SPEC_RSB_FIXUP \
-D NO_SPEC_TRAINING -D NO_SPEC_REDIRECT -D NO_SPEC_STRAIGHT_PATH -D SPEC_RSB_FIXUP -D NO_SPEC_RSB_PUSH \
-D INCLUDE_GDB_CONTROL \
-D BRVF_TRACE \
-D XILINX_BSCAN -D JTAG_TAP