Properly remove pipline stage in fetch and use a seperate rule to do the

proper TLB lookup if necessary.
This commit is contained in:
Jonathan Woodruff
2024-01-16 17:00:10 +00:00
parent eee5a2c23b
commit 4f91e54bd2
2 changed files with 86 additions and 71 deletions

View File

@@ -167,6 +167,7 @@ endinterface
// PC "compression" types to facilitate storing common upper PC bits in a
// shared structure
// Must be at least a page offset.
typedef 12 PcLsbSz; // Defines PC block size for PCs that will share an index for upper bits.
typedef TLog#(TMul#(SupSize,4)) PcIdxSz; // Number of distinct PC blocks allowed in-flight in the Fetch pipeline.
typedef Bit#(PcLsbSz) PcLSB;
@@ -362,8 +363,8 @@ module mkFetchStage(FetchStage);
// rule ordering: Fetch1 (BTB+TLB) < Fetch3 (decode & dir pred) < redirect method
// Fetch1 < Fetch3 to avoid bypassing path on PC and epochs
Bool verbose = False;
Integer verbosity = 0;
Bool verbose = True;
Integer verbosity = 2;
// Basic State Elements
Reg#(Bool) started <- mkConfigReg(False);
@@ -414,7 +415,7 @@ module mkFetchStage(FetchStage);
endaction);
// Pipeline Stage FIFOs
Fifo#(2, Fetch1ToFetch2) f12f2 <- mkBypassFifo;
Fifo#(4, Addr) translateAddress <- mkCFFifo;
Fifo#(4, Fetch2ToFetch3) f22f3 <- mkCFFifo; // FIFO should match I$ latency
// These two fifos needs a capacity of 3 for full throughput if we fire only when we can enq on on channels.
SupFifo#(SupSizeX2, 3, Fetch3ToDecode) f32d <- mkUGSupFifo; // Unguarded to prevent the static analyser from exploding.
@@ -475,64 +476,9 @@ module mkFetchStage(FetchStage);
nextAddrPred.put_pc(pc_reg[pc_final_port]);
endrule
// We don't send req to TLB when waiting for redirect or TLB flush. Since
// there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
// TLB idle to change VM CSR / signal flush TLB, there is no wrong path
// request afterwards to race with the system code that manage paget table.
rule doFetch1(started && !waitForRedirect[0] && !waitForFlush[0]);
let pc = pc_reg[pc_fetch1_port];
// Grab a chain of predictions from the BTB, which predicts targets for the next
// set of addresses based on the current PC.
Vector#(SupSizeX2, Maybe#(CapMem)) pred_future_pc = nextAddrPred.pred;
// Next pc is the first nextPc that breaks the chain of pc+4 or
// that is at the end of a cacheline.
Vector#(SupSizeX2,Integer) indexes = genVector;
function Bool findNextPc(CapMem in_pc, Integer i);
Bool notLastInst = getLineInstOffset(getAddr(in_pc) + fromInteger(2*i)) != maxBound;
Bool noJump = !isValid(pred_future_pc[i]);
return (!(notLastInst && noJump));
endfunction
Bit#(TLog#(SupSizeX2)) posLastSupX2 = fromInteger(fromMaybe(valueof(SupSizeX2) - 1, find(findNextPc(pc), indexes)));
Maybe#(CapMem) pred_next_pc = pred_future_pc[posLastSupX2];
let next_fetch_pc = fromMaybe(addPc(pc, 2 * (zeroExtend(posLastSupX2) + 1)), pred_next_pc);
pc_reg[pc_fetch1_port] <= next_fetch_pc;
`ifdef RVFI_DII
Dii_Parcel_Id dii_pid = dii_pid_reg[pc_fetch1_port];
dii_pid_reg[pc_fetch1_port] <= dii_pid + (zeroExtend(posLastSupX2) + 1);
`endif
// Send TLB request.
tlb_server.request.put (getAddr(pc));
let pc_idxs <- pcBlocks.insertAndReserve(truncateLSB(pc), truncateLSB(next_fetch_pc));
PcIdx pc_idx = pc_idxs.inserted;
PcIdx ppc_idx = pc_idxs.reserved;
let out = Fetch1ToFetch2 {
pc: compressPc(pc_idx, pc),
`ifdef RVFI_DII
dii_pid: dii_pid,
`endif
inst_frags_fetched: posLastSupX2,
pred_next_pc: isValid(pred_next_pc) ?
Valid(compressPc(ppc_idx, validValue(pred_next_pc))) : Invalid,
decode_epoch: decode_epoch[0],
main_epoch: f_main_epoch};
f12f2.enq(out);
if (verbose) $display("%d Fetch1: ", cur_cycle, fshow(out), " posLastSupX2: %d", posLastSupX2);
endrule
rule doFetch2;
let in = f12f2.first;
f12f2.deq;
// Get TLB response
match {.phys_pc, .cause, .allow_cap} <- tlb_server.response.get;
function Action start_imem_lookup(Fetch1ToFetch2 in, TlbResp tr) = action
match {.buffered_phys_pc, .cause, .allow_cap} = tr;
Addr phys_pc = unpack({buffered_phys_pc[63:12],in.pc.lsb[11:0]});
// Access main mem or boot rom if no TLB exception
Bool access_mmio = False;
`ifdef RVFI_DII
@@ -577,11 +523,80 @@ module mkFetchStage(FetchStage);
main_epoch: in.main_epoch };
f22f3.enq(out);
if (verbosity >= 2) begin
$display ("----------------");
$display ("Fetch2: TLB response pyhs_pc 0x%0h cause ", phys_pc, fshow (cause));
$display ("Fetch2: f2_tof3.enq: out ", fshow (out));
end
if (verbosity >= 2) begin
$display ("%d ----------------", cur_cycle);
$display ("%d Fetch1: TLB response pyhs_pc 0x%0h cause ", cur_cycle, phys_pc, fshow (cause));
$display ("%d Fetch1: f2_tof3.enq: out ", cur_cycle, fshow (out));
end
endaction;
Reg#(Maybe#(Vpn)) buffered_translation_virt_pc <- mkReg(Invalid);
Reg#(TlbResp) buffered_translation_tlb_resp <- mkRegU;
rule invalidate_buffered_translation(!iTlb.flush_done);
buffered_translation_virt_pc <= Invalid;
endrule
rule getTlbResp(iTlb.flush_done);
// Get TLB response
TlbResp tr <- tlb_server.response.get;
buffered_translation_virt_pc <= Valid(getVpn(translateAddress.first));
buffered_translation_tlb_resp <= tr;
translateAddress.deq;
if (verbosity >= 2) $display ("%d Fetch Translate: pc: %x, ", cur_cycle, translateAddress.first, fshow (tr));
endrule
// We don't send req to TLB when waiting for redirect or TLB flush. Since
// there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
// TLB idle to change VM CSR / signal flush TLB, there is no wrong path
// request afterwards to race with the system code that manage paget table.
rule doFetch1(started && !waitForRedirect[0] && !waitForFlush[0]);
let pc = pc_reg[pc_fetch1_port];
// Grab a chain of predictions from the BTB, which predicts targets for the next
// set of addresses based on the current PC.
Vector#(SupSizeX2, Maybe#(CapMem)) pred_future_pc = nextAddrPred.pred;
// Next pc is the first nextPc that breaks the chain of pc+4 or
// that is at the end of a cacheline.
Vector#(SupSizeX2,Integer) indexes = genVector;
function Bool findNextPc(CapMem in_pc, Integer i);
Bool notLastInst = getLineInstOffset(getAddr(in_pc) + fromInteger(2*i)) != maxBound;
Bool noJump = !isValid(pred_future_pc[i]);
return (!(notLastInst && noJump));
endfunction
Bit#(TLog#(SupSizeX2)) posLastSupX2 = fromInteger(fromMaybe(valueof(SupSizeX2) - 1, find(findNextPc(pc), indexes)));
Maybe#(CapMem) pred_next_pc = pred_future_pc[posLastSupX2];
`ifdef RVFI_DII
Dii_Parcel_Id dii_pid = dii_pid_reg[pc_fetch1_port];
dii_pid_reg[pc_fetch1_port] <= dii_pid + (zeroExtend(posLastSupX2) + 1);
`endif
if (buffered_translation_virt_pc matches tagged Valid .vpn &&& getVpn(getAddr(pc)) == vpn) begin
let next_fetch_pc = fromMaybe(addPc(pc, 2 * (zeroExtend(posLastSupX2) + 1)), pred_next_pc);
let pc_idxs <- pcBlocks.insertAndReserve(truncateLSB(pc), truncateLSB(next_fetch_pc));
PcIdx pc_idx = pc_idxs.inserted;
PcIdx ppc_idx = pc_idxs.reserved;
let out = Fetch1ToFetch2 {
pc: compressPc(pc_idx, pc),
`ifdef RVFI_DII
dii_pid: dii_pid,
`endif
inst_frags_fetched: posLastSupX2,
pred_next_pc: isValid(pred_next_pc) ?
Valid(compressPc(ppc_idx, validValue(pred_next_pc))) : Invalid,
decode_epoch: decode_epoch[0],
main_epoch: f_main_epoch};
start_imem_lookup(out, buffered_translation_tlb_resp);
pc_reg[pc_fetch1_port] <= next_fetch_pc;
if (verbose) $display("%d Fetch1: ", cur_cycle, fshow(out), " posLastSupX2: %d", posLastSupX2);
end else begin
// Send TLB request.
translateAddress.enq(getAddr(pc));
tlb_server.request.put (getAddr(pc));
if (verbose) $display("%d Fetch1 lookup: ", cur_cycle, " posLastSupX2: %d", posLastSupX2);
end
endrule
// Break out of i$
@@ -591,9 +606,9 @@ module mkFetchStage(FetchStage);
let fetch3In = f22f3.first;
if (verbosity >= 2) begin
if (f22f3.notEmpty)
$display("Fetch3: fetch3In: ", fshow (fetch3In));
$display("%d Fetch3: fetch3In: ", cur_cycle, fshow (fetch3In));
else
$display("Fetch3: Nothing else from Fetch2");
$display("%d Fetch3: Nothing else from Fetch2", cur_cycle);
end
// Get ICache/MMIO response if no exception
@@ -709,7 +724,7 @@ module mkFetchStage(FetchStage);
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)
$display("Decode: dequed %d instruction fragments", used_frag_count);
$display("%d Decode: dequed %d instruction fragments", cur_cycle, used_frag_count);
end
Maybe#(CapMem) redirectPc = Invalid; // next pc redirect by branch predictor
@@ -930,7 +945,7 @@ module mkFetchStage(FetchStage);
// (2) all internal FIFOs are empty (the output sup fifo needs not to be
// empty, but why leave this security hole)
Bool empty_for_flush = waitForFlush[0] &&
!f12f2.notEmpty && !f22f3.notEmpty &&
!translateAddress.notEmpty && !f22f3.notEmpty &&
f32d.internalEmpty && out_fifo.internalEmpty;
interface Vector pipelines = out_fifo.deqS;

View File

@@ -115,7 +115,7 @@ module mkITlb(ITlb::ITlb);
Reg#(Bool) waitFlushP <- mkReg(False);
// resp FIFO to proc
Fifo#(2, TlbResp) hitQ <- mkBypassFifo;
Fifo#(2, TlbResp) hitQ <- mkCFFifo;
// current processor VM information
Reg#(VMInfo) vm_info <- mkReg(defaultValue);