Merge branch 'master' into RVFI_DII, a complex merge for the fetch stage!
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
35
src_Core/RISCY_OOO/procs/lib/FP_Utils.bsv
Normal file
35
src_Core/RISCY_OOO/procs/lib/FP_Utils.bsv
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user