Merge branch 'master' into RVFI_DII, a complex merge for the fetch stage!

This commit is contained in:
Jonathan Woodruff
2020-03-18 11:35:59 +00:00
1059 changed files with 1467937 additions and 324026 deletions

View File

@@ -27,6 +27,18 @@ interface ResetGuard;
method Bool isReady;
endinterface
`ifdef BSIM
module mkResetGuard(ResetGuard);
Reg#(Bool) ready <- mkReg(False);
(* no_implicit_conditions, fire_when_enabled *)
rule rl_ready;
ready <= True;
endrule
method isReady = ready;
endmodule
`else
import "BVI" reset_guard =
module mkResetGuard(ResetGuard);
default_clock clk(CLK);
@@ -36,3 +48,4 @@ module mkResetGuard(ResetGuard);
schedule (isReady) CF (isReady);
endmodule
`endif

View File

@@ -76,10 +76,15 @@ module mkIntDivUnsignedSim(IntDivUnsignedImport);
let {dividend, user} = dividendQ.first;
let divisor = divisorQ.first;
// Be careful to avoid divide-by-zero in bluesim's C++, which turns
// res = cond ? exp1 : exp2
// into
// tmp1 = exp1; tmp2 = exp2; res = cond ? tmp1 : tmp2
// so we must give a fake non-zero input even if it looks unused.
UInt#(64) a = unpack(dividend);
UInt#(64) b = unpack(divisor);
Bit#(64) q = pack(a / b);
Bit#(64) r = pack(a % b);
UInt#(64) b = divisor == 0 ? 1 : unpack(divisor);
Bit#(64) q = divisor == 0 ? maxBound : pack(a / b);
Bit#(64) r = divisor == 0 ? dividend : pack(a % b);
respQ.enq(tuple2({q, r}, user));
endrule

View File

@@ -41,6 +41,8 @@ import SpecFifo::*;
import HasSpecBits::*;
import Bypass::*;
import Cur_Cycle :: *;
// ALU pipeline has 4 stages
// dispatch -> reg read -> exe -> finish (write reg)
// bypass is sent out from the end of exe stage
@@ -78,6 +80,7 @@ typedef struct {
Maybe#(PhyDst) dst;
InstTag tag;
DirPredTrainInfo dpTrain;
Bool isCompressed;
// result
Data data; // alu compute result
Maybe#(Data) csrData; // data to write CSR file
@@ -126,6 +129,7 @@ typedef struct {
Bool taken;
DirPredTrainInfo dpTrain;
Bool mispred;
Bool isCompressed;
} FetchTrainBP deriving(Bits, Eq, FShow);
interface AluExeInput;
@@ -142,6 +146,7 @@ interface AluExeInput;
method Bit #(32) rob_getOrig_Inst (InstTag t);
method Action rob_setExecuted(
InstTag t,
Data dst_data,
Maybe#(Data) csrData,
ControlFlow cf
`ifdef RVFI
@@ -301,6 +306,7 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
dst: x.dst,
tag: x.tag,
dpTrain: x.dpTrain,
isCompressed: x.orig_inst[1:0] != 2'b11,
data: exec_result.data,
csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : tagged Invalid,
`ifdef RVFI
@@ -331,6 +337,7 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
// update the instruction in the reorder buffer.
inIfc.rob_setExecuted(
x.tag,
x.data,
x.csrData,
x.controlFlow
`ifdef RVFI
@@ -352,7 +359,8 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
iType: x.iType,
taken: x.controlFlow.taken,
dpTrain: x.dpTrain,
mispred: True
mispred: True,
isCompressed: x.isCompressed
});
`ifdef PERF_COUNT
// performance counter
@@ -380,7 +388,8 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
iType: x.iType,
taken: x.controlFlow.taken,
dpTrain: x.dpTrain,
mispred: False
mispred: False,
isCompressed: x.isCompressed
});
end
end

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -27,6 +28,7 @@ import GetPut::*;
import Cntrs::*;
import ConfigReg::*;
import FIFO::*;
import FIFOF::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
@@ -42,6 +44,11 @@ import RenameDebugIF::*;
`ifdef RVFI
import RVFI_DII_Types::*;
`endif
import Cur_Cycle :: *;
`ifdef INCLUDE_TANDEM_VERIF
import Trace_Data2 :: *;
`endif
typedef struct {
// info about the inst blocking at ROB head
@@ -60,7 +67,7 @@ typedef struct {
Bool stbEmpty;
Bool stqEmpty;
Bool tlbNoPendingReq;
// CSR info: previlige mode
// CSR info: privilege mode
Bit#(2) prv;
// inst count
Data instCount;
@@ -94,6 +101,9 @@ interface CommitInput;
`endif
);
method Action setFetchWaitRedirect;
`ifdef INCLUDE_GDB_CONTROL
method Action setFetchWaitFlush;
`endif
method Action incrementEpoch;
// record if we commit a CSR inst or interrupt
method Action commitCsrInstOrInterrupt;
@@ -101,6 +111,10 @@ interface CommitInput;
method Bool doStats;
// deadlock check
method Bool checkDeadlock;
`ifdef INCLUDE_TANDEM_VERIF
interface Vector #(SupSize, Put #(Trace_Data2)) v_to_TV;
`endif
endinterface
typedef struct {
@@ -124,6 +138,10 @@ interface CommitStage;
// RVFI trace report. Not an input?
method Get#(Rvfi_Traces) rvfi;
`endif
`ifdef INCLUDE_GDB_CONTROL
method Bool is_debug_halted;
method Action debug_resume;
`endif
endinterface
// we apply actions the end of commit rule
@@ -132,6 +150,7 @@ typedef struct {
Addr pc;
Addr addr;
Trap trap;
Bit #(32) orig_inst;
`ifdef RVFI_DII
ToReorderBuffer x;
`endif
@@ -201,12 +220,117 @@ function Maybe#(RVFI_DII_Execution#(DataSz,DataSz)) genRVFI(ToReorderBuffer rot,
endfunction
`endif
module mkCommitStage#(CommitInput inIfc)(CommitStage);
Bool verbose = True;
`ifdef INCLUDE_GDB_CONTROL
// Bluespec: for lightweight verbosity trace
Integer verbosity = 0;
Reg #(Bit #(64)) rg_instret <- mkReg (0);
typedef enum {
RUN_STATE_RUNNING, // Normal state
RUN_STATE_DEBUGGER_HALTED // When halted for debugger
} Run_State
deriving (Eq, FShow, Bits);
`endif
module mkCommitStage#(CommitInput inIfc)(CommitStage);
Bool verbose = False;
Integer verbosity = 0; // Bluespec: for lightweight verbosity trace
// Used to inform tandem-verifier about program order.
// 0 is used to indicate we've just come out of reset
// TODO: we could use fewer bits and allow and recognize wraparound.
Reg #(Bit #(64)) rg_serial_num <- mkReg (0);
`ifdef INCLUDE_TANDEM_VERIF
FIFOF #(ToReorderBuffer) f_rob_data <- mkFIFOF;
`endif
`ifdef INCLUDE_GDB_CONTROL
Reg #(Run_State) rg_run_state <- mkReg (RUN_STATE_RUNNING);
`endif
`ifdef INCLUDE_TANDEM_VERIF
Integer way0 = 0;
ToReorderBuffer no_deq_data = ?;
Bit #(5) no_fflags = ?;
Data no_mstatus = ?;
Maybe #(Trap_Updates) no_trap_updates = tagged Invalid;
Maybe #(RET_Updates) no_ret_updates = tagged Invalid;
function Action fa_to_TV (Integer way,
Bit #(64) serial_num,
Maybe #(Tuple2 #(Bit #(12), Data)) maybe_csr_upd,
ToReorderBuffer deq_data,
Bit #(5) fflags,
Data mstatus,
Maybe #(Trap_Updates) m_trap_updates,
Maybe #(RET_Updates) m_ret_updates);
action
let tval = (m_trap_updates matches tagged Valid .tu ? tu.tval : deq_data.tval);
let upd_pc = (m_ret_updates matches tagged Valid .ru ? ru.new_pc : deq_data.pc);
let x = Trace_Data2 {serial_num: serial_num,
maybe_csr_upd: maybe_csr_upd,
pc: upd_pc,
orig_inst: deq_data.orig_inst,
iType: deq_data.iType,
dst: deq_data.dst,
dst_data: deq_data.dst_data,
store_data: deq_data.store_data,
store_data_BE: deq_data.store_data_BE,
csr: deq_data.csr,
trap: deq_data.trap,
tval: tval,
ppc_vaddr_csrData: deq_data.ppc_vaddr_csrData,
fflags: fflags, // deq_data.fflags only has incremental flags
will_dirty_fpu_state: deq_data.will_dirty_fpu_state,
mstatus: mstatus, // when SD/XS/FS have changed
// Trap and RET updates
prv: ( m_trap_updates matches tagged Valid .tu
? tu.prv
: (m_ret_updates matches tagged Valid .ru
? ru.prv
: ?)),
status: ( m_trap_updates matches tagged Valid .tu
? tu.status
: (m_ret_updates matches tagged Valid .ru
? ru.status
: ?)),
tvec: fromMaybe (?, m_trap_updates).new_pc,
cause: fromMaybe (?, m_trap_updates).cause,
epc: fromMaybe (?, m_trap_updates).epc
};
inIfc.v_to_TV [way].put (x);
endaction
endfunction
Reg #(Bool) rg_just_after_reset <- mkReg (True);
rule rl_send_tv_reset (rg_just_after_reset);
Bit #(64) serial_num = 0;
fa_to_TV (way0, serial_num,
tagged Invalid,
no_deq_data, no_fflags, no_mstatus, no_trap_updates, no_ret_updates);
rg_just_after_reset <= False;
rg_serial_num <= 1;
endrule
Reg #(Data) rg_old_mip_csr_val <- mkReg (0);
Data new_mip_csr_val = inIfc.csrfIfc.getMIP;
Bool send_mip_csr_change_to_tv = (new_mip_csr_val != rg_old_mip_csr_val);
rule rl_send_mip_csr_change_to_tv ((! rg_just_after_reset) && send_mip_csr_change_to_tv);
fa_to_TV (way0, rg_serial_num,
tagged Valid (tuple2 (pack (CSRmip), new_mip_csr_val)),
no_deq_data, no_fflags, no_mstatus, no_trap_updates, no_ret_updates);
rg_old_mip_csr_val <= new_mip_csr_val;
rg_serial_num <= rg_serial_num + 1;
endrule
`else
Bool send_mip_csr_change_to_tv = False;
`endif
// func units
ReorderBufferSynth rob = inIfc.robIfc;
@@ -432,12 +556,56 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
endaction
endfunction
`ifdef INCLUDE_GDB_CONTROL
// Maintain system consistency when halting into debug mode
// This code is patterned after 'makeSystemConsistent' above
function Action makeSystemConsistent_for_debug_mode;
action
inIfc.setFlushTlbs;
// notify TLB to keep update of CSR changes
inIfc.setUpdateVMInfo;
// always wait store buffer and SQ to be empty
when(inIfc.stbEmpty && inIfc.stqEmpty, noAction);
// We wait TLB to finish all requests and become sync with memory.
// Notice that currently TLB is read only, so TLB is always in sync
// with memory (i.e., there is no write to commit to memory). Since all
// insts have been killed, nothing can be issued to D TLB at this time.
// Since fetch stage is set to wait for redirect, fetch1 stage is
// stalled, and nothing can be issued to I TLB at this time.
// Therefore, we just need to make sure that I and D TLBs are not
// handling any miss req. Besides, when I and D TLBs do not have any
// miss req, L2 TLB must be idling.
when(inIfc.tlbNoPendingReq, noAction);
// yield load reservation in cache
inIfc.setFlushReservation;
inIfc.setFlushBrPred;
inIfc.setFlushCaches;
`ifdef SELF_INV_CACHE
// reconcile I$
if(reconcileI) begin
inIfc.setReconcileI;
end
`ifdef SYSTEM_SELF_INV_L1D
// FIXME is this reconcile of D$ necessary?
inIfc.setReconcileD;
`endif
`endif
endaction
endfunction
`endif
// TODO Currently we don't check spec bits == 0 when we commit an
// instruction. This is because killings of wrong path instructions are
// done in a single cycle. However, when we make killings distributed or
// pipelined, then we need to check spec bits at commit port.
rule doCommitTrap_flush(
`ifdef INCLUDE_GDB_CONTROL
(rg_run_state == RUN_STATE_RUNNING) &&&
`endif
!isValid(commitTrap) &&&
rob.deqPort[0].deq_data.trap matches tagged Valid .trap
);
@@ -457,15 +625,19 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
let commitTrap_val = Valid (CommitTrap {
trap: trap,
pc: x.pc,
addr: vaddr
addr: vaddr,
orig_inst: x.orig_inst
`ifdef RVFI_DII
, x: x
`endif
});
commitTrap <= commitTrap_val;
`ifdef INCLUDE_TANDEM_VERIF
f_rob_data.enq (x); // Save data to be sent to TV in rule doCommitTrap_handle, next
`endif
if (verbosity > 0) begin
$display ("instret:%0d PC:0x%0h instr:0x%08h", rg_instret, x.pc, x.orig_inst,
if (verbosity >= 1) begin
$display ("instret:%0d PC:0x%0h instr:0x%08h", rg_serial_num, x.pc, x.orig_inst,
" iType:", fshow (x.iType), " [doCommitTrap]");
end
if (verbose) begin
@@ -501,41 +673,103 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
doAssert(x.spec_bits == 0, "cannot have spec bits");
endrule
rule doCommitTrap_handle(commitTrap matches tagged Valid .trap);
rule doCommitTrap_handle(
commitTrap matches tagged Valid .trap
`ifdef INCLUDE_GDB_CONTROL
&&& (rg_run_state == RUN_STATE_RUNNING)
`endif
&&& (! send_mip_csr_change_to_tv));
// reset commitTrap
commitTrap <= Invalid;
`ifdef INCLUDE_TANDEM_VERIF
let x = f_rob_data.first;
f_rob_data.deq;
`endif
// notify commit of interrupt (so MMIO pRq may be handled)
if(trap.trap matches tagged Interrupt .inter) begin
inIfc.commitCsrInstOrInterrupt;
end
if (verbose) $display ("CommitStage.doCommitTrap_handle: ", fshow (commitTrap));
// trap handling & redirect
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
inIfc.redirectPc(new_pc
`ifdef INCLUDE_GDB_CONTROL
else if (trap.trap == tagged Exception Breakpoint) begin
inIfc.commitCsrInstOrInterrupt; // TODO: Why?
end
`endif
Bool debugger_halt = False;
`ifdef INCLUDE_GDB_CONTROL
if ((trap.trap == tagged Interrupt DebugHalt)
|| (trap.trap == tagged Interrupt DebugStep)
|| ((trap.trap == tagged Exception Breakpoint) && (csrf.dcsr_break_bit == 1'b1)))
begin
debugger_halt = True;
// Flush everything (tlbs, caches, reservation, branch predictor);
// reconcilei and I; update VM info.
makeSystemConsistent_for_debug_mode;
// Save values in debugger CSRs
Bit #(3) dcsr_cause = ( (trap.trap == tagged Interrupt DebugHalt)
? 3
: ( (trap.trap == tagged Interrupt DebugStep)
? 4
: 1));
csrf.dcsr_cause_write (dcsr_cause);
csrf.dpc_write (trap.pc);
// Tell fetch stage to wait for redirect
// Note: rule doCommitTrap_flush may have done this already; redundant call is ok.
inIfc.setFetchWaitRedirect;
inIfc.setFetchWaitFlush;
// Go to quiescent state until debugger resumes execution
rg_run_state <= RUN_STATE_DEBUGGER_HALTED;
if (verbosity >= 2)
$display ("%0d: %m.commitStage.doCommitTrap_handle; debugger halt:", cur_cycle);
end
`endif
if (! debugger_halt) begin
// trap handling & redirect
let trap_updates <- csrf.trap(trap.trap, trap.pc, trap.addr, trap.orig_inst);
inIfc.redirectPc(trap_updates.new_pc
`ifdef RVFI_DII
, trap.x.diid + 1
, trap.x.diid + 1
`endif
);
);
`ifdef RVFI
Rvfi_Traces rvfis = replicate(tagged Invalid);
rvfis[0] = genRVFI(trap.x, traceCnt, getTSB(), new_pc);
rvfiQ.enq(rvfis);
traceCnt <= traceCnt + 1;
Rvfi_Traces rvfis = replicate(tagged Invalid);
rvfis[0] = genRVFI(trap.x, traceCnt, getTSB(), trap_updates.new_pc);
rvfiQ.enq(rvfis);
traceCnt <= traceCnt + 1;
`endif
// system consistency
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
// does not include prv info, and it has to flush when prv changes.
// XXX As approximation, Trap may cause context switch, so flush for
// security
makeSystemConsistent(False, True, False);
`ifdef INCLUDE_TANDEM_VERIF
fa_to_TV (way0, rg_serial_num,
tagged Invalid,
x, no_fflags, no_mstatus, tagged Valid trap_updates, no_ret_updates);
`endif
rg_serial_num <= rg_serial_num + 1;
// system consistency
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
// does not include prv info, and it has to flush when prv changes.
// XXX As approximation, Trap may cause context switch, so flush for
// security
makeSystemConsistent(False, True, False);
end
endrule
// commit misspeculated load
rule doCommitKilledLd(
`ifdef INCLUDE_GDB_CONTROL
(rg_run_state == RUN_STATE_RUNNING) &&&
`endif
!isValid(commitTrap) &&&
!isValid(rob.deqPort[0].deq_data.trap) &&&
rob.deqPort[0].deq_data.ldKilled matches tagged Valid .killBy
@@ -574,25 +808,33 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
// commit system inst
rule doCommitSystemInst(
`ifdef INCLUDE_GDB_CONTROL
(rg_run_state == RUN_STATE_RUNNING) &&
`endif
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
isSystem(rob.deqPort[0].deq_data.iType)
isSystem(rob.deqPort[0].deq_data.iType) &&
(! send_mip_csr_change_to_tv)
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitSystemInst] ", fshow(x));
if (verbosity > 0) begin
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_instret, x.pc, x.orig_inst,
if (verbosity >= 1) begin
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_serial_num, x.pc, x.orig_inst,
" iType:", fshow (x.iType), " [doCommitSystemInst]");
rg_instret <= rg_instret + 1;
end
// we claim a phy reg for every inst, so commit its renaming
regRenamingTable.commit[0].commit;
Bool write_satp = False; // flush tlb when satp csr is modified
`ifdef INCLUDE_TANDEM_VERIF
Data new_mstatus = no_mstatus;
`endif
Bool write_satp = False; // flush tlb when satp csr is modified
Bool flush_security = False; // flush for security when the flush csr is written
if(x.iType == Csr) begin
// notify commit of CSR (so MMIO pRq may be handled)
@@ -607,6 +849,17 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
doAssert(False, "must have csr data");
end
csrf.csrInstWr(csr_idx, csr_data);
`ifdef INCLUDE_TANDEM_VERIF
Data data_warl_xformed = csrf.warl_xform (csr_idx, csr_data);
x.ppc_vaddr_csrData = tagged CSRData data_warl_xformed;
if (x.will_dirty_fpu_state) begin
Data old_mstatus = csrf.rd (CSRmstatus);
new_mstatus = { 1'b1, old_mstatus [62:15], 2'b11, old_mstatus [12:0] };
end
`endif
// check if satp is modified or not
write_satp = csr_idx == CSRsatp;
`ifdef SECURITY
@@ -617,11 +870,22 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
// redirect (Sret and Mret redirect pc is got from CSRF)
Addr next_pc = x.ppc_vaddr_csrData matches tagged PPC .ppc ? ppc : (x.pc + 4);
doAssert(next_pc == x.pc + 4, "ppc must be pc + 4");
`ifdef INCLUDE_TANDEM_VERIF
Maybe #(RET_Updates) m_ret_updates = no_ret_updates;
`endif
if(x.iType == Sret) begin
next_pc <- csrf.sret;
RET_Updates ret_updates <- csrf.sret;
next_pc = ret_updates.new_pc;
`ifdef INCLUDE_TANDEM_VERIF
m_ret_updates = tagged Valid ret_updates;
`endif
end
else if(x.iType == Mret) begin
next_pc <- csrf.mret;
RET_Updates ret_updates <- csrf.mret;
next_pc = ret_updates.new_pc;
`ifdef INCLUDE_TANDEM_VERIF
m_ret_updates = tagged Valid ret_updates;
`endif
end
inIfc.redirectPc(next_pc
`ifdef RVFI_DII
@@ -636,6 +900,13 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
traceCnt <= traceCnt + 1;
`endif
`ifdef INCLUDE_TANDEM_VERIF
fa_to_TV (way0, rg_serial_num,
tagged Invalid,
x, no_fflags, new_mstatus, no_trap_updates, m_ret_updates);
`endif
rg_serial_num <= rg_serial_num + 1;
// rename stage only sends out system inst when ROB is empty, so no
// need to flush ROB again
@@ -672,7 +943,9 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
// checks
doAssert(x.epochIncremented, "must have already incremented epoch");
doAssert((x.iType == Csr) == isValid(x.csr), "only CSR has valid csr idx");
doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
// RSN 2020-03-08: Removed this assertion. Csr instrs that write to
// fflags/frm/fcsr do indeed 'dirty' the fpu state
// doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
doAssert(x.spec_bits == 0, "cannot have spec bits");
doAssert(x.claimed_phy_reg, "must have claimed phy reg");
`ifdef RENAME_DEBUG
@@ -709,11 +982,15 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
// commit normal: fire when at least one commit can be done
rule doCommitNormalInst(
`ifdef INCLUDE_GDB_CONTROL
(rg_run_state == RUN_STATE_RUNNING) &&
`endif
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
!isSystem(rob.deqPort[0].deq_data.iType)
!isSystem(rob.deqPort[0].deq_data.iType) &&
(! send_mip_csr_change_to_tv)
);
// stop superscalar commit after we
// 1. see a trap or system inst or killed Ld
@@ -747,6 +1024,12 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
Bit #(64) instret = 0;
`ifdef INCLUDE_TANDEM_VERIF
// These variables accumulate fflags and mstatus in sequential Program Order ('po')
// (whereas the 'fflags' variable does just one update after superscalar retirement).
Bit #(5) po_fflags = ?;
Data po_mstatus = ?;
`endif
// compute what actions to take
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && rob.deqPort[i].canDeq) begin
@@ -765,12 +1048,29 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
whichTrace = whichTrace + 1;
`endif
if (verbosity > 0) begin
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_instret + instret, x.pc, x.orig_inst,
if (verbosity >= 1) begin
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_serial_num + instret, x.pc, x.orig_inst,
" iType:", fshow (x.iType), " [doCommitNormalInst [%0d]]", i);
instret = instret + 1;
end
`ifdef INCLUDE_TANDEM_VERIF
Bool init_for_way0 = (i == 0);
match {. new_fflags, .new_mstatus} = csrf.fpuInst_csr_updates (x.fflags,
init_for_way0,
po_fflags,
po_mstatus);
po_fflags = new_fflags;
po_mstatus = new_mstatus;
fa_to_TV (i, rg_serial_num + instret,
tagged Invalid,
x,
po_fflags,
po_mstatus,
no_trap_updates, no_ret_updates);
`endif
instret = instret + 1;
// inst can be committed, deq it
rob.deqPort[i].deq;
@@ -822,7 +1122,7 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
end
end
end
rg_instret <= rg_instret + instret;
rg_serial_num <= rg_serial_num + instret;
// write FPU csr
if(csrf.fpuInstNeedWr(fflags, will_dirty_fpu_state)) begin
@@ -873,6 +1173,8 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
`endif
endrule
// ================================================================
// INTERFACE
method Data getPerf(ComStagePerfType t);
return (case(t)
@@ -926,4 +1228,17 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
endmethod
interface renameErr = nullGet;
`endif
`ifdef INCLUDE_GDB_CONTROL
method Bool is_debug_halted;
return (rg_run_state == RUN_STATE_DEBUGGER_HALTED);
endmethod
method Action debug_resume () if (rg_run_state == RUN_STATE_DEBUGGER_HALTED);
rg_run_state <= RUN_STATE_RUNNING;
if (verbosity >= 2)
$display ("%0d: %m.commitStage.debug_resume", cur_cycle);
endmethod
`endif
endmodule

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -37,6 +38,7 @@ import MemoryTypes::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import DefaultValue::*;
import Ras::*;
import EpochManager::*;
import Performance::*;
@@ -54,6 +56,8 @@ import RVFI_DII_Types::*;
import Types::*;
`endif
import Cur_Cycle :: *;
// ================================================================
// For fv_decode_C function and related types and definitions
@@ -86,10 +90,13 @@ interface FetchStage;
, Dii_Id id
`endif
);
`ifdef INCLUDE_GDB_CONTROL
method Action setWaitFlush;
`endif
method Action done_flushing();
method Action train_predictors(
Addr pc, Addr next_pc, IType iType, Bool taken,
DirPredTrainInfo dpTrain, Bool mispred
DirPredTrainInfo dpTrain, Bool mispred, Bool isCompressed
);
// security
@@ -117,26 +124,34 @@ typedef struct {
typedef struct {
Addr pc;
Addr pred_next_pc;
Maybe#(Addr) pred_next_pc;
Bool fetch3_epoch;
Bool decode_epoch;
Epoch main_epoch;
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr phys_pc;
Addr pred_next_pc;
Maybe#(Addr) pred_next_pc;
Maybe#(Exception) cause;
Addr tval; // in case of exception
Bool access_mmio; // inst fetch from MMIO
Bool fetch3_epoch;
Bool decode_epoch;
Epoch main_epoch;
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);
// TODO: this name 'Fetch3ToDecode' is a misnomer.
// The struct passed from doFetch3 to doDecode is Fetch2ToFetch3 (same type as doFetch2 to doFetch3),
// and Fetch3ToDecode is used purely internally in doDecode.
typedef struct {
Addr pc;
Addr pred_next_pc;
Bool mispred_first_half;
Maybe#(Exception) cause;
Addr tval; // in case of exception
Bool decode_epoch;
Epoch main_epoch;
} Fetch3ToDecode deriving(Bits, Eq, FShow);
// Used purely internally in doDecode.
typedef struct {
Addr pc;
Addr ppc;
@@ -144,7 +159,7 @@ typedef struct {
Epoch main_epoch;
Instruction inst;
Maybe#(Exception) cause;
} Fetch3ToDecode deriving(Bits, Eq, FShow);
} InstrFromFetch3 deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
@@ -198,13 +213,25 @@ endfunction
typedef TMul #(SupSize, 2) SupSizeX2;
typedef Bit #(TLog #(TAdd #(SupSizeX2, 1))) SupCntX2;
// Merging up to SupSize-1 pending instructions with up to SupSizeX2 decoded
// instructions produces up to 3*SupSize-1 instructions; SupSize can be issued
// if present, leaving up to 2*SupSize-1 pending.
typedef TSub #(TMul #(SupSize, 2), 1) SupSizeX2S1;
typedef Bit #(TLog #(TAdd #(SupSizeX2S1, 1))) SupCntX2S1;
typedef TSub #(TMul #(SupSize, 3), 1) SupSizeX3S1;
typedef Bit #(TLog #(TAdd #(SupSizeX3S1, 1))) SupCntX3S1;
// Appending the pending and decoded vectors produces an intermediate
// 4*SupSize-1 vector (with only up to 3*SupSize-1 elements non-empty).
typedef TSub #(TMul #(SupSize, 4), 1) SupSizeX4S1;
typedef Bit #(TLog #(TAdd #(SupSizeX4S1, 1))) SupCntX4S1;
// Parsing a sequence of 16-bit parcels returns a sequence of the
// following kinds or items
typedef enum {Inst_None, // When we run off the end of the sequence
Inst_16b, // A 16b instruction
Inst_32b, // A 32b instruction
Inst_32b_Lsbs // Lower 16b of a 32b instr
Inst_32b // A 32b instruction
} Inst_Kind
deriving (Bits, Eq, FShow);
@@ -219,6 +246,12 @@ typedef struct {
} Inst_Item
deriving (Bits, Eq, FShow);
instance DefaultValue #(Inst_Item);
function Inst_Item defaultValue = Inst_Item {
pc: 0, inst_kind: Inst_None, orig_inst: 0, inst: 0
};
endinstance
// Input 'inst_d' was fetched from memory: up to superscalar-size sequence of 32b parcels.
// Convert this into 16b parcels, prior to re-parsing for possible mix of 32b and 16b instructions.
// This is a pure function; ActionValue is used only to allow $displays for debugging.
@@ -242,35 +275,57 @@ endfunction
// Parse 16b parcels (v_x16) into a sequence of 16b or 32b instructions.
// This is a pure function; ActionValue is used only to allow $displays for debugging.
function ActionValue #(Vector #(SupSize, Inst_Item))
function ActionValue #(Tuple4 #(SupCntX2,
Vector #(SupSizeX2, Inst_Item),
Addr,
Maybe #(Tuple3 #(Addr, Bit #(16), Bool))))
fav_parse_insts (Bool verbose,
Addr pc_start,
SupCntX2 n_x16s,
Vector #(SupSizeX2, Bit #(16)) v_x16);
Maybe #(Addr) pred_next_pc,
Maybe #(Tuple3 #(Addr, Bit #(16), Bool)) pending_straddle,
SupCntX2 n_x16s,
Vector #(SupSizeX2, Bit #(16)) v_x16);
actionvalue
// Parse up to SupSize instructions (v_items) from fetched v_x16 parcels (v_x16).
Vector #(SupSize, Inst_Item) v_items = replicate (Inst_Item {pc: pc_start,
inst_kind: Inst_None,
orig_inst: 0,
inst: 0});
SupCntX2 j = ((pc_start [1:0] == 2'b00) ? 0 : 1); // Start parse at parcel 0/1 depending on pc lsbs
// Parse up to SupSizeX2 instructions (v_items) from fetched v_x16 parcels (v_x16).
Vector #(SupSizeX2, Inst_Item) v_items = replicate (Inst_Item {pc: pc_start,
inst_kind: Inst_None,
orig_inst: 0,
inst: 0});
Maybe #(Tuple3 #(Addr, Bit #(16), Bool)) next_straddle = tagged Invalid;
// Start parse at parcel 0/1 depending on pc lsbs and pending straddle
SupCntX2 j = ((pc_start [1:0] == 2'b00 || isValid(pending_straddle)) ? 0 : 1);
`ifdef RVFI_DII
j = 0;
`endif
Addr pc = pc_start;
for (Integer i = 0; i < valueOf (SupSize); i = i + 1) begin
Integer n_items = 0;
`ifndef RVFI_DII
for (Integer i = 0; i < valueOf (SupSizeX2); i = i + 1) begin
Inst_Kind inst_kind = Inst_None;
Bit #(32) orig_inst = 0;
Bit #(32) inst = 0;
Addr next_pc = pc;
if (j < n_x16s) begin
if (is_16b_inst (v_x16 [j])) begin
if (i == 0 &&& pending_straddle matches tagged Valid {.s_pc, .s_lsbs, .s_mispred}) begin
if (pc != s_pc + 2) begin
$display ("FetchStage.fav_parse_insts: straddle: pc mismatch: pc = 0x%0h but s_pc = 0x%0h", pc, s_pc);
dynamicAssert (False, "FetchStage.fav_parse_insts: straddle: pc mismatch");
end
pc = s_pc;
inst_kind = Inst_32b;
orig_inst = { v_x16[0], s_lsbs };
inst = orig_inst;
j = 1;
next_pc = s_pc + 4;
n_items = 1;
end
else if (is_16b_inst (v_x16 [j])) begin
inst_kind = Inst_16b;
orig_inst = zeroExtend (v_x16 [j]);
inst = fv_decode_C (misa, misa_mxl_64, v_x16 [j]); // Expand 16b inst to 32b inst
j = j + 1;
next_pc = pc + 2;
n_items = i + 1;
if (verbose)
$display ("FetchStage.fav_parse_insts: C inst 0x%0h -> inst 0x%0h", orig_inst, inst);
end
@@ -281,12 +336,15 @@ function ActionValue #(Vector #(SupSize, Inst_Item))
inst = orig_inst;
j = j + 2;
next_pc = pc + 4;
n_items = i + 1;
end
else begin
inst_kind = Inst_32b_Lsbs;
orig_inst = zeroExtend (v_x16 [j]);
next_straddle = tagged Valid tuple3(pc, v_x16[j], isValid(pred_next_pc));
j = j + 1;
next_pc = pc + 2;
// Leave next_pc unchanged and clear pred_next_pc so we
// return the right predicted pc for the vector, which
// excludes the pending straddle.
pred_next_pc = tagged Invalid;
end
end
else begin
@@ -300,14 +358,26 @@ function ActionValue #(Vector #(SupSize, Inst_Item))
v_items [i] = Inst_Item {pc: pc, inst_kind: inst_kind, orig_inst: orig_inst, inst: inst};
pc = next_pc;
end
`else
for (Integer i = 0; i < valueOf(SupSize); i = i + 1) begin
Bit #(32) inst = { v_x16 [(2*i)+1], v_x16 [2*i] };
v_items[i].inst_kind = Inst_32b;
v_items[i].orig_inst = inst;
v_items[i].inst = inst;
end
pc = pc_start + 8;
n_items = 2;
`endif
if (verbose) begin
$display ("FetchStage.fav_parse_insts:");
$display (" v_x16: ", fshow (v_x16));
$display (" v_x16: ", fshow (v_x16), " n_x16s: %d", n_x16s);
$display (" n_items: %0d", n_items);
$display (" v_items: ", fshow (v_items));
$display (" next_straddle: ", fshow (next_straddle));
end
return v_items;
return tuple4(fromInteger(n_items), v_items, fromMaybe(pc, pred_next_pc), next_straddle);
endactionvalue
endfunction
@@ -338,24 +408,28 @@ module mkFetchStage(FetchStage);
// We stall until the flush is done
Reg#(Bool) waitForFlush <- mkReg(False);
Ehr#(3, Addr) pc_reg <- mkEhr(0);
Ehr#(4, Addr) pc_reg <- mkEhr(0);
Integer pc_fetch1_port = 0;
Integer pc_decode_port = 1;
Integer pc_redirect_port = 2;
Integer pc_fetch3_port = 2;
Integer pc_redirect_port = 3;
// Epochs
Reg#(Bool) fetch3_epoch <- mkReg(False);
Ehr#(2, Bool) decode_epoch <- mkEhr(False);
Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch
// Regs/wires to hold the first half of an instruction that straddles a cache line boundary
Ehr #(3, Bool) ehr_pending_straddle <- mkEhr (False);
Ehr #(2, Addr) ehr_half_inst_pc <- mkEhr (?); // The PC of the straddling instruction
Ehr #(2, Bit #(16)) ehr_half_inst_lsbs <- mkEhr (?); // The 16 lsbs of the straddling instruction
// Reg to hold the first half of an instruction that straddles a cache line boundary
Ehr #(2, Maybe #(Tuple3 #(Addr, Bit #(16), Bool))) ehr_pending_straddle <- mkEhr(tagged Invalid);
// Reg to hold extra instructions from Fetch3 to send to decode the next cycle
Reg #(Vector #(SupSizeX2S1, Inst_Item)) rg_pending_decode <- mkReg(replicate(defaultValue));
Reg #(SupCntX2S1) rg_pending_n_items <- mkRegU;
Reg #(Fetch3ToDecode) rg_pending_f32d <- mkRegU;
// Pipeline Stage FIFOs
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo;
Fifo#(2, Tuple2#(Bit#(TLog#(SupSizeX2)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
Fifo#(4, Tuple2#(Bit#(TLog#(SupSizeX2)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch3ToDecode)) f32d <- mkCFFifo;
// Fifo#(2, Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo(); // OLD
// FIFO from rule doFetch3 to rule doDecode
@@ -409,56 +483,37 @@ module mkFetchStage(FetchStage);
`endif
`ifdef RVFI_DII
Ehr#(3, Dii_Id) dii_id_next <- mkEhr(0);
Ehr#(4, Dii_Id) dii_id_next <- mkEhr(0);
Fifo#(2, Dii_Ids) dii_instIds <- mkCFFifo;
Fifo#(2, InstsAndIDs) dii_insts <- mkCFFifo;
Fifo#(2, Dii_Ids) dii_fetched_ids <- mkCFFifo;
Reg#(Dii_Id) last_trace_id <- mkRegU;
//rule feed_dii(!waitForFlush);
//Dii_Id next_id = dii_id_next;
//if (flush_id.notEmpty) begin
// next_id = flush_id.first;
// if (verbosity > 0) $display("DII flushed!");
// flush_id.deq;
//end
//Dii_Ids reqs = replicate(tagged Invalid);
//for (Integer i = 0; i < `sizeSup; i = i + 1)
// reqs[i] = tagged Valid (next_id + fromInteger(i));
//if (verbosity > 0) $display("Requested from DII", fshow(reqs));
//dii_instIds.enq(reqs);
//dii_id_next <= next_id + `sizeSup;
//endrule
`endif
// Predict the next fetch-PC based only on current PC (without
// knowing the instructions).
// Note: this chains calls to nextAddrPred. If this is a critical-path problem,
// alternatively one could apply nextAddrPred in parallel at pc+2, pc+4, pc+6, ...
// and memo-ize them in a vector (TODO).
function ActionValue #(Tuple2 #(Integer, Addr)) fav_pred_next_pc (Addr pc);
function ActionValue #(Tuple2 #(Integer, Maybe #(Addr))) fav_pred_next_pc (Addr pc);
actionvalue
Addr prev_PC = pc;
Addr pred_next_pc = nextAddrPred.predPc (prev_PC);
Integer posLastSup = 0;
Maybe #(Addr) pred_next_pc = nextAddrPred.predPc (prev_PC);
Integer posLastSupX2 = 0;
Bool done = False;
for (Integer i = 0; i < valueof (SupSize); i = i + 1) begin
for (Integer i = 0; i < valueOf (SupSizeX2); i = i + 1) begin
if (! done) begin
Bool lastInstInCacheLine = (getLineInstOffset (prev_PC) == maxBound);
Bool isSeq16 = ((prev_PC + 2) == pred_next_pc);
Bool isSeq32 = ((prev_PC + 4) == pred_next_pc);
Bool isJump = ((! isSeq16) && (! isSeq32));
done = ((i == (valueOf (SupSize) - 1)) || lastInstInCacheLine || isJump);
posLastSup = i;
Bool isLastX2 = (i == (valueOf (SupSizeX2) - 1)) || ((pc[1:0] != 2'b00) && (i == (valueOf (SupSizeX2) - 2)));
Bool lastInstInCacheLine = (getLineInstOffset (prev_PC) == maxBound) && (prev_PC[1:0] != 2'b00);
Bool isJump = isValid(pred_next_pc);
done = isLastX2 || lastInstInCacheLine || isJump;
posLastSupX2 = i;
if (! done) begin
prev_PC = pred_next_pc;
pred_next_pc = nextAddrPred.predPc (pred_next_pc);
prev_PC = prev_PC + 2;
pred_next_pc = nextAddrPred.predPc (prev_PC);
end
end
end
return tuple2 (posLastSup, pred_next_pc);
return tuple2 (posLastSupX2, pred_next_pc);
endactionvalue
endfunction
@@ -492,17 +547,18 @@ module mkFetchStage(FetchStage);
pc_reg[pc_fetch1_port] <= pred_next_pc;
*/
match { .posLastSup, .pred_next_pc } <- fav_pred_next_pc (pc);
pc_reg[pc_fetch1_port] <= pred_next_pc;
match { .posLastSupX2, .pred_next_pc } <- fav_pred_next_pc (pc);
let next_fetch_pc = fromMaybe(pc + 2 * (fromInteger(posLastSupX2) + 1), pred_next_pc);
pc_reg[pc_fetch1_port] <= next_fetch_pc;
`ifdef RVFI_DII
Dii_Id next_id = dii_id_next[pc_fetch1_port];
Dii_Ids reqs = replicate(tagged Invalid);
for (Integer i = 0; i <= posLastSup; i = i + 1)
for (Integer i = 0; i < valueOf(SupSize); i = i + 1)
reqs[i] = tagged Valid (next_id + fromInteger(i));
if (verbosity > 0) $display("Requested from DII", fshow(reqs));
dii_instIds.enq(reqs);
dii_id_next[pc_fetch1_port] <= next_id + fromInteger(posLastSup) + 1;
dii_id_next[pc_fetch1_port] <= next_id + fromInteger(valueOf(SupSize));
`endif
// Send TLB request.
@@ -513,15 +569,19 @@ module mkFetchStage(FetchStage);
let out = Fetch1ToFetch2 {
pc: pc,
pred_next_pc: pred_next_pc,
fetch3_epoch: fetch3_epoch,
decode_epoch: decode_epoch[0],
main_epoch: f_main_epoch
};
f12f2.enq(tuple2(fromInteger(posLastSup),out));
if (verbose) $display("Fetch1: ", fshow(out));
main_epoch: f_main_epoch};
let nbSupX2 = fromInteger(posLastSupX2) + (pc[1:0] == 2'b00 ? 0 : 1);
`ifdef RVFI_DII
nbSupX2 = 3;
`endif
f12f2.enq(tuple2(nbSupX2,out));
if (verbose) $display("Fetch1: ", fshow(out), " posLastSupX2: %d", posLastSupX2, " nbSupX2: %d", nbSupX2);
endrule
rule doFetch2;
let {nbSup,in} = f12f2.first;
let {nbSupX2,in} = f12f2.first;
f12f2.deq;
// Get TLB response
@@ -542,134 +602,251 @@ module mkFetchStage(FetchStage);
// cache line size, so all nbSup+1 insts can be fetched
// from boot rom. It won't happen that insts fetched from
// boot rom is less than requested.
Bit #(TLog #(SupSize)) nbSup = truncate(nbSupX2 >> 1);
mmio.bootRomReq(phys_pc, nbSup);
access_mmio = True;
end
default: begin
// Access fault
cause = Valid (InstAccessFault);
// Without 'C' extension:
// Addr align32b_mask = 'h3;
// tval = (in.pc & (~ align32b_mask));
Addr align16b_mask = 'h1;
tval = (in.pc & (~ align16b_mask));
end
endcase
end
else begin
// TLB exception: record the request address
Addr align32b_mask = 'h3;
tval = (in.pc & (~ align32b_mask));
// Without 'C' extension:
// Addr align32b_mask = 'h3;
// tval = (in.pc & (~ align32b_mask));
Addr align16b_mask = 'h1;
tval = (in.pc & (~ align16b_mask));
end
`endif
let out = Fetch2ToFetch3 {
pc: in.pc,
phys_pc: phys_pc,
pred_next_pc: in.pred_next_pc,
cause: cause,
tval: tval,
access_mmio: access_mmio,
fetch3_epoch: in.fetch3_epoch,
decode_epoch: in.decode_epoch,
main_epoch: in.main_epoch };
f22f3.enq(tuple2(nbSup,out));
f22f3.enq(tuple2(nbSupX2,out));
if (verbosity > 0) begin
$display ("----------------");
$display ("Fetch2: TLB response pyhs_pc 0x%0h cause ", phys_pc, fshow (cause));
$display ("Fetch2: f2_tof3.enq: nbSup %0d out ", nbSup, fshow (out));
end
if (verbosity >= 2) begin
$display ("----------------");
$display ("Fetch2: TLB response pyhs_pc 0x%0h cause ", phys_pc, fshow (cause));
$display ("Fetch2: f2_tof3.enq: nbSupX2 %0d out ", nbSupX2, fshow (out));
end
endrule
// Break out of i$
rule doFetch3;
let {nbSup, fetch3In} = f22f3.first;
f22f3.deq();
if (verbosity > 0)
$display("Fetch3: fetch3In: ", fshow (fetch3In));
let {nbSupX2In, fetch3In} = f22f3.first;
if (verbosity >= 2) begin
if (f22f3.notEmpty)
$display("Fetch3: nbSupX2In: %0d fetch3In: ", nbSupX2In, fshow (fetch3In));
else
$display("Fetch3: Nothing else from Fetch2");
end
`ifdef RVFI_DII
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid dii_nop);
InstsAndIDs ii <- toGet(dii_insts).get();
inst_d = ii.insts;
if (verbosity > 0) $display("Got from DII: ", fshow (ii));
`else
SupCntX2S1 pending_n_items = rg_pending_n_items;
let out = rg_pending_f32d;
Maybe #(Tuple3 #(Addr, Bit #(16), Bool)) pending_straddle = ehr_pending_straddle[0];
if (pending_n_items > 0) begin
if (rg_pending_f32d.main_epoch != f_main_epoch || rg_pending_f32d.decode_epoch != decode_epoch[1]) begin
// Just drop it. Also drop any pending straddle, as that is
// associated with the same epoch.
pending_n_items = 0;
pending_straddle = tagged Invalid;
if (verbosity >= 2) begin
$display ("----------------");
$display ("Fetch3: Drop pending: main_epoch: %d decode epoch: %d", f_main_epoch, decode_epoch[1]);
$display ("Fetch3: rg_pending_n_items: ", fshow (rg_pending_n_items));
$display ("Fetch3: rg_pending_f32d: ", fshow (rg_pending_f32d));
$display ("Fetch3: rg_pending_decode: ", fshow (rg_pending_decode));
end
end
end
SupCntX2 parsed_n_items = 0;
Inst_Item inst_item_none = Inst_Item {pc: fetch3In.pc, inst_kind: Inst_None, orig_inst: 0, inst: 0};
Vector #(SupSizeX2, Inst_Item) parsed_v_items = replicate (inst_item_none);
let mispred_first_half = pending_straddle matches tagged Valid {.s_pc, .s_lsbs, .s_mispred} &&& s_mispred ? True : False;
let can_merge = pending_n_items > 0
&& pending_n_items < fromInteger(valueOf(SupSize))
&& f22f3.notEmpty
&& !isValid(fetch3In.cause)
&& fetch3In.main_epoch == rg_pending_f32d.main_epoch
&& fetch3In.decode_epoch == rg_pending_f32d.decode_epoch
&& !mispred_first_half;
let drop_f22f3 = f22f3.notEmpty
&& ( fetch3In.main_epoch != f_main_epoch
|| fetch3In.decode_epoch != decode_epoch[1]
|| fetch3In.fetch3_epoch != fetch3_epoch);
let parse_f22f3 = !drop_f22f3 && (pending_n_items == 0 || can_merge);
`ifndef RVFI_DII
// Get ICache/MMIO response if no exception
// In case of exception, we still need to process at least inst_data[0]
// (it will be turned to an exception later), so inst_data[0] must be
// valid.
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
if(!isValid(fetch3In.cause)) begin
if(fetch3In.access_mmio) begin
if(verbose) $display("get answer from MMIO %x", fetch3In.pc);
inst_d <- mmio.bootRomResp;
end
else begin
if(verbose) $display("get answer from memory %x", fetch3In.pc);
inst_d <- mem_server.response.get;
if (drop_f22f3 || parse_f22f3) begin
f22f3.deq();
if (!isValid(fetch3In.cause)) begin
if(fetch3In.access_mmio) begin
if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
inst_d <- mmio.bootRomResp;
end
else begin
if(verbose) $display("get answer from memory %d", fetch3In.pc);
inst_d <- mem_server.response.get;
end
end
end
`else
f22f3.deq();
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid dii_nop);
InstsAndIDs ii <- toGet(dii_insts).get();
inst_d = ii.insts;
if (verbosity > 0) $display("Got from DII: ", fshow (ii));
if(verbose) $display("PC is %x", fetch3In.pc);
`endif
if (drop_f22f3) begin
// Drop any pending straddle if this is for a different main or
// decode epoch since that invalidates our Fetch3 redirect, but
// otherwise keep it to flush the pipeline until we get the next
// half of the straddle.
if (fetch3In.main_epoch != f_main_epoch || fetch3In.decode_epoch != decode_epoch[1]) begin
pending_straddle = tagged Invalid;
end
if (verbosity >= 2) begin
$display ("----------------");
$display ("Fetch3: Drop: main_epoch: %d decode epoch: %d fetch3 epoch %d", f_main_epoch, decode_epoch[1], fetch3_epoch);
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
$display ("Fetch3: inst_d: ", fshow (inst_d));
end
end
else if (parse_f22f3) begin
// Re-interpret fetched 32b parcels (inst_d) as 16b parcels
let { n_x16s, v_x16 } <- fav_inst_d_to_x16s (inst_d);
// Cap n_x16s, as otherwise we misattribute the bundle's PC
// prediction to a later instruction and erroneously think we
// took a branch miss. This condition is hit because the cache
// interface uses aligned 32b parcels and thus we can end up with
// an extra 16b parcel after the window we want. Note that
// nbSupX2In will still include the first 16b parcel even if our PC
// is misaligned, but this will be discarded by fav_parse_insts.
if (n_x16s > extend(nbSupX2In) + 1)
n_x16s = extend(nbSupX2In) + 1;
if (fetch3In.decode_epoch != decode_epoch[1]) begin
// Just drop it.
if (verbosity > 0) begin
$display ("----------------");
$display ("Fetch3: Drop: decode epoch: %d", decode_epoch[1]);
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
$display ("Fetch3: inst_d: ", fshow (inst_d));
end
end
else begin
// Re-interpret fetched 32b parcels (inst_d) as 16b parcels
match { .n_x16s, .v_x16 } <- fav_inst_d_to_x16s (inst_d);
Addr start_PC = fetch3In.pc;
// Parse v_x16 into 32-bit and 16-bit instructions
Addr pred_next_pc;
{parsed_n_items, parsed_v_items, pred_next_pc, pending_straddle} <-
fav_parse_insts (verbose, fetch3In.pc, fetch3In.pred_next_pc, pending_straddle, n_x16s, v_x16);
// Handle cache-line boundary straddling instruction, if one is pending
if (ehr_pending_straddle[1]) begin
if (fetch3In.pc != ehr_half_inst_pc[1] + 4) begin
$display ("----------------");
$display ("Fetch3: straddle: pc mismatch");
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
$display ("Fetch3: inst_d: ", fshow (inst_d));
dynamicAssert (False, "Fetch3: straddle: pc mismatch");
end
else begin
// Prepend onto the sequence: { first-half of the instruction , 0 }
v_x16 = shiftInAt0 (shiftInAt0 (v_x16, ehr_half_inst_lsbs[1]), 0);
let bound = valueOf (SupSizeX2) - 1;
if (n_x16s < (fromInteger (bound) - 1))
n_x16s = n_x16s + 2;
else if (n_x16s < fromInteger (bound))
n_x16s = n_x16s + 1;
start_PC = ehr_half_inst_pc[1];
ehr_pending_straddle[1] <= False;
if (verbosity > 0) begin
$display ("----------------");
$display ("Fetch3: straddle: prepend x16 %0h", ehr_half_inst_lsbs[1]);
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
$display ("Fetch3: inst_d: ", fshow (inst_d));
$display ("Fetch3: v_x16: ", fshow (v_x16));
end
end
end
if (pending_n_items == 0) begin
out = Fetch3ToDecode {
pc: fetch3In.pc,
pred_next_pc: pred_next_pc,
mispred_first_half: mispred_first_half,
cause: fetch3In.cause,
tval: fetch3In.tval,
decode_epoch: fetch3In.decode_epoch,
main_epoch: fetch3In.main_epoch
};
end
else begin
out.pred_next_pc = pred_next_pc;
end
// Parse v_x16 into 32-bit and 16-bit instructions
Vector #(SupSize, Inst_Item) v_items <- fav_parse_insts (verbose, start_PC, n_x16s, v_x16);
// Redirect doFetch1 if we predicted a taken compressed branch
// but this is an uncompressed instruction. We will tell decode
// to retrain when we issue the full instruction next time.
if (pending_straddle matches tagged Valid {.s_pc, .s_lsbs, .s_mispred}
&&& s_mispred) begin
pc_reg[pc_fetch3_port] <= s_pc + 2;
fetch3_epoch <= ! fetch3_epoch;
end
end
instdata.enq (v_items);
f32d.enq(f22f3.first);
SupCntX2S1 next_pending_n_items = 0;
if (pending_n_items > 0 || parse_f22f3) begin
SupCntX3S1 n_items = extend(pending_n_items) + extend(parsed_n_items);
Bit #(TLog #(SupSize)) nbSupOut = truncate(n_items - 1);
let pending_spaces = fromInteger(valueOf(SupSizeX2S1)) - pending_n_items;
Vector #(SupSizeX2S1, Inst_Item) pending_items_ralign =
shiftOutFromN(inst_item_none, rg_pending_decode, pending_spaces);
// Appease bluespec compiler with seemingly-unnecessary extension;
// otherwise elaboration fails with:
// Error: "Vector.bs", line 791, column 33: (T0051)
// Literal 7 is not a valid Bit#(2).
// During elaboration of the body of rule `doFetch3' at
// ...
SupCntX4S1 pending_spaces_ext = extend(pending_spaces);
Vector #(SupSizeX3S1, Inst_Item) v_items =
take(shiftOutFrom0(inst_item_none, append(pending_items_ralign, parsed_v_items), pending_spaces_ext));
// Handle decoding more instructions than we can issue this cycle
if (n_items > fromInteger(valueOf(SupSize))) begin
nbSupOut = fromInteger(valueOf(SupSize) - 1);
if (!isValid(out.cause)) begin
next_pending_n_items = truncate(n_items - fromInteger(valueOf(SupSize)));
rg_pending_decode <= drop(v_items);
rg_pending_f32d <= Fetch3ToDecode {
pc: v_items[valueOf(SupSize)].pc,
pred_next_pc: out.pred_next_pc,
mispred_first_half: False,
cause: tagged Invalid,
tval: 0,
decode_epoch: out.decode_epoch,
main_epoch: out.main_epoch
};
end
out.pred_next_pc = v_items[valueOf(SupSize)].pc;
end
if (n_items > 0) begin
`ifdef RVFI_DII
dii_fetched_ids.enq(ii.ids);
dii_fetched_ids.enq(ii.ids);
`endif
if (verbosity > 0) begin
$display ("----------------");
$display ("Fetch3: epoch inst: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
$display ("Fetch3: inst_d: ", fshow (inst_d));
$display ("Fetch3: v_items: ", fshow (v_items));
$display ("Fetch3: f32d.enq: ", fshow (f22f3.first));
end
end
instdata.enq(take(v_items));
f32d.enq(tuple2(nbSupOut, out));
if (verbosity >= 2) begin
$display ("----------------");
$display ("Fetch3: epoch inst: %d, epoch main : %d", out.main_epoch, f_main_epoch);
$display ("Fetch3: inst_d: ", fshow (inst_d));
$display ("Fetch3: v_items: ", fshow (v_items));
$display ("Fetch3: f32d.enq: nbSup %0d out ", nbSupOut, fshow (out));
end
end
else begin
// This means we started fetching from a line straddling
// instruction; need another cycle to have something to
// issue.
dynamicAssert(isValid(pending_straddle), "Decoded no instructions and no straddle!");
end
end
rg_pending_n_items <= next_pending_n_items;
ehr_pending_straddle[0] <= pending_straddle;
endrule: doFetch3
rule doDecode;
let {nbSup, fetch3In} = f32d.first;
let {nbSup, decodeIn} = f32d.first;
f32d.deq();
let inst_data = instdata.first();
instdata.deq();
@@ -680,7 +857,7 @@ module mkFetchStage(FetchStage);
`endif
// The main_epoch check is required to make sure this stage doesn't
// redirect the PC if a later stage already redirected the PC.
if (fetch3In.main_epoch == f_main_epoch) begin
if (decodeIn.main_epoch == f_main_epoch) begin
Bool decode_epoch_local = decode_epoch[0]; // next value for decode epoch
Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
@@ -691,49 +868,23 @@ module mkFetchStage(FetchStage);
`endif
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
if ((inst_data[i].inst_kind == Inst_32b_Lsbs) && (fromInteger(i) <= nbSup)) begin
if (fetch3In.decode_epoch == decode_epoch_local) begin
// Save the half-instruction and redirect doFetch1 to get the next cache line
ehr_pending_straddle[0] <= True;
ehr_half_inst_pc[0] <= inst_data[i].pc;
ehr_half_inst_lsbs[0] <= inst_data[i].orig_inst [15:0];
decode_epoch_local = ! decode_epoch_local;
let next_PC = inst_data[i].pc + 4;
redirectPc = tagged Valid (next_PC);
// We don't train NAP because that's about the dynamic successor to this instruction,
// not about the second half of this instruction.
if (verbosity > 0) begin
$display ("----------------");
$display ("FetchStage.doDecode [%0d]: straddle. pc %0h x16 %0h redirecting to %0h new decode_epoch %d",
i, inst_data[i].pc, x16, next_PC, decode_epoch_local);
end
end
else begin
// just drop wrong path instructions
if (verbose) begin
$display ("FetchStage.doDecode [%0d]: Inst_32b_Lsbs: drop due to decode epoch", i);
$display (" inst_data = ", fshow (inst_data));
end
end
end
else if (inst_data[i].inst_kind != Inst_None && (fromInteger(i) <= nbSup)) begin
if (inst_data[i].inst_kind != Inst_None && (fromInteger(i) <= nbSup)) begin
// Inst_16b or Inst_32b
// get the input to decode
let inst_data_shifted = shiftInAtN (inst_data, ?); // for predicted PCs
let in = Fetch3ToDecode {
let in = InstrFromFetch3 {
pc: inst_data[i].pc,
// last inst, next pc may not be pc+2/pc+4
ppc: ((fromInteger(i) == nbSup)
? fetch3In.pred_next_pc
? decodeIn.pred_next_pc
: inst_data_shifted[i].pc),
decode_epoch: fetch3In.decode_epoch,
main_epoch: fetch3In.main_epoch,
decode_epoch: decodeIn.decode_epoch,
main_epoch: decodeIn.main_epoch,
inst: inst_data [i].inst, // original 32b inst, or expanded version of 16b inst
cause: fetch3In.cause
cause: decodeIn.cause
};
let cause = in.cause;
Addr tval = fetch3In.tval;
Addr tval = decodeIn.tval;
if (verbose)
$display("Decode: %0d in = ", i, fshow (in));
@@ -747,7 +898,7 @@ module mkFetchStage(FetchStage);
// update cause and tval if decode exception and no earlier (TLB) exception
if (!isValid(cause)) begin
cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
tval = fetch3In.tval;
tval = decodeIn.tval;
end
let dInst = decode_result.dInst;
@@ -811,6 +962,13 @@ module mkFetchStage(FetchStage);
fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
end
if (i == 0 && decodeIn.mispred_first_half) begin
// We predicted a taken branch for PC, but this is an
// uncompressed instruction, so we train it to fetch
// the other half in future.
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: in.pc + 2});
end
// check previous mispred
if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
if (verbose) $display("ppc and decodeppc : %h %h", in.ppc, decode_pred_next_pc);
@@ -818,7 +976,9 @@ module mkFetchStage(FetchStage);
redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
in.ppc = decode_pred_next_pc;
// train next addr pred when mispredict
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
let last_x16_pc = in.pc + ((inst_data[i].inst_kind == Inst_32b) ? 2 : 0);
if (!decodeIn.mispred_first_half)
trainNAP = Valid (TrainNAP {pc: last_x16_pc, nextPc: decode_pred_next_pc});
`ifdef RVFI_DII
nextId = fromMaybe(nextId,ids[i]) + 1;
`endif
@@ -843,8 +1003,13 @@ module mkFetchStage(FetchStage);
`endif
tval: tval};
out_fifo.enqS[i].enq(out);
if (verbosity > 0)
$display("Decode: ", fshow(out));
if (verbosity >= 1) begin
$write ("%0d: %m.rule doDecode: out_fifo.enqS[%0d].enq", cur_cycle, i);
$display (" pc %0h inst %08h", out.pc, out.orig_inst);
end
if (verbosity >= 2) begin
$display (" ", fshow(out));
end
end // if (in.decode_epoch == decode_epoch_local)
else begin
if (verbose) $display("Drop decoded within a superscalar");
@@ -882,7 +1047,7 @@ module mkFetchStage(FetchStage);
endcase
end
`endif
end // if (fetch3In.main_epoch == f_main_epoch)
end // if (decodeIn.main_epoch == f_main_epoch)
else begin
if (verbose) $display("drop in fetch3decode");
end
@@ -903,7 +1068,7 @@ module mkFetchStage(FetchStage);
// only when misprediction happens, i.e., train by dec is already at
// wrong path.
TrainNAP train = fromMaybe(validValue(napTrainByDec.wget), napTrainByExe.wget);
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 2);
endrule
// Security: we can flush when front end is empty, i.e.
@@ -954,7 +1119,7 @@ module mkFetchStage(FetchStage);
if (verbose) $display("%t Redirect: dii_id_next %d", $time(), id);
`endif
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
ehr_pending_straddle[2] <= False;
ehr_pending_straddle[1] <= tagged Invalid;
// redirect comes, stop stalling for redirect
waitForRedirect <= False;
setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
@@ -962,6 +1127,14 @@ module mkFetchStage(FetchStage);
// we conservatively set wait for flush TODO make this an input parameter
waitForFlush <= True;
endmethod
`ifdef INCLUDE_GDB_CONTROL
method Action setWaitFlush;
waitForFlush <= True;
// $display ("%0d.%m.setWaitFlush", cur_cycle);
endmethod
`endif
method Action done_flushing() if (waitForFlush);
// signal that the pipeline can resume fetching
waitForFlush <= False;
@@ -976,7 +1149,7 @@ module mkFetchStage(FetchStage);
method Action train_predictors(
Addr pc, Addr next_pc, IType iType, Bool taken,
DirPredTrainInfo dpTrain, Bool mispred
DirPredTrainInfo dpTrain, Bool mispred, Bool isCompressed
);
//if (iType == J || (iType == Br && next_pc < pc)) begin
// // Only train the next address predictor for jumps and backward branches
@@ -989,7 +1162,8 @@ module mkFetchStage(FetchStage);
end
// train next addr pred when mispred
if(mispred) begin
napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
let last_x16_pc = pc + (isCompressed ? 0 : 2);
napTrainByExe.wset(TrainNAP {pc: last_x16_pc, nextPc: next_pc});
end
endmethod

View File

@@ -91,7 +91,7 @@ interface FpuMulDivExeInput;
// CSR file
method Data csrf_rd(CSR csr);
// ROB
method Action rob_setExecuted(InstTag t, Bit#(5) fflags);
method Action rob_setExecuted(InstTag t, Data dst_data, Bit#(5) fflags);
// global broadcast methods
// write reg file & set both conservative and aggressive sb & wake up inst
@@ -228,7 +228,7 @@ module mkFpuMulDivExePipeline#(FpuMulDivExeInput inIfc)(FpuMulDivExePipeline);
inIfc.writeRegFile(valid_dst.indx, data);
end
// update the instruction in the reorder buffer.
inIfc.rob_setExecuted(tag, fflags);
inIfc.rob_setExecuted(tag, data, fflags);
// since FPU op has no spec tag, this doFinish rule is ordered before
// other rules that calls incorrectSpec, and BSV compiler creates
// cycles in scheduling. We manually creates a conflict between this

View File

@@ -51,6 +51,8 @@ import L1CoCache::*;
import Bypass::*;
import LatencyTimer::*;
import Cur_Cycle :: *;
typedef struct {
// inst info
MemFunc mem_func;
@@ -78,7 +80,12 @@ typedef struct {
LdStQTag ldstq_tag;
// result
ByteEn shiftedBE;
Addr vaddr; // virtual addr
Addr vaddr; // virtual addr
`ifdef INCLUDE_TANDEM_VERIF
// for those mem instrs that store data
Data store_data;
ByteEn store_data_BE;
`endif
Bool misaligned;
} MemExeToFinish deriving(Bits, Eq, FShow);
@@ -145,16 +152,22 @@ interface MemExeInput;
method Data csrf_rd(CSR csr);
// ROB
method Addr rob_getPC(InstTag t);
method Action rob_setExecuted_doFinishMem(InstTag t, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
method Action rob_setExecuted_doFinishMem(InstTag t,
Addr vaddr,
Data store_data, ByteEn store_data_BE,
Bool access_at_commit, Bool non_mmio_st_done
`ifdef RVFI
, ExtraTraceBundle tb
, ExtraTraceBundle tb
`endif
);
`ifdef INCLUDE_TANDEM_VERIF
method Action rob_setExecuted_doFinishMem_RegData (InstTag t, Data dst_data);
`endif
);
method Action rob_setExecuted_deqLSQ(InstTag t, Maybe#(Exception) cause, Maybe#(LdKilledBy) ld_killed
`ifdef RVFI
, ExtraTraceBundle tb
, ExtraTraceBundle tb
`endif
);
);
// MMIO
method Bool isMMIOAddr(Addr a);
method Action mmioReq(MMIOCRq r);
@@ -467,6 +480,10 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
ldstq_tag: x.ldstq_tag,
shiftedBE: shiftBE,
vaddr: vaddr,
`ifdef INCLUDE_TANDEM_VERIF
store_data: data,
store_data_BE: origBE,
`endif
misaligned: memAddrMisaligned(vaddr, origBE)
},
specBits: regToExe.spec_bits
@@ -496,6 +513,13 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
if(verbose) $display("[doFinishMem] ", fshow(dTlbResp));
if(isValid(cause) && verbose) $display(" [doFinishMem - dTlb response] PAGEFAULT!");
Data store_data = ?;
ByteEn store_data_BE = ?;
`ifdef INCLUDE_TANDEM_VERIF
store_data = x.store_data;
store_data_BE = x.store_data_BE;
`endif
// check misalignment
if(!isValid(cause) && x.misaligned) begin
case(x.mem_func)
@@ -530,28 +554,22 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
endcase);
Bool access_at_commit = !isValid(cause) && (isMMIO || isLrScAmo);
Bool non_mmio_st_done = !isValid(cause) && !isMMIO && x.mem_func == St;
inIfc.rob_setExecuted_doFinishMem(
x.tag,
x.vaddr,
access_at_commit,
non_mmio_st_done
inIfc.rob_setExecuted_doFinishMem(x.tag, x.vaddr, store_data, store_data_BE,
access_at_commit, non_mmio_st_done
`ifdef RVFI
, ExtraTraceBundle{
regWriteData: memData[pack(x.ldstq_tag)],
memByteEn: unpack(pack(x.shiftedBE) >> x.vaddr[2:0])
}
`endif
);
`ifdef RVFI
$display("%t : memData[%x]: %x", $time(), pack(x.ldstq_tag), memData[pack(x.ldstq_tag)]);
, ExtraTraceBundle{
regWriteData: memData[pack(x.ldstq_tag)],
memByteEn: unpack(pack(x.shiftedBE) >> x.vaddr[2:0])
}
`endif
);
// update LSQ
LSQUpdateAddrResult updRes <- lsq.updateAddr(
x.ldstq_tag, cause, paddr, isMMIO, x.shiftedBE
);
// issue non-MMIO Ld which has no excpetion and is not waiting for
// issue non-MMIO Ld which has no exception and is not waiting for
// wrong path resp
if (x.mem_func == Ld && !isMMIO &&
!isValid(cause) && !updRes.waitWPResp) begin
@@ -674,6 +692,11 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
if(verbose) $display("%t : ", $time, rule_name, " ", fshow(tag), "; ", fshow(data), "; ", fshow(res));
if(res.dst matches tagged Valid .dst) begin
inIfc.writeRegFile(dst.indx, res.data);
`ifdef INCLUDE_TANDEM_VERIF
inIfc.rob_setExecuted_doFinishMem_RegData (res.instTag, res.data);
`endif
`ifdef PERF_COUNT
// perf: load to use latency
let lat <- ldToUseLatTimer.done(tag);
@@ -833,6 +856,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
if(lsqDeqLd.dst matches tagged Valid .dst) begin
inIfc.writeRegFile(dst.indx, resp);
inIfc.setRegReadyAggr_mem(dst.indx);
`ifdef INCLUDE_TANDEM_VERIF
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqLd.instTag, resp);
`endif
end
inIfc.rob_setExecuted_deqLSQ(
lsqDeqLd.instTag,
@@ -918,6 +944,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
if(lsqDeqLd.dst matches tagged Valid .dst) begin
inIfc.writeRegFile(dst.indx, resp);
inIfc.setRegReadyAggr_mem(dst.indx);
`ifdef INCLUDE_TANDEM_VERIF
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqLd.instTag, resp);
`endif
end
inIfc.rob_setExecuted_deqLSQ(lsqDeqLd.instTag, Invalid, Invalid
`ifdef RVFI
@@ -1166,6 +1195,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
if(lsqDeqSt.dst matches tagged Valid .dst) begin
inIfc.writeRegFile(dst.indx, resp);
inIfc.setRegReadyAggr_mem(dst.indx);
`ifdef INCLUDE_TANDEM_VERIF
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqSt.instTag, resp);
`endif
end
inIfc.rob_setExecuted_deqLSQ(lsqDeqSt.instTag, Invalid, Invalid
`ifdef RVFI
@@ -1269,6 +1301,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
if(lsqDeqSt.dst matches tagged Valid .dst) begin
inIfc.writeRegFile(dst.indx, resp);
inIfc.setRegReadyAggr_mem(dst.indx);
`ifdef INCLUDE_TANDEM_VERIF
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqSt.instTag, resp);
`endif
end
inIfc.rob_setExecuted_deqLSQ(lsqDeqSt.instTag, Invalid, Invalid
`ifdef RVFI

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -48,6 +49,8 @@ import ReservationStationMem::*;
import ReservationStationFpuMulDiv::*;
import SplitLSQ::*;
import Cur_Cycle :: *;
typedef struct {
FetchDebugState fetch;
EpochDebugState epoch;
@@ -75,6 +78,9 @@ interface RenameInput;
method Bool checkDeadlock;
// performance
method Bool doStats;
`ifdef INCLUDE_GDB_CONTROL
method Bool core_is_running;
`endif
endinterface
interface RenameStage;
@@ -83,10 +89,16 @@ interface RenameStage;
// deadlock check
interface Get#(RenameStuck) renameInstStuck;
interface Get#(RenameStuck) renameCorrectPathStuck;
`ifdef INCLUDE_GDB_CONTROL
method Action debug_halt_req;
method Action debug_resume;
`endif
endinterface
module mkRenameStage#(RenameInput inIfc)(RenameStage);
Bool verbose = False;
Integer verbosity = 0;
// func units
FetchStage fetchStage = inIfc.fetchIfc;
@@ -153,6 +165,24 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
endrule
`endif
`ifdef INCLUDE_GDB_CONTROL
// Is set to Valid DebugHalt on debugger halt request
// Is set to Valid DebugStep on dcsr[stepbit]==1 and one instruction has been processed.
// Note (step): 1st instruction is guaranteed architectural, cannot possibly be speculative.
// Note (step): 1st instruction may trap; we halt pointing at the trap vector
Reg #(Maybe #(Interrupt)) rg_m_halt_req <- mkReg (tagged Invalid);
function Action fa_step_check;
action
if (csrf.dcsr_step_bit == 1'b1) begin
rg_m_halt_req <= tagged Valid DebugStep;
if (verbosity >= 2)
$display ("%0d: %m.renameStage.fa_step_check: rg_m_halt_req <= tagged Valid DebugStep", cur_cycle);
end
endaction
endfunction
`endif
// kill wrong path inst
// XXX we have to make this a separate rule instead of merging it with rename correct path
// This is because the rename correct path rule is conflict with other rules that redirect
@@ -224,7 +254,8 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
Bool read_only = (csr_addr [11:10] == 2'b11);
Bool write_deny = (writes_csr && read_only);
Bool priv_deny = (csrf.decodeInfo.prv < csr_addr [9:8]);
csr_access_trap = (write_deny || priv_deny);
Bool unimplemented = (csr_addr == 12'h8ff); // Added by Bluespec
csr_access_trap = (write_deny || priv_deny || unimplemented);
end
// Check WFI trap (using a time-out of 0)
@@ -234,6 +265,13 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
&& (mstatus_tw == 1'b1)
&& (csrf.decodeInfo.prv < prvM));
`ifdef INCLUDE_GDB_CONTROL
if (rg_m_halt_req matches tagged Valid .cause) begin
// Stop due to debugger halt or step
trap = tagged Valid (tagged Interrupt cause);
end else
`endif
if (isValid(x.cause)) begin
// previously found exception
trap = tagged Valid (tagged Exception fromMaybe(?, x.cause));
@@ -271,8 +309,23 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& isValid(firstTrap) // take trap
&& rob.isEmpty // stall for ROB empty
`ifdef INCLUDE_GDB_CONTROL
&& inIfc.core_is_running
`endif
);
fetchStage.pipelines[0].deq;
`ifdef INCLUDE_GDB_CONTROL
fa_step_check;
if (verbosity >= 1) begin
if (firstTrap == tagged Valid (tagged Interrupt DebugHalt))
$display ("%0d: %m.renameStage.doRenaming_Trap: DebugHalt", cur_cycle);
else if (firstTrap == tagged Valid (tagged Interrupt DebugStep))
$display ("%0d: %m.renameStage.doRenaming_Trap: DebugStep", cur_cycle);
else if (firstTrap == tagged Valid (tagged Exception Breakpoint))
$display ("%0d: %m.renameStage.doRenaming_Trap: Breakpoint", cur_cycle);
end
`endif
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let orig_inst = x.orig_inst;
@@ -296,6 +349,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
let y = ToReorderBuffer{pc: pc,
orig_inst: orig_inst,
iType: dInst.iType,
dst: arch_regs.dst,
dst_data: ?, // Available only after execution
`ifdef INCLUDE_TANDEM_VERIF
store_data: ?,
store_data_BE: ?,
`endif
csr: dInst.csr,
claimed_phy_reg: False, // no renaming is done
trap: firstTrap,
@@ -322,6 +381,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
if(firstTrap matches tagged Valid (tagged Interrupt .i)) begin
inIfc.issueCsrInstOrInterrupt;
end
`ifdef INCLUDE_GDB_CONTROL
else if (firstTrap == tagged Valid (tagged Exception Breakpoint)) begin
inIfc.issueCsrInstOrInterrupt;
end
`endif
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
@@ -380,11 +445,18 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
&& !isValid(firstTrap) // not trap
&& firstReplay // system inst needs replay
&& rob.isEmpty // stall for ROB empty
`ifdef INCLUDE_GDB_CONTROL
&& inIfc.core_is_running
`endif
);
fetchStage.pipelines[0].deq;
`ifdef INCLUDE_GDB_CONTROL
fa_step_check;
`endif
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let orig_inst = x.orig_inst;
let dst = x.regs.dst;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
@@ -459,10 +531,28 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
will_dirty_fpu_state = True;
doAssert(False, "system inst never touches FP regs");
end
// CSR instrs that touch certain FP CSRs will dirty FP state.
if (dInst.csr matches tagged Valid .csr
&&& ((dInst.iType == Csr)
&& ((csr == CSRfflags) || (csr == CSRfrm) || (csr == CSRfcsr))))
begin
Bool is_CSRR_W = (dInst.execFunc == tagged Alu Csrw);
Bool rs1_is_0 = ((arch_regs.src2 == tagged Valid (tagged Gpr 0))
|| (dInst.imm == tagged Valid 0));
will_dirty_fpu_state = (is_CSRR_W || (! rs1_is_0));
end
RobInstState rob_inst_state = to_exec ? NotDone : Executed;
let y = ToReorderBuffer{pc: pc,
orig_inst: orig_inst,
iType: dInst.iType,
dst: arch_regs.dst,
dst_data: ?, // Available only after execution
`ifdef INCLUDE_TANDEM_VERIF
store_data: ?,
store_data_BE: ?,
`endif
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
@@ -529,8 +619,14 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
// turn off speculation for mem inst only, and first inst is mem
&& (specNonMem && firstMem)
&& rob.isEmpty // stall for ROB empty to process mem inst
`ifdef INCLUDE_GDB_CONTROL
&& inIfc.core_is_running
`endif
);
fetchStage.pipelines[0].deq;
`ifdef INCLUDE_GDB_CONTROL
fa_step_check;
`endif
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let orig_inst = x.orig_inst;
@@ -628,6 +724,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
let y = ToReorderBuffer{pc: pc,
orig_inst: orig_inst,
iType: dInst.iType,
dst: arch_regs.dst,
dst_data: ?, // Available only after execution
`ifdef INCLUDE_TANDEM_VERIF
store_data: ?,
store_data_BE: ?,
`endif
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
@@ -692,6 +794,9 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
&& (!specNone || rob.isEmpty)
// don't process mem inst if we don't allow speculation for mem inst only
&& !(specNonMem && firstMem)
`endif
`ifdef INCLUDE_GDB_CONTROL
&& inIfc.core_is_running
`endif
);
// we stop superscalar rename when an instruction cannot be processed:
@@ -700,6 +805,11 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
// (c) It is system inst (we handle system inst in a separate rule)
// (d) It does not have enough resource
Bool stop = False;
`ifdef INCLUDE_GDB_CONTROL
// (e) One rename has been done and dcsr.step is set
Bool debug_step = False;
`endif
// We automatically stop after an inst cannot be deq from fetch stage
// because canDeq signal for sup-fifo is consecutive
@@ -748,6 +858,13 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
Addr fallthrough_pc = ((orig_inst[1:0] == 2'b11) ? pc + 4 : pc + 2);
`ifdef INCLUDE_GDB_CONTROL
if ((i != 0) && (csrf.dcsr_step_bit == 1'b1)) begin
stop = True;
debug_step = True;
end
`endif
// check for wrong path, if wrong path, don't process it, leave to the other rule in next cycle
if(!epochManager.checkEpoch[i].check(main_epoch)) begin
stop = True;
@@ -971,6 +1088,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
let y = ToReorderBuffer{pc: pc,
orig_inst: orig_inst,
iType: dInst.iType,
dst: arch_regs.dst,
dst_data: ?, // Available only after execution
`ifdef INCLUDE_TANDEM_VERIF
store_data: ?,
store_data_BE: ?,
`endif
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
@@ -1007,6 +1130,11 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
end
end
`ifdef INCLUDE_GDB_CONTROL
if (debug_step)
rg_m_halt_req <= tagged Valid DebugStep;
`endif
// only fire this rule if we make some progress
// otherwise this rule may block other rules forever
when(doCorrectPath, noAction);
@@ -1047,4 +1175,19 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
default: 0;
endcase);
endmethod
`ifdef INCLUDE_GDB_CONTROL
method Action debug_halt_req () if (rg_m_halt_req == tagged Invalid);
rg_m_halt_req <= tagged Valid DebugHalt;
if (verbosity >= 1)
$display ("%0d: %m.renameStage.renameStage.debug_halt_req", cur_cycle);
endmethod
method Action debug_resume;
rg_m_halt_req <= tagged Invalid;
if (verbosity >= 1)
$display ("%0d: %m.renameStage.renameStage.debug_resume", cur_cycle);
endmethod
`endif
endmodule

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -35,8 +36,8 @@ typedef TDiv#(SupSize, 2) FpuMulDivExeNum;
// Phy RFile
// write: Alu < FpuMulDiv < Mem
// read: Alu, FpuMulDiv, Mem
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
// sb lazy lookup num: same as RFile read, becaues all pipelines recv bypass
typedef RFileRdPortNum SbLazyLookupPortNum;
@@ -65,6 +66,7 @@ Integer memWrAggrPort = 1 + valueof(FpuMulDivExeNum) + valueof(AluExeNum);
function Integer aluRdPort(Integer i) = i;
function Integer fpuMulDivRdPort(Integer i) = valueof(AluExeNum) + i;
Integer memRdPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
Integer debuggerPort = memRdPort + 1;
// ports for correct spec, ordering doesn't matter
typedef TAdd#(2, AluExeNum) CorrectSpecPortNum;

View File

@@ -31,8 +31,8 @@ export NextAddrPred(..);
export mkBtb;
interface NextAddrPred;
method Addr predPc(Addr pc);
method Action update(Addr pc, Addr nextPc, Bool taken);
method Maybe#(Addr) predPc(Addr pc);
method Action update(Addr pc, Addr brTarget, Bool taken);
// security
method Action flush;
method Bool flush_done;
@@ -96,13 +96,13 @@ module mkBtb(NextAddrPred);
endrule
`endif
method Addr predPc(Addr pc);
method Maybe#(Addr) predPc(Addr pc);
BtbIndex index = getIndex(pc);
BtbTag tag = getTag(pc);
if(valid[index] && tag == tags.sub(index))
return next_addrs.sub(index);
return tagged Valid next_addrs.sub(index);
else
return (pc + 4);
return tagged Invalid;
endmethod
method Action update(Addr pc, Addr nextPc, Bool taken);

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2019 Bluepec, Inc
// This package implements utility functions used by the floating point
// related logic
package FP_Utils;
import FloatingPoint::*;
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
// nanbox-ing and its inverse (unbox-ing)
// If the raw bits are nan-boxed, the fv_nanbox(fv_unbox) are identity
// functions. However, if the raw input was not properly nanboxed, then the
// output would be a canonical NaN
// Take a single precision value and nanboxes it to be able to write it to a
// 64-bit FPR register file. This is necessary if single precision operands
// used with a register file capable of holding double precision values
function Bit #(64) fv_nanbox (Bit #(64) x);
Bit #(64) fill_bits = (64'h1 << 32) - 1; // [31: 0] all ones
Bit #(64) fill_mask = (fill_bits << 32); // [63:32] all ones
return (x | fill_mask);
endfunction
// Take a 64-bit value and check if it is properly nanboxed if operating in a DP
// capable environment. If not properly nanboxed, return canonicalNaN32
function Float fv_unbox (Bit #(64) x);
//`ifdef ISA_D
if (x [63:32] == 32'hffffffff)
return (unpack (x [31:0]));
else
return (canonicalNaN);
//`else
// return (unpack (x [31:0]));
//`endif
endfunction
endpackage

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2016, 2017 Massachusetts Institute of Technology
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -41,6 +42,7 @@ import XilinxFpu::*;
import HasSpecBits::*;
import SpecFifo::*;
import SpecPoisonFifo::*;
import FP_Utils::*;
export FpuResult(..);
export FpuResp(..);
@@ -50,8 +52,6 @@ export mkFpuExecPipeline;
typedef FloatingPoint::RoundMode FpuRoundMode;
typedef FloatingPoint::Exception FpuException;
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
typedef struct {
Data data;
Bit#(5) fflags;
@@ -172,26 +172,36 @@ function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_wu (Bit#(64) in_bits,
endfunction
function Tuple2#(Bit#(64), FpuException) fmin_s(Bit#(64) in1, Bit#(64) in2);
Float in1_f = unpack(in1[31:0]);
Float in2_f = unpack(in2[31:0]);
// nirajns: interpret the inputs as floats. Observe that this function
// receives raw bits.
Float in1_f = fv_unbox(in1);
Float in2_f = fv_unbox(in2);
Bit #(64) in1_f_packed = fv_nanbox (zeroExtend(pack(in1_f)));
Bit #(64) in2_f_packed = fv_nanbox (zeroExtend(pack(in2_f)));
Float nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(zeroExtend(pack(nan_f)), e);
end
if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end else begin
let signLT = (in1_f.sign && !in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absLT = {in1_f.exp, in1_f.sfd} < {in2_f.exp, in2_f.sfd};
if (signLT || (signEQ && (in1_f.sign ? !absLT : absLT))) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end
end
endfunction
@@ -202,9 +212,14 @@ function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
Double nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(pack(nan_f), e);
end
if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(zeroExtend(pack(nan_f)), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
@@ -222,26 +237,39 @@ function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
endfunction
function Tuple2#(Bit#(64), FpuException) fmax_s(Bit#(64) in1, Bit#(64) in2);
Float in1_f = unpack(in1[31:0]);
Float in2_f = unpack(in2[31:0]);
// nirajns: interpret the inputs as floats. Observe that this function
// receives raw bits.
// If the raw bits are nan-boxed, the fv_nanbox(fv_unbox) are identity
// functions. However, if the raw input was not properly nanboxed, then
// the output would be a canonical NaN
Float in1_f = fv_unbox(in1);
Float in2_f = fv_unbox(in2);
Bit #(64) in1_f_packed = fv_nanbox (zeroExtend(pack(in1_f)));
Bit #(64) in2_f_packed = fv_nanbox (zeroExtend(pack(in2_f)));
Float nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(zeroExtend(pack(nan_f)), e);
end
if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end else begin
let signGT = (!in1_f.sign && in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absGT = {in1_f.exp, in1_f.sfd} > {in2_f.exp, in2_f.sfd};
if (signGT || (signEQ && (in1_f.sign ? !absGT : absGT))) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end
end
endfunction
@@ -252,9 +280,14 @@ function Tuple2#(Bit#(64), FpuException) fmax_d(Bit#(64) in1, Bit#(64) in2);
Double nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(pack(nan_f), e);
end
if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(zeroExtend(pack(nan_f)), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
@@ -417,8 +450,9 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
if (fpu_inst.precision == Single) begin
// single precision
Float in1 = unpack(rVal1[31:0]);
Float in2 = unpack(rVal2[31:0]);
// nirajns: interpret them as floats
Float in1 = fv_unbox(rVal1);
Float in2 = fv_unbox(rVal2);
Float dst = unpack(0);
Maybe#(Data) full_dst = Invalid;
FpuException e = unpack(0);
@@ -436,18 +470,33 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
{x, e} = fmax_s(rVal1, rVal2);
full_dst = tagged Valid x;
end
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
FEq: begin
// nirajns: TEST 10 failure on fcmp ISA tests
Data x;
if (isNaN (in1) || isNaN (in2)) x = 0;
else x = zeroExtend(pack(compareFP(in1, in2) == EQ));
if (isSNaN(in1) || isSNaN(in2)) begin
e.invalid_op = True;
end
full_dst = tagged Valid x;
end
FLt: begin
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
Data x;
if (isNaN (in1) || isNaN (in2)) x = 0;
else x = zeroExtend(pack(compareFP(in1, in2) == LT));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
full_dst = tagged Valid x;
end
FLe: begin
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
Data x;
if (isNaN (in1) || isNaN (in2)) x = 0;
else x = zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ)));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
full_dst = tagged Valid x;
end
// CLASS functions
FClass: begin
@@ -481,9 +530,11 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
dst.sign = unpack(pack(in1.sign) ^ pack(in2.sign));
end
// Float -> Bits
FMv_XF: full_dst = tagged Valid signExtend(pack(in1));
// nirajns: don't interpret the bits - use raw bits rVal1
FMv_XF: full_dst = tagged Valid signExtend(pack(rVal1[31:0]));
// Bits -> Float
FMv_FX: full_dst = tagged Valid zeroExtend(pack(in1));
// nirajns: don't interpret the bits - use raw bits rVal1
FMv_FX: full_dst = tagged Valid fv_nanbox (zeroExtend(pack(rVal1[31:0])));
// Float -> Float
FCvt_FF: begin
Double in1_double = unpack(rVal1);
@@ -529,7 +580,7 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
if (isNaN(dst)) dst = canonicalNaN;
end
endcase
fpu_result.data = (full_dst matches tagged Valid .data ? data : zeroExtend(pack(dst)));
fpu_result.data = (full_dst matches tagged Valid .data ? data : fv_nanbox(zeroExtend(pack(dst))));
fpu_result.fflags = pack(e);
end else if (fpu_inst.precision == Double) begin
// double precision
@@ -552,15 +603,24 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
{x, e} = fmax_d(rVal1, rVal2);
full_dst = tagged Valid x;
end
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
FEq: begin
// nirajns: TEST 10 failure on fcmp ISA tests
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
else dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
if (isSNaN(in1) || isSNaN(in2)) begin
e.invalid_op = True;
end
end
FLt: begin
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
else dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
end
FLe: begin
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
else dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
@@ -720,7 +780,7 @@ module mkFpuExecPipeline(FpuExec);
// canonicalize NaN
out_f = isNaN(out_f) ? canonicalNaN : out_f;
res = FpuResult {
data: zeroExtend(pack(out_f)),
data: fv_nanbox (zeroExtend(pack(out_f))),
fflags: pack(info.exc_conv_in | exc_op | exc_conv_out)
};
end
@@ -778,9 +838,10 @@ module mkFpuExecPipeline(FpuExec);
Double in3 = unpack(rVal3);
if (fpu_inst.precision == Single) begin
// conver single to double
Float f1 = unpack(rVal1[31:0]);
Float f2 = unpack(rVal2[31:0]);
Float f3 = unpack(rVal3[31:0]);
// nirajns: interpret the raw bits as floats first
Float f1 = fv_unbox(rVal1);
Float f2 = fv_unbox(rVal2);
Float f3 = fv_unbox(rVal3);
let {d1, exc1} = fcvt_d_s(f1, fpu_rm);
let {d2, exc2} = fcvt_d_s(f2, fpu_rm);
let {d3, exc3} = fcvt_d_s(f3, fpu_rm);

View File

@@ -1,6 +1,19 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
// This file is a modified version of: RISCY_OOO/procs/lib/LLCDmaConnect.bsv
// Bluespec: this file is has many modifications.
// The original module had 3 params and had an Empty interface.
// The 2nd param was: MemLoaderMemClient memLoader
// which issued only write-transactions (to load memory).
// The module discarded write responses, and ignored read-requests.
// Here, that module parameter is removed and, instead, the module has an
// AXI4_Slave interface, to be connected to the AXI4_Master of the
// Debug Module. This axi4_slave accepts, processes and responds
// to both read and write transactions.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
@@ -21,12 +34,31 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import FShow::*;
import GetPut::*;
import Vector::*;
import BuildVector::*;
import FIFO::*;
import Assert::*;
// ================================================================
// BSV library imports
import FIFOF :: *;
import Connectable :: *;
import FShow :: *;
import GetPut :: *;
import Vector :: *;
import BuildVector :: *;
import FIFO :: *;
import Assert :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import Semi_FIFOF :: *;
import EdgeFIFOFs :: *;
// ================================================================
// Project imports
// ----------------
// From RISCY-OOO
import Types::*;
import ProcTypes::*;
@@ -37,6 +69,15 @@ import MemLoader::*;
import CrossBar::*;
import MemLoader::*;
// ----------------
// From Toooba
import AXI4_Types :: *;
import Fabric_Defs :: *;
import Semi_FIFOF :: *;
// ================================================================
typedef struct {
CoreId core;
TlbMemReqId id;
@@ -48,15 +89,277 @@ typedef union tagged {
TlbDmaReqId Tlb;
} LLCDmaReqId deriving(Bits, Eq, FShow);
module mkLLCDmaConnect#(
// ================================================================
// Help functions for read-modify-writes of 4-Byte values on a 64-Byte Cache Line
typedef enum {CACHELINE_CACHE_INVALID,
CACHELINE_CACHE_WRITING_BACK,
CACHELINE_CACHE_RELOADING,
CACHELINE_CACHE_CLEAN,
CACHELINE_CACHE_DIRTY
} Cacheline_Cache_State
deriving (Bits, Eq, FShow);
function Addr fn_align_addr_to_line (Addr addr);
Addr line_addr = { addr [63:6], 6'b0 };
return line_addr;
endfunction
function Bool fn_addr_is_in_line (Addr addr, Addr line_addr);
return (fn_align_addr_to_line (addr) == line_addr);
endfunction
function Bit #(64) fn_expand_strb_to_mask (Bit #(8) strb);
function Bit #(8) fn_bit_to_byte (Integer j);
return ((strb [j] == 1'b1) ? 8'hFF : 8'h00);
endfunction
Vector #(8, Bit #(8)) v = genWith (fn_bit_to_byte);
return pack (v);
endfunction
// ================================================================
module mkLLCDmaConnect #(
DmaServer#(LLCDmaReqId) llc,
MemLoaderMemClient memLoader,
// MemLoaderMemClient memLoader, // REPLACED BY AXI4_Slave_interface
Vector#(CoreNum, TlbMemClient) tlb
)(Empty) provisos (
)(AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User)) provisos (
Alias#(dmaRqT, DmaRq#(LLCDmaReqId))
);
Bool verbose = False;
Integer verbosity = 0;
// When debugger reads a word, request a line from LLC, and remember dword-in-line here
FIFOF #(Bit #(3)) f_dword_in_line <- mkFIFOF;
// Slave transactor for requests from Debug Module
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) axi4_slave_xactor <- mkAXI4_Slave_Xactor;
// ================================================================
// These regs are a 1-location local cache for an LLC Cache Line,
// to avoid doing a full read-modify-write to the LLC on each transaction.
Reg #(Cacheline_Cache_State) rg_cacheline_cache_state <- mkReg (CACHELINE_CACHE_CLEAN);
Reg #(Addr) rg_cacheline_cache_addr <- mkReg (1); // never matches an LLC line addr
Reg #(Line) rg_cacheline_cache_data <- mkRegU;
// Writeback dirty cacheline_cache if no new store requests within n-cycles
Reg #(Bit #(10)) rg_cacheline_cache_dirty_delay <- mkReg (0);
// ================================================================
// Write transactions from the external client (e.g., Debug Module)
// Respond to store-requests from the external client on store-hit
rule rl_handle_MemLoader_st_req ( ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
|| (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY))
&& (fn_addr_is_in_line (axi4_slave_xactor.o_wr_addr.first.awaddr,
rg_cacheline_cache_addr)));
let wr_addr <- pop_o (axi4_slave_xactor.o_wr_addr);
let wr_data <- pop_o (axi4_slave_xactor.o_wr_data);
Addr addr = wr_addr.awaddr;
Bit #(64) data = wr_data.wdata;
Bit #(64) mask = fn_expand_strb_to_mask (wr_data.wstrb);
// Read rg_cacheline_cache_data as 64-bit words
Vector #(8, Bit #(64)) line_dwords = unpack (pack (rg_cacheline_cache_data));
// Modify relevant bytes of relevant dword
Bit #(3) dword_in_line = addr [5:3];
Bit #(64) old_dword = line_dwords [dword_in_line];
Bit #(64) new_dword = ((old_dword & (~ mask)) | (data & mask));
line_dwords [dword_in_line] = new_dword;
// Save it
rg_cacheline_cache_data <= unpack (pack (line_dwords));
rg_cacheline_cache_state <= CACHELINE_CACHE_DIRTY;
rg_cacheline_cache_dirty_delay <= '1; // start write-back delay countdown
// Send response to external client
AXI4_Wr_Resp #(Wd_Id, Wd_User)
wr_resp = AXI4_Wr_Resp {bid: 0, // TODO: change uniformly to Fabric_id
bresp: axi4_resp_okay,
buser: ?};
axi4_slave_xactor.i_wr_resp.enq (wr_resp);
if (verbosity >= 2) begin
$display ("%0d: %m.rl_handle_MemLoader_st_req: addr %0h data %0h strb %0h",
cur_cycle, wr_addr.awaddr, wr_data.wdata, wr_data.wstrb);
$display (" old_dword: %0h", old_dword);
$display (" new_dword: %0h", old_dword);
end
endrule
// ================================================================
// Read transactions from the external memory client (e.g., Debug Module)
// Responds to load-requests from the external client on load-hit
rule rl_handle_MemLoader_ld_req ( ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
|| (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY))
&& (fn_addr_is_in_line (axi4_slave_xactor.o_rd_addr.first.araddr,
rg_cacheline_cache_addr)));
let rd_addr <- pop_o (axi4_slave_xactor.o_rd_addr);
Addr addr = rd_addr.araddr;
// Read rg_cacheline_cache as 64-bit words
Vector #(8, Bit #(64)) line_dwords = unpack (pack (rg_cacheline_cache_data));
Bit #(3) dword_in_line = addr [5:3];
Bit #(64) dword = line_dwords [dword_in_line];
// Send response to external client
AXI4_Rd_Data #(Wd_Id, Wd_Data, Wd_User)
rd_data = AXI4_Rd_Data {rid: 0, // TODO: fixup
rdata: dword,
rresp: axi4_resp_okay,
rlast: True,
ruser: ?};
axi4_slave_xactor.i_rd_data.enq (rd_data);
if (verbosity >= 2) begin
$display ("%0d: %m.rl_handle_MemLoader_ld_req: addr %0h", cur_cycle, rd_addr.araddr);
$display (" dword: %0h", dword);
end
endrule
// ----------------------------------------------------------------
// Miss and writeback processing
// Maintain dirty delay countdown
rule rl_cacheline_cache_writeback_dirty_delay ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
&& (rg_cacheline_cache_dirty_delay != 0));
rg_cacheline_cache_dirty_delay <= rg_cacheline_cache_dirty_delay - 1;
endrule
function Action fa_writeback;
action
dmaRqT req = DmaRq {addr: rg_cacheline_cache_addr,
byteEn: replicate (True), // Write all bytes
data: rg_cacheline_cache_data,
id: tagged MemLoader (?) // TODO: use wr_addr.awid?
};
llc.memReq.enq (req);
// $display ("%0d: %m.fa_writeback line at %0h", cur_cycle, rg_cacheline_cache_addr);
// $display (" data %0128h", rg_cacheline_cache_data);
endaction
endfunction
// Initiate writeback if dirty for full delay
rule rl_cacheline_cache_writeback_dirty_aged ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
&& (rg_cacheline_cache_dirty_delay == 0));
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_writeback_dirty_aged.", cur_cycle);
$display (" Old line addr %0h", rg_cacheline_cache_addr);
end
fa_writeback;
rg_cacheline_cache_state <= CACHELINE_CACHE_WRITING_BACK;
endrule
// Initiate writeback if dirty and next request is store-miss
rule rl_cacheline_cache_writeback_st_miss ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_wr_addr.first.awaddr,
rg_cacheline_cache_addr)));
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_writeback_st_miss.", cur_cycle);
$display (" Old line addr %0h", rg_cacheline_cache_addr);
$display (" New addr %0h", axi4_slave_xactor.o_wr_addr.first.awaddr);
end
fa_writeback;
rg_cacheline_cache_state <= CACHELINE_CACHE_WRITING_BACK;
endrule
// Initiate writeback if dirty and next request is load-miss
rule rl_cacheline_cache_writeback_ld_miss ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_rd_addr.first.araddr,
rg_cacheline_cache_addr)));
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_writeback_ld_miss.", cur_cycle);
$display (" Old line addr %0h", rg_cacheline_cache_addr);
$display (" New addr %0h", axi4_slave_xactor.o_wr_addr.first.awaddr);
end
fa_writeback;
rg_cacheline_cache_state <= CACHELINE_CACHE_WRITING_BACK;
endrule
// Finish writeback
rule rl_cacheline_cache_writeback_finish (llc.respSt.first matches tagged MemLoader .id
&&& (rg_cacheline_cache_state == CACHELINE_CACHE_WRITING_BACK));
let resp = llc.respSt.first;
llc.respSt.deq;
rg_cacheline_cache_state <= CACHELINE_CACHE_CLEAN;
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_writeback_finish. Line addr %0h",
cur_cycle, rg_cacheline_cache_addr);
$display (" Line data %0h", rg_cacheline_cache_data);
end
endrule
function Action fa_initiate_reload (Addr addr);
action
let line_addr = fn_align_addr_to_line (addr);
dmaRqT req = DmaRq {addr: line_addr,
byteEn: replicate (False), // all False means 'read'
data: ?,
id: tagged MemLoader (?)}; // TODO: change uniformly to wr_addr.awid
llc.memReq.enq (req);
rg_cacheline_cache_addr <= line_addr;
if (verbosity >= 2) begin
$display (" fa_initiate_reload: line_addr %0h", line_addr);
end
endaction
endfunction
// Initiate reload when cacheline_cache is clean on store-miss
rule rl_cacheline_cache_reload_req_st ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_wr_addr.first.awaddr,
rg_cacheline_cache_addr)));
let addr = axi4_slave_xactor.o_wr_addr.first.awaddr;
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_reload_req_st for addr %0h", cur_cycle, addr);
end
fa_initiate_reload (addr);
rg_cacheline_cache_state <= CACHELINE_CACHE_RELOADING;
endrule
// Initiate reload when cacheline_cache is clean on load-miss
rule rl_cacheline_cache_reload_req_ld ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_rd_addr.first.araddr,
rg_cacheline_cache_addr)));
let addr = axi4_slave_xactor.o_rd_addr.first.araddr;
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_reload_req_ld for addr %0h", cur_cycle, addr);
end
fa_initiate_reload (addr);
rg_cacheline_cache_state <= CACHELINE_CACHE_RELOADING;
endrule
// Finish reload
rule rl_cacheline_cache_reload_finish (llc.respLd.first.id matches tagged MemLoader .id
&&& (rg_cacheline_cache_state == CACHELINE_CACHE_RELOADING));
let resp = llc.respLd.first;
llc.respLd.deq;
rg_cacheline_cache_state <= CACHELINE_CACHE_CLEAN;
rg_cacheline_cache_data <= resp.data;
if (verbosity >= 2) begin
$display ("%0d: %m.rl_cacheline_cache_reload_finish. Line addr %0h", cur_cycle, rg_cacheline_cache_addr);
$display (" Line data %0h", resp.data);
end
endrule
// ================================================================
// Transactions from the TLB
// Expecting only LOAD requests from TLB
// This section is unchanged from the original riscy-ooo module.
// helper functions for cross bar
function XBarDstInfo#(Bit#(0), Tuple2#(CoreId, TlbMemReq)) getTlbDst(CoreId core, TlbMemReq r);
return XBarDstInfo {idx: 0, data: tuple2(core, r)};
@@ -83,24 +386,13 @@ module mkLLCDmaConnect#(
};
endfunction
// send req to LLC
rule sendMemLoaderReqToLLC;
memLoader.memReq.deq;
let r = memLoader.memReq.first;
dmaRqT req = DmaRq {
addr: r.addr,
byteEn: r.byteEn,
data: r.data,
id: MemLoader (r.id)
};
llc.memReq.enq(req);
if(verbose) begin
$display("[LLCDmaConnnect sendMemLoaderReqToLLC] ",
fshow(r), " ; ", fshow(req));
end
endrule
// Prioritize external mem client over Tlb
(* descending_urgency = "rl_cacheline_cache_writeback_dirty_aged, sendTlbReqToLLC" *)
(* descending_urgency = "rl_cacheline_cache_writeback_st_miss, sendTlbReqToLLC" *)
(* descending_urgency = "rl_cacheline_cache_writeback_ld_miss, sendTlbReqToLLC" *)
(* descending_urgency = "rl_cacheline_cache_reload_req_st, sendTlbReqToLLC" *)
(* descending_urgency = "rl_cacheline_cache_reload_req_ld, sendTlbReqToLLC" *)
(* descending_urgency = "sendMemLoaderReqToLLC, sendTlbReqToLLC" *)
rule sendTlbReqToLLC;
let {c, r} <- toGet(tlbQ).get;
let req = getTlbDmaReq(c, r);
@@ -110,16 +402,6 @@ module mkLLCDmaConnect#(
end
endrule
// send Ld resp from LLC
rule sendLdRespToMemLoader(llc.respLd.first.id matches tagged MemLoader .id);
llc.respLd.deq;
if(verbose) begin
$display("[LLCDmaConnect sendLdRespToMemLoader] ",
fshow(llc.respLd.first));
end
doAssert(False, "No mem loader ld");
endrule
rule sendLdRespToTlb(llc.respLd.first.id matches tagged Tlb .id);
llc.respLd.deq;
let resp = llc.respLd.first;
@@ -133,16 +415,6 @@ module mkLLCDmaConnect#(
end
endrule
// send St resp from LLC
rule sendStRespToMemLoader(llc.respSt.first matches tagged MemLoader .id);
llc.respSt.deq;
memLoader.respSt.enq(id);
if(verbose) begin
$display("[LLCDmaConnect sendStRespToMemLoader] ",
fshow(llc.respSt.first));
end
endrule
rule sendStRespToTlb(llc.respSt.first matches tagged Tlb .id);
llc.respSt.deq;
if(verbose) begin
@@ -150,4 +422,9 @@ module mkLLCDmaConnect#(
end
doAssert(False, "No TLB st");
endrule
// ================================================================
// INTERFACE
return axi4_slave_xactor.axi_side;
endmodule

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2018 Massachusetts Institute of Technology
// Portions copyright (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
// Portions (c) 2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -275,8 +276,22 @@ typedef enum {
// sanctum user CSR
CSRtrng = 12'hcc0, // random number for secure boot
`endif
CSRtselect = 12'h7A0, // Debug/trace tselect
CSRtdata1 = 12'h7A1, // Debug/trace tdata1
CSRtdata2 = 12'h7A2, // Debug/trace tdata2
CSRtdata3 = 12'h7A3, // Debug/trace tdata3
`ifdef INCLUDE_GDB_CONTROL
CSRdcsr = 12'h7B0, // Debug control and status
CSRdpc = 12'h7B1, // Debug PC
CSRdscratch0 = 12'h7B2, // Debug scratch0
CSRdscratch1 = 12'h7B3, // Debug scratch1
`endif
// CSR that catches all the unimplemented CSRs. To avoid exception on this,
// make it a user non-standard read/write CSR.
// Bluespec: in RenameStage.getTrap(), we force this to be a csr_access_trap
CSRnone = 12'h8ff
} CSR deriving(Bits, Eq, FShow);
@@ -332,6 +347,19 @@ function CSR unpackCSR(Bit#(12) x);
pack(CSR'(CSRmspec )): (CSRmspec );
pack(CSR'(CSRtrng )): (CSRtrng );
`endif
pack(CSR'(CSRtselect )): (CSRtselect );
pack(CSR'(CSRtdata1 )): (CSRtdata1 );
pack(CSR'(CSRtdata2 )): (CSRtdata2 );
pack(CSR'(CSRtdata3 )): (CSRtdata3 );
`ifdef INCLUDE_GDB_CONTROL
pack(CSR'(CSRdcsr )): (CSRdcsr );
pack(CSR'(CSRdpc )): (CSRdpc );
pack(CSR'(CSRdscratch0 )): (CSRdscratch0 );
pack(CSR'(CSRdscratch1 )): (CSRdscratch1 );
`endif
default : (CSRnone );
endcase);
endfunction
@@ -452,13 +480,20 @@ typedef enum {
MachineTimer = 4'd7,
UserExternal = 4'd8,
SupervisorExternel = 4'd9,
MachineExternal = 4'd11,
MachineExternal = 4'd11
`ifdef INCLUDE_GDB_CONTROL
, DebugHalt = 4'd14, // Debugger halt command (^C in GDB)
DebugStep = 4'd15 // dcsr.step is set and 1 instr has been processed
`endif
DebugExternal = 4'd14 // Bluespec: for debug mode
} Interrupt deriving(Bits, Eq, FShow);
// typedef 12 InterruptNum;
typedef 15 InterruptNum; // Bluespec: extended to 15 bits for debug interrupt
`ifdef INCLUDE_GDB_CONTROL
typedef 16 InterruptNum; // With debugger
`else
typedef 12 InterruptNum; // Without debugger
`endif
// Traps are either an exception or an interrupt
typedef union tagged {

View File

@@ -34,6 +34,8 @@ import RevertingVirtualReg::*;
import RVFI_DII_Types::*;
`endif
import Cur_Cycle :: *;
// right after execution, full_result has more up-to-date data (e.g. ppc of mispredicted branch)
// some parts of full_result are for verification
// but some are truly used for execution
@@ -58,6 +60,13 @@ typedef struct {
Addr pc;
Bit #(32) orig_inst; // original 16b or 32b instruction ([1:0] will distinguish 16b or 32b)
IType iType;
Maybe#(ArchRIndx) dst; // Invalid, GPR or FPR destination ("Rd")
Data dst_data; // Output of instruction into destination register
`ifdef INCLUDE_TANDEM_VERIF
// Store-data, for those mem instrs that store data
Data store_data;
ByteEn store_data_BE;
`endif
Maybe#(CSR) csr;
Bool claimed_phy_reg; // whether we need to commmit renaming
Maybe#(Trap) trap;
@@ -100,6 +109,7 @@ typedef enum {
interface Row_setExecuted_doFinishAlu;
method Action set(
Data dst_data,
Maybe#(Data) csrData,
ControlFlow cf
`ifdef RVFI
@@ -109,7 +119,7 @@ interface Row_setExecuted_doFinishAlu;
endinterface
interface Row_setExecuted_doFinishFpuMulDiv;
method Action set(Bit#(5) fflags);
method Action set(Data dst_data, Bit#(5) fflags);
endinterface
interface ReorderBufferRowEhr#(numeric type aluExeNum, numeric type fpuMulDivExeNum);
@@ -130,11 +140,20 @@ interface ReorderBufferRowEhr#(numeric type aluExeNum, numeric type fpuMulDivExe
// faulting inst cannot have this set, since there is no access to
// perform), and non-MMIO St can become Executed (NOTE faulting
// instructions are not Executed, they are set at deqLSQ time)
method Action setExecuted_doFinishMem(Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
method Action setExecuted_doFinishMem(Addr vaddr,
Data store_data, ByteEn store_data_BE,
Bool access_at_commit, Bool non_mmio_st_done
`ifdef RVFI
, ExtraTraceBundle tb
, ExtraTraceBundle tb
`endif
);
`ifdef INCLUDE_TANDEM_VERIF
// Used after a Ld, Lr, Sc, Amo to record reg data
method Action setExecuted_doFinishMem_RegData (Data dst_data);
`endif
`ifdef INORDER_CORE
// in-order core sets LSQ tag after getting out of issue queue
method Action setLSQTag(LdStQTag t, Bool isFence);
@@ -201,6 +220,12 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
Reg#(Addr) pc <- mkRegU;
Reg #(Bit #(32)) orig_inst <- mkRegU;
Reg#(IType) iType <- mkRegU;
Reg #(Maybe #(ArchRIndx)) rg_dst_reg <- mkRegU;
Reg #(Data) rg_dst_data <- mkRegU;
`ifdef INCLUDE_TANDEM_VERIF
Reg #(Data) rg_store_data <- mkRegU;
Reg #(ByteEn) rg_store_data_BE <- mkRegU;
`endif
Reg#(Maybe#(CSR)) csr <- mkRegU;
Reg#(Bool) claimed_phy_reg <- mkRegU;
Ehr#(3, Maybe#(Trap)) trap <- mkEhr(?);
@@ -235,6 +260,7 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
for(Integer i = 0; i < valueof(aluExeNum); i = i+1) begin
aluSetExe[i] = (interface Row_setExecuted_doFinishAlu;
method Action set(
Data dst_data,
Maybe#(Data) csrData,
ControlFlow cf
`ifdef RVFI
@@ -242,7 +268,10 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
`endif
);
// inst is done
rob_inst_state[state_finishAlu_port(i)] <= Executed;
rob_inst_state[state_finishAlu_port(i)] <= Executed;
// Destination register data, for Tandem Verification
rg_dst_data <= dst_data;
// update PPC or csrData (vaddr is always useless for ALU results)
if(csrData matches tagged Valid .d) begin
ppc_vaddr_csrData[pvc_finishAlu_port(i)] <= CSRData (d);
@@ -262,9 +291,10 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
Vector#(fpuMulDivExeNum, Row_setExecuted_doFinishFpuMulDiv) fpuMulDivExe;
for(Integer i = 0; i < valueof(fpuMulDivExeNum); i = i+1) begin
fpuMulDivExe[i] = (interface Row_setExecuted_doFinishFpuMulDiv;
method Action set(Bit#(5) new_fflags);
method Action set(Data dst_data, Bit#(5) new_fflags);
// inst is done
rob_inst_state[state_finishFpuMulDiv_port(i)] <= Executed;
rg_dst_data <= dst_data;
// update fflags
fflags[fflags_finishFpuMulDiv_port(i)] <= new_fflags;
endmethod
@@ -279,9 +309,11 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
interface setExecuted_doFinishFpuMulDiv = fpuMulDivExe;
method Action setExecuted_doFinishMem(Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
method Action setExecuted_doFinishMem(Addr vaddr,
Data store_data, ByteEn store_data_BE,
Bool access_at_commit, Bool non_mmio_st_done
`ifdef RVFI
, ExtraTraceBundle tb
, ExtraTraceBundle tb
`endif
);
doAssert(!(access_at_commit && non_mmio_st_done),
@@ -296,6 +328,11 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
`ifdef RVFI
//$display("%t : traceBundle = ", $time(), fshow(tb), " in setExecuted_doFinishMem for %x", pc);
traceBundle[pvc_finishMem_port] <= tb;
`endif
`ifdef INCLUDE_TANDEM_VERIF
// Store-data (for mem instrs that store data)
rg_store_data <= store_data;
rg_store_data_BE <= store_data_BE;
`endif
// update access at commit
memAccessAtCommit[accessCom_finishMem_port] <= access_at_commit;
@@ -303,6 +340,13 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
nonMMIOStDone[nonMMIOSt_finishMem_port] <= non_mmio_st_done;
endmethod
`ifdef INCLUDE_TANDEM_VERIF
// Used after a Ld, Lr, Sc, Amo to record reg data
method Action setExecuted_doFinishMem_RegData (Data dst_data);
rg_dst_data <= dst_data;
endmethod
`endif
`ifdef INORDER_CORE
method Action setLSQTag(LdStQTag t, Bool isFence);
lsqTag <= t;
@@ -315,6 +359,10 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
pc <= x.pc;
orig_inst <= x.orig_inst;
iType <= x.iType;
rg_dst_reg <= x.dst;
// rg_dst_data will be written after inst execution
// rg_store_data will be written in Mem pipeline
// rg_store_data_BE will be written in Mem pipeline
csr <= x.csr;
claimed_phy_reg <= x.claimed_phy_reg;
trap[trap_enq_port] <= x.trap;
@@ -355,6 +403,12 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
pc: pc,
orig_inst: orig_inst,
iType: iType,
dst: rg_dst_reg,
dst_data: rg_dst_data,
`ifdef INCLUDE_TANDEM_VERIF
store_data: rg_store_data,
store_data_BE: rg_store_data_BE,
`endif
csr: csr,
claimed_phy_reg: claimed_phy_reg,
trap: trap[trap_deq_port],
@@ -466,6 +520,7 @@ endinterface
interface ROB_setExecuted_doFinishAlu;
method Action set(InstTag x,
Data dst_data,
Maybe#(Data) csrData,
ControlFlow cf
`ifdef RVFI
@@ -475,7 +530,7 @@ interface ROB_setExecuted_doFinishAlu;
endinterface
interface ROB_setExecuted_doFinishFpuMulDiv;
method Action set(InstTag x, Bit#(5) fflags);
method Action set(InstTag x, Data dst_data, Bit#(5) fflags);
endinterface
interface ROB_getOrigPC;
@@ -508,11 +563,20 @@ interface SupReorderBuffer#(numeric type aluExeNum, numeric type fpuMulDivExeNum
interface Vector#(aluExeNum, ROB_setExecuted_doFinishAlu) setExecuted_doFinishAlu;
interface Vector#(fpuMulDivExeNum, ROB_setExecuted_doFinishFpuMulDiv) setExecuted_doFinishFpuMulDiv;
// doFinishMem, after addr translation
method Action setExecuted_doFinishMem(InstTag x, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
method Action setExecuted_doFinishMem(InstTag x,
Addr vaddr,
Data store_data, ByteEn store_data_BE,
Bool access_at_commit, Bool non_mmio_st_done
`ifdef RVFI
, ExtraTraceBundle tb
, ExtraTraceBundle tb
`endif
);
`ifdef INCLUDE_TANDEM_VERIF
// Used after a Ld, Lr, Sc, Amo to record reg data
method Action setExecuted_doFinishMem_RegData (InstTag x, Data dst_data);
`endif
`ifdef INORDER_CORE
// in-order core sets LSQ tag after getting out of issue queue
method Action setLSQTag(InstTag x, LdStQTag t, Bool isFence);
@@ -992,6 +1056,7 @@ module mkSupReorderBuffer#(
aluSetExeIfc[i] = (interface ROB_setExecuted_doFinishAlu;
method Action set(
InstTag x,
Data dst_data,
Maybe#(Data) csrData,
ControlFlow cf
`ifdef RVFI
@@ -1001,6 +1066,7 @@ module mkSupReorderBuffer#(
all(id, readVReg(setExeAlu_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_doFinishAlu[i].set(
dst_data,
csrData,
cf
`ifdef RVFI
@@ -1015,11 +1081,11 @@ module mkSupReorderBuffer#(
for(Integer i = 0; i < valueof(fpuMulDivExeNum); i = i+1) begin
fpuMulDivSetExeIfc[i] = (interface ROB_setExecuted_doFinishFpuMulDiv;
method Action set(
InstTag x, Bit#(5) fflags
InstTag x, Data dst_data, Bit#(5) fflags
) if(
all(id, readVReg(setExeFpuMulDiv_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_doFinishFpuMulDiv[i].set(fflags);
row[x.way][x.ptr].setExecuted_doFinishFpuMulDiv[i].set(dst_data, fflags);
endmethod
endinterface);
end
@@ -1095,20 +1161,29 @@ module mkSupReorderBuffer#(
interface setExecuted_doFinishFpuMulDiv = fpuMulDivSetExeIfc;
method Action setExecuted_doFinishMem(
InstTag x, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
InstTag x, Addr vaddr, Data store_data, ByteEn store_data_BE, Bool access_at_commit, Bool non_mmio_st_done
`ifdef RVFI
, tb
`endif
) if(
all(id, readVReg(setExeMem_SB_enq)) // ordering: < enq
);
row[x.way][x.ptr].setExecuted_doFinishMem(vaddr, access_at_commit, non_mmio_st_done
row[x.way][x.ptr].setExecuted_doFinishMem(vaddr,
store_data, store_data_BE,
access_at_commit, non_mmio_st_done
`ifdef RVFI
, tb
, tb
`endif
);
endmethod
`ifdef INCLUDE_TANDEM_VERIF
// Used after a Ld, Lr, Sc, Amo to record reg data
method Action setExecuted_doFinishMem_RegData (InstTag x, Data dst_data);
row[x.way][x.ptr].setExecuted_doFinishMem_RegData (dst_data);
endmethod
`endif
`ifdef INORDER_CORE
method Action setLSQTag(InstTag x, LdStQTag t, Bool isFence);
row[x.way][x.ptr].setLSQTag(t, isFence);

View File

@@ -33,6 +33,7 @@ import HasSpecBits::*;
import SpecFifo::*;
import StoreBuffer::*;
import Exec::*;
import FP_Utils::*;
// I don't want to export auxiliary functions, so manually export all types
export LdQMemFunc(..);
@@ -279,6 +280,9 @@ typedef struct {
typedef struct {
Bool wrongPath;
Maybe#(PhyDst) dst;
`ifdef INCLUDE_TANDEM_VERIF
InstTag instTag; // For recording Ld data in ROB
`endif
Data data;
} LSQRespLdResult deriving(Bits, Eq, FShow);
@@ -1974,6 +1978,9 @@ module mkSplitLSQ(SplitLSQ);
let res = LSQRespLdResult {
wrongPath: False,
dst: Invalid,
`ifdef INCLUDE_TANDEM_VERIF
instTag: ld_instTag [t], // For recording Ld data in ROB
`endif
data: ?
};
if(ld_waitWPResp_resp[t]) begin
@@ -1993,9 +2000,20 @@ module mkSplitLSQ(SplitLSQ);
// mark load as done, and shift resp
ld_done_resp[t] <= True;
res.wrongPath = False;
// nirajns: checking if this is a 32-bit load response to a FPR
// In that case, the data needs to be nanboxed before writing to
// the register files as the Toooba FPR is 64-bit
let bEn = ld_byteEn[t];
let dst = ld_dst[t];
let is32BitLd = (bEn[3] && !bEn[7]);
res.dst = ld_dst[t];
res.data = gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
ld_unsigned[t], alignedData);
if (dst.Valid.isFpuReg && is32BitLd)
res.data = fv_nanbox (gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
ld_unsigned[t], alignedData));
else
res.data = gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
ld_unsigned[t], alignedData);
end
if(verbose) begin
$display("[LSQ - respLd] ", fshow(t), "; ", fshow(alignedData),