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

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

View File

@@ -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
@@ -21,8 +22,6 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) Bluespec, Inc.
`include "ProcConfig.bsv"
import Vector::*;
@@ -88,6 +87,29 @@ import Toooba_RVFI_DII_Bridge::*;
import CsrFile :: *;
// ================================================================
// Toooba
import Cur_Cycle :: *;
import FIFOF :: *;
import GetPut_Aux :: *;
`ifdef INCLUDE_GDB_CONTROL
import DM_CPU_Req_Rsp :: *;
`endif
`ifdef INCLUDE_TANDEM_VERIF
import Trace_Data2 :: *;
`endif
// ================================================================
`ifdef SECURITY
`define SECURITY_OR_INCLUDE_GDB_CONTROL
`elsif INCLUDE_GDB_CONTROL
`define SECURITY_OR_INCLUDE_GDB_CONTROL
`endif
interface CoreReq;
method Action start(
Addr startpc,
@@ -139,13 +161,28 @@ interface Core;
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
method Action setMEIP (Bit #(1) v);
method Action setSEIP (Bit #(1) v);
// Bluespec: external interrupt to enter debug mode
method Action setDEIP (Bit #(1) v);
`ifdef RVFI_DII
interface Toooba_RVFI_DII_Server rvfi_dii_server;
`endif
`ifdef INCLUDE_GDB_CONTROL
interface Server #(Bool, Bool) hart0_run_halt_server;
interface Server #(DM_CPU_Req #(5, 64), DM_CPU_Rsp #(64)) hart0_gpr_mem_server;
`ifdef ISA_F
interface Server #(DM_CPU_Req #(5, 64), DM_CPU_Rsp #(64)) hart0_fpr_mem_server;
`endif
interface Server #(DM_CPU_Req #(12, 64), DM_CPU_Rsp #(64)) hart0_csr_mem_server;
`endif
`ifdef INCLUDE_TANDEM_VERIF
// Note: this is a SupSize vector of streams of Trace_Data2 structs,
// each of which has a serialnum field. Each of the SupSize
// streams has serialnums in increasing order. Each serialnum
// appears exactly once in exactly one of the streams. Thus, the
// channels can easily be merged into a single program-order stream.
interface Vector #(SupSize, Get #(Trace_Data2)) v_to_TV;
`endif
endinterface
// fixpoint to instantiate modules
@@ -157,9 +194,22 @@ interface CoreFixPoint;
interface Reg#(Bool) doStatsIfc;
endinterface
typedef enum {
`ifdef INCLUDE_GDB_CONTROL
CORE_HALTING,
CORE_HALTED,
`endif
CORE_RUNNING
} Core_Run_State
deriving (Bits, Eq, FShow);
(* synthesize *)
module mkCore#(CoreId coreId)(Core);
let verbose = False;
// ================================================================
Integer verbosity = 0; // More levels of verbosity control than 'Bool verbose'
Reg#(Bool) outOfReset <- mkReg(False);
rule rl_outOfReset if (!outOfReset);
$fwrite(stderr, "mkProc came out of reset\n");
@@ -168,6 +218,19 @@ module mkCore#(CoreId coreId)(Core);
Reg#(Bool) started <- mkReg(False);
// ================================================================
`ifdef INCLUDE_GDB_CONTROL
// Using a ConfigReg since scheduling of reads/writes not critical (TODO: verify this)
Reg #(Core_Run_State) rg_core_run_state <- mkConfigReg (CORE_RUNNING);
`endif
`ifdef INCLUDE_TANDEM_VERIF
Vector #(SupSize, FIFOF #(Trace_Data2)) v_f_to_TV <- replicateM (mkFIFOF);
`endif
// ================================================================
// front end
FetchStage fetchStage <- mkFetchStage;
ITlb iTlb = fetchStage.iTlbIfc;
@@ -244,18 +307,6 @@ module mkCore#(CoreId coreId)(Core);
// whether perf data is collected
Reg#(Bool) doStatsReg <- mkConfigReg(False);
// redirect func
//function Action redirectFunc(Addr trap_pc, Maybe#(SpecTag) spec_tag, InstTag inst_tag );
//action
// if (verbose) $fdisplay(stdout, "[redirect_action] new pc = 0x%8x, spec_tag = ", trap_pc, fshow(spec_tag));
// epochManager.redirect;
// fetchStage.redirect(trap_pc);
// if (spec_tag matches tagged Valid .valid_spec_tag) begin
// globalSpecUpdate.incorrectSpec(valid_spec_tag, inst_tag);
// end
//endaction
//endfunction
// write aggressive elements + wakupe reservation stations
function Action writeAggr(Integer wrAggrPort, PhyRIndx dst);
action
@@ -332,7 +383,7 @@ module mkCore#(CoreId coreId)(Core);
let train <- toGet(trainBPQ[i]).get;
fetchStage.train_predictors(
train.pc, train.nextPc, train.iType, train.taken,
train.dpTrain, train.mispred
train.dpTrain, train.mispred, train.isCompressed
);
endrule
end
@@ -363,6 +414,9 @@ module mkCore#(CoreId coreId)(Core);
method csrf_rd = csrf.rd;
method rob_getPC = rob.getOrigPC[valueof(AluExeNum)].get; // last getPC port
method rob_setExecuted_doFinishMem = rob.setExecuted_doFinishMem;
`ifdef INCLUDE_TANDEM_VERIF
method rob_setExecuted_doFinishMem_RegData = rob.setExecuted_doFinishMem_RegData;
`endif
method rob_setExecuted_deqLSQ = rob.setExecuted_deqLSQ;
method isMMIOAddr = mmio.isMMIOAddr;
method mmioReq = mmio.dataReq;
@@ -407,13 +461,15 @@ module mkCore#(CoreId coreId)(Core);
Reg#(Bool) flush_tlbs <- mkReg(False);
Reg#(Bool) update_vm_info <- mkReg(False);
Reg#(Bool) flush_reservation <- mkReg(False);
`ifdef SECURITY
`ifdef SECURITY_OR_INCLUDE_GDB_CONTROL
Reg#(Bool) flush_caches <- mkReg(False);
Reg#(Bool) flush_brpred <- mkReg(False);
`else
Reg#(Bool) flush_caches <- mkReadOnlyReg(False);
Reg#(Bool) flush_brpred <- mkReadOnlyReg(False);
`endif
`ifdef SELF_INV_CACHE
Reg#(Bool) reconcile_i <- mkReg(False);
`else
@@ -500,6 +556,9 @@ module mkCore#(CoreId coreId)(Core);
`endif
endmethod
method doStats = coreFix.doStatsIfc._read;
`ifdef INCLUDE_GDB_CONTROL
method Bool core_is_running = (rg_core_run_state == CORE_RUNNING);
`endif
endinterface);
RenameStage renameStage <- mkRenameStage(renameInput);
@@ -512,16 +571,50 @@ module mkCore#(CoreId coreId)(Core);
method stqEmpty = lsq.stqEmpty;
method lsqSetAtCommit = lsq.setAtCommit;
method tlbNoPendingReq = iTlb.noPendingReq && dTlb.noPendingReq;
method setFlushTlbs = flush_tlbs._write(True);
method setUpdateVMInfo = update_vm_info._write(True);
method setFlushReservation = flush_reservation._write(True);
method setFlushBrPred = flush_brpred._write(True);
method setFlushCaches = flush_caches._write(True);
method setFlushTlbs;
action
flush_tlbs <= True;
// $display ("%0d: %m.commitInput.setFlushTlbs", cur_cycle);
endaction
endmethod
method setUpdateVMInfo;
action
update_vm_info <= True;
// $display ("%0d: %m.commitInput.setUpdateVMInfo", cur_cycle);
endaction
endmethod
method setFlushReservation;
action
flush_reservation <= True;
// $display ("%0d: %m.commitInput.setFlushReservation", cur_cycle);
endaction
endmethod
method setFlushBrPred;
action
flush_brpred <= True;
// $display ("%0d: %m.commitInput.setFlushBrPred", cur_cycle);
endaction
endmethod
method setFlushCaches;
action
flush_caches <= True;
// $display ("%0d: %m.commitInput.setFlushCaches", cur_cycle);
endaction
endmethod
method setReconcileI = reconcile_i._write(True);
method setReconcileD = reconcile_d._write(True);
method killAll = coreFix.killAll;
method redirectPc = fetchStage.redirect;
method setFetchWaitRedirect = fetchStage.setWaitRedirect;
`ifdef INCLUDE_GDB_CONTROL
method setFetchWaitFlush = fetchStage.setWaitFlush;
`endif
method incrementEpoch = epochManager.incrementEpoch;
method commitCsrInstOrInterrupt = csrInstOrInterruptInflight_commit._write(False);
method doStats = coreFix.doStatsIfc._read;
@@ -532,6 +625,11 @@ module mkCore#(CoreId coreId)(Core);
return False;
`endif
endmethod
`ifdef INCLUDE_TANDEM_VERIF
interface v_to_TV = map (toPut, v_f_to_TV);
`endif
endinterface);
CommitStage commitStage <- mkCommitStage(commitInput);
@@ -568,11 +666,13 @@ module mkCore#(CoreId coreId)(Core);
if (flush_reservation) begin
flush_reservation <= False;
dMem.resetLinkAddr;
// $display ("%0d: %m.rule prepareCachesAndTlbs: flushing reservation", cur_cycle);
end
if (flush_tlbs) begin
flush_tlbs <= False;
iTlb.flush;
dTlb.flush;
// $display ("%0d: %m.rule prepareCachesAndTlbs: flushing iTlb and dTlb", cur_cycle);
end
if (update_vm_info) begin
update_vm_info <= False;
@@ -581,10 +681,11 @@ module mkCore#(CoreId coreId)(Core);
iTlb.updateVMInfo(vmI);
dTlb.updateVMInfo(vmD);
l2Tlb.updateVMInfo(vmI, vmD);
// $display ("%0d: %m.rule prepareCachesAndTlbs: updating VMInfo", cur_cycle);
end
endrule
`ifdef SECURITY
`ifdef SECURITY_OR_INCLUDE_GDB_CONTROL
// Use wires to capture flush regs and empty signals. This is ok because
// there cannot be any activity to make empty -> not-empty or need-flush ->
// no-need-flush when we are trying to flush.
@@ -593,6 +694,7 @@ module mkCore#(CoreId coreId)(Core);
rule setDoFlushCaches(flush_caches && fetchStage.emptyForFlush && lsq.noWrongPathLoads);
doFlushCaches.send;
// $display ("%0d: %m.rl_setDoFlushCaches", cur_cycle);
endrule
rule setDoFlushBrPred(flush_brpred && fetchStage.emptyForFlush);
@@ -605,6 +707,7 @@ module mkCore#(CoreId coreId)(Core);
flush_caches <= False;
iMem.flush;
dMem.flush;
// $display ("%0d: %m.rule flushCaches (imem and dmem)", cur_cycle);
endrule
// security flush branch predictors: wait for wrong path inst fetches to
@@ -612,6 +715,7 @@ module mkCore#(CoreId coreId)(Core);
rule flushBrPred(doFlushBrPred);
flush_brpred <= False;
fetchStage.flush_predictors;
// $display ("%0d: %m.rule flushBrPred", cur_cycle);
endrule
`endif
@@ -656,9 +760,12 @@ module mkCore#(CoreId coreId)(Core);
`endif // SELF_INV_CACHE
rule readyToFetch(
`ifdef INCLUDE_GDB_CONTROL
(rg_core_run_state == CORE_RUNNING) &&
`endif
!flush_reservation && !flush_tlbs && !update_vm_info
&& iTlb.flush_done && dTlb.flush_done
`ifdef SECURITY
`ifdef SECURITY_OR_INCLUDE_GDB_CONTROL
&& !flush_caches && !flush_brpred
&& iMem.flush_done && dMem.flush_done
&& fetchStage.flush_predictors_done
@@ -671,6 +778,15 @@ module mkCore#(CoreId coreId)(Core);
`endif
);
fetchStage.done_flushing();
`ifdef INCLUDE_GDB_CONTROL
if (commitStage.is_debug_halted) begin
started <= False;
rg_core_run_state <= CORE_HALTING;
if (verbosity >= 1)
$display ("%0d: %m.rule readyToFetch: halting for debug mode", cur_cycle);
end
`endif
endrule
`ifdef PERF_COUNT
@@ -930,6 +1046,280 @@ module mkCore#(CoreId coreId)(Core);
endrule
`endif
`ifdef INCLUDE_GDB_CONTROL
// ================================================================
// DEBUG MODULE INTERFACE
Bool show_DM_interactions = False; // for debugging the interactions
// ----------------------------------------------------------------
// Debug Module GPR read/write
FIFOF #(DM_CPU_Req #(5, 64)) f_gpr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(64)) f_gpr_rsps <- mkFIFOF1;
rule rl_debug_gpr_read ( (rg_core_run_state == CORE_HALTED)
&& f_gpr_reqs.notEmpty
&& (! f_gpr_reqs.first.write));
let req <- pop (f_gpr_reqs);
Bit #(5) regnum = req.address;
let arch_regs = ArchRegs {src1: tagged Valid (tagged Gpr regnum),
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid};
let rename_result = regRenamingTable.rename[0].getRename (arch_regs);
let phy_rindx = fromMaybe (?, rename_result.phy_regs.src1);
let data_out = rf.read [debuggerPort].rd1 (phy_rindx);
let rsp = DM_CPU_Rsp {ok: True, data: data_out};
f_gpr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_read_gpr: reg %0d => 0x%0h", cur_cycle, regnum, data_out);
endrule
rule rl_debug_gpr_write ( (rg_core_run_state == CORE_HALTED)
&& f_gpr_reqs.notEmpty
&& f_gpr_reqs.first.write);
let req <- pop (f_gpr_reqs);
Bit #(5) regnum = req.address;
let data_in = req.data;
let arch_regs = ArchRegs {src1: tagged Valid (tagged Gpr regnum),
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid};
let rename_result = regRenamingTable.rename[0].getRename (arch_regs);
let phy_rindx = fromMaybe (?, rename_result.phy_regs.src1);
rf.write [debuggerPort].wr (phy_rindx, data_in);
let rsp = DM_CPU_Rsp {ok: True, data: ?};
f_gpr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_gpr_write: reg %0d <= 0x%0h (phy_rindx = %0d)",
cur_cycle, regnum, data_in, phy_rindx);
endrule
rule rl_debug_gpr_access_busy (rg_core_run_state == CORE_RUNNING);
let req <- pop (f_gpr_reqs);
let rsp = DM_CPU_Rsp {ok: False, data: ?};
f_gpr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_gpr_access_busy", cur_cycle);
endrule
`ifdef ISA_F
// ----------------------------------------------------------------
// Debug Module FPR read/write
FIFOF #(DM_CPU_Req #(5, 64)) f_fpr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(64)) f_fpr_rsps <- mkFIFOF1;
rule rl_debug_fpr_read ( (rg_core_run_state == CORE_HALTED)
&& (! f_gpr_reqs.notEmpty) // prioritize gpr reqs
&& (! f_fpr_reqs.first.write));
let req <- pop (f_fpr_reqs);
Bit #(5) regnum = req.address;
let arch_regs = ArchRegs {src1: tagged Valid (tagged Fpu regnum),
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid};
let rename_result = regRenamingTable.rename[0].getRename (arch_regs);
let phy_rindx = fromMaybe (?, rename_result.phy_regs.src1);
let data_out = rf.read [debuggerPort].rd1 (phy_rindx);
let rsp = DM_CPU_Rsp {ok: True, data: data_out};
f_fpr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_read_fpr: reg %0d => 0x%0h", cur_cycle, regnum, data_out);
endrule
rule rl_debug_fpr_write ( (rg_core_run_state == CORE_HALTED)
&& (! f_gpr_reqs.notEmpty) // prioritize gpr reqs
&& f_fpr_reqs.first.write);
let req <- pop (f_fpr_reqs);
Bit #(5) regnum = req.address;
let data_in = req.data;
let arch_regs = ArchRegs {src1: tagged Valid (tagged Fpu regnum),
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid};
let rename_result = regRenamingTable.rename[0].getRename (arch_regs);
let phy_rindx = fromMaybe (?, rename_result.phy_regs.src1);
rf.write [debuggerPort].wr (phy_rindx, data_in);
let rsp = DM_CPU_Rsp {ok: True, data: ?};
f_fpr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_write_fpr: reg %0d <= 0x%0h (phy_rindx %0d)",
cur_cycle, regnum, data_in, phy_rindx);
endrule
rule rl_debug_fpr_access_busy ( (rg_core_run_state == CORE_RUNNING)
&& f_fpr_reqs.notEmpty);
let req <- pop (f_fpr_reqs);
let rsp = DM_CPU_Rsp {ok: False, data: ?};
f_fpr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_fpr_access_busy", cur_cycle);
endrule
`endif
// ----------------------------------------------------------------
// Debug Module CSR read/write
// Debugger CSR read/write request/response
FIFOF #(DM_CPU_Req #(12, 64)) f_csr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(64)) f_csr_rsps <- mkFIFOF1;
rule rl_debug_csr_read ( (rg_core_run_state == CORE_HALTED)
&& (! f_csr_reqs.first.write));
let req <- pop (f_csr_reqs);
Bit #(12) csr_addr = req.address;
let data_out = csrf.rd (unpack (csr_addr));
let rsp = DM_CPU_Rsp {ok: True, data: data_out};
f_csr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_read_csr: csr [%0h] => 0x%0h", cur_cycle, csr_addr, data_out);
endrule
rule rl_debug_csr_write ( (rg_core_run_state == CORE_HALTED)
&& f_csr_reqs.first.write);
let req <- pop (f_csr_reqs);
Bit #(12) csr_addr = req.address;
let data_in = req.data;
csrf.csrInstWr (unpack (csr_addr), data_in);
let rsp = DM_CPU_Rsp {ok: True, data: ?};
f_csr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_write_csr: csr [%0h] <= 0x%0h", cur_cycle, csr_addr, data_in);
endrule
rule rl_debug_csr_access_busy (rg_core_run_state == CORE_RUNNING);
let req <- pop (f_csr_reqs);
let rsp = DM_CPU_Rsp {ok: False, data: ?};
f_csr_rsps.enq (rsp);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_csr_access_busy", cur_cycle);
endrule
// ----------------------------------------------------------------
// Debug Module run-halt control
FIFOF #(Bool) f_run_halt_reqs <- mkFIFOF;
FIFOF #(Bool) f_run_halt_rsps <- mkFIFOF;
// ----------------
// Debug Module Halt control
rule rl_debug_halt_req ( (rg_core_run_state == CORE_RUNNING)
&& (f_run_halt_reqs.first == False));
f_run_halt_reqs.deq;
// Debugger 'halt' request (e.g., GDB '^C' command)
// This is initiated just like an interrupt.
renameStage.debug_halt_req;
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_halt_req", cur_cycle);
endrule
rule rl_debug_halt_req_already_halted ( (rg_core_run_state != CORE_RUNNING)
&& (f_run_halt_reqs.first == False));
f_run_halt_reqs.deq;
// Notify debugger that we're halted, but otherwise ignore the request
f_run_halt_rsps.enq (False);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_halt_req_already_halted", cur_cycle);
endrule
// Monitors when we've reached halted state while running
// (due to halt, step or EBREAK) and notifies DM
rule rl_debug_halted (rg_core_run_state == CORE_HALTING);
// Notify debugger that we've halted
f_run_halt_rsps.enq (False);
rg_core_run_state <= CORE_HALTED;
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_halted", cur_cycle);
endrule
// ----------------
// Debug Module Resume (run) control
// Resume command when in debug mode
rule rl_debug_resume ( (rg_core_run_state == CORE_HALTED)
&& (f_run_halt_reqs.first == True)
// prioritise gpr/fpr/csr read/write requests before resuming
&& (! f_gpr_reqs.notEmpty)
`ifdef ISA_F
&& (! f_fpr_reqs.notEmpty)
`endif
&& (! f_csr_reqs.notEmpty));
f_run_halt_reqs.deq;
// In Debug Mode, debugger may have updated DCSR (hence privilege level, DCSR[1:0]),
// and also other VM-related state.
// The following TLB actions update to a consistent state.
iTlb.flush;
dTlb.flush;
let vmI = csrf.vmI;
let vmD = csrf.vmD;
iTlb.updateVMInfo(vmI);
dTlb.updateVMInfo(vmD);
l2Tlb.updateVMInfo(vmI, vmD);
let startpc = csrf.dpc_read;
fetchStage.redirect (startpc);
renameStage.debug_resume;
commitStage.debug_resume;
started <= True;
rg_core_run_state <= CORE_RUNNING;
// Notify debugger that we've started running
f_run_halt_rsps.enq (True);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_resume, dpc = 0x%0h", cur_cycle, startpc);
endrule
// Run command when already running
rule rl_debug_run_redundant ( (rg_core_run_state == CORE_RUNNING)
&& (f_run_halt_reqs.first == True));
f_run_halt_reqs.deq;
// Notify debugger that we're running
f_run_halt_rsps.enq (True);
if (show_DM_interactions)
$display ("%0d: %m.rl_debug_run_redundant", cur_cycle);
endrule
// ================================================================
`endif
// ================================================================
// INTERFACE
interface CoreReq coreReq;
method Action start(
Bit#(64) startpc,
@@ -941,8 +1331,11 @@ module mkCore#(CoreId coreId)(Core);
`endif
);
started <= True;
`ifdef INCLUDE_GDB_CONTROL
rg_core_run_state <= CORE_RUNNING;
`endif
mmio.setHtifAddrs(toHostAddr, fromHostAddr);
// start rename debug
commitStage.startRenameDebug;
endmethod
@@ -1013,7 +1406,17 @@ module mkCore#(CoreId coreId)(Core);
method Action setMEIP (v) = csrf.setMEIP (v);
method Action setSEIP (v) = csrf.setSEIP (v);
// Bluespec: external interrupt to enter debug mode
method Action setDEIP (v) = csrf.setDEIP (v);
endmodule
`ifdef INCLUDE_GDB_CONTROL
interface Server hart0_run_halt_server = toGPServer (f_run_halt_reqs, f_run_halt_rsps);
interface Server hart0_gpr_mem_server = toGPServer (f_gpr_reqs, f_gpr_rsps);
`ifdef ISA_F
interface Server hart0_fpr_mem_server = toGPServer (f_fpr_reqs, f_fpr_rsps);
`endif
interface Server hart0_csr_mem_server = toGPServer (f_csr_reqs, f_csr_rsps);
`endif
`ifdef INCLUDE_TANDEM_VERIF
interface v_to_TV = map (toGet, v_f_to_TV);
`endif
endmodule

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -21,8 +22,6 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) Bluespec, Inc.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
@@ -37,6 +36,46 @@ import GetPut::*;
import BuildVector::*;
//import TRNG::*;
// ================================================================
// BSV additional libs
import Cur_Cycle :: *;
// ================================================================
// Project imports from Toooba
import SoC_Map :: *;
// ================================================================
// Information returned on traps and mret/sret/uret
typedef struct {
Addr new_pc;
`ifdef INCLUDE_TANDEM_VERIF
// The fields below are for tandem verification only
Bit #(2) prv;
Data status;
Data cause;
Data epc;
Data tval;
`endif
} Trap_Updates
deriving (Bits, FShow);
typedef struct {
Addr new_pc;
`ifdef INCLUDE_TANDEM_VERIF
// The fields below are for tandem verification only
Bit #(2) prv;
Data status;
`endif
} RET_Updates
deriving (Bits, FShow);
// ================================================================
interface CsrFile;
// Read
method Data rd(CSR csr);
@@ -45,12 +84,23 @@ interface CsrFile;
// normal write by FPU inst to FPU CSR
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
method Action fpuInstWr(Bit#(5) fflags); // FPU must become dirty
`ifdef INCLUDE_TANDEM_VERIF
// Returns new fcsr and mstatus (pure function)
method Tuple2 #(Bit #(5), Data) fpuInst_csr_updates (Bit #(5) fflags,
Bool init_for_way0,
Bit #(5) old_fflags,
Data old_mstatus);
method Data getMIP;
`endif
// The WARL transform performed during CSRRx writes to a CSR
method Data warl_xform (CSR csr, Data x);
// Methods for handling traps
method Maybe#(Interrupt) pending_interrupt;
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr faultAddr);
method ActionValue#(Addr) sret;
method ActionValue#(Addr) mret;
method ActionValue#(Trap_Updates) trap(Trap t, Addr pc, Addr faultAddr, Bit #(32) orig_inst);
method ActionValue#(RET_Updates) sret;
method ActionValue#(RET_Updates) mret;
// Outputs for CSRs that the rest of the processor needs to know about
method VMInfo vmI;
@@ -79,9 +129,6 @@ interface CsrFile;
method Action setMEIP (Bit #(1) v);
method Action setSEIP (Bit #(1) v);
// Bluespec: external interrupt to enter debug mode
method Action setDEIP (Bit #(1) v);
// performance stats is collected or not
method Bool doPerfStats;
// send/recv updates on stats CSR globally
@@ -90,6 +137,26 @@ interface CsrFile;
// terminate
method ActionValue#(void) terminate;
`ifdef INCLUDE_GDB_CONTROL
// Read dpc
method Addr dpc_read ();
// Update dpc
method Action dpc_write (Addr pc);
// Check whether to enter Debug Mode based on dcsr.{ebreakm, ebreaks, ebreaku}
method Bit #(1) dcsr_break_bit;
// Read dcsr[2], the step bit
method Bit #(1) dcsr_step_bit;
// Update 'cause' in DCSR
// Is invoked by logic that stops a hart, to enter Debug Mode
(* always_ready *)
method Action dcsr_cause_write (Bit #(3) dcsr_cause);
`endif
endinterface
// Fancy Reg functions
@@ -244,10 +311,13 @@ module mkCsrFile #(Data hartid)(CsrFile);
// Machine level CSRs
// mstatus
Reg#(Bit#(2)) xs_reg <- mkReadOnlyReg(0); // XXX no extension
Reg#(Bit#(2)) fs_reg <- (isa.f || isa.d) ? mkCsrReg(0) : mkReadOnlyReg(0);
Reg#(Bit#(2)) fs_reg <- (isa.f || isa.d) ? mkCsrReg(2'b01) : mkReadOnlyReg(0);
Reg#(Bit#(1)) sd_reg = readOnlyReg(
((xs_reg == 2'b11) || (fs_reg == 2'b11)) ? 1 : 0
);
function Bit #(1) fn_sd_val (Bit #(2) xs_val, Bit #(2) fs_val);
return (((xs_val == 2'b11) || (fs_val == 2'b11)) ? 1 : 0);
endfunction
Reg#(Bit#(2)) sxl_reg = readOnlyReg(getXLBits);
Reg#(Bit#(2)) uxl_reg = readOnlyReg(getXLBits);
Reg#(Bit#(1)) tsr_reg <- mkCsrReg(0);
@@ -286,6 +356,25 @@ module mkCsrFile #(Data hartid)(CsrFile);
ie_vec[prvM], readOnlyReg(1'b0),
ie_vec[prvS], ie_vec[prvU]
);
function Data fn_mstatus_val (Bit #(2) sxl_val, Bit #(2) uxl_val,
Bit #(1) tsr_val, Bit #(1) tw_val, Bit #(1) tvm_val,
Bit #(1) mxr_val, Bit #(1) sum_val, Bit #(1) mprv_val,
Bit #(2) xs_val, Bit #(2) fs_val,
Bit #(2) mpp_val, Bit #(1) spp_val,
Bit #(1) prev_ie_vec_prvM_val,
Bit #(1) prev_ie_vec_prvS_val, Bit #(1) prev_ie_vec_prvU_val,
Bit #(1) ie_vec_prvM_val,
Bit #(1) ie_vec_prvS_val, Bit #(1) ie_vec_prvU_val);
return {fn_sd_val (xs_val, fs_val),
27'b0, sxl_val, uxl_val, 9'b0,
tsr_val, tw_val, tvm_val, mxr_val, sum_val, mprv_val, xs_val, fs_val,
mpp_val, 2'b0, spp_val,
prev_ie_vec_prvM_val, 1'b0,
prev_ie_vec_prvS_val, prev_ie_vec_prvU_val,
ie_vec_prvM_val, 1'b0,
ie_vec_prvS_val, ie_vec_prvU_val};
endfunction
// misa
Reg#(Data) misa_csr = readOnlyReg({getXLBits, 36'b0, getExtensionBits(isa)});
// medeleg: some exceptions don't exist, fix corresponding bits to 0
@@ -309,7 +398,6 @@ module mkCsrFile #(Data hartid)(CsrFile);
readOnlyReg(1'b0), mideleg_1_0_reg
);
// mie
Reg #(Bit #(1)) debug_int_en = readOnlyReg (1);
Vector#(4, Reg#(Bit#(1))) external_int_en_vec = replicate(readOnlyReg(0));
external_int_en_vec[prvU] <- mkCsrReg(0);
external_int_en_vec[prvS] <- mkCsrReg(0);
@@ -322,16 +410,14 @@ module mkCsrFile #(Data hartid)(CsrFile);
software_int_en_vec[prvU] <- mkCsrReg(0);
software_int_en_vec[prvS] <- mkCsrReg(0);
software_int_en_vec[prvM] <- mkCsrReg(0);
Reg#(Data) mie_csr = concatReg15(
readOnlyReg(49'b0),
debug_int_en, // mie [14]
readOnlyReg(2'b0),
Reg#(Data) mie_csr = concatReg13(
readOnlyReg(52'b0),
external_int_en_vec[prvM], readOnlyReg(1'b0),
external_int_en_vec[prvS], external_int_en_vec[prvU],
external_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_en_vec[prvU],
timer_int_en_vec[prvM], readOnlyReg(1'b0),
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
timer_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_en_vec[prvU],
software_int_en_vec[prvM], readOnlyReg(1'b0),
software_int_en_vec[prvS], software_int_en_vec[prvU]
software_int_en_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_en_vec[prvU]
);
// mtvec
Reg#(Bit#(62)) mtvec_base_hi_reg <- mkCsrReg(0); // this is BASE[63:2]
@@ -359,34 +445,48 @@ module mkCsrFile #(Data hartid)(CsrFile);
Reg#(Data) mcause_csr = concatReg3(
mcause_interrupt_reg, readOnlyReg(59'b0), mcause_code_reg
);
function Data fn_mcause_val (Bit #(1) mcause_interrupt_val, Bit #(4) mcause_code_val);
return { mcause_interrupt_val, 59'b0, mcause_code_val };
endfunction
// mtval (mbadaddr in spike)
Reg#(Data) mtval_csr <- mkCsrReg(0);
// mip
Reg #(Bit #(1)) debug_int_pend <- mkCsrReg (0);
Vector#(4, Reg#(Bit#(1))) external_int_pend_vec = replicate(readOnlyReg(0));
external_int_pend_vec[prvU] <- mkCsrReg(0);
external_int_pend_vec[prvS] <- mkCsrReg(0);
external_int_pend_vec[prvM] <- mkCsrReg(0);
external_int_pend_vec[prvM] <- mkCsrReg(0); // TODO: bug (writeable by CSRRx)?
Vector#(4, Reg#(Bit#(1))) timer_int_pend_vec = replicate(readOnlyReg(0));
timer_int_pend_vec[prvU] <- mkCsrReg(0);
timer_int_pend_vec[prvS] <- mkCsrReg(0);
timer_int_pend_vec[prvM] <- mkCsrReg(0);
timer_int_pend_vec[prvM] <- mkCsrReg(0); // TODO: bug (writeable by CSRRx)?
Vector#(4, Reg#(Bit#(1))) software_int_pend_vec = replicate(readOnlyReg(0));
software_int_pend_vec[prvU] <- mkCsrReg(0);
software_int_pend_vec[prvS] <- mkCsrReg(0);
software_int_pend_vec[prvM] <- mkCsrReg(0);
Reg#(Data) mip_csr = concatReg15(
readOnlyReg(49'b0),
debug_int_pend,
readOnlyReg(2'b0),
external_int_pend_vec[prvM], readOnlyReg(1'b0),
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software
software_int_pend_vec[prvM] <- mkCsrReg(0); // TODO: bug (writeable by CSRRx)?
Reg#(Data) mip_csr = concatReg13(
readOnlyReg(52'b0),
// External interrupts
readOnlyReg(external_int_pend_vec[prvM]), // MEIP is read-only to software
readOnlyReg(1'b0),
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
software_int_pend_vec[prvM], readOnlyReg(1'b0),
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
external_int_pend_vec[prvS],
readOnlyReg(1'b0), // only if misa.N: external_int_pend_vec[prvU],
// Timer interrupts
readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software
readOnlyReg(1'b0),
timer_int_pend_vec[prvS],
readOnlyReg(1'b0), // only if misa.N: timer_int_pend_vec[prvU],
// Software interrupts
readOnlyReg(software_int_pend_vec[prvM]), // MSIP is read-only to software
readOnlyReg(1'b0),
software_int_pend_vec[prvS],
readOnlyReg(1'b0) // only if misa.N: software_int_pend_vec[prvU]
);
// MIP and MIE fields are WARL (Write Any Read Legal)
// We support M-privilege and S-privilege bits only;
// this mask allows only those bits through.
Data mip_mie_warl_mask = zeroExtend (12'h_222);
// minstret
Ehr#(2, Data) minstret_ehr <- mkCsrEhr(0);
Reg#(Data) minstret_csr = minstret_ehr[0];
@@ -411,14 +511,32 @@ module mkCsrFile #(Data hartid)(CsrFile);
readOnlyReg(2'b0), prev_ie_vec[prvS], prev_ie_vec[prvU],
readOnlyReg(2'b0), ie_vec[prvS], ie_vec[prvU]
);
function Data fn_sstatus_val (Bit #(2) uxl_val,
Bit #(1) mxr_val, Bit #(1) sum_val,
Bit #(2) xs_val, Bit #(2) fs_val,
Bit #(1) spp_val,
Bit #(1) prev_ie_vec_prvS_val,
Bit #(1) prev_ie_vec_prvU_val,
Bit #(1) ie_vec_prvS_val,
Bit #(1) ie_vec_prvU_val);
return {fn_sd_val (xs_val, fs_val),
27'b0, 2'b0, uxl_val, 12'b0,
mxr_val, sum_val, 1'b0, xs_val, fs_val,
4'b0, spp_val,
2'b0,
prev_ie_vec_prvS_val, prev_ie_vec_prvU_val,
2'b0,
ie_vec_prvS_val, ie_vec_prvU_val};
endfunction
// sie: restricted view of mie
Reg#(Data) sie_csr = concatReg9(
readOnlyReg(54'b0),
external_int_en_vec[prvS], external_int_en_vec[prvU],
external_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_en_vec[prvU],
readOnlyReg(2'b0),
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
timer_int_en_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_en_vec[prvU],
readOnlyReg(2'b0),
software_int_en_vec[prvS], software_int_en_vec[prvU]
software_int_en_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_en_vec[prvU]
);
// stvec
Reg#(Bit#(62)) stvec_base_hi_reg <- mkCsrReg(0); // BASE[63:2]
@@ -446,17 +564,27 @@ module mkCsrFile #(Data hartid)(CsrFile);
Reg#(Data) scause_csr = concatReg3(
scause_interrupt_reg, readOnlyReg(59'b0), scause_code_reg
);
function Data fn_scause_val (Bit #(1) scause_interrupt_val, Bit #(4) scause_code_val);
return { scause_interrupt_val, 59'b0, scause_code_val };
endfunction
// stval (sbadaddr in spike)
Reg#(Data) stval_csr <- mkCsrReg(0);
// sip: restricted view of mip
Reg#(Data) sip_csr = concatReg9(
readOnlyReg(54'b0),
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
external_int_pend_vec[prvS], readOnlyReg(1'b0), // only if misa.N: external_int_pend_vec[prvU],
readOnlyReg(2'b0),
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
timer_int_pend_vec[prvS], readOnlyReg(1'b0), // only if misa.N: timer_int_pend_vec[prvU],
readOnlyReg(2'b0),
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
software_int_pend_vec[prvS], readOnlyReg(1'b0) // only if misa.N: software_int_pend_vec[prvU]
);
// SIP and SIE fields are WARL (Write Any Read Legal)
// We support S-privilege bits only;
// this mask allows only those bits through.
Data sip_sie_warl_mask = zeroExtend (12'h_222);
// satp (sptbr in spike): FIXME we only support Bare and Sv39, so we hack
// the encoding of mode[3:0] field. Only mode[3] is relevant, other bits
// are always 0
@@ -501,6 +629,43 @@ module mkCsrFile #(Data hartid)(CsrFile);
StatsCsr stats_module <- mkStatsCsr;
Reg#(Data) stats_csr = stats_module.reg_ifc;
Reg #(Data) rg_tselect <- mkConfigReg (0);
// Note: ISA test rv64mi-p-breakpoint assumes tdata1's reset value == 0
// Until we implement trigger functionality,
// force 'tdata1.type' field ([xlen-1:xlen-4]) to zero
// meaning: 'There is no trigger at this tselect'
Reg #(Bit #(4)) rg_tdata1_type <- mkReadOnlyReg (0);
Reg #(Bit #(1)) rg_tdata1_dmode <- mkCsrReg (0);
Reg #(Bit #(59)) rg_tdata1_data <- mkCsrReg (0);
Reg #(Data) rg_tdata1 = concatReg3 (rg_tdata1_type, rg_tdata1_dmode, rg_tdata1_data);
Reg #(Data) rg_tdata2 <- mkConfigRegU;
Reg #(Data) rg_tdata3 <- mkConfigRegU;
`ifdef INCLUDE_GDB_CONTROL
// DCSR is 32b even in RV64
Bit #(32) dcsr_reset_value = {4'h4, // [31:28] xdebugver
12'h0, // [27:16] reserved
1'h0, // [15] ebreakm
1'h0, // [14] reserved
1'h0, // [13] ebreaks
1'h0, // [12] ebreaku
1'h0, // [11] stepie
1'h0, // [10] stopcount
1'h0, // [9] stoptime
3'h0, // [8:6] cause // WARNING: 0 is non-standard
1'h0, // [5] reserved
1'h1, // [4] mprven
1'h0, // [3] nmip // non-maskable interrupt pending
1'h0, // [2] step
2'h3}; // [1:0] prv (machine mode)
// RV64: dcsr's upper 32b zeroExtended/ignored
Reg #(Data) rg_dcsr <- mkConfigReg (zeroExtend (dcsr_reset_value));
Reg #(Data) rg_dpc <- mkConfigReg (truncate (soc_map_struct.pc_reset_value));
Reg #(Data) rg_dscratch0 <- mkConfigRegU;
Reg #(Data) rg_dscratch1 <- mkConfigRegU;
`endif
`ifdef SECURITY
// sanctum machine CSRs
@@ -607,16 +772,117 @@ module mkCsrFile #(Data hartid)(CsrFile);
CSRmspec: mspec_csr;
CSRtrng: trng_csr;
`endif
CSRtselect: rg_tselect;
CSRtdata1: rg_tdata1;
CSRtdata2: rg_tdata2;
CSRtdata3: rg_tdata3;
`ifdef INCLUDE_GDB_CONTROL
CSRdcsr: rg_dcsr; // TODO: take NMI into account (cf. Piccolo/Flute)
CSRdpc: rg_dpc;
CSRdscratch0: rg_dscratch0;
CSRdscratch1: rg_dscratch1;
`endif
default: readOnlyReg(64'b0);
endcase);
endfunction
// ================================================================
// This function is the WARL (Write Any Read Legal) transform
// performed during CSR writes. Currently it duplicates the logic
// in the _write method of CSRs; ideally this function should be
// separate from the _write method, which should remain as an
// ordinary _write. The WARL'd value is needed for Tandem
// Verification.
function Data fv_warl_xform (CSR csr, Data x);
Asid x_asid = truncate (x [59:44]);
Bit #(16) asid = zeroExtend (x_asid);
return (
case (csr)
// Machine CSRs
CSRmisa: {getXLBits, 36'b0, getExtensionBits(isa)};
CSRmvendorid: 0;
CSRmarchid: 0;
CSRmimpid: 0;
CSRmhartid: hartid;
CSRmstatus: fn_mstatus_val (getXLBits, // sxl
getXLBits, // uxl
x [22], // tsr
x [21], // tw
x [20], // tvm
x [19], // mxr
x [18], // sum
x [17], // mprv
2'b0, // xs
((isa.f || isa.d) ? x [14:13] : 2'b0), // fs
x [12:11], // mpp
x [8], // spp
x [7], // prev_ie_vec[prvM]
x [5], // prev_ie_vec[prvS]
x [4], // prev_ie_vec[prvU]
x [3], // ie_vec[prvM]
x [1], // ie_vec[prvS]
x [0]); // ie_vec[prvU]
CSRmtvec: { x[63:2], 1'b0, x[0]};
CSRmedeleg: { 48'b0, x[15], 1'b0, x[13:12], x[11], 1'b0, x[9:0]};
CSRmideleg: { 52'b0, x[11], 1'b0, x[9:8], x[7], 1'b0, x[5:4], x[3], 1'b0, x[1:0]};
CSRmip: ((mip_csr & (~ mip_mie_warl_mask)) | (x & mip_mie_warl_mask));
CSRmie: (x & mip_mie_warl_mask);
CSRmcounteren: { 61'b0, x[2:0]};
CSRmcause: { x[63], 59'b0, x[3:0] };
CSRtdata1: { 4'b0, x [59:0] }; // Force tdata.type == 0 ("no trigger at this tselect")
// Supervisor level CSRs
CSRsstatus: fn_sstatus_val (getXLBits, // uxl
x [19], // mxr
x [18], // sum
2'b0, // xs
((isa.f || isa.d) ? x [14:13] : 2'b0), // fs
x [8], // spp
x [5], // prev_ie_vec[prvS]
x [4], // prev_ie_vec[prvU]
x [1], // ie_vec[prvS]
x [0]); // ie_vec[prvU]
CSRstvec: { x[63:2], 1'b0, x[0]};
CSRsip: ((sip_csr & (~ sip_sie_warl_mask)) | (x & sip_sie_warl_mask));
CSRsie: (x & sip_sie_warl_mask);
CSRscounteren: { 61'b0, x[2:0]};
CSRscause: { x[63], 59'b0, x[3:0] };
CSRsatp: { x[63], 3'b0, asid, x [43:0] };
// User level CSRs
CSRfflags: { 59'b0, x [4:0] };
CSRfrm: { 61'b0, x [2:0] };
CSRfcsr: { 56'b0, x [7:0] };
`ifdef INCLUDE_GDB_CONTROL
// Debug Mode CSRs
CSRdcsr: { 32'b0, x[31:28], 12'b0, x[14], 1'b0, x[13:6], 1'b0, x[4:0] };
`endif
default: x;
endcase);
endfunction
// ================================================================
// INTERFACE
method Data rd(CSR csr);
return get_csr(csr)._read;
endmethod
method Action csrInstWr(CSR csr, Data x);
get_csr(csr)._write(x);
`ifdef INCLUDE_GDB_CONTROL
if (csr == CSRdcsr) begin
let prv = x [1:0];
prv_reg <= prv;
end
`endif
endmethod
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
@@ -633,6 +899,35 @@ module mkCsrFile #(Data hartid)(CsrFile);
fflags_reg <= fflags_reg | fflags;
endmethod
`ifdef INCLUDE_TANDEM_VERIF
method Tuple2 #(Bit #(5), Data) fpuInst_csr_updates (Bit #(5) fflags,
Bool init_for_way0,
Bit #(5) old_fflags,
Data old_mstatus);
// Note: old_fflags and old_mstatus are accumulated in
// sequential program order, and so may differ from fflags_reg
// and mstatus_csr, which only change after superscalar-wide
// retirement.
Bit #(5) old_fflags1 = (init_for_way0 ? fflags_reg : old_fflags);
Data old_mstatus1 = (init_for_way0 ? mstatus_csr : old_mstatus);
Bit #(5) new_fflags = (old_fflags1 | fflags);
Data new_mstatus = { 1'b1, old_mstatus1 [62:15], 2'b11, old_mstatus1 [12:0] };
return tuple2 (new_fflags, new_mstatus);
endmethod
method Data getMIP;
return mip_csr;
endmethod
`endif
method Data warl_xform (CSR csr, Data x);
return fv_warl_xform (csr, x);
endmethod
method Maybe#(Interrupt) pending_interrupt;
// first get all the pending interrupts
Bit#(InterruptNum) pend_ints = truncate(mie_csr & mip_csr);
@@ -662,7 +957,7 @@ module mkCsrFile #(Data hartid)(CsrFile);
end
endmethod
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr addr);
method ActionValue#(Trap_Updates) trap(Trap t, Addr pc, Addr addr, Bit #(32) orig_inst);
// figure out trap cause & trap val
Bit#(1) cause_interrupt = 0;
Bit#(4) cause_code = 0;
@@ -671,6 +966,7 @@ module mkCsrFile #(Data hartid)(CsrFile);
tagged Exception .e: begin
cause_code = pack(e);
trap_val = (case(e)
IllegalInst: zeroExtend (orig_inst);
InstAddrMisaligned, Breakpoint: return pc;
InstAccessFault, InstPageFault,
@@ -716,7 +1012,25 @@ module mkCsrFile #(Data hartid)(CsrFile);
scause_code_reg <= cause_code;
stval_csr <= trap_val;
// return next pc
return getNextPc(stvec_mode_low_reg, stvec_base_hi_reg);
// return getNextPc(stvec_mode_low_reg, stvec_base_hi_reg);
Data sstatus_val = fn_sstatus_val (uxl_reg,
mxr_reg, sum_reg,
xs_reg, fs_reg,
/* spp_reg */ prv_reg [0],
/* prev_ie_vec_[prvS] */ ie_vec[prvS],
prev_ie_vec [prvU],
/* ie_vec [prvS] */ 0,
ie_vec [prvU]);
Data scause_val = fn_scause_val (cause_interrupt, cause_code);
return Trap_Updates {new_pc: getNextPc(stvec_mode_low_reg, stvec_base_hi_reg)
`ifdef INCLUDE_TANDEM_VERIF
, prv: prvS,
status: sstatus_val,
cause: scause_val,
epc: pc,
tval: trap_val
`endif
};
end
else begin
// ie/prv stack
@@ -730,25 +1044,85 @@ module mkCsrFile #(Data hartid)(CsrFile);
mcause_code_reg <= cause_code;
mtval_csr <= trap_val;
// return next pc
return getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg);
// return getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg);
Data mstatus_val = fn_mstatus_val (sxl_reg, uxl_reg,
tsr_reg, tw_reg, tvm_reg,
mxr_reg, sum_reg, mprv_reg,
xs_reg, fs_reg,
/* mpp */ prv_reg, spp_reg,
/* prev_ie_vec [prvM] */ ie_vec [prvM],
prev_ie_vec [prvS],
prev_ie_vec [prvU],
/* ie_vec [prvM] */ 0,
ie_vec [prvS],
ie_vec [prvU]);
Data mcause_val = fn_mcause_val (cause_interrupt, cause_code);
return Trap_Updates {new_pc: getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg)
`ifdef INCLUDE_TANDEM_VERIF
, prv: prvM,
status: mstatus_val,
cause: mcause_val,
epc: pc,
tval: trap_val
`endif
};
end
// XXX yield load reservation should be done outside this method
endmethod
method ActionValue#(Addr) mret;
method ActionValue#(RET_Updates) mret;
prv_reg <= prev_prv_vec[prvM];
prev_prv_vec[prvM] <= prvU;
ie_vec[prvM] <= prev_ie_vec[prvM];
prev_ie_vec[prvM] <= 1;
return mepc_csr;
Data mstatus_val = fn_mstatus_val(sxl_reg, uxl_reg,
tsr_reg, tw_reg, tvm_reg,
mxr_reg, sum_reg, mprv_reg,
xs_reg, fs_reg,
/* mpp */ prvU,
spp_reg,
/* prev_ie_vec [prvM] */ 1,
prev_ie_vec [prvS],
prev_ie_vec [prvU],
/* ie_vec [prvM] */ prev_ie_vec[prvM],
ie_vec [prvS],
ie_vec [prvU]);
return RET_Updates {new_pc: mepc_csr
`ifdef INCLUDE_TANDEM_VERIF
, prv: prev_prv_vec[prvM],
status: mstatus_val
`endif
};
endmethod
method ActionValue#(Addr) sret;
method ActionValue#(RET_Updates) sret;
prv_reg <= prev_prv_vec[prvS];
prev_prv_vec[prvS] <= prvU;
ie_vec[prvS] <= prev_ie_vec[prvS];
prev_ie_vec[prvS] <= 1;
return sepc_csr;
// For Tandem Verification, we return the full underlying MSTATUS register
Data mstatus_val = fn_mstatus_val(sxl_reg, uxl_reg,
tsr_reg, tw_reg, tvm_reg,
mxr_reg, sum_reg, mprv_reg,
xs_reg, fs_reg,
mpp_reg,
/* spp_reg */ prvU [0],
prev_ie_vec [prvM],
/* prev_ie_vec_[prvS] */ 1,
prev_ie_vec [prvU],
ie_vec [prvM],
/* ie_vec [prvS] */ prev_ie_vec[prvS],
ie_vec [prvU]);
return RET_Updates {new_pc: sepc_csr
`ifdef INCLUDE_TANDEM_VERIF
, prv: prev_prv_vec[prvS],
status: mstatus_val
`endif
};
endmethod
method VMInfo vmI;
@@ -845,15 +1219,53 @@ module mkCsrFile #(Data hartid)(CsrFile);
external_int_pend_vec[prvS] <= v;
endmethod
// Bluespec: external interrupt to enter debug mode
method Action setDEIP (Bit #(1) v);
debug_int_pend <= v;
endmethod
method terminate = terminate_module.terminate;
// performance stats
method doPerfStats = stats_module.doPerfStats;
method sendDoStats = stats_module.sendDoStats;
method recvDoStats = stats_module.recvDoStats;
// ----------------
// Bluespec:
// Methods when Debug Module is present
`ifdef INCLUDE_GDB_CONTROL
// Read dpc
method Addr dpc_read ();
return rg_dpc;
endmethod
// Update dpc
method Action dpc_write (Addr pc);
rg_dpc <= pc;
endmethod
// Check whether to enter Debug Mode based on dcsr.{ebreakm, ebreaks, ebreaku}
method Bit #(1) dcsr_break_bit;
return case (prv_reg)
prvM: rg_dcsr [15];
prvS: rg_dcsr [13];
prvU: rg_dcsr [12];
endcase;
endmethod
// Check whether to enter Debug Mode based on dcsr.step
method Bit #(1) dcsr_step_bit;
return rg_dcsr [2];
endmethod
// Update 'cause' in DCSR
// Is invoked by logic that stops a hart, to enter Debug Mode
method Action dcsr_cause_write (Bit #(3) dcsr_cause);
rg_dcsr <= { 32'b0, rg_dcsr [31:9], dcsr_cause, rg_dcsr [5:2], prv_reg };
/*
$display ("%0d: %m mkCsrFile.method-dcsr_cause_write: cause %0d, prv %0d",
cur_cycle, dcsr_cause, prv_reg);
*/
endmethod
`endif
endmodule

View File

@@ -107,8 +107,7 @@ module mkLLC_AXi4_Adapter #(MemFifoClient #(idT, childT) llc)
awregion: fabric_default_region,
awuser: fabric_default_user};
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
wdata: st_val,
let mem_req_wr_data = AXI4_Wr_Data {wdata: st_val,
wstrb: strb,
wlast: True,
wuser: fabric_default_user};

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2018 Massachusetts Institute of Technology
// Portions (c) 2019-2020 Bluespec, Inc.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
@@ -21,8 +22,6 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions (c) 2019 Bluespec, Inc.
// This file is adapted from: MIT-riscy/riscy-OOO/procs/lib/MMIOPlatform.bsv
// Modifications to fit into Bluespec's RISC-V execution environments.
@@ -289,7 +288,8 @@ module mkMMIOPlatform #(Vector#(CoreNum, MMIOCoreToPlatform) cores,
// To avoid posting timer interrupt repeatedly, we keep a copy of MTIP
// here. Since each core cannot write MTIP by CSRXXX inst, the only way to
// change MTIP is through here.
Vector#(CoreNum, Reg#(Bool)) mtip <- replicateM(mkReg(False));
// We initialize to True to avoid an timer interrupt at start of time.
Vector#(CoreNum, Reg#(Bool)) mtip <- replicateM(mkReg(True));
// pass mtime to each core
rule propagateTime(state != Init);

View File

@@ -1,5 +1,14 @@
// Copyright (c) 2019-2020 Bluespec, Inc.
package MMIO_AXI4_Adapter;
// ================================================================
// This is an adapter to connect MIT's RISCY-OOO to an AXI4 fabric in
// Bluespec's Toooba setup. All IO traffic to the fabric flows through
// this. Note: a few IO addresses (e.g., MTIME, MTIMECMP, MSIP,
// TOHOST, FROMHOST are intercepted and handled before they reach this
// adapter).
// ================================================================
// BSV lib imports
@@ -30,6 +39,7 @@ import ProcTypes :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
import SoC_Map :: *;
// ================================================================
@@ -56,6 +66,8 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
FIFOF #(MMIOCRq) f_reqs_from_core <- mkFIFOF;
FIFOF #(MMIODataPRs) f_rsps_to_core <- mkFIFOF;
SoC_Map_IFC soc_map <- mkSoC_Map; // for m_is_IO_addr
// ================================================================
// Fabric request/response
@@ -108,12 +120,26 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
awregion: fabric_default_region,
awuser: fabric_default_user};
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
wdata: st_val,
let mem_req_wr_data = AXI4_Wr_Data {wdata: st_val,
wstrb: strb,
wlast: True,
wuser: fabric_default_user};
`ifdef FABRIC64
// Work-around for a misbehavior on Xilinx UART and its
// Xilinx AXI4 adapter. On 64-bit fabrics, for a write where
// axsize says '8 bytes' but wstrb is for <= 4 bytes, the
// adapter converts it two 32-bit writes, one of which has
// wstrb=4'b0000. The Xilinx UART, in turn ignores wstrb and
// therefore performs a spurious write. This workaround
// changes axsize for such writes to '4 bytes', avoiding this
// problem.
if (strb [7:4] == 0 || strb [3:0] == 0) begin
mem_req_wr_addr.awsize = axsize_4;
end
`endif
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
master_xactor.i_wr_data.enq (mem_req_wr_data);
@@ -122,8 +148,8 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
// Debugging
if (cfg_verbosity > 0) begin
$display (" To fabric: ", fshow (mem_req_wr_addr));
$display (" ", fshow (mem_req_wr_data));
$display (" To fabric: ", fshow (mem_req_wr_addr));
$display (" ", fshow (mem_req_wr_data));
end
endaction
endfunction
@@ -138,11 +164,25 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
let req <- pop (f_reqs_from_core);
if (cfg_verbosity > 0) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_req: Ld request", cur_cycle);
$display ("%0d: %m.rl_handle_read_req: Ld request", cur_cycle);
$display (" ", fshow (req));
end
fa_fabric_send_read_req (req.addr);
// Technically the following check for legal IO addrs is not
// necessary; the AXI4 fabric should return a DECERR for illegal
// addrs; but not all AXI4 fabrics do the right thing.
if (soc_map.m_is_IO_addr (req.addr))
fa_fabric_send_read_req (req.addr);
else begin
let rsp = MMIODataPRs {valid: False,
data: req.addr}; // For debugging convenience only
f_rsps_to_core.enq (rsp);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_handle_read_req: unmapped IO address; returning error response",
cur_cycle);
$display (" ", fshow (req));
end
end
endrule
// ----------------
@@ -151,12 +191,12 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
let mem_rsp <- pop_o (master_xactor.o_rd_data);
if (cfg_verbosity > 0) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsps ", cur_cycle);
$display ("%0d: %m.rl_handle_read_rsps ", cur_cycle);
$display (" ", fshow (mem_rsp));
end
if ((cfg_verbosity > 0) && (mem_rsp.rresp != axi4_resp_okay)) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsp: fabric response error", cur_cycle);
$display ("%0d: %m.rl_handle_read_rsp: fabric response error", cur_cycle);
$display (" ", fshow (mem_rsp));
end
@@ -175,11 +215,25 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
let req <- pop (f_reqs_from_core);
if (cfg_verbosity > 0) begin
$display ("%d: MMIO_AXI4_Adapter.rl_handle_write_req: St request:", cur_cycle);
$display ("%d: %m.rl_handle_write_req: St request:", cur_cycle);
$display (" ", fshow (req));
end
fa_fabric_send_write_req (req.addr, pack (req.byteEn), req.data);
// Technically the following check for legal IO addrs is not
// necessary; the AXI4 fabric should return a DECERR for illegal
// addrs; but not all AXI4 fabrics do the right thing.
if (soc_map.m_is_IO_addr (req.addr))
fa_fabric_send_write_req (req.addr, pack (req.byteEn), req.data);
else begin
let rsp = MMIODataPRs {valid: False,
data: req.addr}; // For debugging convenience only
f_rsps_to_core.enq (rsp);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_handle_write_req: unmapped IO address; returning error response",
cur_cycle);
$display (" ", fshow (req));
end
end
endrule
// ----------------
@@ -189,12 +243,12 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
let wr_resp <- pop_o (master_xactor.o_wr_resp);
if (cfg_verbosity > 0) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp", cur_cycle);
$display ("%0d: %m.rl_discard_write_rsp", cur_cycle);
$display (" ", fshow (wr_resp));
end
if (ctr_wr_rsps_pending.value == 0) begin
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
$display ("%0d:%m.rl_discard_write_rsp: ERROR:unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1); // Assertion failure
@@ -204,7 +258,7 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
if (wr_resp.bresp != axi4_resp_okay) begin
// TODO: need to raise a non-maskable interrupt (NMI) here
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
$display ("%0d:%m.rl_discard_write_rsp: ERROR: fabric response error: exit.", cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1);
end
@@ -228,8 +282,7 @@ module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
rule rl_handle_non_Ld_St (! fn_is_Ld_or_St (f_reqs_from_core.first));
let req <- pop (f_reqs_from_core);
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_handle_non_Ld_St",
cur_cycle);
$display ("%0d:%m.rl_handle_non_Ld_St: ERROR: neither Ld nor St? exit.", cur_cycle);
$display (" ", fshow (req));
$finish (1); // Assertion failure
endrule

View File

@@ -1,6 +1,9 @@
package Proc;
// Note: this module corresponds to module 'mkCPU' in Piccolo/Flute.
// 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
@@ -22,8 +25,6 @@ package Proc;
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) 2019 Bluespec, Inc.
// ================================================================
// BSV lib imports
@@ -77,17 +78,13 @@ import SoC_Map :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
`ifdef INCLUDE_GDB_CONTROL
import DM_CPU_Req_Rsp :: *;
`endif
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
`endif
`ifdef EXTERNAL_DEBUG_MODULE
`undef INCLUDE_GDB_CONTROL
import ProcTypes :: *;
import Trace_Data2 :: *;
`endif
// ================================================================
@@ -108,50 +105,6 @@ module mkProc (Proc_IFC);
// Verbosity: 0=quiet; 1=instruction trace; 2=more detail
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
// ----------------
// Reset requests and responses (TODO: to be implemented)
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
// ----------------
// Communication to/from External debug module (TODO: to be implemented)
`ifdef INCLUDE_GDB_CONTROL
// Debugger run-control
FIFOF #(Bool) f_run_halt_reqs <- mkFIFOF;
FIFOF #(Bool) f_run_halt_rsps <- mkFIFOF;
// Stop-request from debugger (e.g., GDB ^C or Dsharp 'stop')
Reg #(Bool) rg_stop_req <- mkReg (False);
// Count instrs after step-request from debugger (via dcsr.step)
Reg #(Bit #(1)) rg_step_count <- mkReg (0);
// Debugger GPR read/write request/response
FIFOF #(DM_CPU_Req #(5, XLEN)) f_gpr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(XLEN)) f_gpr_rsps <- mkFIFOF1;
`ifdef ISA_F
// Debugger FPR read/write request/response
FIFOF #(DM_CPU_Req #(5, FLEN)) f_fpr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(FLEN)) f_fpr_rsps <- mkFIFOF1;
`endif
// Debugger CSR read/write request/response
FIFOF #(DM_CPU_Req #(12, XLEN)) f_csr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(XLEN)) f_csr_rsps <- mkFIFOF1;
`endif
// ----------------
// Tandem Verification (TODO: to be implemented)
`ifdef INCLUDE_TANDEM_VERIF
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
`endif
// ----------------
// MMIO
@@ -185,16 +138,11 @@ module mkProc (Proc_IFC);
tlbToMem[i] = core[i].tlbToMem;
end
// Stub out memLoader (TODO: can be Debug Module's access)
let memLoaderStub = interface MemLoaderMemClient;
interface memReq = nullFifoDeq;
interface respSt = nullFifoEnq;
endinterface;
mkLLCDmaConnect(llc.dma, memLoaderStub, tlbToMem);
// Note: mkLLCDmaConnect is Toooba version, different from riscy-ooo version
let llc_mem_server <- mkLLCDmaConnect(llc.dma, tlbToMem);
// ================================================================
// interface LLC to AXI4
// interface Back-side of LLC to AXI4
LLC_AXI4_Adapter_IFC llc_axi4_adapter <- mkLLC_AXi4_Adapter (llc.to_mem);
@@ -249,18 +197,6 @@ module mkProc (Proc_IFC);
end
// ================================================================
// Reset
rule rl_reset;
let x <- pop (f_reset_reqs);
llc_axi4_adapter.reset;
mmio_axi4_adapter.reset;
f_reset_rsps.enq (?);
endrule
// ----------------
// Termination detection
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
@@ -270,7 +206,9 @@ module mkProc (Proc_IFC);
endrule
end
// ================================================================
// Print out values written 'tohost'
rule rl_tohost;
let x <- mmioPlatform.to_host;
$display ("%0d: mmioPlatform.rl_tohost: 0x%0x (= %0d)", cur_cycle, x, x);
@@ -292,11 +230,9 @@ module mkProc (Proc_IFC);
// ================================================================
// INTERFACE
// Reset
interface Server hart0_server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
// ----------------
// Start the cores running
// Use toHostAddr = 0 if not monitoring tohost
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
action
for(Integer i = 0; i < valueof(CoreNum); i = i+1)
@@ -305,8 +241,8 @@ module mkProc (Proc_IFC);
mmioPlatform.start (tohostAddr, fromhostAddr);
$display ("Proc.start: startpc = 0x%0h, tohostAddr = 0x%0h, fromhostAddr = %0h",
startpc, tohostAddr, fromhostAddr);
$display ("%0d: %m.method start: startpc %0h, tohostAddr %0h, fromhostAddr %0h",
cur_cycle, startpc, tohostAddr, fromhostAddr);
endmethod
// ----------------
@@ -329,13 +265,6 @@ module mkProc (Proc_IFC);
core[0].setSEIP (pack (x));
endmethod
// ----------------
// External interrupt [14] to go into Debug Mode
method Action debug_external_interrupt_req (Bool set_not_clear);
core[0].setDEIP (pack (set_not_clear));
endmethod
// ----------------
// Non-maskable interrupt
@@ -350,11 +279,9 @@ module mkProc (Proc_IFC);
endmethod
// ----------------
// Optional interface to Tandem Verifier
// Coherent port into LLC (used by Debug Module, DMA engines, ... to read/write memory)
`ifdef INCLUDE_TANDEM_VERIF
interface Get trace_data_out = toGet (f_trace_data);
`endif
interface debug_module_mem_server = llc_mem_server;
`ifdef RVFI_DII
interface Toooba_RVFI_DII_Server rvfi_dii_server = core[0].rvfi_dii_server;
@@ -364,8 +291,8 @@ module mkProc (Proc_IFC);
// Optional interface to Debug Module
`ifdef INCLUDE_GDB_CONTROL
// run-control, other
interface Server hart0_server_run_halt = toGPServer (f_run_halt_reqs, f_run_halt_rsps);
// run/halt, gpr, mem and csr control goes to core
interface Server hart0_run_halt_server = core [0].hart0_run_halt_server;
interface Put hart0_put_other_req;
method Action put (Bit #(4) req);
@@ -373,16 +300,16 @@ module mkProc (Proc_IFC);
endmethod
endinterface
// GPR access
interface Server hart0_gpr_mem_server = toGPServer (f_gpr_reqs, f_gpr_rsps);
interface Server hart0_gpr_mem_server = core[0].hart0_gpr_mem_server;
`ifdef ISA_F
// FPR access
interface Server hart0_fpr_mem_server = toGPServer (f_fpr_reqs, f_fpr_rsps);
interface Server hart0_fpr_mem_server = core[0].hart0_fpr_mem_server;
`endif
interface Server hart0_csr_mem_server = core[0].hart0_csr_mem_server;
`endif
// CSR access
interface Server hart0_csr_mem_server = toGPServer (f_csr_reqs, f_csr_rsps);
`ifdef INCLUDE_TANDEM_VERIF
interface v_to_TV = core [0].v_to_TV;
`endif
endmodule: mkProc

View File

@@ -1,10 +1,11 @@
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved
package Proc_IFC;
// ================================================================
// BSV library imports
import Vector :: *;
import GetPut :: *;
import ClientServer :: *;
@@ -21,7 +22,8 @@ import DM_CPU_Req_Rsp :: *;
`endif
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
import ProcTypes :: *;
import Trace_Data2 :: *;
`endif
`ifdef RVFI_DII
@@ -36,11 +38,10 @@ import Types :: *;
// because the RISCY-OOO mkProc contains those elements.
interface Proc_IFC;
// Reset
interface Server #(Token, Token) hart0_server_reset;
// ----------------
// Start the cores running
// Use toHostAddr = 0 if not monitoring tohost
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
// ----------------
@@ -61,12 +62,6 @@ interface Proc_IFC;
(* always_ready, always_enabled *)
method Action s_external_interrupt_req (Bool set_not_clear);
// ----------------
// External interrupt [14] to go into Debug Mode
(* always_ready, always_enabled *)
method Action debug_external_interrupt_req (Bool set_not_clear);
// ----------------
// Non-maskable interrupt
@@ -79,11 +74,9 @@ interface Proc_IFC;
method Action set_verbosity (Bit #(4) verbosity);
// ----------------
// Optional interface to Tandem Verifier
// Coherent port into LLC (used by Debug Module, DMA engines, ... to read/write memory)
`ifdef INCLUDE_TANDEM_VERIF
interface Get #(Trace_Data) trace_data_out;
`endif
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) debug_module_mem_server;
`ifdef RVFI_DII
interface Toooba_RVFI_DII_Server rvfi_dii_server;
@@ -93,20 +86,24 @@ interface Proc_IFC;
// Optional interface to Debug Module
`ifdef INCLUDE_GDB_CONTROL
// run-control, other
interface Server #(Bool, Bool) hart0_server_run_halt;
interface Put #(Bit #(4)) hart0_put_other_req;
// GPR access
interface Server #(Bool, Bool) hart0_run_halt_server;
interface Server #(DM_CPU_Req #(5, XLEN), DM_CPU_Rsp #(XLEN)) hart0_gpr_mem_server;
`ifdef ISA_F
// FPR access
interface Server #(DM_CPU_Req #(5, FLEN), DM_CPU_Rsp #(FLEN)) hart0_fpr_mem_server;
`endif
// CSR access
interface Server #(DM_CPU_Req #(12, XLEN), DM_CPU_Rsp #(XLEN)) hart0_csr_mem_server;
// Non-standard
interface Put #(Bit #(4)) hart0_put_other_req;
`endif
`ifdef INCLUDE_TANDEM_VERIF
// Note: this is a SupSize vector of streams of Trace_Data2 structs,
// each of which has a serialnum field. Each of the SupSize
// streams has serialnums in increasing order. Each serialnum
// appears exactly once in exactly one of the streams. Thus, the
// channels can easily be merged into a single program-order stream.
interface Vector #(SupSize, Get #(Trace_Data2)) v_to_TV;
`endif
endinterface

View File

@@ -1,8 +1,21 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
// Copyright (c) 2018-2020 Bluespec, Inc. All Rights Reserved.
package CoreW;
// ================================================================
// This package is called 'CoreW' for 'Core Wrapper'
// and corresponds to 'Core' in Piccolo and Flute.
//
// Here in Toooba, we use the name 'CoreW' to avoid a name-clash with
// an inner module called 'Core' in MIT's RISCY-OOO.
//
// The specific correspondence with Piccolo/Flute structure is:
// Piccolo/Flute Toooba
// mkCore mkCoreW
// mkProc
// mkCPU mkCore
// This package defines:
// Core_IFC
// mkCore #(Core_IFC)
@@ -19,12 +32,12 @@ package CoreW;
// ================================================================
// BSV library imports
import Vector :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Clocks :: *;
import Vector :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Clocks :: *;
// ----------------
// BSV additional libs
@@ -35,6 +48,13 @@ import GetPut_Aux :: *;
// ================================================================
// Project imports
// ----------------
// From RISCY-ooo
import ProcTypes :: *;
// ----------------
// From Toooba
// Main fabric
import AXI4_Types :: *;
import AXI4_Fabric :: *;
@@ -52,8 +72,10 @@ import Proc_IFC :: *;
import Proc :: *;
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
import TV_Encode :: *;
import TV_Info :: *;
import Trace_Data2 :: *;
import TV_Encode :: *;
import Trace_Data2_to_Trace_Data :: *;
`endif
// TV_Taps needed when both GDB_CONTROL and TANDEM_VERIF are present
@@ -69,13 +91,52 @@ import DM_CPU_Req_Rsp ::*;
// The Core module
(* synthesize *)
module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
module mkCoreW #(Reset dm_power_on_reset)
(CoreW_IFC #(N_External_Interrupt_Sources));
`ifdef EXTERNAL_DEBUG_MODULE
// ================================================================
// Notes on 'reset'
// This module's default reset (Verilog RST_N) is a
// 'non-debug-module reset', or 'ndm-reset': it resets everything
// in mkCoreW other than the optional RISC-V Debug Module (DM).
// DM is reset ONLY by 'dm_power_on_reset' (parameter of this module).
// This is expected to be performed exactly once, on power-up.
// Note: DM has an internal functionality that the DM spec calls
// 'dm_reset'. This is not really an electrical reset, it is just
// a module initializer wholly within the DM to put it into a
// known state. To be able to do a dm_reset, the DM has to be
// working already, at least to the point that it can field DMI
// requests from the external debugger asking the DM to proform a
// dm_reset.
// DM can ask the environment to perform an 'ndm-reset', which the
// environment does by asserting the default reset (RST_N). At the
// same time, the environment may also reset part or all of the
// rest of the SoC.
// DM can also individually reset each hart in mkCPU.
// 'hart' = hardware thread = independent PC and fetch-and-execute pipeline.
// mkCPU (instantiated in this module) has one or more harts.
// This hart-reset logic is entirely within this module.
// ================================================================
// The CPU's (hart's) reset is the ``or'' of the default reset
// (power-on reset) and the Debug Module's 'hart_reset' control.
let ndm_reset <- exposeCurrentReset;
`ifdef INCLUDE_GDB_CONTROL
let clk <- exposeCurrentClock;
let cpu_reset <- mkReset(50, True, clk);
let cpu_halt <- mkReset(50, True, clk);
let cpu_reset_either <- mkResetEither(cpu_reset.new_rst, cpu_halt.new_rst);
Bool initial_reset_val = False;
Integer hart_reset_duration = 10; // NOTE: assuming 10 cycle reset enough for hart
let dm_hart0_reset_controller <- mkReset(hart_reset_duration, initial_reset_val, clk);
let hart0_reset <- mkResetEither (ndm_reset, dm_hart0_reset_controller.new_rst);
`else
let hart0_reset = ndm_reset;
`endif
// ================================================================
@@ -84,12 +145,9 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
// System address map
SoC_Map_IFC soc_map <- mkSoC_Map;
// McStriiv processor
`ifdef EXTERNAL_DEBUG_MODULE
Proc_IFC proc <- mkProc(reset_by cpu_reset_either);
`else
Proc_IFC proc <- mkProc;
`endif
// RISCY-OOO processor
// TODO (when we do multicore): need resets for each core.
Proc_IFC proc <- mkProc (reset_by hart0_reset);
// A 2x3 fabric for connecting {CPU, Debug_Module} to {Fabric, PLIC}
Fabric_2x3_IFC fabric_2x3 <- mkFabric_2x3;
@@ -97,231 +155,99 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
// PLIC (Platform-Level Interrupt Controller)
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
// Reset requests from SoC and responses to SoC
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
`ifdef INCLUDE_GDB_CONTROL
// Debug Module
Debug_Module_IFC debug_module <- mkDebug_Module (reset_by dm_power_on_reset);
`endif
`ifdef INCLUDE_TANDEM_VERIF
// The TV encoder transforms Trace_Data structures produced by the CPU and DM
// The following are a superscalar-wide set of transformers from RISCY-OOO output Trace_Data2
// to Trace_Data which is input to the TV encoder
Vector #(SupSize, Trace_Data2_to_Trace_Data_IFC) v_td2_to_td <- replicateM (mkTrace_Data2_to_Trace_Data);
// The TV encoder transforms Trace_Data structures from the CPU and DM
// into encoded byte vectors for transmission to the Tandem Verifier
TV_Encode_IFC tv_encode <- mkTV_Encode;
`endif
`ifdef INCLUDE_GDB_CONTROL
// Debug Module
Debug_Module_IFC debug_module <- mkDebug_Module;
`endif
// HTIF locations (for debugging only)
Reg #(Bit #(64)) rg_tohost_addr <- mkReg (0);
Reg #(Bit #(64)) rg_fromhost_addr <- mkReg (0);
// ================================================================
// RESET
// There are two sources of reset requests to the CPU: externally
// from the SoC and, optionally, the DM. The SoC requires a
// response, the DM does not. When both requestors are present
// (i.e., DM is present), we merge the reset requests into the CPU,
// and we remember which one was the requestor in
// f_reset_requestor, so that we know whether or not to respond to
// the SoC.
Bit #(1) reset_requestor_dm = 0;
Bit #(1) reset_requestor_soc = 1;
`ifdef INCLUDE_GDB_CONTROL
FIFOF #(Bit #(1)) f_reset_requestor <- mkFIFOF;
`endif
// Reset-hart0 request from SoC
rule rl_cpu_hart0_reset_from_soc_start;
let req <- pop (f_reset_reqs);
`ifdef EXTERNAL_DEBUG_MODULE
cpu_reset.assertReset;
`else
proc.hart0_server_reset.request.put (?); // CPU
`endif
plic.server_reset.request.put (?); // PLIC
fabric_2x3.reset; // Local 2x3 Fabric
// Hart-reset from DM
`ifdef INCLUDE_GDB_CONTROL
`ifndef EXTERNAL_DEBUG_MODULE
// Remember the requestor, so we can respond to it
f_reset_requestor.enq (reset_requestor_soc);
`endif
`endif
$display ("%0d: Core.rl_cpu_hart0_reset_from_soc_start", cur_cycle);
Reg #(Bit #(8)) rg_hart0_reset_delay <- mkReg (0);
Reg #(Bit #(64)) rg_tohost_addr <- mkReg (0);
Reg #(Bit #(64)) rg_fromhost_addr <- mkReg (0);
rule rl_dm_hart0_reset (rg_hart0_reset_delay == 0);
let x <- debug_module.hart0_reset_client.request.get;
dm_hart0_reset_controller.assertReset;
rg_hart0_reset_delay <= fromInteger (hart_reset_duration + 200); // NOTE: heuristic
$display ("%0d: %m.rl_dm_hart0_reset: asserting hart0 reset for %0d cycles",
cur_cycle, hart_reset_duration);
endrule
`ifdef INCLUDE_GDB_CONTROL
`ifndef EXTERNAL_DEBUG_MODULE
// Reset-hart0 from Debug Module
rule rl_cpu_hart0_reset_from_dm_start;
let req <- debug_module.hart0_get_reset_req.get;
rule rl_dm_hart0_reset_wait (rg_hart0_reset_delay != 0);
if (rg_hart0_reset_delay == 1) begin
let pc = soc_map_struct.pc_reset_value;
proc.start (pc, rg_tohost_addr, rg_fromhost_addr);
proc.hart0_server_reset.request.put (?); // CPU
plic.server_reset.request.put (?); // PLIC
fabric_2x3.reset; // Local 2x3 fabric
// Remember the requestor, so we can respond to it
f_reset_requestor.enq (reset_requestor_dm);
$display ("%0d: Core.rl_cpu_hart0_reset_from_dm_start", cur_cycle);
endrule
`endif
`endif
`ifdef EXTERNAL_DEBUG_MODULE
rule rl_cpu_hart0_reset_complete(!cpu_reset.isAsserted);
`else
rule rl_cpu_hart0_reset_complete;
let rsp1 <- proc.hart0_server_reset.response.get; // CPU
`endif
let rsp3 <- plic.server_reset.response.get; // PLIC
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
zeroExtend (soc_map.m_plic_addr_lim));
Bit #(1) requestor = reset_requestor_soc;
`ifdef INCLUDE_GDB_CONTROL
`ifndef EXTERNAL_DEBUG_MODULE
requestor <- pop (f_reset_requestor);
`endif
`endif
if (requestor == reset_requestor_soc)
f_reset_rsps.enq (?);
`ifndef EXTERNAL_DEBUG_MODULE
// Start running the cores
proc.start (soc_map_struct.pc_reset_value,
rg_tohost_addr,
rg_fromhost_addr);
`endif
$display ("%0d: Core.rl_cpu_hart0_reset_complete; started running proc", cur_cycle);
Bool is_running = True;
debug_module.hart0_reset_client.response.put (is_running);
$display ("%0d: %m.rl_dm_hart0_reset_wait: proc.start (pc %0h, tohostAddr %0h, fromhostAddr %0h",
cur_cycle, pc, rg_tohost_addr, rg_fromhost_addr);
end
rg_hart0_reset_delay <= rg_hart0_reset_delay - 1;
endrule
`endif
`ifdef INCLUDE_GDB_CONTROL
// ================================================================
// Direct DM-to-CPU connections
// Direct DM-to-CPU connections for run-control and other misc requests
`ifdef INCLUDE_GDB_CONTROL
`ifndef EXTERNAL_DEBUG_MODULE
// DM to CPU connections for run-control and other misc requests
mkConnection (debug_module.hart0_client_run_halt, proc.hart0_server_run_halt);
mkConnection (debug_module.hart0_client_run_halt, proc.hart0_run_halt_server);
mkConnection (debug_module.hart0_get_other_req, proc.hart0_put_other_req);
`endif
`endif
// external debug module connections
`ifdef INCLUDE_GDB_CONTROL
`ifdef EXTERNAL_DEBUG_MODULE
Reg#(Bool) once <- mkReg(False, reset_by cpu_reset_either);
rule rl_once(!once && !cpu_reset.isAsserted && !cpu_halt.isAsserted);
proc.hart0_server_reset.request.put(?);
once <= True;
endrule
rule rl_hart0_server_reset;
let tmp <- proc.hart0_server_reset.response.get;
proc.start (soc_map_struct.pc_reset_value,
rg_tohost_addr,
rg_fromhost_addr);
endrule
rule rl_hart0_server_run_halt;
let tmp <- proc.hart0_server_run_halt.response.get;
endrule
Reg#(Bool) hart0_halt <- mkReg(False);
rule rl_halt_reset(hart0_halt);
cpu_halt.assertReset;
endrule
rule rl_halt;
let halt <- debug_module.hart0_client_run_halt.request.get;
hart0_halt <= !halt;
debug_module.hart0_client_run_halt.response.put(halt);
endrule
rule rl_gpr;
let req <- debug_module.hart0_gpr_mem_client.request.get;
debug_module.hart0_gpr_mem_client.response.put(DM_CPU_Rsp { ok: True, data: 0 });
endrule
`ifdef ISA_F
rule rl_fpr;
let req <- debug_module.hart0_fpr_mem_client.request.get;
debug_module.hart0_fpr_mem_client.response.put(DM_CPU_Rsp { ok: True, data: 0 });
endrule
`endif
rule rl_csr;
let req <- debug_module.hart0_csr_mem_client.request.get;
debug_module.hart0_csr_mem_client.response.put(DM_CPU_Rsp { ok: True, data: 0 });
endrule
rule rl_cpu_hart0_reset_from_dm_start;
let req <- debug_module.hart0_get_reset_req.get;
cpu_reset.assertReset;
f_reset_requestor.enq (reset_requestor_dm);
endrule
rule rl_cpu_hart0_reset_from_dm_complete (f_reset_requestor.first == reset_requestor_dm && !cpu_reset.isAsserted);
f_reset_requestor.deq;
endrule
`endif
`endif
`ifdef INCLUDE_TANDEM_VERIF
// ================================================================
// Other CPU/DM/TV connections
// (depends on whether DM, TV or both are present)
// Direct CPU-to-TV connections for TV trace data
for (Integer j = 0; j < valueOf (SupSize); j = j + 1) begin
// CPU Trace_Data2 output streams to Trace_Data2_to_Trace_Data converters
mkConnection (proc.v_to_TV [j], v_td2_to_td [j].in);
// Trace_Data2_to_Trace_Data converters to TV encoder
mkConnection (v_td2_to_td [j].out, tv_encode.v_cpu_in [j]);
end
`endif
`ifdef INCLUDE_GDB_CONTROL
`ifdef INCLUDE_TANDEM_VERIF
// BEGIN SECTION: GDB and TV
// ----------------------------------------------------------------
// DM and TV both present. We instantiate 'taps' into connections
// where the DM writes CPU GPRs, CPU FPRs, CPU CSRs, and main memory,
// in order to produce corresponding writes for the Tandem Verifier.
// Then, we merge the Trace_Data from these three taps with the
// Trace_Data produced by the PROC.
FIFOF #(Trace_Data) f_trace_data_merged <- mkFIFOF;
// Connect merged trace data to trace encoder
mkConnection (toGet (f_trace_data_merged), tv_encode.trace_data_in);
// Merge-in CPU's trace data.
// This is equivalent to: mkConnection (proc.trace_data_out, toPut (f_trace_data_merged))
// but using a rule allows us to name it in scheduling attributes.
rule merge_cpu_trace_data;
let tmp <- proc.trace_data_out.get;
f_trace_data_merged.enq (tmp);
endrule
// ================================================================
// BEGIN SECTION: DM and TV both present
// We instantiate 'taps' into connections where DM writes CPU GPRs,
// FPRs, CSRs, and main memory. The tap outputs go the TV encoder,
// to keep the tandem verifier in sync with DM updates to the CPU.
// Create a tap for DM's memory-writes to the bus, and merge-in the trace data.
DM_Mem_Tap_IFC dm_mem_tap <- mkDM_Mem_Tap;
mkConnection (debug_module.master, dm_mem_tap.slave);
let dm_master_local = dm_mem_tap.master;
rule merge_dm_mem_trace_data;
rule rl_merge_dm_mem_trace_data;
let tmp <- dm_mem_tap.trace_data_out.get;
f_trace_data_merged.enq (tmp);
tv_encode.dm_in.put (tmp);
endrule
`ifndef EXTERNAL_DEBUG_MODULE
// Create a tap for DM's GPR writes to the CPU, and merge-in the trace data.
DM_GPR_Tap_IFC dm_gpr_tap_ifc <- mkDM_GPR_Tap;
mkConnection (debug_module.hart0_gpr_mem_client, dm_gpr_tap_ifc.server);
mkConnection (dm_gpr_tap_ifc.client, proc.hart0_gpr_mem_server);
rule merge_dm_gpr_trace_data;
rule rl_merge_dm_gpr_trace_data;
let tmp <- dm_gpr_tap_ifc.trace_data_out.get;
f_trace_data_merged.enq (tmp);
tv_encode.dm_in.put (tmp);
endrule
`ifdef ISA_F_OR_D
@@ -330,9 +256,9 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
mkConnection (debug_module.hart0_fpr_mem_client, dm_fpr_tap_ifc.server);
mkConnection (dm_fpr_tap_ifc.client, proc.hart0_fpr_mem_server);
rule merge_dm_fpr_trace_data;
rule rl_merge_dm_fpr_trace_data;
let tmp <- dm_fpr_tap_ifc.trace_data_out.get;
f_trace_data_merged.enq (tmp);
tv_encode.dm_in.put (tmp);
endrule
`endif
// for ifdef ISA_F_OR_D
@@ -342,25 +268,25 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
mkConnection(debug_module.hart0_csr_mem_client, dm_csr_tap.server);
mkConnection(dm_csr_tap.client, proc.hart0_csr_mem_server);
`ifdef ISA_F_OR_D
(* descending_urgency = "merge_dm_fpr_trace_data, merge_dm_gpr_trace_data" *)
`endif
(* descending_urgency = "merge_dm_gpr_trace_data, merge_dm_csr_trace_data" *)
(* descending_urgency = "merge_dm_csr_trace_data, merge_dm_mem_trace_data" *)
(* descending_urgency = "merge_dm_mem_trace_data, merge_cpu_trace_data" *)
rule merge_dm_csr_trace_data;
rule rl_merge_dm_csr_trace_data;
let tmp <- dm_csr_tap.trace_data_out.get;
f_trace_data_merged.enq(tmp);
tv_encode.dm_in.put(tmp);
endrule
`ifdef ISA_F_OR_D
(* descending_urgency = "rl_merge_dm_fpr_trace_data, rl_merge_dm_gpr_trace_data" *)
`endif
(* descending_urgency = "rl_merge_dm_gpr_trace_data, rl_merge_dm_csr_trace_data" *)
(* descending_urgency = "rl_merge_dm_csr_trace_data, rl_merge_dm_mem_trace_data" *)
rule rl_bogus_for_sched_attributes;
endrule
// END SECTION: GDB and TV
`else
// for ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// BEGIN SECTION: GDB and no TV
// END SECTION: DM and TV
// ================================================================
`else // of ifdef INCLUDE_TANDEM_VERIF
// ================================================================
// BEGIN SECTION: DM, no TV
`ifndef EXTERNAL_DEBUG_MODULE
// Connect DM's GPR interface directly to CPU
mkConnection (debug_module.hart0_gpr_mem_client, proc.hart0_gpr_mem_server);
@@ -371,33 +297,30 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
// Connect DM's CSR interface directly to CPU
mkConnection (debug_module.hart0_csr_mem_client, proc.hart0_csr_mem_server);
`endif
// DM's bus master is directly the bus master
let dm_master_local = debug_module.master;
// END SECTION: GDB and no TV
`endif
// for ifdef INCLUDE_TANDEM_VERIF
// END SECTION: DM, no TV
// ================================================================
`endif // for ifdef INCLUDE_TANDEM_VERIF
// ================================================================
`else // for ifdef INCLUDE_GDB_CONTROL
// ================================================================
// BEGIN SECTION: no DM
`else
// for ifdef INCLUDE_GDB_CONTROL
// BEGIN SECTION: no GDB
// No DM, so 'DM bus master' is dummy
// No DM, so 'DM bus master' is AXI4 dummy
AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User)
dm_master_local = dummy_AXI4_Master_ifc;
`ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// BEGIN SECTION: no GDB, TV
// TV, no DM: stub out the dm input to TV
Get #(Trace_Data) gs = getstub;
mkConnection (tv_encode.dm_in, gs);
`endif
`endif // for ifdef INCLUDE_GDB_CONTROL
// Connect CPU's TV out directly to TV encoder
mkConnection (proc.trace_data_out, tv_encode.trace_data_in);
// END SECTION: no GDB, TV
`endif
`endif
// for ifdef INCLUDE_GDB_CONTROL
// ================================================================
// Connect the local 2x3 fabric
@@ -407,12 +330,10 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
mkConnection (dm_master_local, fabric_2x3.v_from_masters [debug_module_sba_master_num]);
// Slaves on the local 2x3 fabric
// default slave is taken out directly to the Core interface
mkConnection (fabric_2x3.v_to_slaves [plic_slave_num], plic.axi4_slave);
// TODO: This slave can be connected to mkLLCDmaConnect for Debug Module System Bus Access
AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) dummy_slave = dummy_AXI4_Slave_ifc;
mkConnection (fabric_2x3.v_to_slaves [near_mem_io_slave_num], dummy_slave);
// Two of the slaves are connected here.
// The third slave (default slave) is taken out directly to the Core interface
mkConnection (fabric_2x3.v_to_slaves [plic_slave_num], plic.axi4_slave);
mkConnection (fabric_2x3.v_to_slaves [llc_slave_num], proc.debug_module_mem_server);
// ================================================================
// Connect external interrupt lines from PLIC to CPU
@@ -427,13 +348,6 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
// $display ("%0d: Core.rl_relay_external_interrupts: relaying: %d", cur_cycle, pack (x));
endrule
// TODO: fixup. Need to combine NMIs from multiple sources (cache, fabric, devices, ...)
rule rl_relay_non_maskable_interrupt;
proc.non_maskable_interrupt_req (False);
// $display ("%0d: Core.rl_relay_non_maskable_interrupts: relaying: %d", cur_cycle, pack (x));
endrule
// ================================================================
// INTERFACE
@@ -445,16 +359,26 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
proc.set_verbosity (verbosity);
endmethod
method Action set_htif_addrs (Bit #(64) tohost_addr, Bit #(64) fromhost_addr);
// ----------------------------------------------------------------
// Start
method Action start (Bit #(64) tohost_addr, Bit #(64) fromhost_addr);
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
zeroExtend (soc_map.m_plic_addr_lim));
let pc = soc_map_struct.pc_reset_value;
proc.start (pc, tohost_addr, fromhost_addr);
`ifdef INCLUDE_GDB_CONTROL
// Save for potential future use by rl_dm_hart0_reset
rg_tohost_addr <= tohost_addr;
rg_fromhost_addr <= fromhost_addr;
`endif
$display ("%0d: %m.method start: proc.start (pc %0h, tohostAddr %0h, fromhostAddr %0h)",
cur_cycle, pc, tohost_addr, fromhost_addr);
endmethod
// ----------------------------------------------------------------
// Soft reset
interface Server cpu_reset_server = toGPServer (f_reset_reqs, f_reset_rsps);
// ----------------------------------------------------------------
// AXI4 Fabric interfaces
@@ -469,43 +393,44 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources));
interface core_external_interrupt_sources = plic.v_sources;
// ----------------
// External interrupt [14] to go into Debug Mode
method Action debug_external_interrupt_req (Bool set_not_clear);
proc.debug_external_interrupt_req (set_not_clear);
endmethod
// ----------------------------------------------------------------
// Optional TV interface
// Non-maskable interrupt request
`ifdef INCLUDE_TANDEM_VERIF
interface Get tv_verifier_info_get;
method ActionValue #(Info_CPU_to_Verifier) get();
match { .n, .v } <- tv_encode.tv_vb_out.get;
return (Info_CPU_to_Verifier { num_bytes: n, vec_bytes: v });
endmethod
endinterface
`endif
method Action nmi_req (Bool set_not_clear);
// TODO: fixup; passing const False for now
proc.non_maskable_interrupt_req (False);
endmethod
`ifdef RVFI_DII
interface Toooba_RVFI_DII_Server rvfi_dii_server = proc.rvfi_dii_server;
`endif
`ifdef INCLUDE_GDB_CONTROL
// ----------------------------------------------------------------
// Optional DM interfaces
`ifdef INCLUDE_GDB_CONTROL
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dm_dmi = debug_module.dmi;
interface DMI dmi = debug_module.dmi;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get dm_ndm_reset_req_get = debug_module.get_ndm_reset_req;
interface Client ndm_reset_client = debug_module.ndm_reset_client;
`endif
`ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// Optional TV interface
interface Get tv_verifier_info_get;
method ActionValue #(Info_CPU_to_Verifier) get();
match { .n, .v } <- tv_encode.out.get;
return (Info_CPU_to_Verifier { num_bytes: n, vec_bytes: v });
endmethod
endinterface
`endif
endmodule: mkCoreW
@@ -531,12 +456,9 @@ typedef 3 Num_Slaves_2x3;
typedef Bit #(TLog #(Num_Slaves_2x3)) Slave_Num_2x3;
Slave_Num_2x3 default_slave_num = 0;
Slave_Num_2x3 plic_slave_num = 1;
// TODO: repurpose this for Debug Module System Bus Access to connect to mkLLCDramConnect
Slave_Num_2x3 near_mem_io_slave_num = 2;
Slave_Num_2x3 default_slave_num = 0; // for I/O, uncached memory, etc.
Slave_Num_2x3 plic_slave_num = 1; // PLIC mem-mapped registers
Slave_Num_2x3 llc_slave_num = 2; // Normal cached memory (connects to coherent Last-Level Cache)
// ----------------
// Specialization of parameterized AXI4 fabric for 2x3 Core fabric
@@ -561,9 +483,9 @@ module mkFabric_2x3 (Fabric_2x3_IFC);
// Any addr is legal, and there is only one slave to service it.
function Tuple2 #(Bool, Slave_Num_2x3) fn_addr_to_slave_num_2x3 (Fabric_Addr addr);
if ( (soc_map.m_near_mem_io_addr_base <= addr)
&& (addr < soc_map.m_near_mem_io_addr_lim))
return tuple2 (True, near_mem_io_slave_num);
if ( (soc_map.m_mem0_controller_addr_base <= addr)
&& (addr < soc_map.m_mem0_controller_addr_lim))
return tuple2 (True, llc_slave_num);
else if ( (soc_map.m_plic_addr_base <= addr)
&& (addr < soc_map.m_plic_addr_lim))

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
// Copyright (c) 2018-2020 Bluespec, Inc. All Rights Reserved.
package CoreW_IFC;
@@ -6,9 +6,8 @@ package CoreW_IFC;
// This package defines the interface of a CoreW module which
// contains:
// - mkProc (the RISC-V CPU; this a variant of MIT's RISCY-OOO mkProc)
// Note: MIT's RISCY-OOO internally contains a 'mkCore'
// and hence this interface and its module is called
// 'CoreW', to disambiguate.
// Note: MIT's RISCY-OOO internally has a 'mkCore' and hence this
// interface and its module is called 'CoreW', to disambiguate.
// - mkFabric_2x3
// - mkNear_Mem_IO_AXI4
// - mkPLIC_16_2_7
@@ -32,10 +31,6 @@ import Fabric_Defs :: *;
// External interrupt request interface
import PLIC :: *;
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
`endif
`ifdef INCLUDE_GDB_CONTROL
import Debug_Module :: *;
`endif
@@ -44,22 +39,26 @@ import Debug_Module :: *;
import Types :: *;
`endif
`ifdef INCLUDE_TANDEM_VERIF
import ProcTypes :: *;
import Trace_Data2 :: *;
import TV_Info :: *;
`endif
// ================================================================
// The CoreW interface
interface CoreW_IFC #(numeric type t_n_interrupt_sources);
// ----------------------------------------------------------------
// Debugging: set core's verbosity, htif addrs
// Debugging: set core's verbosity
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
method Action set_htif_addrs (Bit #(64) tohost_addr, Bit #(64) fromhost_addr);
// ----------------------------------------------------------------
// Soft reset
// Start
interface Server #(Bit #(0), Bit #(0)) cpu_reset_server;
method Action start (Bit #(64) tohost_addr, Bit #(64) fromhost_addr);
// ----------------------------------------------------------------
// AXI4 Fabric interfaces
@@ -75,40 +74,41 @@ interface CoreW_IFC #(numeric type t_n_interrupt_sources);
interface Vector #(t_n_interrupt_sources, PLIC_Source_IFC) core_external_interrupt_sources;
// ----------------
// External interrupt [14] to go into Debug Mode
// ----------------------------------------------------------------
// Non-maskable interrupt request
(* always_ready, always_enabled *)
method Action debug_external_interrupt_req (Bool set_not_clear);
// ----------------------------------------------------------------
// Optional Tandem Verifier interface output tuples (n,vb),
// where 'vb' is a vector of bytes
// with relevant bytes in locations [0]..[n-1]
`ifdef INCLUDE_TANDEM_VERIF
interface Get #(Info_CPU_to_Verifier) tv_verifier_info_get;
`endif
method Action nmi_req (Bool set_not_clear);
`ifdef RVFI_DII
interface Toooba_RVFI_DII_Server rvfi_dii_server;
`endif
`ifdef INCLUDE_GDB_CONTROL
// ----------------------------------------------------------------
// Optional Debug Module interfaces
`ifdef INCLUDE_GDB_CONTROL
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dm_dmi;
interface DMI dmi;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get #(Bit #(0)) dm_ndm_reset_req_get;
interface Client #(Bool, Bool) ndm_reset_client;
`endif
`ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// Optional Tandem Verifier interface output tuples (n,vb),
// where 'vb' is a vector of bytes
// with relevant bytes in locations [0]..[n-1]
interface Get #(Info_CPU_to_Verifier) tv_verifier_info_get;
`endif
endinterface
// ================================================================

View File

@@ -1,10 +1,14 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
// Copyright (c) 2013-2020 Bluespec, Inc. All Rights Reserved.
package TV_Encode;
// ================================================================
// module mkTV_Encode is a transforming FIFO
// converting Trace_Data into encoded byte vectors
// module mkTV_Encode inputs:
// - A superscalar-wide vector of (serial_num, Trace_Data) streams
// from a superscalar CPU
// - A Trace_Data stream
// from the Debug Module
// and produces an output stream of encoded byte vectors.
// ================================================================
// BSV lib imports
@@ -18,26 +22,37 @@ import Connectable :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
// ================================================================
// Project imports
// ----------------
// From RISCY-OOO
import ProcTypes :: *;
// ----------------
// From Toooba
import ISA_Decls :: *;
import TV_Info :: *;
// ================================================================
interface TV_Encode_IFC;
method Action reset;
// Superscalar trace data from the CPU.
// Each item in the stream is (serialnum, td).
interface Vector #(SupSize, Put #(Tuple2 #(Bit #(64), Trace_Data))) v_cpu_in;
// This module receives Trace_Data structs from the CPU and Debug Module
interface Put #(Trace_Data) trace_data_in;
// Trace data from the Debug Module
interface Put #(Trace_Data) dm_in;
// This module produces tuples (n,vb),
// where 'vb' is a vector of bytes
// with relevant bytes in locations [0]..[n-1]
interface Get #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) tv_vb_out;
interface Get #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) out;
endinterface
// ================================================================
@@ -45,20 +60,58 @@ endinterface
(* synthesize *)
module mkTV_Encode (TV_Encode_IFC);
Reg #(Bool) rg_reset_done <- mkReg (True);
Integer verbosity = 0; // For debugging
// Keep track of last PC for more efficient encoding of incremented PCs
// TODO: currently always sending full PC
Reg #(WordXL) rg_last_pc <- mkReg (0);
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
FIFOF #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) f_vb <- mkFIFOF;
// Superscalar-wide inputs from CPU
Vector #(SupSize, FIFOF #(Tuple2 #(Bit #(64), Trace_Data))) v_f_cpu_ins <- replicateM (mkFIFOF);
Reg #(Bit #(64)) rg_serialnum <- mkReg (0);
// Input from Debug Module
FIFOF #(Trace_Data) f_dm_in <- mkFIFOF;
// Merges CPU and Debug Module inputs
FIFOF #(Trace_Data) f_merged <- mkFIFOF;
// Encoded output
FIFOF #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) f_out <- mkFIFOF;
// ----------------------------------------------------------------
// BEHAVIOR
// BEHAVIOR: MERGING
// v_f_cpu_ins and f_dm_in are merged into f_merged
rule rl_log_trace_RESET (rg_reset_done && (f_trace_data.first.op == TRACE_RESET));
let td <- pop (f_trace_data);
// v_f_cpu_ins are merged in program order (using serialnum)
for (Integer j = 0; j < valueOf (SupSize); j = j + 1)
rule rl_merge_cpu_ins (tpl_1 (v_f_cpu_ins [j].first) == rg_serialnum);
let td = tpl_2 (v_f_cpu_ins [j].first);
v_f_cpu_ins [j].deq;
f_merged.enq (td);
rg_serialnum <= rg_serialnum + 1;
if (verbosity != 0) begin
$display ("%0d: %m.rl_merge_cpu_in [%0d]: serialnum = %0d", cur_cycle, j, rg_serialnum);
end
endrule
// f_dm_ins is merged in at any time
rule rl_merge_dm_in;
// let td <- pop (f_dm_in.first); // Surprise: this gives no type-check error?
let td = f_dm_in.first; f_dm_in.deq;
f_merged.enq (td);
if (verbosity != 0) begin
$display ("%0d: %m.rl_merge_dm_in", cur_cycle);
end
endrule
// ----------------------------------------------------------------
// BEHAVIOR: ENCODING
rule rl_log_trace_RESET (f_merged.first.op == TRACE_RESET);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -70,11 +123,11 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nnN, .xN } = vsubst (nn1, x1, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_GPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_GPR_WRITE));
let td <- pop (f_trace_data);
rule rl_log_trace_GPR_WRITE (f_merged.first.op == TRACE_GPR_WRITE);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -88,11 +141,11 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_FPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_FPR_WRITE));
let td <- pop (f_trace_data);
rule rl_log_trace_FPR_WRITE (f_merged.first.op == TRACE_FPR_WRITE);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -106,11 +159,11 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_CSR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_CSR_WRITE));
let td <- pop (f_trace_data);
rule rl_log_trace_CSR_WRITE (f_merged.first.op == TRACE_CSR_WRITE);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -124,11 +177,11 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_MEM_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_MEM_WRITE));
let td <- pop (f_trace_data);
rule rl_log_trace_MEM_WRITE (f_merged.first.op == TRACE_MEM_WRITE);
let td <- pop (f_merged);
Bit #(2) mem_req_size = td.word1 [1:0];
Byte size_and_mem_req_op = { 2'b0, mem_req_size, te_mem_req_op_Store };
@@ -157,11 +210,11 @@ module mkTV_Encode (TV_Encode_IFC);
//match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_OTHER (rg_reset_done && (f_trace_data.first.op == TRACE_OTHER));
let td <- pop (f_trace_data);
rule rl_log_trace_OTHER (f_merged.first.op == TRACE_OTHER);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -175,11 +228,14 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
if (verbosity != 0)
$display ("%0d: %m.rl_log_trace_OTHER, pc = %0h", cur_cycle, td.pc);
endrule
rule rl_log_trace_I_RD (rg_reset_done && (f_trace_data.first.op == TRACE_I_RD));
let td <- pop (f_trace_data);
rule rl_log_trace_I_RD (f_merged.first.op == TRACE_I_RD);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -195,30 +251,68 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nnN, .xN } = vsubst (nn3, x3, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
if (verbosity != 0)
$display ("%0d: %m.rl_log_trace_I_RD, pc = %0h", cur_cycle, td.pc);
endrule
rule rl_log_trace_F_RD (rg_reset_done && (f_trace_data.first.op == TRACE_F_RD));
let td <- pop (f_trace_data);
`ifdef ISA_F
// New opcode to track GPR updates due to F/D instructions. Also updates
// the CSR FFLAGS
rule rl_log_trace_F_GRD (f_merged.first.op == TRACE_F_GRD);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_fflags), td.word2);
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word4);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_I_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_I_LOAD));
let td <- pop (f_trace_data);
// New opcode to track FPR updates due to F/D instructions. Also updates
// the CSRs FFLAGS and MSTATUS
rule rl_log_trace_F_FRD (f_merged.first.op == TRACE_F_FRD);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_fpr (fv_fpr_regnum (td.rd), td.word5);
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_fflags), td.word2);
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word4);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_out.enq (tuple2 (nnN, xN));
endrule
`endif
rule rl_log_trace_I_LOAD (f_merged.first.op == TRACE_I_LOAD);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -236,18 +330,20 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_F_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_F_LOAD));
let td <- pop (f_trace_data);
`ifdef ISA_F
rule rl_log_trace_F_LOAD (f_merged.first.op == TRACE_F_LOAD);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
match { .n3, .vb3 } = encode_fpr (fv_fpr_regnum (td.rd), td.word5);
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word4);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
@@ -256,16 +352,17 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
`endif
rule rl_log_trace_STORE (rg_reset_done && (f_trace_data.first.op == TRACE_STORE));
let td <- pop (f_trace_data);
rule rl_log_trace_I_STORE (f_merged.first.op == TRACE_I_STORE);
let td <- pop (f_merged);
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
let mem_req_size = funct3 [1:0];
let mem_req_size = td.word1 [1:0]; // funct3
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -283,14 +380,39 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_AMO (rg_reset_done && (f_trace_data.first.op == TRACE_AMO));
let td <- pop (f_trace_data);
`ifdef ISA_F
rule rl_log_trace_F_STORE (f_merged.first.op == TRACE_F_STORE);
let td <- pop (f_merged);
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
let mem_req_size = funct3 [1:0];
let mem_req_size = td.word1 [1:0]; // funct3
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_fstval (mem_req_size, td.word5);
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_out.enq (tuple2 (nnN, xN));
endrule
`endif
rule rl_log_trace_AMO (f_merged.first.op == TRACE_AMO);
let td <- pop (f_merged);
let mem_req_size = td.word4 [1:0]; // funct3
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -310,20 +432,31 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
if (verbosity != 0)
$display ("%0d: %m.rl_log_trace_AMO, pc = %0h", cur_cycle, td.pc);
endrule
rule rl_log_trace_CSRRX (rg_reset_done && (f_trace_data.first.op == TRACE_CSRRX));
let td <- pop (f_trace_data);
rule rl_log_trace_CSRRX (f_merged.first.op == TRACE_CSRRX);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .n4, .vb4 } = ((td.word2 == 0)
? tuple2 (0, ?) // CSR was not written
: encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4));
Bool csr_written = (td.word2 [0] == 1'b1);
match { .n4, .vb4 } = (csr_written
? encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4)
: tuple2 (0, ?));
`ifdef ISA_F
// MSTATUS.FS and .SD also updated if CSR instr wrote FFLAGS, FRM or FCSR
Bool mstatus_written = (td.word2 [1] == 1'b1);
match { .n5, .vb5 } = (mstatus_written
? encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word5)
: tuple2 (0, ?));
`endif
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
@@ -332,13 +465,18 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
`ifdef ISA_F
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
`else
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
`endif
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_TRAP (rg_reset_done && (f_trace_data.first.op == TRACE_TRAP));
let td <- pop (f_trace_data);
rule rl_log_trace_TRAP (f_merged.first.op == TRACE_TRAP);
let td <- pop (f_merged);
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
Priv_Mode priv = truncate (td.rd);
@@ -387,11 +525,11 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_INTR (rg_reset_done && (f_trace_data.first.op == TRACE_INTR));
let td <- pop (f_trace_data);
rule rl_log_trace_INTR (f_merged.first.op == TRACE_INTR);
let td <- pop (f_merged);
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
Priv_Mode priv = truncate (td.rd);
@@ -432,11 +570,11 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
match { .nnN, .xN } = vsubst (nn6, x6, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_RET (rg_reset_done && (f_trace_data.first.op == TRACE_RET));
let td <- pop (f_trace_data);
rule rl_log_trace_RET (f_merged.first.op == TRACE_RET);
let td <- pop (f_merged);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
@@ -454,17 +592,15 @@ module mkTV_Encode (TV_Encode_IFC);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
f_out.enq (tuple2 (nnN, xN));
endrule
// ----------------------------------------------------------------
// INTERFACE
method Action reset ();
endmethod
interface Put trace_data_in = toPut (f_trace_data);
interface Get tv_vb_out = toGet (f_vb);
interface v_cpu_in = map (toPut, v_f_cpu_ins);
interface dm_in = toPut (f_dm_in);
interface out = toGet (f_out);
endmodule
// ****************************************************************
@@ -648,6 +784,29 @@ function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_reg (Bit #(16) r
return tuple2 (n, vb);
endfunction
`ifdef ISA_F
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_fpr (Bit #(16) regnum, WordFL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n = 0;
vb [0] = te_op_full_reg;
vb [1] = regnum [7:0];
vb [2] = regnum [15:8];
vb [3] = word[7:0];
vb [4] = word [15:8];
vb [5] = word [23:16];
vb [6] = word [31:24];
n = 7;
`ifdef ISA_D
vb [7] = word [39:32];
vb [8] = word [47:40];
vb [9] = word [55:48];
vb [10] = word [63:56];
n = 11;
`endif
return tuple2 (n, vb);
endfunction
`endif
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_priv (Bit #(5) priv);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
vb [0] = te_op_addl_state;
@@ -719,6 +878,31 @@ function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_stval (MemReqSiz
return tuple2 (n, vb);
endfunction
`ifdef ISA_F
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_fstval (MemReqSize mem_req_size, WordFL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
vb [0] = te_op_addl_state;
vb [1] = case (mem_req_size)
f3_SIZE_B: te_op_addl_state_data8; // not possible
f3_SIZE_H: te_op_addl_state_data16; // not possible
f3_SIZE_W: te_op_addl_state_data32;
f3_SIZE_D: te_op_addl_state_data64;
endcase;
vb [2] = word [7:0];
vb [3] = word [15:8];
vb [4] = word [23:16];
vb [5] = word [31:24];
`ifdef ISA_D
vb [6] = word [39:32];
vb [7] = word [47:40];
vb [8] = word [55:48];
vb [9] = word [63:56];
`endif
Bit #(32) n = (1 << pack(mem_req_size)) + 2;
return tuple2 (n, vb);
endfunction
`endif
// ================================================================
endpackage

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
// Copyright (c) 2018-2020 Bluespec, Inc. All Rights Reserved.
package TV_Taps;
@@ -73,24 +73,35 @@ module mkDM_Mem_Tap (DM_Mem_Tap_IFC);
master_xactor.i_wr_data.enq (wr_data);
// Tap
Bit #(64) paddr = ?;
Bit #(64) stval = ?;
Bit #(64) paddr = ?;
Bit #(64) stval = ?;
Integer sh = 0;
Fabric_Data mask = 0;
MemReqSize sz = ?;
case (wr_data.wstrb)
`ifdef FABRIC64
if (wr_data.wstrb == 'h0f) begin
paddr = zeroExtend (wr_addr.awaddr);
stval = (wr_data.wdata & 'h_FFFF_FFFF);
end
else if (wr_data.wstrb == 'hf0) begin
paddr = zeroExtend (wr_addr.awaddr);
stval = ((wr_data.wdata >> 32) & 'h_FFFF_FFFF);
end
else
dynamicAssert(False, "mkDM_Mem_Tap: unsupported byte enables");
`else
'hFF: begin sh= 0; mask = 'hFFFF_FFFF_FFFF_FFFF; sz=f3_SIZE_D; end
'hF0: begin sh=32; mask = 'hFFFF_FFFF; sz=f3_SIZE_W; end
'hC0: begin sh=48; mask = 'hFFFF; sz=f3_SIZE_H; end
'h30: begin sh=32; mask = 'hFFFF; sz=f3_SIZE_H; end
'h80: begin sh=56; mask = 'hFF; sz=f3_SIZE_B; end
'h40: begin sh=48; mask = 'hFF; sz=f3_SIZE_B; end
'h20: begin sh=40; mask = 'hFF; sz=f3_SIZE_B; end
'h10: begin sh=32; mask = 'hFF; sz=f3_SIZE_B; end
`endif
'hF: begin sh= 0; mask = 'hFFFF_FFFF; sz=f3_SIZE_W; end
'hC: begin sh=16; mask = 'hFFFF; sz=f3_SIZE_H; end
'h3: begin sh= 0; mask = 'hFFFF; sz=f3_SIZE_H; end
'h8: begin sh=24; mask = 'hFF; sz=f3_SIZE_B; end
'h4: begin sh=16; mask = 'hFF; sz=f3_SIZE_B; end
'h2: begin sh= 8; mask = 'hFF; sz=f3_SIZE_B; end
'h1: begin sh= 0; mask = 'hFF; sz=f3_SIZE_B; end
default: dynamicAssert(False, "mkDM_Mem_Tap: unsupported byte enables");
endcase
paddr = zeroExtend (wr_addr.awaddr);
stval = zeroExtend (wr_data.wdata);
`endif
Trace_Data td = mkTrace_MEM_WRITE (f3_SIZE_W, truncate (stval), paddr);
stval = ((zeroExtend (wr_data.wdata) >> sh) & mask);
Trace_Data td = mkTrace_MEM_WRITE (sz, truncate (stval), paddr);
f_trace_data.enq (td);
endrule

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2020 Bluespec, Inc. All Rights Reserved.
package Trace_Data2;
// ================================================================
// Project imports
// ----------------
// From RISCY-OOO
import Types :: *;
import ProcTypes :: *;
import ReorderBuffer :: *;
// ================================================================
// This struct has a subset of the fields of struct ToReorderBuffer in
// Toooba/RISCY-OOO, to be encoded and emitted for Tandem
// Verification.
// In RISCY-OOO's CommitStage, when we dequeue (retire) an entry
// (struct ToReorderBuffer), we simply copy out these fields and
// enqueue this struct into a FIFO. All transformations/encoding for
// TV are done on the dequeue side of the FIFO. Thus, this should not
// add to the critical path or scheduling requirements of CommitStage.
typedef struct {
// TV message serial number
Bit #(64) serial_num;
// For asynchronous CSR updates (e.g., MIP change on external interrupt)
Maybe #(Tuple2 #(Bit #(12), Data)) maybe_csr_upd;
// Remaining fields relevant only if maybe_csr_upd is Invalid
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;
Data store_data; // For mem instrs that store data
ByteEn store_data_BE;
Maybe #(CSR) csr;
Maybe #(Trap) trap;
Addr tval; // in case of trap
PPCVAddrCSRData ppc_vaddr_csrData;
Bit #(5) fflags;
Bool will_dirty_fpu_state; // True means 2'b11 will be written to FS
Data mstatus; // For Fpu ops, since [MX] bit may have changed
// Trap updates
Bit #(2) prv;
Addr tvec;
Data status;
Data cause;
Data epc;
} Trace_Data2
deriving (Bits, Eq, FShow);
// ================================================================
endpackage

View File

@@ -0,0 +1,252 @@
// Copyright (c) 2020 Bluespec, Inc. All Rights Reserved.
package Trace_Data2_to_Trace_Data;
// ================================================================
// This package defines a module to transform a stream of Trace_Data2
// to a stream of (serial_num, Trace_Data)
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
// ================================================================
// Project riscy-ooo imports (for fields in Trace_Data2)
import Types :: *;
import ProcTypes :: *;
import ReorderBuffer :: *; // for PPCVAddrCSRData
// ================================================================
// Project Toooba imports
import ISA_Decls :: *;
import TV_Info :: *;
import Trace_Data2 :: *;
// ================================================================
interface Trace_Data2_to_Trace_Data_IFC;
// From Toooba's CommitStage
interface Put #(Trace_Data2) in;
// To Trace Encoder
interface Get #(Tuple2 #(Bit #(64), Trace_Data)) out;
endinterface
// ================================================================
(* synthesize *)
module mkTrace_Data2_to_Trace_Data (Trace_Data2_to_Trace_Data_IFC);
Integer verbosity = 1; // for debugging
// Input stream
FIFOF #(Trace_Data2) f_in <- mkFIFOF;
// Output stream
FIFOF #(Tuple2 #(Bit #(64), Trace_Data)) f_out <- mkFIFOF;
// ================================================================
// Transformer: Trace_Data2 -> (serial_num, Trace_Data)
function ActionValue #(Tuple2 #(Bit #(64), Trace_Data)) fav_td2_to_td (Trace_Data2 td2);
actionvalue
let serial_num = td2.serial_num;
Trace_Data td = ?;
ISize isize = ((td2.orig_inst [1:0] == 2'b11) ? ISIZE32BIT : ISIZE16BIT);
Addr fall_thru_PC = td2.pc + ((td2.orig_inst [1:0] == 2'b11) ? 4 : 2);
Bit #(3) st_funct3 = (td2.store_data_BE [7] ? 3'b_011 // Doubleword
: (td2.store_data_BE [3] ? 3'b_010 // Word
: (td2.store_data_BE [1] ? 3'b_001 // HalfWord
: 3'b000))); // Byte
Bit #(5) gpr_rd = 0;
if (td2.dst matches tagged Valid (tagged Gpr .r)) gpr_rd = r;
if (serial_num == 0)
td = mkTrace_RESET;
else if (td2.maybe_csr_upd matches tagged Valid { .csr_addr, .csr_value })
td = mkTrace_CSR_WRITE (csr_addr, csr_value);
else if (isValid (td2.trap))
td = mkTrace_TRAP (td2.tvec,
isize,
td2.orig_inst,
td2.prv,
td2.status,
td2.cause,
td2.epc,
td2.tval);
else if (td2.ppc_vaddr_csrData matches tagged PPC .target_addr
&&& (td2.iType == Br))
td = mkTrace_OTHER (target_addr, isize, td2.orig_inst);
else if (td2.ppc_vaddr_csrData matches tagged PPC .target_addr
&&& ( (td2.iType == J)
|| (td2.iType == Jr)))
td = mkTrace_I_RD (target_addr,
isize,
td2.orig_inst,
gpr_rd,
td2.dst_data); // return-pc
else if ( (td2.iType == Alu)
|| (td2.iType == Auipc))
td = mkTrace_I_RD (fall_thru_PC,
isize,
td2.orig_inst,
gpr_rd,
td2.dst_data); // rd_val
else if (td2.dst matches tagged Valid (tagged Fpu .fpr_rd)
&&& (td2.iType == Fpu))
td = mkTrace_F_FRD (fall_thru_PC,
isize,
td2.orig_inst,
fpr_rd,
td2.dst_data, // rdval
td2.fflags,
td2.mstatus); // [FX] updated
else if (td2.iType == Fpu)
td = mkTrace_F_GRD (fall_thru_PC,
isize,
td2.orig_inst,
gpr_rd,
td2.dst_data, // rdval
td2.fflags,
td2.mstatus); // [FX] updated
else if (td2.ppc_vaddr_csrData matches tagged VAddr .eaddr
&&& td2.dst matches tagged Valid (tagged Fpu .fpr_rd)
&&& (td2.iType == Ld))
td = mkTrace_F_LOAD (fall_thru_PC,
isize,
td2.orig_inst,
fpr_rd,
td2.dst_data, // rd_val
eaddr,
td2.mstatus);
else if (td2.ppc_vaddr_csrData matches tagged VAddr .eaddr
&&& (td2.iType == Ld))
td = mkTrace_I_LOAD (fall_thru_PC,
isize,
td2.orig_inst,
gpr_rd,
td2.dst_data, // rd_val
eaddr);
else if (td2.ppc_vaddr_csrData matches tagged VAddr .eaddr
&&& (td2.iType == St))
td = mkTrace_I_STORE (fall_thru_PC,
st_funct3,
isize,
td2.orig_inst,
td2.store_data, // rs2_val
eaddr);
else if (td2.ppc_vaddr_csrData matches tagged CSRData .csr_data
&&& (td2.iType == Csr))
begin
Bit #(3) funct3 = td2.orig_inst [14:12];
Bit #(5) rs1_or_imm = td2.orig_inst [19:15];
Bool csr_valid = False;
CSR_Addr csr_addr = 0;
if (td2.csr matches tagged Valid .c) begin
csr_addr = pack (c);
csr_valid = ( (funct3 [1:0] == 2'b01) // CSRRW, CSRRWI
|| ( ( (funct3 [1:0] == 2'b10) // CSRRS, CSRRSI
|| (funct3 [1:0] == 2'b11)) // CSRRC, CSRRCI
&& (rs1_or_imm != 0)));
end
td = mkTrace_CSRRX (fall_thru_PC,
isize,
td2.orig_inst,
gpr_rd,
td2.dst_data, // rdval
csr_valid,
csr_addr,
csr_data,
// For CSR writes to FFLAGS/FRM/FCSR, also changes MSTATUS
td2.will_dirty_fpu_state,
td2.mstatus);
end
else if ( (td2.iType == Mret)
|| (td2.iType == Sret))
td = mkTrace_RET (td2.pc, isize, td2.orig_inst, td2.prv, td2.status);
else if ( (td2.iType == Fence)
|| (td2.iType == FenceI)
|| (td2.iType == SFence)
|| (td2.iType == Ecall) // Handled by TRAP above?
|| (td2.iType == Ebreak)) // Handled by TRAP above?
td = mkTrace_OTHER (fall_thru_PC, isize, td2.orig_inst);
else if (td2.ppc_vaddr_csrData matches tagged VAddr .eaddr
&&& ( (td2.iType == Amo)
|| (td2.iType == Lr)
|| (td2.iType == Sc)))
td = mkTrace_AMO (fall_thru_PC,
st_funct3,
isize,
td2.orig_inst,
gpr_rd,
td2.dst_data, // rd_val
td2.store_data, // rs2_val
eaddr);
else if ( (td2.iType == Unsupported)
|| (td2.iType == Nop)
|| (td2.iType == Interrupt))
td = mkTrace_OTHER (fall_thru_PC, isize, td2.orig_inst);
else begin
if (verbosity > 0) begin
$display (" fav_td2_to_td: TBD: Unknown iType: Using mkTrace_OTHER for now");
$display (" ", fshow (td2));
end
td = mkTrace_OTHER (fall_thru_PC, isize, td2.orig_inst);
end
return tuple2 (serial_num, td);
endactionvalue
endfunction
// ================================================================
// RULES
rule rl_td2_to_td;
Trace_Data2 td2 <- pop (f_in);
if (verbosity > 1)
$display ("%0d: %m.rl_td2_to_td: serial_num:%0d PC:0x%0h instr:0x%08h",
cur_cycle, td2.serial_num, td2.pc, td2.orig_inst,
" iType:", fshow (td2.iType));
match { .serial_num, .td } <- fav_td2_to_td (td2);
f_out.enq (tuple2 (serial_num, td));
endrule
// ================================================================
// INTERFACE
interface in = toPut (f_in);
interface out = toGet (f_out);
endmodule
// ================================================================
endpackage

View File

@@ -58,18 +58,18 @@ module mkDM_Abstract_Commands (DM_Abstract_Commands_IFC);
Reg #(Bool) rg_start_reg_access <- mkReg (False);
// FIFOs for request/response to access GPRs
FIFOF #(DM_CPU_Req #(5, XLEN)) f_hart0_gpr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(XLEN)) f_hart0_gpr_rsps <- mkFIFOF1;
FIFOF #(DM_CPU_Req #(5, XLEN)) f_hart0_gpr_reqs <- mkFIFOF;
FIFOF #(DM_CPU_Rsp #(XLEN)) f_hart0_gpr_rsps <- mkFIFOF;
// FIFOs for request/response to access FPRs
`ifdef ISA_F
FIFOF #(DM_CPU_Req #(5, FLEN)) f_hart0_fpr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(FLEN)) f_hart0_fpr_rsps <- mkFIFOF1;
FIFOF #(DM_CPU_Req #(5, FLEN)) f_hart0_fpr_reqs <- mkFIFOF;
FIFOF #(DM_CPU_Rsp #(FLEN)) f_hart0_fpr_rsps <- mkFIFOF;
`endif
// FIFOs for request/response to access CSRs
FIFOF #(DM_CPU_Req #(12, XLEN)) f_hart0_csr_reqs <- mkFIFOF1;
FIFOF #(DM_CPU_Rsp #(XLEN)) f_hart0_csr_rsps <- mkFIFOF1;
FIFOF #(DM_CPU_Req #(12, XLEN)) f_hart0_csr_reqs <- mkFIFOF;
FIFOF #(DM_CPU_Rsp #(XLEN)) f_hart0_csr_rsps <- mkFIFOF;
// ----------------------------------------------------------------
// rg_data0
@@ -89,15 +89,18 @@ module mkDM_Abstract_Commands (DM_Abstract_Commands_IFC);
Reg #(Bool) rg_abstractcs_busy <- mkRegU;
Reg #(DM_abstractcs_cmderr) rg_abstractcs_cmderr <- mkRegU;
Bit #(5) abstractcs_progsize = 0;
Bit #(5) abstractcs_datacount = 0;
// Size of program buffer, in 32b words
Bit #(5) abstractcs_progbufsize = 0;
// Number of data registers implemented (rg_data0, rg_data1)
Bit #(4) abstractcs_datacount = ((xlen == 32) ? 1 : 2);
DM_Word virt_rg_abstractcs = {3'b0,
abstractcs_progsize,
abstractcs_progbufsize,
11'b0,
pack (rg_abstractcs_busy),
1'b0,
pack (rg_abstractcs_cmderr),
3'b0,
4'b0,
abstractcs_datacount};
function Action fa_rg_abstractcs_write (DM_Word dm_word);

View File

@@ -9,9 +9,15 @@ package DM_Run_Control;
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ----------------
// Other library imports
import Cur_Cycle :: *;
import GetPut_Aux :: *;
// ================================================================
// Project imports
@@ -33,13 +39,14 @@ interface DM_Run_Control_IFC;
// ----------------
// Facing a hart: reset and run-control
interface Get #(Token) hart0_get_reset_req;
interface Client #(Bool, Bool) hart0_reset_client;
interface Client #(Bool, Bool) hart0_client_run_halt;
interface Get #(Bit #(4)) hart0_get_other_req;
// ----------------
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
interface Get #(Token) get_ndm_reset_req;
// Bool indicates 'running' hart state.
interface Client #(Bool, Bool) ndm_reset_client;
endinterface
// ================================================================
@@ -52,7 +59,8 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
// ----------------------------------------------------------------
// NDM Reset
FIFOF #(Token) f_ndm_reset_reqs <- mkFIFOF;
FIFOF #(Bool) f_ndm_reset_reqs <- mkFIFOF;
FIFOF #(Bool) f_ndm_reset_rsps <- mkFIFOF;
// ----------------------------------------------------------------
// Hart0 run control
@@ -60,7 +68,8 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
Reg #(Bool) rg_hart0_running <- mkRegU;
// Reset requests to hart
FIFOF #(Token) f_hart0_reset_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart0_reset_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart0_reset_rsps <- mkFIFOF;
// Run/halt requests to hart and responses
FIFOF #(Bool) f_hart0_run_halt_reqs <- mkFIFOF;
@@ -81,7 +90,13 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
// 'anyXX' = 'allXX'
// 'allrunning' = NOT 'allhalted'
Reg#(Bool) rg_dmstatus_allresumeack <- mkRegU;
Bool dmstatus_impebreak = False;
Reg #(Bool) rg_hart0_hasreset <- mkRegU;
Bool dmstatus_allhavereset = rg_hart0_hasreset;
Bool dmstatus_anyhavereset = rg_hart0_hasreset;
Reg #(Bool) rg_dmstatus_allresumeack <- mkRegU;
Bool dmstatus_allresumeack = rg_dmstatus_allresumeack;
Bool dmstatus_anyresumeack = rg_dmstatus_allresumeack;
@@ -89,8 +104,9 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
Bool dmstatus_allnonexistent = False;
Bool dmstatus_anynonexistent = dmstatus_allnonexistent;
Bool dmstatus_allunavail = False;
Bool dmstatus_anyunavail = dmstatus_allunavail;
Reg #(Bool) rg_dmstatus_allunavail <- mkReg (False);
Bool dmstatus_allunavail = rg_dmstatus_allunavail;
Bool dmstatus_anyunavail = rg_dmstatus_allunavail;
Bool dmstatus_allrunning = rg_hart0_running;
Bool dmstatus_anyrunning = dmstatus_allrunning;
@@ -98,7 +114,11 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
Bool dmstatus_allhalted = (! rg_hart0_running);
Bool dmstatus_anyhalted = dmstatus_allhalted;
DM_Word virt_rg_dmstatus = {14'b0,
DM_Word virt_rg_dmstatus = {9'b0,
pack (dmstatus_impebreak),
2'b0,
pack (dmstatus_allhavereset),
pack (dmstatus_anyhavereset),
pack (dmstatus_allresumeack),
pack (dmstatus_anyresumeack),
pack (dmstatus_allnonexistent),
@@ -151,19 +171,17 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
// Debug Module reset
if (! dmactive) begin
// Reset the DM module itself
$display ("DM_Run_Control.write: dmcontrol 0x%08h (dmactive=0): resetting Debug Module",
dm_word);
$display ("%0d: %m.dmcontrol_write 0x%08h (dmactive=0): resetting Debug Module",
cur_cycle, dm_word);
// Error-checking
if (ndmreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" WARNING: DM_Run_Control: dmcontrol_write 0x%08h:", dm_word);
$display (" [1] (ndmreset) and [0] (dmactive) both asserted");
$display (" dmactive has priority; ignoring ndmreset");
end
if (hartreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" WARNING: DM_Run_Control: dmcontrol_write 0x%08h:", dm_word);
$display (" [29] (hartreset) and [0] (dmactive) both asserted");
$display (" dmactive has priority; ignoring hartreset");
end
@@ -172,63 +190,84 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
noAction;
end
// Platform reset (non-Debug Module)
else if (ndmreset) begin
$display ("DM_Run_Control.write: dmcontrol 0x%08h: ndmreset=1: resetting platform",
dm_word);
f_ndm_reset_reqs.enq (?);
rg_hart0_running <= True; // Must be same as run/halt state of CPU after hart_reset!
// Ignore if NDM reset is in progress
else if (rg_dmstatus_allunavail) begin
$display ("%0d: %m.dmcontrol_write 0x%0h: ndm reset in progress; ignoring this write",
cur_cycle, dm_word);
end
// Non-Debug-Module reset (platform reset) posedge: ignore
else if ((! rg_dmcontrol_ndmreset) && ndmreset) begin
if (verbosity != 0)
$display ("%0d: %m.dmcontrol_write 0x%08h: ndmreset: 0->1: ignoring",
cur_cycle, dm_word);
end
// Non-Debug-Module reset (platform reset) negedge: do it
else if (rg_dmcontrol_ndmreset && (! ndmreset)) begin
Bool running = (! haltreq);
if (verbosity != 0) begin
$display ("%0d: %m.dmcontrol_write 0x%08h: ndmreset: 1->0: resetting platform",
cur_cycle, dm_word);
$display (" Requested 'running' state = ", fshow (running));
end
f_ndm_reset_reqs.enq (running);
rg_dmstatus_allunavail <= True;
// Error-checking
if (hartreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" Both ndmreset (bit 1) and hartreset (bit 29) are asserted");
$display (" WARNING: %m.dmcontrol_write 0x%08h:", dm_word);
$display (" Both ndmreset [1] and hartreset [29] are asserted");
$display (" ndmreset has priority; ignoring hartreset");
end
end
else begin
// Hart reset
else if (hartreset) begin
Bool running = (! haltreq);
f_hart0_reset_reqs.enq (running);
rg_hart0_hasreset <= True;
// Deassert platform reset
if ((verbosity != 0) && rg_dmcontrol_ndmreset)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing ndmreset", dm_word);
// Hart reset
if (hartreset) begin
if (verbosity != 0)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: hartreset=1: resetting hart",
dm_word);
f_hart0_reset_reqs.enq (?);
rg_hart0_running <= True; // Must be same as run/halt state of CPU after hart_reset!
if (verbosity != 0) begin
$display ("%0d: %m.dmcontrol_write 0x%08h: hartreset=1: resetting hart",
cur_cycle, dm_word);
$display (" Requested 'running' state = ", fshow (running));
end
else begin
// Deassert hart reset
if ((verbosity != 0) && rg_dmcontrol_hartreset)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing hartreset", dm_word);
end
if (hasel)
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: 'hasel' is not supported",
dm_word);
// run/halt commands
else begin
// Deassert hart reset
if ((verbosity != 0) && rg_dmcontrol_hartreset)
$display ("%0d: %m.dmcontrol_write 0x%08h: clearing hartreset",
cur_cycle, dm_word);
if (hartsel != 0)
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: hartsel 0x%0h not supported",
dm_word, hartsel);
if (hasel)
$display ("%0d:ERROR: %m.dmcontrol_write 0x%08h: hasel is not supported",
cur_cycle, dm_word);
if (haltreq && resumereq) begin
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: haltreq=1 and resumereq=1",
dm_word);
$display (" This behavior is 'undefined' in the spec; ignoring");
end
// Resume hart(s) if not running
else if (resumereq && (! rg_hart0_running)) begin
f_hart0_run_halt_reqs.enq (True);
rg_dmstatus_allresumeack <= False;
$display ("DM_Run_Control.write: hart0 resume request");
end
// Halt hart(s)
else if (haltreq && rg_hart0_running) begin
f_hart0_run_halt_reqs.enq (False);
$display ("DM_Run_Control.write: hart0 halt request");
end
if (hartsel != 0)
$display ("%0d:ERROR: %m.dmcontrol_write 0x%08h: hartsel 0x%0h not supported",
cur_cycle, dm_word, hartsel);
if (haltreq && resumereq) begin
$display ("%0d:ERROR: %m.dmcontrol_write 0x%08h: haltreq=1 and resumereq=1",
cur_cycle, dm_word);
$display (" This behavior is 'undefined' in the spec; ignoring");
end
// Resume hart(s) if not running
else if (resumereq && (! rg_hart0_running)) begin
f_hart0_run_halt_reqs.enq (True);
rg_dmstatus_allresumeack <= False;
$display ("%0d: %m.dmcontrol_write: hart0 resume request", cur_cycle);
end
// Halt hart(s)
else if (haltreq && rg_hart0_running) begin
f_hart0_run_halt_reqs.enq (False);
$display ("%0d: %m.dmcontrol_write: hart0 halt request", cur_cycle);
end
end
endaction
@@ -250,19 +289,37 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
Reg #(Bit #(4)) rg_verbosity <- mkRegU;
// ----------------------------------------------------------------
// System responses
rule rl_hart0_run_rsp;
let x = f_hart0_run_halt_rsps.first;
f_hart0_run_halt_rsps.deq;
// Response from system for hart0 reset
rule rl_hart0_reset_rsp;
Bool running <- pop (f_hart0_reset_rsps);
rg_hart0_hasreset <= False;
rg_hart0_running <= running;
rg_hart0_running <= x;
if (x) begin
if (verbosity != 0)
$display ("%0d: %m.rl_hart0_reset_rsp: hart running = ", cur_cycle, fshow (running));
endrule
// Response from system for NDM reset
rule rl_ndm_reset_rsp;
Bool running <- pop (f_ndm_reset_rsps);
rg_hart0_running <= running;
rg_dmstatus_allunavail <= False;
// if (verbosity != 0) TODO: UNCOMMENT AFTER DEBUGGING
$display ("%0d: %m.rl_ndm_reset_rsp: hart running = ", cur_cycle, fshow (running));
endrule
// Response from system for run/halt request
rule rl_hart0_run_rsp (! f_ndm_reset_rsps.notEmpty);
let running <- pop (f_hart0_run_halt_rsps);
rg_hart0_running <= running;
if (running)
rg_dmstatus_allresumeack <= True;
$display ("DM_Run_Control: hart0 running");
end
else begin
$display ("DM_Run_Control: hart0 halted");
end
if (verbosity != 0)
$display ("%0d: %m.rl_hart0_run_rsp: 'running' = ", cur_cycle, fshow (running));
endrule
// ----------------------------------------------------------------
@@ -274,9 +331,12 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
method Action reset;
f_ndm_reset_reqs.clear;
f_ndm_reset_rsps.clear;
rg_hart0_running <= True; // Must be same as run/halt state of CPU after hart_reset!
f_hart0_reset_reqs.clear;
f_hart0_reset_rsps.clear;
rg_hart0_running <= True; // Safe approximation of whether the CPU is running or not
f_hart0_run_halt_reqs.clear;
f_hart0_run_halt_rsps.clear;
@@ -285,12 +345,14 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
rg_dmcontrol_ndmreset <= False;
rg_dmcontrol_dmactive <= True; // DM module is now active
rg_hart0_hasreset <= False;
rg_dmstatus_allresumeack <= False;
rg_dmstatus_allunavail <= False; // NDM not in progress
rg_verbosity <= 0;
if (verbosity != 0)
$display ("DM_Run_Control: reset");
$display ("%0d: %m.reset", cur_cycle);
endmethod
// ----------------
@@ -307,7 +369,7 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
endcase;
if (verbosity != 0)
$display ("DM_Run_Control.av_read: [", fshow_dm_addr (dm_addr), "] => 0x%08h", dm_word);
$display ("%0d: %m.av_read: [", cur_cycle, fshow_dm_addr (dm_addr), "] => 0x%08h", dm_word);
return dm_word;
endactionvalue
@@ -316,7 +378,7 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
if (verbosity != 0)
$display ("DM_Run_Control.write: [", fshow_dm_addr (dm_addr), "] <= 0x%08h", dm_word);
$display ("%0d: %m.write: [", cur_cycle, fshow_dm_addr (dm_addr), "] <= 0x%08h", dm_word);
case (dm_addr)
dm_addr_dmcontrol: fa_rg_dmcontrol_write (dm_word);
@@ -331,13 +393,13 @@ module mkDM_Run_Control (DM_Run_Control_IFC);
// ----------------
// Facing Hart: Reset, Run-control, etc.
interface Get hart0_get_reset_req = toGet (f_hart0_reset_reqs);
interface Client hart0_reset_client = toGPClient (f_hart0_reset_reqs, f_hart0_reset_rsps);
interface Client hart0_client_run_halt = toGPClient (f_hart0_run_halt_reqs, f_hart0_run_halt_rsps);
interface Get hart0_get_other_req = toGet (f_hart0_other_reqs);
// ----------------
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
interface Get get_ndm_reset_req = toGet (f_ndm_reset_reqs);
interface Client ndm_reset_client = toGPClient (f_ndm_reset_reqs, f_ndm_reset_rsps);
endmodule
// ================================================================

View File

@@ -179,7 +179,7 @@ module mkDM_System_Bus (DM_System_Bus_IFC);
// ----------------------------------------------------------------
// Interface to memory fabric
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;
// ----------------------------------------------------------------
// System Bus state
@@ -318,8 +318,7 @@ module mkDM_System_Bus (DM_System_Bus_IFC);
awuser: fabric_default_user};
master_xactor.i_wr_addr.enq (wra);
let wrd = AXI4_Wr_Data {wid: fabric_default_id,
wdata: fabric_data,
let wrd = AXI4_Wr_Data {wdata: fabric_data,
wstrb: fabric_strb,
wlast: True,
wuser: fabric_default_user};

View File

@@ -96,7 +96,7 @@ interface Debug_Module_IFC;
// This section replicated for additional harts.
// Reset and run-control
interface Get #(Token) hart0_get_reset_req;
interface Client #(Bool, Bool) hart0_reset_client;
interface Client #(Bool, Bool) hart0_client_run_halt;
interface Get #(Bit #(4)) hart0_get_other_req;
@@ -115,7 +115,8 @@ interface Debug_Module_IFC;
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get #(Token) get_ndm_reset_req;
// Bool indicates 'running' hart state.
interface Client #(Bool, Bool) ndm_reset_client;
// Read/Write RISC-V memory
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
@@ -126,6 +127,9 @@ endinterface
(* synthesize *)
module mkDebug_Module (Debug_Module_IFC);
// Local verbosity: 0 = quiet; 1 = print DMI transactions
Integer verbosity = 0;
// The three parts
DM_Run_Control_IFC dm_run_control <- mkDM_Run_Control;
DM_Abstract_Commands_IFC dm_abstract_commands <- mkDM_Abstract_Commands;
@@ -152,6 +156,9 @@ module mkDebug_Module (Debug_Module_IFC);
interface DMI dmi;
method Action read_addr (DM_Addr dm_addr);
f_read_addr.enq(dm_addr);
if (verbosity != 0)
$display ("%0d: %m.DMI read: dm_addr 0x%0h", cur_cycle, dm_addr);
endmethod
method ActionValue #(DM_Word) read_data;
@@ -209,6 +216,10 @@ module mkDebug_Module (Debug_Module_IFC);
dm_word = 0;
end
if (verbosity != 0)
$display ("%0d: %m.DMI read response: dm_addr 0x%0h, dm_word 0x%0h",
cur_cycle, dm_addr, dm_word);
return dm_word;
endmethod
@@ -261,6 +272,10 @@ module mkDebug_Module (Debug_Module_IFC);
// TODO: set error status?
noAction;
end
if (verbosity != 0)
$display ("%0d: %m.DMI write: dm_addr 0x%0h, dm_word 0x%0h",
cur_cycle, dm_addr, dm_word);
endmethod
endinterface
@@ -268,7 +283,7 @@ module mkDebug_Module (Debug_Module_IFC);
// Facing CPU/hart0
// Reset and run-control
interface Get hart0_get_reset_req = dm_run_control.hart0_get_reset_req;
interface Client hart0_reset_client = dm_run_control.hart0_reset_client;
interface Client hart0_client_run_halt = dm_run_control.hart0_client_run_halt;
interface Get hart0_get_other_req = dm_run_control.hart0_get_other_req;
@@ -287,7 +302,7 @@ module mkDebug_Module (Debug_Module_IFC);
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get get_ndm_reset_req = dm_run_control.get_ndm_reset_req;
interface Client ndm_reset_client = dm_run_control.ndm_reset_client;
// Read/Write RISC-V memory
interface AXI4_Master_IFC master = dm_system_bus.master;

View File

@@ -1,3 +1,206 @@
'Debug_Module' implements a Debug Module for RISC-V processors in
accordance with the RISC-V standard "External Debug Support" spec:
RISC-V External Debug Support
Version 0.13.2
d5029366d59e8563c08b6b9435f82573b603e48e
Fri Mar 22 09:06:04 2019 -0700
Note: the spec is independent of any particular RISC-V CPU
implementation. It just specifies the standard registers in the Debug
Module that can be read and written by an external debugger (such as
GDB). It specifies the address map of these registers, and the
semantics, i.e., what happens when one reads or writes these
registers. The spec does not say anything about how this spec is
implemented.
Please see comments in Debug_Module.bsv for more details on our
implementation of the Debug Module spec. This implementation is also
not specific to any particular CPU implementation. We use it in
multiple Bluespec RISC-V CPU implementations, and it could be used
with other CPU implementations as well.
// ================================================================
What follows is a concise cheat-sheet for the registers in the Debug Module.
DM_Addr dm_addr_data0 = 'h04;
DM_Addr dm_addr_data1 = 'h05;
DM_Addr dm_addr_data2 = 'h06;
DM_Addr dm_addr_data3 = 'h07;
DM_Addr dm_addr_data4 = 'h08;
DM_Addr dm_addr_data5 = 'h09;
DM_Addr dm_addr_data6 = 'h0a;
DM_Addr dm_addr_data7 = 'h0b;
DM_Addr dm_addr_data8 = 'h0c;
DM_Addr dm_addr_data9 = 'h0d;
DM_Addr dm_addr_data10 = 'h0d;
DM_Addr dm_addr_data11 = 'h0f;
// ----------------
// Run Control
DM_Addr dm_addr_dmcontrol = 'h10;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | 0 | | | | | 0 0 | | | |dmactive
| | | | | | | | | | | |ndmreset
| | | | | | | | | | |clrresethaltreq
| | | | | | | | | |setresethaltreq
| | | | | | | |-----------10-----------|hartselhi
| | | | | |------------10--------------|hartsello
| | | | |hasel
| | | | 0: Single hart selected (hartsel)
| | | | 1: Multiple harts selected (hartsel + hart array mask)
| | | |ackhavereset
| | |hartreset
| |resumereq
|haltreq
DM_Addr dm_addr_dmstatus = 'h11;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
0 0 0 0 0 0 0 0 | | 0 0 | | | | | | | | | | | | | | | | |--4--|version
| | | | | | | | | | | | | | | | | | | 0: no DM present
| | | | | | | | | | | | | | | | | | | 1: DM v011
| | | | | | | | | | | | | | | | | | | 2: DM v013
| | | | | | | | | | | | | | | | | | | 15: DM vUnknown
| | | | | | | | | | | | | | | | | |confstrptrvalid
| | | | | | | | | | | | | | | | |hasresethaltreq
| | | | | | | | | | | | | | | |authbusy
| | | | | | | | | | | | | | |authenticated
| | | | | | | | | | | | | |anyhalted
| | | | | | | | | | | | |allhalted
| | | | | | | | | | | |anyrunning
| | | | | | | | | | |allrunning
| | | | | | | | | |anyunavail
| | | | | | | | |allunavail
| | | | | | | |anynonexistent
| | | | | | |allnonexistent
| | | | | |anyresumeack
| | | | |allresumeack
| | | |anyhavereset
| | |allhavereset
| |impebreak
| 0 No implicit EBREAK at end of PB
| 1 Implicit EBREAK at end of PB
DM_Addr dm_addr_hartinfo = 'h12;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
0 0 0 0 0 0 0 0 | | 0 0 0 | | | |-----------12-----------|dataaddr
| | | |----4---|datasize
|----4---|nscratch |dataaccess
DM_Addr dm_addr_haltsum1 = 'h13;
DM_Addr dm_addr_hawindowsel = 'h14;
DM_Addr dm_addr_hawindow = 'h15;
// ----------------
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
DM_Addr dm_addr_abstractcs = 'h16;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
0 0 0 | | 0 0 0 0 0 0 0 0 0 0 0 | 0 | | 0 0 0 0 |--4--|datacount
0 0 0 |-----5------|progbufsize |busy |-3-|cmderr
DM_Addr dm_addr_command = 'h17;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| 0 | | | | | | |-----------------16------------------|regno
| | | | | | | | 0x0000-0x0FFF CSRs (dpc => PC)
| | | | | | | | 0x1000-0x101F GPRs
| | | | | | | | 0x1020-0x103F Floating Point Regs
| | | | | | | | 0xC000-0xFFFF Reserved
| | | | | | |write
| | | | | | 0: specified reg -> arg0 of data
| | | | | | 1: specified reg <- arg0 of data
| | | | | |transfer
| | | | | 0 Don't do the 'write' op
| | | | | 1 Do the 'write' op
| | | | | Allows exec of PB without valid vals in 'size' and 'regno'
| | | | |postexec
| | | | 1 exec Program Buffer exactly once after the xfer
| | | |aarpostincrement
| |--3--|aarsize
| 2 Lowest 32b of reg
| 3 Lowest 64b of reg
| 4 Lowest 128b of reg
|---------8-----------|cmdtype
0 ACCESS_REG
1 QUICK_ACCESS
DM_Addr dm_addr_abstractauto = 'h18;
DM_Addr dm_addr_confstrptr0 = 'h19;
DM_Addr dm_addr_confstrptr1 = 'h1a;
DM_Addr dm_addr_confstrptr2 = 'h1b;
DM_Addr dm_addr_confstrptr3 = 'h1c;
DM_Addr dm_addr_nextdm = 'h1d;
DM_Addr dm_addr_progbuf0 = 'h20;
DM_Addr dm_addr_progbuf1 = 'h21;
DM_Addr dm_addr_progbuf2 = 'h22;
DM_Addr dm_addr_progbuf3 = 'h23;
DM_Addr dm_addr_progbuf4 = 'h24;
DM_Addr dm_addr_progbuf5 = 'h25;
DM_Addr dm_addr_progbuf6 = 'h26;
DM_Addr dm_addr_progbuf7 = 'h27;
DM_Addr dm_addr_progbuf8 = 'h28;
DM_Addr dm_addr_progbuf9 = 'h29;
DM_Addr dm_addr_progbuf10 = 'h2a;
DM_Addr dm_addr_progbuf11 = 'h2b;
DM_Addr dm_addr_progbuf12 = 'h2c;
DM_Addr dm_addr_progbuf13 = 'h2d;
DM_Addr dm_addr_progbuf14 = 'h2e;
DM_Addr dm_addr_progbuf15 = 'h2f;
DM_Addr dm_addr_authdata = 'h30;
DM_Addr dm_addr_haltsum2 = 'h34;
DM_Addr dm_addr_haltsum3 = 'h35;
DM_Addr dm_addr_sbaddress3 = 'h37;
// ----------------
// System Bus access (read/write RISC-V memory/devices)
DM_Addr dm_addr_sbcs = 'h38;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | 0 0 0 0 0 0 | | | | | | | | | | | | | | | |sbaccess8
| | | | | | | | | | | | | | | | |sbaccess16
| | | | | | | | | | | | | | | |sbaccess32
| | | | | | | | | | | | | | |sbaccess64
| | | | | | | | | | | | | |sbaccess128
| | | | | | | | | | | |-----7-------|sbasize
| | | | | | | | | |--3--|sberror
| | | | | | | | | 0: no bus err
| | | | | | | | | 1: timeout
| | | | | | | | | 2: bad addr
| | | | | | | | | 3: alignment err
| | | | | | | | | 4: unsupported size
| | | | | | | | | 7: other
| | | | | | | | |sbreadondata: read on sbdata0 triggers sb read
| | | | | | | |sbautoincrement
| | | | | |--3--|sbaccess
| | | | | 0:8b, 1:16b, 2:32b, 3:64b, 4:128b
| | | | |sbreadonaddr
| | | | 1 Every write to sbaddress0 triggers sb read at new addr
| | | |sbbusy
| | |sbbusyerror
|--3--|sbversion
0: System Bus interface spec version < 2018-01-01
1: This System Bus interface spec version
DM_Addr dm_addr_sbaddress0 = 'h39;
DM_Addr dm_addr_sbaddress1 = 'h3a;
DM_Addr dm_addr_sbaddress2 = 'h3b;
DM_Addr dm_addr_sbdata0 = 'h3c;
DM_Addr dm_addr_sbdata1 = 'h3d;
DM_Addr dm_addr_sbdata2 = 'h3e;
DM_Addr dm_addr_sbdata3 = 'h3f;
DM_Addr dm_addr_haltsum0 = 'h40;
// ================================================================
// ================================================================
// OLDER VERSIONS OF THE DEBUG MODULE SPEC
// ================================================================
// ================================================================
'Debug_Module' implements a Debug Module for RISC-V processors in
accordance with the RISC-V standard "External Debug Support" spec:

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
// Copyright (c) 2013-2020 Bluespec, Inc. All Rights Reserved
// ================================================================
// ISA defs for UC Berkeley RISC V
@@ -361,6 +361,20 @@ RegName reg_s10 = 26; RegName reg_s11 = 27;
RegName reg_t3 = 28; RegName reg_t4 = 29; RegName reg_t5 = 30; RegName reg_t6 = 31;
// ----------------
// Is 'r' a standard register for PC save/restore on call/return?
// This function is used in branch-predictors for managing the return-address stack.
function Bool fn_reg_is_link (RegName r);
return ((r == x1) || (r == x5));
endfunction
// ================================================================
// Kinds of memory access (excluding AMOs)
typedef enum { Access_RWX_R, Access_RWX_W, Access_RWX_X } Access_RWX
deriving (Eq, Bits, FShow);
// ================================================================
// Data sizes for LOAD/STORE

View File

@@ -33,9 +33,9 @@ typedef enum {// These are not from instruction flow and do not have a PC or ins
// These are from instruction flow and have a PC and instruction
TRACE_OTHER,
TRACE_I_RD, TRACE_F_RD,
TRACE_I_RD, TRACE_F_GRD, TRACE_F_FRD,
TRACE_I_LOAD, TRACE_F_LOAD,
TRACE_STORE,
TRACE_I_STORE, TRACE_F_STORE,
TRACE_AMO,
TRACE_TRAP,
TRACE_RET,
@@ -56,6 +56,9 @@ typedef struct {
WordXL word2;
Bit #(64) word3; // Wider than WordXL because can contain paddr (in RV32, paddr can be 34 bits)
WordXL word4;
`ifdef ISA_F
WordFL word5;
`endif
} Trace_Data
deriving (Bits);
@@ -139,19 +142,39 @@ function Trace_Data mkTrace_I_RD (WordXL pc, ISize isize, Bit #(32) instr, RegNa
return td;
endfunction
// F_RD
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval
function Trace_Data mkTrace_F_RD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval);
`ifdef ISA_F
// F_FRD
// op pc instr_sz instr rd word1 word2 word3 word4 word5
// x x x x x fflags mstatus rdval
function Trace_Data mkTrace_F_FRD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordFL rdval, Bit#(5) fflags, WordXL mstatus);
Trace_Data td = ?;
td.op = TRACE_F_RD;
td.op = TRACE_F_FRD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word2 = extend (fflags);
td.word4 = mstatus;
td.word5 = rdval;
return td;
endfunction
// F_GRD
// op pc instr_sz instr rd word1 word2 word3 word4 word5
// x x x x x rdval fflags mstatus
function Trace_Data mkTrace_F_GRD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval, Bit#(5) fflags, WordXL mstatus);
Trace_Data td = ?;
td.op = TRACE_F_GRD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word2 = extend (fflags);
td.word4 = mstatus;
return td;
endfunction
`endif
// I_LOAD
// op pc instr_sz instr rd word1 word2 word3 word4
@@ -168,39 +191,69 @@ function Trace_Data mkTrace_I_LOAD (WordXL pc, ISize isize, Bit #(32) instr, Reg
return td;
endfunction
// F_LOAD
// I_STORE
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval eaddr
function Trace_Data mkTrace_F_LOAD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval, WordXL eaddr);
// x x x x funct3 stval eaddr
function Trace_Data mkTrace_I_STORE (WordXL pc, Bit #(3) funct3, ISize isize, Bit #(32) instr, WordXL stval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_I_STORE;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.word1 = zeroExtend (funct3);
td.word2 = stval;
td.word3 = zeroExtend (eaddr);
return td;
endfunction
`ifdef ISA_F
// F_LOAD
// op pc instr_sz instr rd word1 word2 word3 word4 word5
// x x x x x eaddr mstatus rdval
function Trace_Data mkTrace_F_LOAD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordFL rdval, WordXL eaddr, WordXL mstatus);
Trace_Data td = ?;
td.op = TRACE_F_LOAD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word3 = zeroExtend (eaddr);
td.word4 = mstatus;
td.word5 = rdval;
return td;
endfunction
// STORE
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x stval eaddr
function Trace_Data mkTrace_STORE (WordXL pc, ISize isize, Bit #(32) instr, WordXL stval, WordXL eaddr);
// F_STORE
// op pc instr_sz instr rd word1 word2 word3 word4 word5
// x x x x funct3 eaddr stval
function Trace_Data mkTrace_F_STORE (WordXL pc, Bit #(3) funct3, ISize isize, Bit #(32) instr, WordFL stval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_STORE;
td.op = TRACE_F_STORE;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.word2 = stval;
td.word3 = zeroExtend (eaddr);
td.word5 = stval;
return td;
endfunction
function Trace_Data fv_trace_update_mstatus_fs (Trace_Data td, Bit #(2) fs);
let ntd = td;
ntd.word4 = fv_assign_bits (td.word4, fromInteger (mstatus_fs_bitpos), fs);
return (ntd);
endfunction
function Trace_Data fv_trace_update_fcsr_fflags (Trace_Data td, Bit #(5) fflags);
let ntd = td;
ntd.word2 = (td.word2 | extend (fflags));
return (ntd);
endfunction
`endif
// AMO
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval stval eaddr
function Trace_Data mkTrace_AMO (WordXL pc, ISize isize, Bit #(32) instr,
// x x x x x rdval stval eaddr funct3
function Trace_Data mkTrace_AMO (WordXL pc, Bit #(3) funct3, ISize isize, Bit #(32) instr,
RegName rd, WordXL rdval, WordXL stval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_AMO;
@@ -211,6 +264,7 @@ function Trace_Data mkTrace_AMO (WordXL pc, ISize isize, Bit #(32) instr,
td.word1 = rdval;
td.word2 = stval;
td.word3 = zeroExtend (eaddr);
td.word4 = zeroExtend (funct3);
return td;
endfunction
@@ -247,10 +301,14 @@ function Trace_Data mkTrace_RET (WordXL pc, ISize isize, Bit #(32) instr, Priv_M
endfunction
// CSRRX
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval csrvalid csraddr csrval
// op pc instr_sz instr rd word1 word2 word3 word4 word5
// x x x x x rdval [1] mstatus_valid csraddr csrval mstatus
// [0] csrvalid
function Trace_Data mkTrace_CSRRX (WordXL pc, ISize isize, Bit #(32) instr,
RegName rd, WordXL rdval, Bool csrvalid, CSR_Addr csraddr, WordXL csrval);
RegName rd, WordXL rdval,
Bool csrvalid, CSR_Addr csraddr, WordXL csrval,
Bool mstatus_valid,
WordXL mstatus);
Trace_Data td = ?;
td.op = TRACE_CSRRX;
td.pc = pc;
@@ -258,9 +316,12 @@ function Trace_Data mkTrace_CSRRX (WordXL pc, ISize isize, Bit #(32) instr,
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word2 = (csrvalid ? 1 : 0);
td.word2 = ((mstatus_valid ? 2 : 0) | (csrvalid ? 1 : 0));
td.word3 = zeroExtend (csraddr);
td.word4 = csrval;
`ifdef ISA_F
td.word5 = mstatus;
`endif
return td;
endfunction
@@ -305,14 +366,27 @@ instance FShow #(Trace_Data);
if (td.op != TRACE_INTR)
fmt = fmt + $format (" instr.%0d %0h:", pack (td.instr_sz), td.instr);
if ((td.op == TRACE_I_RD) || (td.op == TRACE_F_RD))
if (td.op == TRACE_I_RD)
fmt = fmt + $format (" rd %0d rdval %0h", td.rd, td.word1);
`ifdef ISA_F
else if (td.op == TRACE_F_FRD)
fmt = fmt + $format (" rd %0d rdval %0h fflags %05b", td.rd, td.word5, td.word2);
else if ((td.op == TRACE_I_LOAD) || (td.op == TRACE_F_LOAD))
else if (td.op == TRACE_F_GRD)
fmt = fmt + $format (" rd %0d rdval %0h fflags %05b", td.rd, td.word1, td.word2);
else if (td.op == TRACE_F_LOAD)
fmt = fmt + $format (" rd %0d rdval %0h eaddr %0h",
td.rd, td.word5, td.word3);
else if (td.op == TRACE_F_STORE)
fmt = fmt + $format (" stval %0h eaddr %0h", td.word5, td.word3);
`endif
else if (td.op == TRACE_I_LOAD)
fmt = fmt + $format (" rd %0d rdval %0h eaddr %0h",
td.rd, td.word1, td.word3);
else if (td.op == TRACE_STORE)
else if (td.op == TRACE_I_STORE)
fmt = fmt + $format (" stval %0h eaddr %0h", td.word2, td.word3);
else if (td.op == TRACE_AMO)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
// Copyright (c) 2019-2020 Bluespec, Inc. All Rights Reserved
package PLIC;
@@ -102,6 +102,7 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority))
Add #(_any_1, TLog #(t_n_targets), T_wd_target_id),
Log #(TAdd #(t_max_priority, 1), t_wd_priority));
// 0 = quiet; 1 = show PLIC transactions; 2 = also show AXI4 transactions
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
// Source_Ids and Priorities are read and written over the memory interface
@@ -551,12 +552,12 @@ module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority))
return interface PLIC_Source_IFC;
method Action m_interrupt_req (Bool set_not_clear);
action
if (! vrg_source_busy [source_id]) begin
vrg_source_ip [source_id] <= set_not_clear;
if (! vrg_source_busy [source_id + 1]) begin
vrg_source_ip [source_id + 1] <= set_not_clear;
if ((cfg_verbosity > 0) && (vrg_source_ip [source_id] != set_not_clear))
$display ("%0d: Changing vrg_source_ip [%0d] to %0d",
cur_cycle, source_id, pack (set_not_clear));
if ((cfg_verbosity > 0) && (vrg_source_ip [source_id + 1] != set_not_clear))
$display ("%0d: %m.m_interrupt_req: changing vrg_source_ip [%0d] to %0d",
cur_cycle, source_id + 1, pack (set_not_clear));
end
endaction
endmethod

View File

@@ -1,4 +1,29 @@
The (relative) Memory map for this PLIC follows the one given in:
// Copyright (c) 2019-2020 Bluespec, Inc. All Rights Reserved
>================================================================
Our BSV PLIC here follows the example of the SiFive PLIC spec
described below ("Background"), in order to be able to use the same
Linux driver.
>================================================================
Background on PLIC ("Platform Level Interrupt Controller")
As of this writing (2019-04-29), there is not yet any official PLIC
spec for RISC-V.
In an earlier version (v1.10) of the RISC-V Privilege Architecture
spec, Chapter 7 was a PLIC spec, but it was taken out of the Priv Arch
spec to be considered separately in the future as part of a platform
spec rather than as part of the ISA spec. That chapter spec was
partial in that, while it described the required functionality, it did
not specify any specific address map for the functional components.
SiFive has implemented that PLIC spec for their cores, taking
particular decisions for memory mappings, etc., and they have
implemented a Linux driver for it.
Below is a summary of their (relative) Memory map for the PLIC the
following SiFive chip:
SiFive U54-MC Core Complex Manual, v1p0, Oct 4 2017
Chapter 8 Platform Level Interrupt Controller, pp.32-38.
@@ -27,7 +52,7 @@ IP (Interrupt Pending) array: 32 sources per 32b word
>----------------
Reserved
0x1018 ... 0x1FFF
0x1040 ... 0x1FFF
>----------------
IE (interrupt enables) array for Hart0 M mode (1 bit per source)
@@ -105,30 +130,32 @@ Reserved
>----------------
Threshold (of priority) and Claim/Complete registers
0x0C20 0000 Hart 0 M-mode
0x0C20 0004 Hart 0 M-mode claim/complete
0x0020 0000 Hart 0 M-mode
0x0020 0004 Hart 0 M-mode claim/complete
>----------------
0x0C20 1000 Hart 1 M-mode
0x0C20 1004 Hart 1 M-mode claim/complete
0x0020 1000 Hart 1 M-mode
0x0020 1004 Hart 1 M-mode claim/complete
>----------------
0x0C20 2000 Hart 1 S-mode
0x0C20 2004 Hart 1 S-mode claim/complete
0x0020 2000 Hart 1 S-mode
0x0020 2004 Hart 1 S-mode claim/complete
>----------------
0x0C20 3000 Hart 2 M-mode
0x0C20 3004 Hart 2 M-mode claim/complete
0x0020 3000 Hart 2 M-mode
0x0020 3004 Hart 2 M-mode claim/complete
>----------------
0x0C20 4000 Hart 2 S-mode
0x0C20 4004 Hart 2 S-mode claim/complete
0x0020 4000 Hart 2 S-mode
0x0020 4004 Hart 2 S-mode claim/complete
>----------------
0x0C20 5000 Hart 3 M-mode
0x0C20 5004 Hart 3 M-mode claim/complete
0x0020 5000 Hart 3 M-mode
0x0020 5004 Hart 3 M-mode claim/complete
>----------------
0x0C20 6000 Hart 3 S-mode
0x0C20 6004 Hart 3 S-mode claim/complete
0x0020 6000 Hart 3 S-mode
0x0020 6004 Hart 3 S-mode claim/complete
>----------------
0x0C20 7000 Hart 4 M-mode
0x0C20 7004 Hart 4 M-mode claim/complete
0x0020 7000 Hart 4 M-mode
0x0020 7004 Hart 4 M-mode claim/complete
>----------------
0x0C20 8000 Hart 4 S-mode
0x0C20 8004 Hart 4 S-mode claim/complete
0x0020 8000 Hart 4 S-mode
0x0020 8004 Hart 4 S-mode claim/complete
>----------------
>================================================================

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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