Merge branch 'master' into RVFI_DII, a complex merge for the fetch stage!
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
// ================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
60
src_Core/Core/Trace_Data2.bsv
Normal file
60
src_Core/Core/Trace_Data2.bsv
Normal 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
|
||||
252
src_Core/Core/Trace_Data2_to_Trace_Data.bsv
Normal file
252
src_Core/Core/Trace_Data2_to_Trace_Data.bsv
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
// ================================================================
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
>----------------
|
||||
|
||||
>================================================================
|
||||
|
||||
@@ -27,6 +27,18 @@ interface ResetGuard;
|
||||
method Bool isReady;
|
||||
endinterface
|
||||
|
||||
`ifdef BSIM
|
||||
module mkResetGuard(ResetGuard);
|
||||
Reg#(Bool) ready <- mkReg(False);
|
||||
|
||||
(* no_implicit_conditions, fire_when_enabled *)
|
||||
rule rl_ready;
|
||||
ready <= True;
|
||||
endrule
|
||||
|
||||
method isReady = ready;
|
||||
endmodule
|
||||
`else
|
||||
import "BVI" reset_guard =
|
||||
module mkResetGuard(ResetGuard);
|
||||
default_clock clk(CLK);
|
||||
@@ -36,3 +48,4 @@ module mkResetGuard(ResetGuard);
|
||||
|
||||
schedule (isReady) CF (isReady);
|
||||
endmodule
|
||||
`endif
|
||||
|
||||
@@ -76,10 +76,15 @@ module mkIntDivUnsignedSim(IntDivUnsignedImport);
|
||||
let {dividend, user} = dividendQ.first;
|
||||
let divisor = divisorQ.first;
|
||||
|
||||
// Be careful to avoid divide-by-zero in bluesim's C++, which turns
|
||||
// res = cond ? exp1 : exp2
|
||||
// into
|
||||
// tmp1 = exp1; tmp2 = exp2; res = cond ? tmp1 : tmp2
|
||||
// so we must give a fake non-zero input even if it looks unused.
|
||||
UInt#(64) a = unpack(dividend);
|
||||
UInt#(64) b = unpack(divisor);
|
||||
Bit#(64) q = pack(a / b);
|
||||
Bit#(64) r = pack(a % b);
|
||||
UInt#(64) b = divisor == 0 ? 1 : unpack(divisor);
|
||||
Bit#(64) q = divisor == 0 ? maxBound : pack(a / b);
|
||||
Bit#(64) r = divisor == 0 ? dividend : pack(a % b);
|
||||
respQ.enq(tuple2({q, r}, user));
|
||||
endrule
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ import SpecFifo::*;
|
||||
import HasSpecBits::*;
|
||||
import Bypass::*;
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ALU pipeline has 4 stages
|
||||
// dispatch -> reg read -> exe -> finish (write reg)
|
||||
// bypass is sent out from the end of exe stage
|
||||
@@ -78,6 +80,7 @@ typedef struct {
|
||||
Maybe#(PhyDst) dst;
|
||||
InstTag tag;
|
||||
DirPredTrainInfo dpTrain;
|
||||
Bool isCompressed;
|
||||
// result
|
||||
Data data; // alu compute result
|
||||
Maybe#(Data) csrData; // data to write CSR file
|
||||
@@ -126,6 +129,7 @@ typedef struct {
|
||||
Bool taken;
|
||||
DirPredTrainInfo dpTrain;
|
||||
Bool mispred;
|
||||
Bool isCompressed;
|
||||
} FetchTrainBP deriving(Bits, Eq, FShow);
|
||||
|
||||
interface AluExeInput;
|
||||
@@ -142,6 +146,7 @@ interface AluExeInput;
|
||||
method Bit #(32) rob_getOrig_Inst (InstTag t);
|
||||
method Action rob_setExecuted(
|
||||
InstTag t,
|
||||
Data dst_data,
|
||||
Maybe#(Data) csrData,
|
||||
ControlFlow cf
|
||||
`ifdef RVFI
|
||||
@@ -301,6 +306,7 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
|
||||
dst: x.dst,
|
||||
tag: x.tag,
|
||||
dpTrain: x.dpTrain,
|
||||
isCompressed: x.orig_inst[1:0] != 2'b11,
|
||||
data: exec_result.data,
|
||||
csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : tagged Invalid,
|
||||
`ifdef RVFI
|
||||
@@ -331,6 +337,7 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
|
||||
// update the instruction in the reorder buffer.
|
||||
inIfc.rob_setExecuted(
|
||||
x.tag,
|
||||
x.data,
|
||||
x.csrData,
|
||||
x.controlFlow
|
||||
`ifdef RVFI
|
||||
@@ -352,7 +359,8 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
|
||||
iType: x.iType,
|
||||
taken: x.controlFlow.taken,
|
||||
dpTrain: x.dpTrain,
|
||||
mispred: True
|
||||
mispred: True,
|
||||
isCompressed: x.isCompressed
|
||||
});
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter
|
||||
@@ -380,7 +388,8 @@ module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
|
||||
iType: x.iType,
|
||||
taken: x.controlFlow.taken,
|
||||
dpTrain: x.dpTrain,
|
||||
mispred: False
|
||||
mispred: False,
|
||||
isCompressed: x.isCompressed
|
||||
});
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
@@ -27,6 +28,7 @@ import GetPut::*;
|
||||
import Cntrs::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import FIFOF::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import CCTypes::*;
|
||||
@@ -42,6 +44,11 @@ import RenameDebugIF::*;
|
||||
`ifdef RVFI
|
||||
import RVFI_DII_Types::*;
|
||||
`endif
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import Trace_Data2 :: *;
|
||||
`endif
|
||||
|
||||
typedef struct {
|
||||
// info about the inst blocking at ROB head
|
||||
@@ -60,7 +67,7 @@ typedef struct {
|
||||
Bool stbEmpty;
|
||||
Bool stqEmpty;
|
||||
Bool tlbNoPendingReq;
|
||||
// CSR info: previlige mode
|
||||
// CSR info: privilege mode
|
||||
Bit#(2) prv;
|
||||
// inst count
|
||||
Data instCount;
|
||||
@@ -94,6 +101,9 @@ interface CommitInput;
|
||||
`endif
|
||||
);
|
||||
method Action setFetchWaitRedirect;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action setFetchWaitFlush;
|
||||
`endif
|
||||
method Action incrementEpoch;
|
||||
// record if we commit a CSR inst or interrupt
|
||||
method Action commitCsrInstOrInterrupt;
|
||||
@@ -101,6 +111,10 @@ interface CommitInput;
|
||||
method Bool doStats;
|
||||
// deadlock check
|
||||
method Bool checkDeadlock;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Vector #(SupSize, Put #(Trace_Data2)) v_to_TV;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
typedef struct {
|
||||
@@ -124,6 +138,10 @@ interface CommitStage;
|
||||
// RVFI trace report. Not an input?
|
||||
method Get#(Rvfi_Traces) rvfi;
|
||||
`endif
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Bool is_debug_halted;
|
||||
method Action debug_resume;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// we apply actions the end of commit rule
|
||||
@@ -132,6 +150,7 @@ typedef struct {
|
||||
Addr pc;
|
||||
Addr addr;
|
||||
Trap trap;
|
||||
Bit #(32) orig_inst;
|
||||
`ifdef RVFI_DII
|
||||
ToReorderBuffer x;
|
||||
`endif
|
||||
@@ -201,12 +220,117 @@ function Maybe#(RVFI_DII_Execution#(DataSz,DataSz)) genRVFI(ToReorderBuffer rot,
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
Bool verbose = True;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
|
||||
// Bluespec: for lightweight verbosity trace
|
||||
Integer verbosity = 0;
|
||||
Reg #(Bit #(64)) rg_instret <- mkReg (0);
|
||||
typedef enum {
|
||||
RUN_STATE_RUNNING, // Normal state
|
||||
RUN_STATE_DEBUGGER_HALTED // When halted for debugger
|
||||
} Run_State
|
||||
deriving (Eq, FShow, Bits);
|
||||
|
||||
`endif
|
||||
|
||||
module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
Bool verbose = False;
|
||||
|
||||
Integer verbosity = 0; // Bluespec: for lightweight verbosity trace
|
||||
|
||||
// Used to inform tandem-verifier about program order.
|
||||
// 0 is used to indicate we've just come out of reset
|
||||
// TODO: we could use fewer bits and allow and recognize wraparound.
|
||||
Reg #(Bit #(64)) rg_serial_num <- mkReg (0);
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
FIFOF #(ToReorderBuffer) f_rob_data <- mkFIFOF;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
Reg #(Run_State) rg_run_state <- mkReg (RUN_STATE_RUNNING);
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
Integer way0 = 0;
|
||||
|
||||
ToReorderBuffer no_deq_data = ?;
|
||||
Bit #(5) no_fflags = ?;
|
||||
Data no_mstatus = ?;
|
||||
Maybe #(Trap_Updates) no_trap_updates = tagged Invalid;
|
||||
Maybe #(RET_Updates) no_ret_updates = tagged Invalid;
|
||||
|
||||
function Action fa_to_TV (Integer way,
|
||||
Bit #(64) serial_num,
|
||||
Maybe #(Tuple2 #(Bit #(12), Data)) maybe_csr_upd,
|
||||
ToReorderBuffer deq_data,
|
||||
Bit #(5) fflags,
|
||||
Data mstatus,
|
||||
Maybe #(Trap_Updates) m_trap_updates,
|
||||
Maybe #(RET_Updates) m_ret_updates);
|
||||
action
|
||||
let tval = (m_trap_updates matches tagged Valid .tu ? tu.tval : deq_data.tval);
|
||||
let upd_pc = (m_ret_updates matches tagged Valid .ru ? ru.new_pc : deq_data.pc);
|
||||
let x = Trace_Data2 {serial_num: serial_num,
|
||||
maybe_csr_upd: maybe_csr_upd,
|
||||
pc: upd_pc,
|
||||
orig_inst: deq_data.orig_inst,
|
||||
iType: deq_data.iType,
|
||||
dst: deq_data.dst,
|
||||
dst_data: deq_data.dst_data,
|
||||
store_data: deq_data.store_data,
|
||||
store_data_BE: deq_data.store_data_BE,
|
||||
csr: deq_data.csr,
|
||||
trap: deq_data.trap,
|
||||
tval: tval,
|
||||
ppc_vaddr_csrData: deq_data.ppc_vaddr_csrData,
|
||||
fflags: fflags, // deq_data.fflags only has incremental flags
|
||||
will_dirty_fpu_state: deq_data.will_dirty_fpu_state,
|
||||
mstatus: mstatus, // when SD/XS/FS have changed
|
||||
|
||||
// Trap and RET updates
|
||||
prv: ( m_trap_updates matches tagged Valid .tu
|
||||
? tu.prv
|
||||
: (m_ret_updates matches tagged Valid .ru
|
||||
? ru.prv
|
||||
: ?)),
|
||||
status: ( m_trap_updates matches tagged Valid .tu
|
||||
? tu.status
|
||||
: (m_ret_updates matches tagged Valid .ru
|
||||
? ru.status
|
||||
: ?)),
|
||||
tvec: fromMaybe (?, m_trap_updates).new_pc,
|
||||
cause: fromMaybe (?, m_trap_updates).cause,
|
||||
epc: fromMaybe (?, m_trap_updates).epc
|
||||
};
|
||||
inIfc.v_to_TV [way].put (x);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
Reg #(Bool) rg_just_after_reset <- mkReg (True);
|
||||
|
||||
rule rl_send_tv_reset (rg_just_after_reset);
|
||||
Bit #(64) serial_num = 0;
|
||||
fa_to_TV (way0, serial_num,
|
||||
tagged Invalid,
|
||||
no_deq_data, no_fflags, no_mstatus, no_trap_updates, no_ret_updates);
|
||||
rg_just_after_reset <= False;
|
||||
rg_serial_num <= 1;
|
||||
endrule
|
||||
|
||||
Reg #(Data) rg_old_mip_csr_val <- mkReg (0);
|
||||
|
||||
Data new_mip_csr_val = inIfc.csrfIfc.getMIP;
|
||||
|
||||
Bool send_mip_csr_change_to_tv = (new_mip_csr_val != rg_old_mip_csr_val);
|
||||
|
||||
rule rl_send_mip_csr_change_to_tv ((! rg_just_after_reset) && send_mip_csr_change_to_tv);
|
||||
fa_to_TV (way0, rg_serial_num,
|
||||
tagged Valid (tuple2 (pack (CSRmip), new_mip_csr_val)),
|
||||
no_deq_data, no_fflags, no_mstatus, no_trap_updates, no_ret_updates);
|
||||
rg_old_mip_csr_val <= new_mip_csr_val;
|
||||
rg_serial_num <= rg_serial_num + 1;
|
||||
endrule
|
||||
`else
|
||||
Bool send_mip_csr_change_to_tv = False;
|
||||
`endif
|
||||
|
||||
// func units
|
||||
ReorderBufferSynth rob = inIfc.robIfc;
|
||||
@@ -432,12 +556,56 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Maintain system consistency when halting into debug mode
|
||||
// This code is patterned after 'makeSystemConsistent' above
|
||||
function Action makeSystemConsistent_for_debug_mode;
|
||||
action
|
||||
inIfc.setFlushTlbs;
|
||||
|
||||
// notify TLB to keep update of CSR changes
|
||||
inIfc.setUpdateVMInfo;
|
||||
// always wait store buffer and SQ to be empty
|
||||
when(inIfc.stbEmpty && inIfc.stqEmpty, noAction);
|
||||
// We wait TLB to finish all requests and become sync with memory.
|
||||
// Notice that currently TLB is read only, so TLB is always in sync
|
||||
// with memory (i.e., there is no write to commit to memory). Since all
|
||||
// insts have been killed, nothing can be issued to D TLB at this time.
|
||||
// Since fetch stage is set to wait for redirect, fetch1 stage is
|
||||
// stalled, and nothing can be issued to I TLB at this time.
|
||||
// Therefore, we just need to make sure that I and D TLBs are not
|
||||
// handling any miss req. Besides, when I and D TLBs do not have any
|
||||
// miss req, L2 TLB must be idling.
|
||||
when(inIfc.tlbNoPendingReq, noAction);
|
||||
// yield load reservation in cache
|
||||
inIfc.setFlushReservation;
|
||||
|
||||
inIfc.setFlushBrPred;
|
||||
inIfc.setFlushCaches;
|
||||
|
||||
`ifdef SELF_INV_CACHE
|
||||
// reconcile I$
|
||||
if(reconcileI) begin
|
||||
inIfc.setReconcileI;
|
||||
end
|
||||
`ifdef SYSTEM_SELF_INV_L1D
|
||||
// FIXME is this reconcile of D$ necessary?
|
||||
inIfc.setReconcileD;
|
||||
`endif
|
||||
`endif
|
||||
endaction
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
// TODO Currently we don't check spec bits == 0 when we commit an
|
||||
// instruction. This is because killings of wrong path instructions are
|
||||
// done in a single cycle. However, when we make killings distributed or
|
||||
// pipelined, then we need to check spec bits at commit port.
|
||||
|
||||
rule doCommitTrap_flush(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == RUN_STATE_RUNNING) &&&
|
||||
`endif
|
||||
!isValid(commitTrap) &&&
|
||||
rob.deqPort[0].deq_data.trap matches tagged Valid .trap
|
||||
);
|
||||
@@ -457,15 +625,19 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
let commitTrap_val = Valid (CommitTrap {
|
||||
trap: trap,
|
||||
pc: x.pc,
|
||||
addr: vaddr
|
||||
addr: vaddr,
|
||||
orig_inst: x.orig_inst
|
||||
`ifdef RVFI_DII
|
||||
, x: x
|
||||
`endif
|
||||
});
|
||||
commitTrap <= commitTrap_val;
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
f_rob_data.enq (x); // Save data to be sent to TV in rule doCommitTrap_handle, next
|
||||
`endif
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display ("instret:%0d PC:0x%0h instr:0x%08h", rg_instret, x.pc, x.orig_inst,
|
||||
if (verbosity >= 1) begin
|
||||
$display ("instret:%0d PC:0x%0h instr:0x%08h", rg_serial_num, x.pc, x.orig_inst,
|
||||
" iType:", fshow (x.iType), " [doCommitTrap]");
|
||||
end
|
||||
if (verbose) begin
|
||||
@@ -501,41 +673,103 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
doAssert(x.spec_bits == 0, "cannot have spec bits");
|
||||
endrule
|
||||
|
||||
rule doCommitTrap_handle(commitTrap matches tagged Valid .trap);
|
||||
rule doCommitTrap_handle(
|
||||
commitTrap matches tagged Valid .trap
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
&&& (rg_run_state == RUN_STATE_RUNNING)
|
||||
`endif
|
||||
&&& (! send_mip_csr_change_to_tv));
|
||||
|
||||
// reset commitTrap
|
||||
commitTrap <= Invalid;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
let x = f_rob_data.first;
|
||||
f_rob_data.deq;
|
||||
`endif
|
||||
|
||||
// notify commit of interrupt (so MMIO pRq may be handled)
|
||||
if(trap.trap matches tagged Interrupt .inter) begin
|
||||
inIfc.commitCsrInstOrInterrupt;
|
||||
end
|
||||
if (verbose) $display ("CommitStage.doCommitTrap_handle: ", fshow (commitTrap));
|
||||
|
||||
// trap handling & redirect
|
||||
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
|
||||
inIfc.redirectPc(new_pc
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
else if (trap.trap == tagged Exception Breakpoint) begin
|
||||
inIfc.commitCsrInstOrInterrupt; // TODO: Why?
|
||||
end
|
||||
`endif
|
||||
|
||||
Bool debugger_halt = False;
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
if ((trap.trap == tagged Interrupt DebugHalt)
|
||||
|| (trap.trap == tagged Interrupt DebugStep)
|
||||
|| ((trap.trap == tagged Exception Breakpoint) && (csrf.dcsr_break_bit == 1'b1)))
|
||||
begin
|
||||
debugger_halt = True;
|
||||
|
||||
// Flush everything (tlbs, caches, reservation, branch predictor);
|
||||
// reconcilei and I; update VM info.
|
||||
makeSystemConsistent_for_debug_mode;
|
||||
|
||||
// Save values in debugger CSRs
|
||||
Bit #(3) dcsr_cause = ( (trap.trap == tagged Interrupt DebugHalt)
|
||||
? 3
|
||||
: ( (trap.trap == tagged Interrupt DebugStep)
|
||||
? 4
|
||||
: 1));
|
||||
csrf.dcsr_cause_write (dcsr_cause);
|
||||
csrf.dpc_write (trap.pc);
|
||||
|
||||
// Tell fetch stage to wait for redirect
|
||||
// Note: rule doCommitTrap_flush may have done this already; redundant call is ok.
|
||||
inIfc.setFetchWaitRedirect;
|
||||
inIfc.setFetchWaitFlush;
|
||||
|
||||
// Go to quiescent state until debugger resumes execution
|
||||
rg_run_state <= RUN_STATE_DEBUGGER_HALTED;
|
||||
|
||||
if (verbosity >= 2)
|
||||
$display ("%0d: %m.commitStage.doCommitTrap_handle; debugger halt:", cur_cycle);
|
||||
end
|
||||
`endif
|
||||
|
||||
if (! debugger_halt) begin
|
||||
// trap handling & redirect
|
||||
let trap_updates <- csrf.trap(trap.trap, trap.pc, trap.addr, trap.orig_inst);
|
||||
inIfc.redirectPc(trap_updates.new_pc
|
||||
`ifdef RVFI_DII
|
||||
, trap.x.diid + 1
|
||||
, trap.x.diid + 1
|
||||
`endif
|
||||
);
|
||||
|
||||
);
|
||||
`ifdef RVFI
|
||||
Rvfi_Traces rvfis = replicate(tagged Invalid);
|
||||
rvfis[0] = genRVFI(trap.x, traceCnt, getTSB(), new_pc);
|
||||
rvfiQ.enq(rvfis);
|
||||
traceCnt <= traceCnt + 1;
|
||||
Rvfi_Traces rvfis = replicate(tagged Invalid);
|
||||
rvfis[0] = genRVFI(trap.x, traceCnt, getTSB(), trap_updates.new_pc);
|
||||
rvfiQ.enq(rvfis);
|
||||
traceCnt <= traceCnt + 1;
|
||||
`endif
|
||||
|
||||
// system consistency
|
||||
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
|
||||
// does not include prv info, and it has to flush when prv changes.
|
||||
// XXX As approximation, Trap may cause context switch, so flush for
|
||||
// security
|
||||
makeSystemConsistent(False, True, False);
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
fa_to_TV (way0, rg_serial_num,
|
||||
tagged Invalid,
|
||||
x, no_fflags, no_mstatus, tagged Valid trap_updates, no_ret_updates);
|
||||
`endif
|
||||
rg_serial_num <= rg_serial_num + 1;
|
||||
|
||||
// system consistency
|
||||
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
|
||||
// does not include prv info, and it has to flush when prv changes.
|
||||
// XXX As approximation, Trap may cause context switch, so flush for
|
||||
// security
|
||||
makeSystemConsistent(False, True, False);
|
||||
end
|
||||
endrule
|
||||
|
||||
// commit misspeculated load
|
||||
rule doCommitKilledLd(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == RUN_STATE_RUNNING) &&&
|
||||
`endif
|
||||
!isValid(commitTrap) &&&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&&
|
||||
rob.deqPort[0].deq_data.ldKilled matches tagged Valid .killBy
|
||||
@@ -574,25 +808,33 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
// commit system inst
|
||||
rule doCommitSystemInst(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == RUN_STATE_RUNNING) &&
|
||||
`endif
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
|
||||
isSystem(rob.deqPort[0].deq_data.iType)
|
||||
isSystem(rob.deqPort[0].deq_data.iType) &&
|
||||
(! send_mip_csr_change_to_tv)
|
||||
);
|
||||
rob.deqPort[0].deq;
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
|
||||
if(verbose) $display("[doCommitSystemInst] ", fshow(x));
|
||||
if (verbosity > 0) begin
|
||||
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_instret, x.pc, x.orig_inst,
|
||||
if (verbosity >= 1) begin
|
||||
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_serial_num, x.pc, x.orig_inst,
|
||||
" iType:", fshow (x.iType), " [doCommitSystemInst]");
|
||||
rg_instret <= rg_instret + 1;
|
||||
end
|
||||
|
||||
// we claim a phy reg for every inst, so commit its renaming
|
||||
regRenamingTable.commit[0].commit;
|
||||
|
||||
Bool write_satp = False; // flush tlb when satp csr is modified
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
Data new_mstatus = no_mstatus;
|
||||
`endif
|
||||
|
||||
Bool write_satp = False; // flush tlb when satp csr is modified
|
||||
Bool flush_security = False; // flush for security when the flush csr is written
|
||||
if(x.iType == Csr) begin
|
||||
// notify commit of CSR (so MMIO pRq may be handled)
|
||||
@@ -607,6 +849,17 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
doAssert(False, "must have csr data");
|
||||
end
|
||||
csrf.csrInstWr(csr_idx, csr_data);
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
Data data_warl_xformed = csrf.warl_xform (csr_idx, csr_data);
|
||||
x.ppc_vaddr_csrData = tagged CSRData data_warl_xformed;
|
||||
|
||||
if (x.will_dirty_fpu_state) begin
|
||||
Data old_mstatus = csrf.rd (CSRmstatus);
|
||||
new_mstatus = { 1'b1, old_mstatus [62:15], 2'b11, old_mstatus [12:0] };
|
||||
end
|
||||
`endif
|
||||
|
||||
// check if satp is modified or not
|
||||
write_satp = csr_idx == CSRsatp;
|
||||
`ifdef SECURITY
|
||||
@@ -617,11 +870,22 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
// redirect (Sret and Mret redirect pc is got from CSRF)
|
||||
Addr next_pc = x.ppc_vaddr_csrData matches tagged PPC .ppc ? ppc : (x.pc + 4);
|
||||
doAssert(next_pc == x.pc + 4, "ppc must be pc + 4");
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
Maybe #(RET_Updates) m_ret_updates = no_ret_updates;
|
||||
`endif
|
||||
if(x.iType == Sret) begin
|
||||
next_pc <- csrf.sret;
|
||||
RET_Updates ret_updates <- csrf.sret;
|
||||
next_pc = ret_updates.new_pc;
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
m_ret_updates = tagged Valid ret_updates;
|
||||
`endif
|
||||
end
|
||||
else if(x.iType == Mret) begin
|
||||
next_pc <- csrf.mret;
|
||||
RET_Updates ret_updates <- csrf.mret;
|
||||
next_pc = ret_updates.new_pc;
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
m_ret_updates = tagged Valid ret_updates;
|
||||
`endif
|
||||
end
|
||||
inIfc.redirectPc(next_pc
|
||||
`ifdef RVFI_DII
|
||||
@@ -636,6 +900,13 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
traceCnt <= traceCnt + 1;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
fa_to_TV (way0, rg_serial_num,
|
||||
tagged Invalid,
|
||||
x, no_fflags, new_mstatus, no_trap_updates, m_ret_updates);
|
||||
`endif
|
||||
rg_serial_num <= rg_serial_num + 1;
|
||||
|
||||
// rename stage only sends out system inst when ROB is empty, so no
|
||||
// need to flush ROB again
|
||||
|
||||
@@ -672,7 +943,9 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
// checks
|
||||
doAssert(x.epochIncremented, "must have already incremented epoch");
|
||||
doAssert((x.iType == Csr) == isValid(x.csr), "only CSR has valid csr idx");
|
||||
doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
|
||||
// RSN 2020-03-08: Removed this assertion. Csr instrs that write to
|
||||
// fflags/frm/fcsr do indeed 'dirty' the fpu state
|
||||
// doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
|
||||
doAssert(x.spec_bits == 0, "cannot have spec bits");
|
||||
doAssert(x.claimed_phy_reg, "must have claimed phy reg");
|
||||
`ifdef RENAME_DEBUG
|
||||
@@ -709,11 +982,15 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
// commit normal: fire when at least one commit can be done
|
||||
rule doCommitNormalInst(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == RUN_STATE_RUNNING) &&
|
||||
`endif
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
|
||||
!isSystem(rob.deqPort[0].deq_data.iType)
|
||||
!isSystem(rob.deqPort[0].deq_data.iType) &&
|
||||
(! send_mip_csr_change_to_tv)
|
||||
);
|
||||
// stop superscalar commit after we
|
||||
// 1. see a trap or system inst or killed Ld
|
||||
@@ -747,6 +1024,12 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
Bit #(64) instret = 0;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// These variables accumulate fflags and mstatus in sequential Program Order ('po')
|
||||
// (whereas the 'fflags' variable does just one update after superscalar retirement).
|
||||
Bit #(5) po_fflags = ?;
|
||||
Data po_mstatus = ?;
|
||||
`endif
|
||||
// compute what actions to take
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
if(!stop && rob.deqPort[i].canDeq) begin
|
||||
@@ -765,12 +1048,29 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
whichTrace = whichTrace + 1;
|
||||
`endif
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_instret + instret, x.pc, x.orig_inst,
|
||||
if (verbosity >= 1) begin
|
||||
$display("instret:%0d PC:0x%0h instr:0x%08h", rg_serial_num + instret, x.pc, x.orig_inst,
|
||||
" iType:", fshow (x.iType), " [doCommitNormalInst [%0d]]", i);
|
||||
instret = instret + 1;
|
||||
end
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
Bool init_for_way0 = (i == 0);
|
||||
match {. new_fflags, .new_mstatus} = csrf.fpuInst_csr_updates (x.fflags,
|
||||
init_for_way0,
|
||||
po_fflags,
|
||||
po_mstatus);
|
||||
po_fflags = new_fflags;
|
||||
po_mstatus = new_mstatus;
|
||||
|
||||
fa_to_TV (i, rg_serial_num + instret,
|
||||
tagged Invalid,
|
||||
x,
|
||||
po_fflags,
|
||||
po_mstatus,
|
||||
no_trap_updates, no_ret_updates);
|
||||
`endif
|
||||
instret = instret + 1;
|
||||
|
||||
// inst can be committed, deq it
|
||||
rob.deqPort[i].deq;
|
||||
|
||||
@@ -822,7 +1122,7 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
end
|
||||
end
|
||||
end
|
||||
rg_instret <= rg_instret + instret;
|
||||
rg_serial_num <= rg_serial_num + instret;
|
||||
|
||||
// write FPU csr
|
||||
if(csrf.fpuInstNeedWr(fflags, will_dirty_fpu_state)) begin
|
||||
@@ -873,6 +1173,8 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Data getPerf(ComStagePerfType t);
|
||||
return (case(t)
|
||||
@@ -926,4 +1228,17 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
endmethod
|
||||
interface renameErr = nullGet;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Bool is_debug_halted;
|
||||
return (rg_run_state == RUN_STATE_DEBUGGER_HALTED);
|
||||
endmethod
|
||||
|
||||
method Action debug_resume () if (rg_run_state == RUN_STATE_DEBUGGER_HALTED);
|
||||
rg_run_state <= RUN_STATE_RUNNING;
|
||||
if (verbosity >= 2)
|
||||
$display ("%0d: %m.commitStage.debug_resume", cur_cycle);
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
@@ -37,6 +38,7 @@ import MemoryTypes::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ras::*;
|
||||
import EpochManager::*;
|
||||
import Performance::*;
|
||||
@@ -54,6 +56,8 @@ import RVFI_DII_Types::*;
|
||||
import Types::*;
|
||||
`endif
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ================================================================
|
||||
// For fv_decode_C function and related types and definitions
|
||||
|
||||
@@ -86,10 +90,13 @@ interface FetchStage;
|
||||
, Dii_Id id
|
||||
`endif
|
||||
);
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action setWaitFlush;
|
||||
`endif
|
||||
method Action done_flushing();
|
||||
method Action train_predictors(
|
||||
Addr pc, Addr next_pc, IType iType, Bool taken,
|
||||
DirPredTrainInfo dpTrain, Bool mispred
|
||||
DirPredTrainInfo dpTrain, Bool mispred, Bool isCompressed
|
||||
);
|
||||
|
||||
// security
|
||||
@@ -117,26 +124,34 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr pred_next_pc;
|
||||
Maybe#(Addr) pred_next_pc;
|
||||
Bool fetch3_epoch;
|
||||
Bool decode_epoch;
|
||||
Epoch main_epoch;
|
||||
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr phys_pc;
|
||||
Addr pred_next_pc;
|
||||
Maybe#(Addr) pred_next_pc;
|
||||
Maybe#(Exception) cause;
|
||||
Addr tval; // in case of exception
|
||||
Bool access_mmio; // inst fetch from MMIO
|
||||
Bool fetch3_epoch;
|
||||
Bool decode_epoch;
|
||||
Epoch main_epoch;
|
||||
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);
|
||||
|
||||
// TODO: this name 'Fetch3ToDecode' is a misnomer.
|
||||
// The struct passed from doFetch3 to doDecode is Fetch2ToFetch3 (same type as doFetch2 to doFetch3),
|
||||
// and Fetch3ToDecode is used purely internally in doDecode.
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr pred_next_pc;
|
||||
Bool mispred_first_half;
|
||||
Maybe#(Exception) cause;
|
||||
Addr tval; // in case of exception
|
||||
Bool decode_epoch;
|
||||
Epoch main_epoch;
|
||||
} Fetch3ToDecode deriving(Bits, Eq, FShow);
|
||||
|
||||
// Used purely internally in doDecode.
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr ppc;
|
||||
@@ -144,7 +159,7 @@ typedef struct {
|
||||
Epoch main_epoch;
|
||||
Instruction inst;
|
||||
Maybe#(Exception) cause;
|
||||
} Fetch3ToDecode deriving(Bits, Eq, FShow);
|
||||
} InstrFromFetch3 deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
@@ -198,13 +213,25 @@ endfunction
|
||||
typedef TMul #(SupSize, 2) SupSizeX2;
|
||||
typedef Bit #(TLog #(TAdd #(SupSizeX2, 1))) SupCntX2;
|
||||
|
||||
// Merging up to SupSize-1 pending instructions with up to SupSizeX2 decoded
|
||||
// instructions produces up to 3*SupSize-1 instructions; SupSize can be issued
|
||||
// if present, leaving up to 2*SupSize-1 pending.
|
||||
typedef TSub #(TMul #(SupSize, 2), 1) SupSizeX2S1;
|
||||
typedef Bit #(TLog #(TAdd #(SupSizeX2S1, 1))) SupCntX2S1;
|
||||
typedef TSub #(TMul #(SupSize, 3), 1) SupSizeX3S1;
|
||||
typedef Bit #(TLog #(TAdd #(SupSizeX3S1, 1))) SupCntX3S1;
|
||||
|
||||
// Appending the pending and decoded vectors produces an intermediate
|
||||
// 4*SupSize-1 vector (with only up to 3*SupSize-1 elements non-empty).
|
||||
typedef TSub #(TMul #(SupSize, 4), 1) SupSizeX4S1;
|
||||
typedef Bit #(TLog #(TAdd #(SupSizeX4S1, 1))) SupCntX4S1;
|
||||
|
||||
// Parsing a sequence of 16-bit parcels returns a sequence of the
|
||||
// following kinds or items
|
||||
|
||||
typedef enum {Inst_None, // When we run off the end of the sequence
|
||||
Inst_16b, // A 16b instruction
|
||||
Inst_32b, // A 32b instruction
|
||||
Inst_32b_Lsbs // Lower 16b of a 32b instr
|
||||
Inst_32b // A 32b instruction
|
||||
} Inst_Kind
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
@@ -219,6 +246,12 @@ typedef struct {
|
||||
} Inst_Item
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
instance DefaultValue #(Inst_Item);
|
||||
function Inst_Item defaultValue = Inst_Item {
|
||||
pc: 0, inst_kind: Inst_None, orig_inst: 0, inst: 0
|
||||
};
|
||||
endinstance
|
||||
|
||||
// Input 'inst_d' was fetched from memory: up to superscalar-size sequence of 32b parcels.
|
||||
// Convert this into 16b parcels, prior to re-parsing for possible mix of 32b and 16b instructions.
|
||||
// This is a pure function; ActionValue is used only to allow $displays for debugging.
|
||||
@@ -242,35 +275,57 @@ endfunction
|
||||
|
||||
// Parse 16b parcels (v_x16) into a sequence of 16b or 32b instructions.
|
||||
// This is a pure function; ActionValue is used only to allow $displays for debugging.
|
||||
|
||||
function ActionValue #(Vector #(SupSize, Inst_Item))
|
||||
function ActionValue #(Tuple4 #(SupCntX2,
|
||||
Vector #(SupSizeX2, Inst_Item),
|
||||
Addr,
|
||||
Maybe #(Tuple3 #(Addr, Bit #(16), Bool))))
|
||||
fav_parse_insts (Bool verbose,
|
||||
Addr pc_start,
|
||||
SupCntX2 n_x16s,
|
||||
Vector #(SupSizeX2, Bit #(16)) v_x16);
|
||||
Maybe #(Addr) pred_next_pc,
|
||||
Maybe #(Tuple3 #(Addr, Bit #(16), Bool)) pending_straddle,
|
||||
SupCntX2 n_x16s,
|
||||
Vector #(SupSizeX2, Bit #(16)) v_x16);
|
||||
actionvalue
|
||||
// Parse up to SupSize instructions (v_items) from fetched v_x16 parcels (v_x16).
|
||||
Vector #(SupSize, Inst_Item) v_items = replicate (Inst_Item {pc: pc_start,
|
||||
inst_kind: Inst_None,
|
||||
orig_inst: 0,
|
||||
inst: 0});
|
||||
SupCntX2 j = ((pc_start [1:0] == 2'b00) ? 0 : 1); // Start parse at parcel 0/1 depending on pc lsbs
|
||||
// Parse up to SupSizeX2 instructions (v_items) from fetched v_x16 parcels (v_x16).
|
||||
Vector #(SupSizeX2, Inst_Item) v_items = replicate (Inst_Item {pc: pc_start,
|
||||
inst_kind: Inst_None,
|
||||
orig_inst: 0,
|
||||
inst: 0});
|
||||
Maybe #(Tuple3 #(Addr, Bit #(16), Bool)) next_straddle = tagged Invalid;
|
||||
// Start parse at parcel 0/1 depending on pc lsbs and pending straddle
|
||||
SupCntX2 j = ((pc_start [1:0] == 2'b00 || isValid(pending_straddle)) ? 0 : 1);
|
||||
`ifdef RVFI_DII
|
||||
j = 0;
|
||||
`endif
|
||||
Addr pc = pc_start;
|
||||
for (Integer i = 0; i < valueOf (SupSize); i = i + 1) begin
|
||||
Integer n_items = 0;
|
||||
`ifndef RVFI_DII
|
||||
for (Integer i = 0; i < valueOf (SupSizeX2); i = i + 1) begin
|
||||
Inst_Kind inst_kind = Inst_None;
|
||||
Bit #(32) orig_inst = 0;
|
||||
Bit #(32) inst = 0;
|
||||
Addr next_pc = pc;
|
||||
if (j < n_x16s) begin
|
||||
if (is_16b_inst (v_x16 [j])) begin
|
||||
if (i == 0 &&& pending_straddle matches tagged Valid {.s_pc, .s_lsbs, .s_mispred}) begin
|
||||
if (pc != s_pc + 2) begin
|
||||
$display ("FetchStage.fav_parse_insts: straddle: pc mismatch: pc = 0x%0h but s_pc = 0x%0h", pc, s_pc);
|
||||
dynamicAssert (False, "FetchStage.fav_parse_insts: straddle: pc mismatch");
|
||||
end
|
||||
pc = s_pc;
|
||||
inst_kind = Inst_32b;
|
||||
orig_inst = { v_x16[0], s_lsbs };
|
||||
inst = orig_inst;
|
||||
j = 1;
|
||||
next_pc = s_pc + 4;
|
||||
n_items = 1;
|
||||
end
|
||||
else if (is_16b_inst (v_x16 [j])) begin
|
||||
inst_kind = Inst_16b;
|
||||
orig_inst = zeroExtend (v_x16 [j]);
|
||||
inst = fv_decode_C (misa, misa_mxl_64, v_x16 [j]); // Expand 16b inst to 32b inst
|
||||
j = j + 1;
|
||||
next_pc = pc + 2;
|
||||
n_items = i + 1;
|
||||
if (verbose)
|
||||
$display ("FetchStage.fav_parse_insts: C inst 0x%0h -> inst 0x%0h", orig_inst, inst);
|
||||
end
|
||||
@@ -281,12 +336,15 @@ function ActionValue #(Vector #(SupSize, Inst_Item))
|
||||
inst = orig_inst;
|
||||
j = j + 2;
|
||||
next_pc = pc + 4;
|
||||
n_items = i + 1;
|
||||
end
|
||||
else begin
|
||||
inst_kind = Inst_32b_Lsbs;
|
||||
orig_inst = zeroExtend (v_x16 [j]);
|
||||
next_straddle = tagged Valid tuple3(pc, v_x16[j], isValid(pred_next_pc));
|
||||
j = j + 1;
|
||||
next_pc = pc + 2;
|
||||
// Leave next_pc unchanged and clear pred_next_pc so we
|
||||
// return the right predicted pc for the vector, which
|
||||
// excludes the pending straddle.
|
||||
pred_next_pc = tagged Invalid;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
@@ -300,14 +358,26 @@ function ActionValue #(Vector #(SupSize, Inst_Item))
|
||||
v_items [i] = Inst_Item {pc: pc, inst_kind: inst_kind, orig_inst: orig_inst, inst: inst};
|
||||
pc = next_pc;
|
||||
end
|
||||
`else
|
||||
for (Integer i = 0; i < valueOf(SupSize); i = i + 1) begin
|
||||
Bit #(32) inst = { v_x16 [(2*i)+1], v_x16 [2*i] };
|
||||
v_items[i].inst_kind = Inst_32b;
|
||||
v_items[i].orig_inst = inst;
|
||||
v_items[i].inst = inst;
|
||||
end
|
||||
pc = pc_start + 8;
|
||||
n_items = 2;
|
||||
`endif
|
||||
|
||||
if (verbose) begin
|
||||
$display ("FetchStage.fav_parse_insts:");
|
||||
$display (" v_x16: ", fshow (v_x16));
|
||||
$display (" v_x16: ", fshow (v_x16), " n_x16s: %d", n_x16s);
|
||||
$display (" n_items: %0d", n_items);
|
||||
$display (" v_items: ", fshow (v_items));
|
||||
$display (" next_straddle: ", fshow (next_straddle));
|
||||
end
|
||||
|
||||
return v_items;
|
||||
return tuple4(fromInteger(n_items), v_items, fromMaybe(pc, pred_next_pc), next_straddle);
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
@@ -338,24 +408,28 @@ module mkFetchStage(FetchStage);
|
||||
// We stall until the flush is done
|
||||
Reg#(Bool) waitForFlush <- mkReg(False);
|
||||
|
||||
Ehr#(3, Addr) pc_reg <- mkEhr(0);
|
||||
Ehr#(4, Addr) pc_reg <- mkEhr(0);
|
||||
Integer pc_fetch1_port = 0;
|
||||
Integer pc_decode_port = 1;
|
||||
Integer pc_redirect_port = 2;
|
||||
Integer pc_fetch3_port = 2;
|
||||
Integer pc_redirect_port = 3;
|
||||
|
||||
// Epochs
|
||||
Reg#(Bool) fetch3_epoch <- mkReg(False);
|
||||
Ehr#(2, Bool) decode_epoch <- mkEhr(False);
|
||||
Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch
|
||||
|
||||
// Regs/wires to hold the first half of an instruction that straddles a cache line boundary
|
||||
Ehr #(3, Bool) ehr_pending_straddle <- mkEhr (False);
|
||||
Ehr #(2, Addr) ehr_half_inst_pc <- mkEhr (?); // The PC of the straddling instruction
|
||||
Ehr #(2, Bit #(16)) ehr_half_inst_lsbs <- mkEhr (?); // The 16 lsbs of the straddling instruction
|
||||
// Reg to hold the first half of an instruction that straddles a cache line boundary
|
||||
Ehr #(2, Maybe #(Tuple3 #(Addr, Bit #(16), Bool))) ehr_pending_straddle <- mkEhr(tagged Invalid);
|
||||
// Reg to hold extra instructions from Fetch3 to send to decode the next cycle
|
||||
Reg #(Vector #(SupSizeX2S1, Inst_Item)) rg_pending_decode <- mkReg(replicate(defaultValue));
|
||||
Reg #(SupCntX2S1) rg_pending_n_items <- mkRegU;
|
||||
Reg #(Fetch3ToDecode) rg_pending_f32d <- mkRegU;
|
||||
|
||||
// Pipeline Stage FIFOs
|
||||
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
|
||||
Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
|
||||
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo;
|
||||
Fifo#(2, Tuple2#(Bit#(TLog#(SupSizeX2)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
|
||||
Fifo#(4, Tuple2#(Bit#(TLog#(SupSizeX2)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
|
||||
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch3ToDecode)) f32d <- mkCFFifo;
|
||||
|
||||
// Fifo#(2, Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo(); // OLD
|
||||
// FIFO from rule doFetch3 to rule doDecode
|
||||
@@ -409,56 +483,37 @@ module mkFetchStage(FetchStage);
|
||||
`endif
|
||||
|
||||
`ifdef RVFI_DII
|
||||
Ehr#(3, Dii_Id) dii_id_next <- mkEhr(0);
|
||||
Ehr#(4, Dii_Id) dii_id_next <- mkEhr(0);
|
||||
Fifo#(2, Dii_Ids) dii_instIds <- mkCFFifo;
|
||||
Fifo#(2, InstsAndIDs) dii_insts <- mkCFFifo;
|
||||
Fifo#(2, Dii_Ids) dii_fetched_ids <- mkCFFifo;
|
||||
|
||||
Reg#(Dii_Id) last_trace_id <- mkRegU;
|
||||
|
||||
//rule feed_dii(!waitForFlush);
|
||||
//Dii_Id next_id = dii_id_next;
|
||||
//if (flush_id.notEmpty) begin
|
||||
// next_id = flush_id.first;
|
||||
// if (verbosity > 0) $display("DII flushed!");
|
||||
// flush_id.deq;
|
||||
//end
|
||||
//Dii_Ids reqs = replicate(tagged Invalid);
|
||||
//for (Integer i = 0; i < `sizeSup; i = i + 1)
|
||||
// reqs[i] = tagged Valid (next_id + fromInteger(i));
|
||||
//if (verbosity > 0) $display("Requested from DII", fshow(reqs));
|
||||
//dii_instIds.enq(reqs);
|
||||
//dii_id_next <= next_id + `sizeSup;
|
||||
//endrule
|
||||
`endif
|
||||
|
||||
// Predict the next fetch-PC based only on current PC (without
|
||||
// knowing the instructions).
|
||||
// Note: this chains calls to nextAddrPred. If this is a critical-path problem,
|
||||
// alternatively one could apply nextAddrPred in parallel at pc+2, pc+4, pc+6, ...
|
||||
// and memo-ize them in a vector (TODO).
|
||||
|
||||
function ActionValue #(Tuple2 #(Integer, Addr)) fav_pred_next_pc (Addr pc);
|
||||
function ActionValue #(Tuple2 #(Integer, Maybe #(Addr))) fav_pred_next_pc (Addr pc);
|
||||
actionvalue
|
||||
Addr prev_PC = pc;
|
||||
Addr pred_next_pc = nextAddrPred.predPc (prev_PC);
|
||||
Integer posLastSup = 0;
|
||||
Maybe #(Addr) pred_next_pc = nextAddrPred.predPc (prev_PC);
|
||||
Integer posLastSupX2 = 0;
|
||||
Bool done = False;
|
||||
for (Integer i = 0; i < valueof (SupSize); i = i + 1) begin
|
||||
for (Integer i = 0; i < valueOf (SupSizeX2); i = i + 1) begin
|
||||
if (! done) begin
|
||||
Bool lastInstInCacheLine = (getLineInstOffset (prev_PC) == maxBound);
|
||||
Bool isSeq16 = ((prev_PC + 2) == pred_next_pc);
|
||||
Bool isSeq32 = ((prev_PC + 4) == pred_next_pc);
|
||||
Bool isJump = ((! isSeq16) && (! isSeq32));
|
||||
done = ((i == (valueOf (SupSize) - 1)) || lastInstInCacheLine || isJump);
|
||||
posLastSup = i;
|
||||
Bool isLastX2 = (i == (valueOf (SupSizeX2) - 1)) || ((pc[1:0] != 2'b00) && (i == (valueOf (SupSizeX2) - 2)));
|
||||
Bool lastInstInCacheLine = (getLineInstOffset (prev_PC) == maxBound) && (prev_PC[1:0] != 2'b00);
|
||||
Bool isJump = isValid(pred_next_pc);
|
||||
done = isLastX2 || lastInstInCacheLine || isJump;
|
||||
posLastSupX2 = i;
|
||||
if (! done) begin
|
||||
prev_PC = pred_next_pc;
|
||||
pred_next_pc = nextAddrPred.predPc (pred_next_pc);
|
||||
prev_PC = prev_PC + 2;
|
||||
pred_next_pc = nextAddrPred.predPc (prev_PC);
|
||||
end
|
||||
end
|
||||
end
|
||||
return tuple2 (posLastSup, pred_next_pc);
|
||||
return tuple2 (posLastSupX2, pred_next_pc);
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
@@ -492,17 +547,18 @@ module mkFetchStage(FetchStage);
|
||||
pc_reg[pc_fetch1_port] <= pred_next_pc;
|
||||
*/
|
||||
|
||||
match { .posLastSup, .pred_next_pc } <- fav_pred_next_pc (pc);
|
||||
pc_reg[pc_fetch1_port] <= pred_next_pc;
|
||||
|
||||
match { .posLastSupX2, .pred_next_pc } <- fav_pred_next_pc (pc);
|
||||
let next_fetch_pc = fromMaybe(pc + 2 * (fromInteger(posLastSupX2) + 1), pred_next_pc);
|
||||
pc_reg[pc_fetch1_port] <= next_fetch_pc;
|
||||
|
||||
`ifdef RVFI_DII
|
||||
Dii_Id next_id = dii_id_next[pc_fetch1_port];
|
||||
Dii_Ids reqs = replicate(tagged Invalid);
|
||||
for (Integer i = 0; i <= posLastSup; i = i + 1)
|
||||
for (Integer i = 0; i < valueOf(SupSize); i = i + 1)
|
||||
reqs[i] = tagged Valid (next_id + fromInteger(i));
|
||||
if (verbosity > 0) $display("Requested from DII", fshow(reqs));
|
||||
dii_instIds.enq(reqs);
|
||||
dii_id_next[pc_fetch1_port] <= next_id + fromInteger(posLastSup) + 1;
|
||||
dii_id_next[pc_fetch1_port] <= next_id + fromInteger(valueOf(SupSize));
|
||||
`endif
|
||||
|
||||
// Send TLB request.
|
||||
@@ -513,15 +569,19 @@ module mkFetchStage(FetchStage);
|
||||
let out = Fetch1ToFetch2 {
|
||||
pc: pc,
|
||||
pred_next_pc: pred_next_pc,
|
||||
fetch3_epoch: fetch3_epoch,
|
||||
decode_epoch: decode_epoch[0],
|
||||
main_epoch: f_main_epoch
|
||||
};
|
||||
f12f2.enq(tuple2(fromInteger(posLastSup),out));
|
||||
if (verbose) $display("Fetch1: ", fshow(out));
|
||||
main_epoch: f_main_epoch};
|
||||
let nbSupX2 = fromInteger(posLastSupX2) + (pc[1:0] == 2'b00 ? 0 : 1);
|
||||
`ifdef RVFI_DII
|
||||
nbSupX2 = 3;
|
||||
`endif
|
||||
f12f2.enq(tuple2(nbSupX2,out));
|
||||
if (verbose) $display("Fetch1: ", fshow(out), " posLastSupX2: %d", posLastSupX2, " nbSupX2: %d", nbSupX2);
|
||||
endrule
|
||||
|
||||
rule doFetch2;
|
||||
let {nbSup,in} = f12f2.first;
|
||||
let {nbSupX2,in} = f12f2.first;
|
||||
f12f2.deq;
|
||||
|
||||
// Get TLB response
|
||||
@@ -542,134 +602,251 @@ module mkFetchStage(FetchStage);
|
||||
// cache line size, so all nbSup+1 insts can be fetched
|
||||
// from boot rom. It won't happen that insts fetched from
|
||||
// boot rom is less than requested.
|
||||
Bit #(TLog #(SupSize)) nbSup = truncate(nbSupX2 >> 1);
|
||||
mmio.bootRomReq(phys_pc, nbSup);
|
||||
access_mmio = True;
|
||||
end
|
||||
default: begin
|
||||
// Access fault
|
||||
cause = Valid (InstAccessFault);
|
||||
// Without 'C' extension:
|
||||
// Addr align32b_mask = 'h3;
|
||||
// tval = (in.pc & (~ align32b_mask));
|
||||
Addr align16b_mask = 'h1;
|
||||
tval = (in.pc & (~ align16b_mask));
|
||||
end
|
||||
endcase
|
||||
end
|
||||
else begin
|
||||
// TLB exception: record the request address
|
||||
Addr align32b_mask = 'h3;
|
||||
tval = (in.pc & (~ align32b_mask));
|
||||
// Without 'C' extension:
|
||||
// Addr align32b_mask = 'h3;
|
||||
// tval = (in.pc & (~ align32b_mask));
|
||||
Addr align16b_mask = 'h1;
|
||||
tval = (in.pc & (~ align16b_mask));
|
||||
end
|
||||
`endif
|
||||
|
||||
let out = Fetch2ToFetch3 {
|
||||
pc: in.pc,
|
||||
phys_pc: phys_pc,
|
||||
pred_next_pc: in.pred_next_pc,
|
||||
cause: cause,
|
||||
tval: tval,
|
||||
access_mmio: access_mmio,
|
||||
fetch3_epoch: in.fetch3_epoch,
|
||||
decode_epoch: in.decode_epoch,
|
||||
main_epoch: in.main_epoch };
|
||||
f22f3.enq(tuple2(nbSup,out));
|
||||
f22f3.enq(tuple2(nbSupX2,out));
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch2: TLB response pyhs_pc 0x%0h cause ", phys_pc, fshow (cause));
|
||||
$display ("Fetch2: f2_tof3.enq: nbSup %0d out ", nbSup, fshow (out));
|
||||
end
|
||||
if (verbosity >= 2) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch2: TLB response pyhs_pc 0x%0h cause ", phys_pc, fshow (cause));
|
||||
$display ("Fetch2: f2_tof3.enq: nbSupX2 %0d out ", nbSupX2, fshow (out));
|
||||
end
|
||||
endrule
|
||||
|
||||
// Break out of i$
|
||||
rule doFetch3;
|
||||
let {nbSup, fetch3In} = f22f3.first;
|
||||
f22f3.deq();
|
||||
if (verbosity > 0)
|
||||
$display("Fetch3: fetch3In: ", fshow (fetch3In));
|
||||
let {nbSupX2In, fetch3In} = f22f3.first;
|
||||
if (verbosity >= 2) begin
|
||||
if (f22f3.notEmpty)
|
||||
$display("Fetch3: nbSupX2In: %0d fetch3In: ", nbSupX2In, fshow (fetch3In));
|
||||
else
|
||||
$display("Fetch3: Nothing else from Fetch2");
|
||||
end
|
||||
|
||||
`ifdef RVFI_DII
|
||||
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid dii_nop);
|
||||
InstsAndIDs ii <- toGet(dii_insts).get();
|
||||
inst_d = ii.insts;
|
||||
if (verbosity > 0) $display("Got from DII: ", fshow (ii));
|
||||
`else
|
||||
SupCntX2S1 pending_n_items = rg_pending_n_items;
|
||||
let out = rg_pending_f32d;
|
||||
Maybe #(Tuple3 #(Addr, Bit #(16), Bool)) pending_straddle = ehr_pending_straddle[0];
|
||||
|
||||
if (pending_n_items > 0) begin
|
||||
if (rg_pending_f32d.main_epoch != f_main_epoch || rg_pending_f32d.decode_epoch != decode_epoch[1]) begin
|
||||
// Just drop it. Also drop any pending straddle, as that is
|
||||
// associated with the same epoch.
|
||||
pending_n_items = 0;
|
||||
pending_straddle = tagged Invalid;
|
||||
if (verbosity >= 2) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: Drop pending: main_epoch: %d decode epoch: %d", f_main_epoch, decode_epoch[1]);
|
||||
$display ("Fetch3: rg_pending_n_items: ", fshow (rg_pending_n_items));
|
||||
$display ("Fetch3: rg_pending_f32d: ", fshow (rg_pending_f32d));
|
||||
$display ("Fetch3: rg_pending_decode: ", fshow (rg_pending_decode));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SupCntX2 parsed_n_items = 0;
|
||||
Inst_Item inst_item_none = Inst_Item {pc: fetch3In.pc, inst_kind: Inst_None, orig_inst: 0, inst: 0};
|
||||
Vector #(SupSizeX2, Inst_Item) parsed_v_items = replicate (inst_item_none);
|
||||
|
||||
let mispred_first_half = pending_straddle matches tagged Valid {.s_pc, .s_lsbs, .s_mispred} &&& s_mispred ? True : False;
|
||||
let can_merge = pending_n_items > 0
|
||||
&& pending_n_items < fromInteger(valueOf(SupSize))
|
||||
&& f22f3.notEmpty
|
||||
&& !isValid(fetch3In.cause)
|
||||
&& fetch3In.main_epoch == rg_pending_f32d.main_epoch
|
||||
&& fetch3In.decode_epoch == rg_pending_f32d.decode_epoch
|
||||
&& !mispred_first_half;
|
||||
|
||||
let drop_f22f3 = f22f3.notEmpty
|
||||
&& ( fetch3In.main_epoch != f_main_epoch
|
||||
|| fetch3In.decode_epoch != decode_epoch[1]
|
||||
|| fetch3In.fetch3_epoch != fetch3_epoch);
|
||||
|
||||
let parse_f22f3 = !drop_f22f3 && (pending_n_items == 0 || can_merge);
|
||||
|
||||
`ifndef RVFI_DII
|
||||
// Get ICache/MMIO response if no exception
|
||||
// In case of exception, we still need to process at least inst_data[0]
|
||||
// (it will be turned to an exception later), so inst_data[0] must be
|
||||
// valid.
|
||||
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
|
||||
if(!isValid(fetch3In.cause)) begin
|
||||
if(fetch3In.access_mmio) begin
|
||||
if(verbose) $display("get answer from MMIO %x", fetch3In.pc);
|
||||
inst_d <- mmio.bootRomResp;
|
||||
end
|
||||
else begin
|
||||
if(verbose) $display("get answer from memory %x", fetch3In.pc);
|
||||
inst_d <- mem_server.response.get;
|
||||
if (drop_f22f3 || parse_f22f3) begin
|
||||
f22f3.deq();
|
||||
if (!isValid(fetch3In.cause)) begin
|
||||
if(fetch3In.access_mmio) begin
|
||||
if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
|
||||
inst_d <- mmio.bootRomResp;
|
||||
end
|
||||
else begin
|
||||
if(verbose) $display("get answer from memory %d", fetch3In.pc);
|
||||
inst_d <- mem_server.response.get;
|
||||
end
|
||||
end
|
||||
end
|
||||
`else
|
||||
f22f3.deq();
|
||||
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid dii_nop);
|
||||
InstsAndIDs ii <- toGet(dii_insts).get();
|
||||
inst_d = ii.insts;
|
||||
if (verbosity > 0) $display("Got from DII: ", fshow (ii));
|
||||
if(verbose) $display("PC is %x", fetch3In.pc);
|
||||
`endif
|
||||
if (drop_f22f3) begin
|
||||
// Drop any pending straddle if this is for a different main or
|
||||
// decode epoch since that invalidates our Fetch3 redirect, but
|
||||
// otherwise keep it to flush the pipeline until we get the next
|
||||
// half of the straddle.
|
||||
if (fetch3In.main_epoch != f_main_epoch || fetch3In.decode_epoch != decode_epoch[1]) begin
|
||||
pending_straddle = tagged Invalid;
|
||||
end
|
||||
if (verbosity >= 2) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: Drop: main_epoch: %d decode epoch: %d fetch3 epoch %d", f_main_epoch, decode_epoch[1], fetch3_epoch);
|
||||
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
|
||||
$display ("Fetch3: inst_d: ", fshow (inst_d));
|
||||
end
|
||||
end
|
||||
else if (parse_f22f3) begin
|
||||
// Re-interpret fetched 32b parcels (inst_d) as 16b parcels
|
||||
let { n_x16s, v_x16 } <- fav_inst_d_to_x16s (inst_d);
|
||||
// Cap n_x16s, as otherwise we misattribute the bundle's PC
|
||||
// prediction to a later instruction and erroneously think we
|
||||
// took a branch miss. This condition is hit because the cache
|
||||
// interface uses aligned 32b parcels and thus we can end up with
|
||||
// an extra 16b parcel after the window we want. Note that
|
||||
// nbSupX2In will still include the first 16b parcel even if our PC
|
||||
// is misaligned, but this will be discarded by fav_parse_insts.
|
||||
if (n_x16s > extend(nbSupX2In) + 1)
|
||||
n_x16s = extend(nbSupX2In) + 1;
|
||||
|
||||
if (fetch3In.decode_epoch != decode_epoch[1]) begin
|
||||
// Just drop it.
|
||||
if (verbosity > 0) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: Drop: decode epoch: %d", decode_epoch[1]);
|
||||
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
|
||||
$display ("Fetch3: inst_d: ", fshow (inst_d));
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// Re-interpret fetched 32b parcels (inst_d) as 16b parcels
|
||||
match { .n_x16s, .v_x16 } <- fav_inst_d_to_x16s (inst_d);
|
||||
Addr start_PC = fetch3In.pc;
|
||||
// Parse v_x16 into 32-bit and 16-bit instructions
|
||||
Addr pred_next_pc;
|
||||
{parsed_n_items, parsed_v_items, pred_next_pc, pending_straddle} <-
|
||||
fav_parse_insts (verbose, fetch3In.pc, fetch3In.pred_next_pc, pending_straddle, n_x16s, v_x16);
|
||||
|
||||
// Handle cache-line boundary straddling instruction, if one is pending
|
||||
if (ehr_pending_straddle[1]) begin
|
||||
if (fetch3In.pc != ehr_half_inst_pc[1] + 4) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: straddle: pc mismatch");
|
||||
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
|
||||
$display ("Fetch3: inst_d: ", fshow (inst_d));
|
||||
dynamicAssert (False, "Fetch3: straddle: pc mismatch");
|
||||
end
|
||||
else begin
|
||||
// Prepend onto the sequence: { first-half of the instruction , 0 }
|
||||
v_x16 = shiftInAt0 (shiftInAt0 (v_x16, ehr_half_inst_lsbs[1]), 0);
|
||||
let bound = valueOf (SupSizeX2) - 1;
|
||||
if (n_x16s < (fromInteger (bound) - 1))
|
||||
n_x16s = n_x16s + 2;
|
||||
else if (n_x16s < fromInteger (bound))
|
||||
n_x16s = n_x16s + 1;
|
||||
start_PC = ehr_half_inst_pc[1];
|
||||
ehr_pending_straddle[1] <= False;
|
||||
if (verbosity > 0) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: straddle: prepend x16 %0h", ehr_half_inst_lsbs[1]);
|
||||
$display ("Fetch3: f22f3.first: ", fshow (f22f3.first));
|
||||
$display ("Fetch3: inst_d: ", fshow (inst_d));
|
||||
$display ("Fetch3: v_x16: ", fshow (v_x16));
|
||||
end
|
||||
end
|
||||
end
|
||||
if (pending_n_items == 0) begin
|
||||
out = Fetch3ToDecode {
|
||||
pc: fetch3In.pc,
|
||||
pred_next_pc: pred_next_pc,
|
||||
mispred_first_half: mispred_first_half,
|
||||
cause: fetch3In.cause,
|
||||
tval: fetch3In.tval,
|
||||
decode_epoch: fetch3In.decode_epoch,
|
||||
main_epoch: fetch3In.main_epoch
|
||||
};
|
||||
end
|
||||
else begin
|
||||
out.pred_next_pc = pred_next_pc;
|
||||
end
|
||||
|
||||
// Parse v_x16 into 32-bit and 16-bit instructions
|
||||
Vector #(SupSize, Inst_Item) v_items <- fav_parse_insts (verbose, start_PC, n_x16s, v_x16);
|
||||
// Redirect doFetch1 if we predicted a taken compressed branch
|
||||
// but this is an uncompressed instruction. We will tell decode
|
||||
// to retrain when we issue the full instruction next time.
|
||||
if (pending_straddle matches tagged Valid {.s_pc, .s_lsbs, .s_mispred}
|
||||
&&& s_mispred) begin
|
||||
pc_reg[pc_fetch3_port] <= s_pc + 2;
|
||||
fetch3_epoch <= ! fetch3_epoch;
|
||||
end
|
||||
end
|
||||
|
||||
instdata.enq (v_items);
|
||||
f32d.enq(f22f3.first);
|
||||
SupCntX2S1 next_pending_n_items = 0;
|
||||
|
||||
if (pending_n_items > 0 || parse_f22f3) begin
|
||||
SupCntX3S1 n_items = extend(pending_n_items) + extend(parsed_n_items);
|
||||
Bit #(TLog #(SupSize)) nbSupOut = truncate(n_items - 1);
|
||||
|
||||
let pending_spaces = fromInteger(valueOf(SupSizeX2S1)) - pending_n_items;
|
||||
Vector #(SupSizeX2S1, Inst_Item) pending_items_ralign =
|
||||
shiftOutFromN(inst_item_none, rg_pending_decode, pending_spaces);
|
||||
// Appease bluespec compiler with seemingly-unnecessary extension;
|
||||
// otherwise elaboration fails with:
|
||||
// Error: "Vector.bs", line 791, column 33: (T0051)
|
||||
// Literal 7 is not a valid Bit#(2).
|
||||
// During elaboration of the body of rule `doFetch3' at
|
||||
// ...
|
||||
SupCntX4S1 pending_spaces_ext = extend(pending_spaces);
|
||||
Vector #(SupSizeX3S1, Inst_Item) v_items =
|
||||
take(shiftOutFrom0(inst_item_none, append(pending_items_ralign, parsed_v_items), pending_spaces_ext));
|
||||
|
||||
// Handle decoding more instructions than we can issue this cycle
|
||||
if (n_items > fromInteger(valueOf(SupSize))) begin
|
||||
nbSupOut = fromInteger(valueOf(SupSize) - 1);
|
||||
|
||||
if (!isValid(out.cause)) begin
|
||||
next_pending_n_items = truncate(n_items - fromInteger(valueOf(SupSize)));
|
||||
rg_pending_decode <= drop(v_items);
|
||||
rg_pending_f32d <= Fetch3ToDecode {
|
||||
pc: v_items[valueOf(SupSize)].pc,
|
||||
pred_next_pc: out.pred_next_pc,
|
||||
mispred_first_half: False,
|
||||
cause: tagged Invalid,
|
||||
tval: 0,
|
||||
decode_epoch: out.decode_epoch,
|
||||
main_epoch: out.main_epoch
|
||||
};
|
||||
end
|
||||
|
||||
out.pred_next_pc = v_items[valueOf(SupSize)].pc;
|
||||
end
|
||||
|
||||
if (n_items > 0) begin
|
||||
`ifdef RVFI_DII
|
||||
dii_fetched_ids.enq(ii.ids);
|
||||
dii_fetched_ids.enq(ii.ids);
|
||||
`endif
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: epoch inst: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
|
||||
$display ("Fetch3: inst_d: ", fshow (inst_d));
|
||||
$display ("Fetch3: v_items: ", fshow (v_items));
|
||||
$display ("Fetch3: f32d.enq: ", fshow (f22f3.first));
|
||||
end
|
||||
end
|
||||
instdata.enq(take(v_items));
|
||||
f32d.enq(tuple2(nbSupOut, out));
|
||||
if (verbosity >= 2) begin
|
||||
$display ("----------------");
|
||||
$display ("Fetch3: epoch inst: %d, epoch main : %d", out.main_epoch, f_main_epoch);
|
||||
$display ("Fetch3: inst_d: ", fshow (inst_d));
|
||||
$display ("Fetch3: v_items: ", fshow (v_items));
|
||||
$display ("Fetch3: f32d.enq: nbSup %0d out ", nbSupOut, fshow (out));
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// This means we started fetching from a line straddling
|
||||
// instruction; need another cycle to have something to
|
||||
// issue.
|
||||
dynamicAssert(isValid(pending_straddle), "Decoded no instructions and no straddle!");
|
||||
end
|
||||
end
|
||||
rg_pending_n_items <= next_pending_n_items;
|
||||
ehr_pending_straddle[0] <= pending_straddle;
|
||||
endrule: doFetch3
|
||||
|
||||
rule doDecode;
|
||||
let {nbSup, fetch3In} = f32d.first;
|
||||
let {nbSup, decodeIn} = f32d.first;
|
||||
f32d.deq();
|
||||
let inst_data = instdata.first();
|
||||
instdata.deq();
|
||||
@@ -680,7 +857,7 @@ module mkFetchStage(FetchStage);
|
||||
`endif
|
||||
// The main_epoch check is required to make sure this stage doesn't
|
||||
// redirect the PC if a later stage already redirected the PC.
|
||||
if (fetch3In.main_epoch == f_main_epoch) begin
|
||||
if (decodeIn.main_epoch == f_main_epoch) begin
|
||||
Bool decode_epoch_local = decode_epoch[0]; // next value for decode epoch
|
||||
Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
|
||||
Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
|
||||
@@ -691,49 +868,23 @@ module mkFetchStage(FetchStage);
|
||||
`endif
|
||||
|
||||
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
|
||||
if ((inst_data[i].inst_kind == Inst_32b_Lsbs) && (fromInteger(i) <= nbSup)) begin
|
||||
if (fetch3In.decode_epoch == decode_epoch_local) begin
|
||||
// Save the half-instruction and redirect doFetch1 to get the next cache line
|
||||
ehr_pending_straddle[0] <= True;
|
||||
ehr_half_inst_pc[0] <= inst_data[i].pc;
|
||||
ehr_half_inst_lsbs[0] <= inst_data[i].orig_inst [15:0];
|
||||
decode_epoch_local = ! decode_epoch_local;
|
||||
let next_PC = inst_data[i].pc + 4;
|
||||
redirectPc = tagged Valid (next_PC);
|
||||
// We don't train NAP because that's about the dynamic successor to this instruction,
|
||||
// not about the second half of this instruction.
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display ("----------------");
|
||||
$display ("FetchStage.doDecode [%0d]: straddle. pc %0h x16 %0h redirecting to %0h new decode_epoch %d",
|
||||
i, inst_data[i].pc, x16, next_PC, decode_epoch_local);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// just drop wrong path instructions
|
||||
if (verbose) begin
|
||||
$display ("FetchStage.doDecode [%0d]: Inst_32b_Lsbs: drop due to decode epoch", i);
|
||||
$display (" inst_data = ", fshow (inst_data));
|
||||
end
|
||||
end
|
||||
end
|
||||
else if (inst_data[i].inst_kind != Inst_None && (fromInteger(i) <= nbSup)) begin
|
||||
if (inst_data[i].inst_kind != Inst_None && (fromInteger(i) <= nbSup)) begin
|
||||
// Inst_16b or Inst_32b
|
||||
// get the input to decode
|
||||
let inst_data_shifted = shiftInAtN (inst_data, ?); // for predicted PCs
|
||||
let in = Fetch3ToDecode {
|
||||
let in = InstrFromFetch3 {
|
||||
pc: inst_data[i].pc,
|
||||
// last inst, next pc may not be pc+2/pc+4
|
||||
ppc: ((fromInteger(i) == nbSup)
|
||||
? fetch3In.pred_next_pc
|
||||
? decodeIn.pred_next_pc
|
||||
: inst_data_shifted[i].pc),
|
||||
decode_epoch: fetch3In.decode_epoch,
|
||||
main_epoch: fetch3In.main_epoch,
|
||||
decode_epoch: decodeIn.decode_epoch,
|
||||
main_epoch: decodeIn.main_epoch,
|
||||
inst: inst_data [i].inst, // original 32b inst, or expanded version of 16b inst
|
||||
cause: fetch3In.cause
|
||||
cause: decodeIn.cause
|
||||
};
|
||||
let cause = in.cause;
|
||||
Addr tval = fetch3In.tval;
|
||||
Addr tval = decodeIn.tval;
|
||||
if (verbose)
|
||||
$display("Decode: %0d in = ", i, fshow (in));
|
||||
|
||||
@@ -747,7 +898,7 @@ module mkFetchStage(FetchStage);
|
||||
// update cause and tval if decode exception and no earlier (TLB) exception
|
||||
if (!isValid(cause)) begin
|
||||
cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
|
||||
tval = fetch3In.tval;
|
||||
tval = decodeIn.tval;
|
||||
end
|
||||
|
||||
let dInst = decode_result.dInst;
|
||||
@@ -811,6 +962,13 @@ module mkFetchStage(FetchStage);
|
||||
fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
|
||||
end
|
||||
|
||||
if (i == 0 && decodeIn.mispred_first_half) begin
|
||||
// We predicted a taken branch for PC, but this is an
|
||||
// uncompressed instruction, so we train it to fetch
|
||||
// the other half in future.
|
||||
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: in.pc + 2});
|
||||
end
|
||||
|
||||
// check previous mispred
|
||||
if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
|
||||
if (verbose) $display("ppc and decodeppc : %h %h", in.ppc, decode_pred_next_pc);
|
||||
@@ -818,7 +976,9 @@ module mkFetchStage(FetchStage);
|
||||
redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
|
||||
in.ppc = decode_pred_next_pc;
|
||||
// train next addr pred when mispredict
|
||||
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
|
||||
let last_x16_pc = in.pc + ((inst_data[i].inst_kind == Inst_32b) ? 2 : 0);
|
||||
if (!decodeIn.mispred_first_half)
|
||||
trainNAP = Valid (TrainNAP {pc: last_x16_pc, nextPc: decode_pred_next_pc});
|
||||
`ifdef RVFI_DII
|
||||
nextId = fromMaybe(nextId,ids[i]) + 1;
|
||||
`endif
|
||||
@@ -843,8 +1003,13 @@ module mkFetchStage(FetchStage);
|
||||
`endif
|
||||
tval: tval};
|
||||
out_fifo.enqS[i].enq(out);
|
||||
if (verbosity > 0)
|
||||
$display("Decode: ", fshow(out));
|
||||
if (verbosity >= 1) begin
|
||||
$write ("%0d: %m.rule doDecode: out_fifo.enqS[%0d].enq", cur_cycle, i);
|
||||
$display (" pc %0h inst %08h", out.pc, out.orig_inst);
|
||||
end
|
||||
if (verbosity >= 2) begin
|
||||
$display (" ", fshow(out));
|
||||
end
|
||||
end // if (in.decode_epoch == decode_epoch_local)
|
||||
else begin
|
||||
if (verbose) $display("Drop decoded within a superscalar");
|
||||
@@ -882,7 +1047,7 @@ module mkFetchStage(FetchStage);
|
||||
endcase
|
||||
end
|
||||
`endif
|
||||
end // if (fetch3In.main_epoch == f_main_epoch)
|
||||
end // if (decodeIn.main_epoch == f_main_epoch)
|
||||
else begin
|
||||
if (verbose) $display("drop in fetch3decode");
|
||||
end
|
||||
@@ -903,7 +1068,7 @@ module mkFetchStage(FetchStage);
|
||||
// only when misprediction happens, i.e., train by dec is already at
|
||||
// wrong path.
|
||||
TrainNAP train = fromMaybe(validValue(napTrainByDec.wget), napTrainByExe.wget);
|
||||
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
|
||||
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 2);
|
||||
endrule
|
||||
|
||||
// Security: we can flush when front end is empty, i.e.
|
||||
@@ -954,7 +1119,7 @@ module mkFetchStage(FetchStage);
|
||||
if (verbose) $display("%t Redirect: dii_id_next %d", $time(), id);
|
||||
`endif
|
||||
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
|
||||
ehr_pending_straddle[2] <= False;
|
||||
ehr_pending_straddle[1] <= tagged Invalid;
|
||||
// redirect comes, stop stalling for redirect
|
||||
waitForRedirect <= False;
|
||||
setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
|
||||
@@ -962,6 +1127,14 @@ module mkFetchStage(FetchStage);
|
||||
// we conservatively set wait for flush TODO make this an input parameter
|
||||
waitForFlush <= True;
|
||||
endmethod
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action setWaitFlush;
|
||||
waitForFlush <= True;
|
||||
// $display ("%0d.%m.setWaitFlush", cur_cycle);
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
method Action done_flushing() if (waitForFlush);
|
||||
// signal that the pipeline can resume fetching
|
||||
waitForFlush <= False;
|
||||
@@ -976,7 +1149,7 @@ module mkFetchStage(FetchStage);
|
||||
|
||||
method Action train_predictors(
|
||||
Addr pc, Addr next_pc, IType iType, Bool taken,
|
||||
DirPredTrainInfo dpTrain, Bool mispred
|
||||
DirPredTrainInfo dpTrain, Bool mispred, Bool isCompressed
|
||||
);
|
||||
//if (iType == J || (iType == Br && next_pc < pc)) begin
|
||||
// // Only train the next address predictor for jumps and backward branches
|
||||
@@ -989,7 +1162,8 @@ module mkFetchStage(FetchStage);
|
||||
end
|
||||
// train next addr pred when mispred
|
||||
if(mispred) begin
|
||||
napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
|
||||
let last_x16_pc = pc + (isCompressed ? 0 : 2);
|
||||
napTrainByExe.wset(TrainNAP {pc: last_x16_pc, nextPc: next_pc});
|
||||
end
|
||||
endmethod
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ interface FpuMulDivExeInput;
|
||||
// CSR file
|
||||
method Data csrf_rd(CSR csr);
|
||||
// ROB
|
||||
method Action rob_setExecuted(InstTag t, Bit#(5) fflags);
|
||||
method Action rob_setExecuted(InstTag t, Data dst_data, Bit#(5) fflags);
|
||||
|
||||
// global broadcast methods
|
||||
// write reg file & set both conservative and aggressive sb & wake up inst
|
||||
@@ -228,7 +228,7 @@ module mkFpuMulDivExePipeline#(FpuMulDivExeInput inIfc)(FpuMulDivExePipeline);
|
||||
inIfc.writeRegFile(valid_dst.indx, data);
|
||||
end
|
||||
// update the instruction in the reorder buffer.
|
||||
inIfc.rob_setExecuted(tag, fflags);
|
||||
inIfc.rob_setExecuted(tag, data, fflags);
|
||||
// since FPU op has no spec tag, this doFinish rule is ordered before
|
||||
// other rules that calls incorrectSpec, and BSV compiler creates
|
||||
// cycles in scheduling. We manually creates a conflict between this
|
||||
|
||||
@@ -51,6 +51,8 @@ import L1CoCache::*;
|
||||
import Bypass::*;
|
||||
import LatencyTimer::*;
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
MemFunc mem_func;
|
||||
@@ -78,7 +80,12 @@ typedef struct {
|
||||
LdStQTag ldstq_tag;
|
||||
// result
|
||||
ByteEn shiftedBE;
|
||||
Addr vaddr; // virtual addr
|
||||
Addr vaddr; // virtual addr
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// for those mem instrs that store data
|
||||
Data store_data;
|
||||
ByteEn store_data_BE;
|
||||
`endif
|
||||
Bool misaligned;
|
||||
} MemExeToFinish deriving(Bits, Eq, FShow);
|
||||
|
||||
@@ -145,16 +152,22 @@ interface MemExeInput;
|
||||
method Data csrf_rd(CSR csr);
|
||||
// ROB
|
||||
method Addr rob_getPC(InstTag t);
|
||||
method Action rob_setExecuted_doFinishMem(InstTag t, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
|
||||
method Action rob_setExecuted_doFinishMem(InstTag t,
|
||||
Addr vaddr,
|
||||
Data store_data, ByteEn store_data_BE,
|
||||
Bool access_at_commit, Bool non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, ExtraTraceBundle tb
|
||||
, ExtraTraceBundle tb
|
||||
`endif
|
||||
);
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
method Action rob_setExecuted_doFinishMem_RegData (InstTag t, Data dst_data);
|
||||
`endif
|
||||
);
|
||||
method Action rob_setExecuted_deqLSQ(InstTag t, Maybe#(Exception) cause, Maybe#(LdKilledBy) ld_killed
|
||||
`ifdef RVFI
|
||||
, ExtraTraceBundle tb
|
||||
, ExtraTraceBundle tb
|
||||
`endif
|
||||
);
|
||||
);
|
||||
// MMIO
|
||||
method Bool isMMIOAddr(Addr a);
|
||||
method Action mmioReq(MMIOCRq r);
|
||||
@@ -467,6 +480,10 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
ldstq_tag: x.ldstq_tag,
|
||||
shiftedBE: shiftBE,
|
||||
vaddr: vaddr,
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data: data,
|
||||
store_data_BE: origBE,
|
||||
`endif
|
||||
misaligned: memAddrMisaligned(vaddr, origBE)
|
||||
},
|
||||
specBits: regToExe.spec_bits
|
||||
@@ -496,6 +513,13 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
if(verbose) $display("[doFinishMem] ", fshow(dTlbResp));
|
||||
if(isValid(cause) && verbose) $display(" [doFinishMem - dTlb response] PAGEFAULT!");
|
||||
|
||||
Data store_data = ?;
|
||||
ByteEn store_data_BE = ?;
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data = x.store_data;
|
||||
store_data_BE = x.store_data_BE;
|
||||
`endif
|
||||
|
||||
// check misalignment
|
||||
if(!isValid(cause) && x.misaligned) begin
|
||||
case(x.mem_func)
|
||||
@@ -530,28 +554,22 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
endcase);
|
||||
Bool access_at_commit = !isValid(cause) && (isMMIO || isLrScAmo);
|
||||
Bool non_mmio_st_done = !isValid(cause) && !isMMIO && x.mem_func == St;
|
||||
inIfc.rob_setExecuted_doFinishMem(
|
||||
x.tag,
|
||||
x.vaddr,
|
||||
access_at_commit,
|
||||
non_mmio_st_done
|
||||
inIfc.rob_setExecuted_doFinishMem(x.tag, x.vaddr, store_data, store_data_BE,
|
||||
access_at_commit, non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, ExtraTraceBundle{
|
||||
regWriteData: memData[pack(x.ldstq_tag)],
|
||||
memByteEn: unpack(pack(x.shiftedBE) >> x.vaddr[2:0])
|
||||
}
|
||||
`endif
|
||||
);
|
||||
`ifdef RVFI
|
||||
$display("%t : memData[%x]: %x", $time(), pack(x.ldstq_tag), memData[pack(x.ldstq_tag)]);
|
||||
, ExtraTraceBundle{
|
||||
regWriteData: memData[pack(x.ldstq_tag)],
|
||||
memByteEn: unpack(pack(x.shiftedBE) >> x.vaddr[2:0])
|
||||
}
|
||||
`endif
|
||||
);
|
||||
|
||||
// update LSQ
|
||||
LSQUpdateAddrResult updRes <- lsq.updateAddr(
|
||||
x.ldstq_tag, cause, paddr, isMMIO, x.shiftedBE
|
||||
);
|
||||
|
||||
// issue non-MMIO Ld which has no excpetion and is not waiting for
|
||||
// issue non-MMIO Ld which has no exception and is not waiting for
|
||||
// wrong path resp
|
||||
if (x.mem_func == Ld && !isMMIO &&
|
||||
!isValid(cause) && !updRes.waitWPResp) begin
|
||||
@@ -674,6 +692,11 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
if(verbose) $display("%t : ", $time, rule_name, " ", fshow(tag), "; ", fshow(data), "; ", fshow(res));
|
||||
if(res.dst matches tagged Valid .dst) begin
|
||||
inIfc.writeRegFile(dst.indx, res.data);
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
inIfc.rob_setExecuted_doFinishMem_RegData (res.instTag, res.data);
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// perf: load to use latency
|
||||
let lat <- ldToUseLatTimer.done(tag);
|
||||
@@ -833,6 +856,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
if(lsqDeqLd.dst matches tagged Valid .dst) begin
|
||||
inIfc.writeRegFile(dst.indx, resp);
|
||||
inIfc.setRegReadyAggr_mem(dst.indx);
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqLd.instTag, resp);
|
||||
`endif
|
||||
end
|
||||
inIfc.rob_setExecuted_deqLSQ(
|
||||
lsqDeqLd.instTag,
|
||||
@@ -918,6 +944,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
if(lsqDeqLd.dst matches tagged Valid .dst) begin
|
||||
inIfc.writeRegFile(dst.indx, resp);
|
||||
inIfc.setRegReadyAggr_mem(dst.indx);
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqLd.instTag, resp);
|
||||
`endif
|
||||
end
|
||||
inIfc.rob_setExecuted_deqLSQ(lsqDeqLd.instTag, Invalid, Invalid
|
||||
`ifdef RVFI
|
||||
@@ -1166,6 +1195,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
if(lsqDeqSt.dst matches tagged Valid .dst) begin
|
||||
inIfc.writeRegFile(dst.indx, resp);
|
||||
inIfc.setRegReadyAggr_mem(dst.indx);
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqSt.instTag, resp);
|
||||
`endif
|
||||
end
|
||||
inIfc.rob_setExecuted_deqLSQ(lsqDeqSt.instTag, Invalid, Invalid
|
||||
`ifdef RVFI
|
||||
@@ -1269,6 +1301,9 @@ module mkMemExePipeline#(MemExeInput inIfc)(MemExePipeline);
|
||||
if(lsqDeqSt.dst matches tagged Valid .dst) begin
|
||||
inIfc.writeRegFile(dst.indx, resp);
|
||||
inIfc.setRegReadyAggr_mem(dst.indx);
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
inIfc.rob_setExecuted_doFinishMem_RegData (lsqDeqSt.instTag, resp);
|
||||
`endif
|
||||
end
|
||||
inIfc.rob_setExecuted_deqLSQ(lsqDeqSt.instTag, Invalid, Invalid
|
||||
`ifdef RVFI
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
@@ -48,6 +49,8 @@ import ReservationStationMem::*;
|
||||
import ReservationStationFpuMulDiv::*;
|
||||
import SplitLSQ::*;
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
typedef struct {
|
||||
FetchDebugState fetch;
|
||||
EpochDebugState epoch;
|
||||
@@ -75,6 +78,9 @@ interface RenameInput;
|
||||
method Bool checkDeadlock;
|
||||
// performance
|
||||
method Bool doStats;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Bool core_is_running;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface RenameStage;
|
||||
@@ -83,10 +89,16 @@ interface RenameStage;
|
||||
// deadlock check
|
||||
interface Get#(RenameStuck) renameInstStuck;
|
||||
interface Get#(RenameStuck) renameCorrectPathStuck;
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action debug_halt_req;
|
||||
method Action debug_resume;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
Bool verbose = False;
|
||||
Integer verbosity = 0;
|
||||
|
||||
// func units
|
||||
FetchStage fetchStage = inIfc.fetchIfc;
|
||||
@@ -153,6 +165,24 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Is set to Valid DebugHalt on debugger halt request
|
||||
// Is set to Valid DebugStep on dcsr[stepbit]==1 and one instruction has been processed.
|
||||
// Note (step): 1st instruction is guaranteed architectural, cannot possibly be speculative.
|
||||
// Note (step): 1st instruction may trap; we halt pointing at the trap vector
|
||||
Reg #(Maybe #(Interrupt)) rg_m_halt_req <- mkReg (tagged Invalid);
|
||||
|
||||
function Action fa_step_check;
|
||||
action
|
||||
if (csrf.dcsr_step_bit == 1'b1) begin
|
||||
rg_m_halt_req <= tagged Valid DebugStep;
|
||||
if (verbosity >= 2)
|
||||
$display ("%0d: %m.renameStage.fa_step_check: rg_m_halt_req <= tagged Valid DebugStep", cur_cycle);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
// kill wrong path inst
|
||||
// XXX we have to make this a separate rule instead of merging it with rename correct path
|
||||
// This is because the rename correct path rule is conflict with other rules that redirect
|
||||
@@ -224,7 +254,8 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
Bool read_only = (csr_addr [11:10] == 2'b11);
|
||||
Bool write_deny = (writes_csr && read_only);
|
||||
Bool priv_deny = (csrf.decodeInfo.prv < csr_addr [9:8]);
|
||||
csr_access_trap = (write_deny || priv_deny);
|
||||
Bool unimplemented = (csr_addr == 12'h8ff); // Added by Bluespec
|
||||
csr_access_trap = (write_deny || priv_deny || unimplemented);
|
||||
end
|
||||
|
||||
// Check WFI trap (using a time-out of 0)
|
||||
@@ -234,6 +265,13 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
&& (mstatus_tw == 1'b1)
|
||||
&& (csrf.decodeInfo.prv < prvM));
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
if (rg_m_halt_req matches tagged Valid .cause) begin
|
||||
// Stop due to debugger halt or step
|
||||
trap = tagged Valid (tagged Interrupt cause);
|
||||
end else
|
||||
`endif
|
||||
|
||||
if (isValid(x.cause)) begin
|
||||
// previously found exception
|
||||
trap = tagged Valid (tagged Exception fromMaybe(?, x.cause));
|
||||
@@ -271,8 +309,23 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
|
||||
&& isValid(firstTrap) // take trap
|
||||
&& rob.isEmpty // stall for ROB empty
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
&& inIfc.core_is_running
|
||||
`endif
|
||||
);
|
||||
fetchStage.pipelines[0].deq;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
fa_step_check;
|
||||
|
||||
if (verbosity >= 1) begin
|
||||
if (firstTrap == tagged Valid (tagged Interrupt DebugHalt))
|
||||
$display ("%0d: %m.renameStage.doRenaming_Trap: DebugHalt", cur_cycle);
|
||||
else if (firstTrap == tagged Valid (tagged Interrupt DebugStep))
|
||||
$display ("%0d: %m.renameStage.doRenaming_Trap: DebugStep", cur_cycle);
|
||||
else if (firstTrap == tagged Valid (tagged Exception Breakpoint))
|
||||
$display ("%0d: %m.renameStage.doRenaming_Trap: Breakpoint", cur_cycle);
|
||||
end
|
||||
`endif
|
||||
let x = fetchStage.pipelines[0].first;
|
||||
let pc = x.pc;
|
||||
let orig_inst = x.orig_inst;
|
||||
@@ -296,6 +349,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
orig_inst: orig_inst,
|
||||
iType: dInst.iType,
|
||||
dst: arch_regs.dst,
|
||||
dst_data: ?, // Available only after execution
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data: ?,
|
||||
store_data_BE: ?,
|
||||
`endif
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: False, // no renaming is done
|
||||
trap: firstTrap,
|
||||
@@ -322,6 +381,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
if(firstTrap matches tagged Valid (tagged Interrupt .i)) begin
|
||||
inIfc.issueCsrInstOrInterrupt;
|
||||
end
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
else if (firstTrap == tagged Valid (tagged Exception Breakpoint)) begin
|
||||
inIfc.issueCsrInstOrInterrupt;
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
renameCorrectPath.send;
|
||||
`endif
|
||||
@@ -380,11 +445,18 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
&& !isValid(firstTrap) // not trap
|
||||
&& firstReplay // system inst needs replay
|
||||
&& rob.isEmpty // stall for ROB empty
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
&& inIfc.core_is_running
|
||||
`endif
|
||||
);
|
||||
fetchStage.pipelines[0].deq;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
fa_step_check;
|
||||
`endif
|
||||
let x = fetchStage.pipelines[0].first;
|
||||
let pc = x.pc;
|
||||
let orig_inst = x.orig_inst;
|
||||
let dst = x.regs.dst;
|
||||
let ppc = x.ppc;
|
||||
let main_epoch = x.main_epoch;
|
||||
let dpTrain = x.dpTrain;
|
||||
@@ -459,10 +531,28 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
will_dirty_fpu_state = True;
|
||||
doAssert(False, "system inst never touches FP regs");
|
||||
end
|
||||
|
||||
// CSR instrs that touch certain FP CSRs will dirty FP state.
|
||||
if (dInst.csr matches tagged Valid .csr
|
||||
&&& ((dInst.iType == Csr)
|
||||
&& ((csr == CSRfflags) || (csr == CSRfrm) || (csr == CSRfcsr))))
|
||||
begin
|
||||
Bool is_CSRR_W = (dInst.execFunc == tagged Alu Csrw);
|
||||
Bool rs1_is_0 = ((arch_regs.src2 == tagged Valid (tagged Gpr 0))
|
||||
|| (dInst.imm == tagged Valid 0));
|
||||
will_dirty_fpu_state = (is_CSRR_W || (! rs1_is_0));
|
||||
end
|
||||
|
||||
RobInstState rob_inst_state = to_exec ? NotDone : Executed;
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
orig_inst: orig_inst,
|
||||
iType: dInst.iType,
|
||||
dst: arch_regs.dst,
|
||||
dst_data: ?, // Available only after execution
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data: ?,
|
||||
store_data_BE: ?,
|
||||
`endif
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: True, // XXX we always claim a free reg in rename
|
||||
trap: Invalid, // no trap
|
||||
@@ -529,8 +619,14 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
// turn off speculation for mem inst only, and first inst is mem
|
||||
&& (specNonMem && firstMem)
|
||||
&& rob.isEmpty // stall for ROB empty to process mem inst
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
&& inIfc.core_is_running
|
||||
`endif
|
||||
);
|
||||
fetchStage.pipelines[0].deq;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
fa_step_check;
|
||||
`endif
|
||||
let x = fetchStage.pipelines[0].first;
|
||||
let pc = x.pc;
|
||||
let orig_inst = x.orig_inst;
|
||||
@@ -628,6 +724,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
orig_inst: orig_inst,
|
||||
iType: dInst.iType,
|
||||
dst: arch_regs.dst,
|
||||
dst_data: ?, // Available only after execution
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data: ?,
|
||||
store_data_BE: ?,
|
||||
`endif
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: True, // XXX we always claim a free reg in rename
|
||||
trap: Invalid, // no trap
|
||||
@@ -692,6 +794,9 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
&& (!specNone || rob.isEmpty)
|
||||
// don't process mem inst if we don't allow speculation for mem inst only
|
||||
&& !(specNonMem && firstMem)
|
||||
`endif
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
&& inIfc.core_is_running
|
||||
`endif
|
||||
);
|
||||
// we stop superscalar rename when an instruction cannot be processed:
|
||||
@@ -700,6 +805,11 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
// (c) It is system inst (we handle system inst in a separate rule)
|
||||
// (d) It does not have enough resource
|
||||
Bool stop = False;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// (e) One rename has been done and dcsr.step is set
|
||||
Bool debug_step = False;
|
||||
`endif
|
||||
|
||||
// We automatically stop after an inst cannot be deq from fetch stage
|
||||
// because canDeq signal for sup-fifo is consecutive
|
||||
|
||||
@@ -748,6 +858,13 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
|
||||
Addr fallthrough_pc = ((orig_inst[1:0] == 2'b11) ? pc + 4 : pc + 2);
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
if ((i != 0) && (csrf.dcsr_step_bit == 1'b1)) begin
|
||||
stop = True;
|
||||
debug_step = True;
|
||||
end
|
||||
`endif
|
||||
|
||||
// check for wrong path, if wrong path, don't process it, leave to the other rule in next cycle
|
||||
if(!epochManager.checkEpoch[i].check(main_epoch)) begin
|
||||
stop = True;
|
||||
@@ -971,6 +1088,12 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
orig_inst: orig_inst,
|
||||
iType: dInst.iType,
|
||||
dst: arch_regs.dst,
|
||||
dst_data: ?, // Available only after execution
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data: ?,
|
||||
store_data_BE: ?,
|
||||
`endif
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: True, // XXX we always claim a free reg in rename
|
||||
trap: Invalid, // no trap
|
||||
@@ -1007,6 +1130,11 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
if (debug_step)
|
||||
rg_m_halt_req <= tagged Valid DebugStep;
|
||||
`endif
|
||||
|
||||
// only fire this rule if we make some progress
|
||||
// otherwise this rule may block other rules forever
|
||||
when(doCorrectPath, noAction);
|
||||
@@ -1047,4 +1175,19 @@ module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action debug_halt_req () if (rg_m_halt_req == tagged Invalid);
|
||||
rg_m_halt_req <= tagged Valid DebugHalt;
|
||||
if (verbosity >= 1)
|
||||
$display ("%0d: %m.renameStage.renameStage.debug_halt_req", cur_cycle);
|
||||
endmethod
|
||||
|
||||
method Action debug_resume;
|
||||
rg_m_halt_req <= tagged Invalid;
|
||||
if (verbosity >= 1)
|
||||
$display ("%0d: %m.renameStage.renameStage.debug_resume", cur_cycle);
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
@@ -35,8 +36,8 @@ typedef TDiv#(SupSize, 2) FpuMulDivExeNum;
|
||||
// Phy RFile
|
||||
// write: Alu < FpuMulDiv < Mem
|
||||
// read: Alu, FpuMulDiv, Mem
|
||||
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
|
||||
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
|
||||
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
|
||||
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
|
||||
|
||||
// sb lazy lookup num: same as RFile read, becaues all pipelines recv bypass
|
||||
typedef RFileRdPortNum SbLazyLookupPortNum;
|
||||
@@ -65,6 +66,7 @@ Integer memWrAggrPort = 1 + valueof(FpuMulDivExeNum) + valueof(AluExeNum);
|
||||
function Integer aluRdPort(Integer i) = i;
|
||||
function Integer fpuMulDivRdPort(Integer i) = valueof(AluExeNum) + i;
|
||||
Integer memRdPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
|
||||
Integer debuggerPort = memRdPort + 1;
|
||||
|
||||
// ports for correct spec, ordering doesn't matter
|
||||
typedef TAdd#(2, AluExeNum) CorrectSpecPortNum;
|
||||
|
||||
@@ -31,8 +31,8 @@ export NextAddrPred(..);
|
||||
export mkBtb;
|
||||
|
||||
interface NextAddrPred;
|
||||
method Addr predPc(Addr pc);
|
||||
method Action update(Addr pc, Addr nextPc, Bool taken);
|
||||
method Maybe#(Addr) predPc(Addr pc);
|
||||
method Action update(Addr pc, Addr brTarget, Bool taken);
|
||||
// security
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
@@ -96,13 +96,13 @@ module mkBtb(NextAddrPred);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
method Addr predPc(Addr pc);
|
||||
method Maybe#(Addr) predPc(Addr pc);
|
||||
BtbIndex index = getIndex(pc);
|
||||
BtbTag tag = getTag(pc);
|
||||
if(valid[index] && tag == tags.sub(index))
|
||||
return next_addrs.sub(index);
|
||||
return tagged Valid next_addrs.sub(index);
|
||||
else
|
||||
return (pc + 4);
|
||||
return tagged Invalid;
|
||||
endmethod
|
||||
|
||||
method Action update(Addr pc, Addr nextPc, Bool taken);
|
||||
|
||||
35
src_Core/RISCY_OOO/procs/lib/FP_Utils.bsv
Normal file
35
src_Core/RISCY_OOO/procs/lib/FP_Utils.bsv
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2019 Bluepec, Inc
|
||||
// This package implements utility functions used by the floating point
|
||||
// related logic
|
||||
package FP_Utils;
|
||||
import FloatingPoint::*;
|
||||
|
||||
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
|
||||
|
||||
// nanbox-ing and its inverse (unbox-ing)
|
||||
// If the raw bits are nan-boxed, the fv_nanbox(fv_unbox) are identity
|
||||
// functions. However, if the raw input was not properly nanboxed, then the
|
||||
// output would be a canonical NaN
|
||||
|
||||
// Take a single precision value and nanboxes it to be able to write it to a
|
||||
// 64-bit FPR register file. This is necessary if single precision operands
|
||||
// used with a register file capable of holding double precision values
|
||||
function Bit #(64) fv_nanbox (Bit #(64) x);
|
||||
Bit #(64) fill_bits = (64'h1 << 32) - 1; // [31: 0] all ones
|
||||
Bit #(64) fill_mask = (fill_bits << 32); // [63:32] all ones
|
||||
return (x | fill_mask);
|
||||
endfunction
|
||||
|
||||
// Take a 64-bit value and check if it is properly nanboxed if operating in a DP
|
||||
// capable environment. If not properly nanboxed, return canonicalNaN32
|
||||
function Float fv_unbox (Bit #(64) x);
|
||||
//`ifdef ISA_D
|
||||
if (x [63:32] == 32'hffffffff)
|
||||
return (unpack (x [31:0]));
|
||||
else
|
||||
return (canonicalNaN);
|
||||
//`else
|
||||
// return (unpack (x [31:0]));
|
||||
//`endif
|
||||
endfunction
|
||||
endpackage
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2016, 2017 Massachusetts Institute of Technology
|
||||
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
@@ -41,6 +42,7 @@ import XilinxFpu::*;
|
||||
import HasSpecBits::*;
|
||||
import SpecFifo::*;
|
||||
import SpecPoisonFifo::*;
|
||||
import FP_Utils::*;
|
||||
|
||||
export FpuResult(..);
|
||||
export FpuResp(..);
|
||||
@@ -50,8 +52,6 @@ export mkFpuExecPipeline;
|
||||
typedef FloatingPoint::RoundMode FpuRoundMode;
|
||||
typedef FloatingPoint::Exception FpuException;
|
||||
|
||||
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
|
||||
|
||||
typedef struct {
|
||||
Data data;
|
||||
Bit#(5) fflags;
|
||||
@@ -172,26 +172,36 @@ function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_wu (Bit#(64) in_bits,
|
||||
endfunction
|
||||
|
||||
function Tuple2#(Bit#(64), FpuException) fmin_s(Bit#(64) in1, Bit#(64) in2);
|
||||
Float in1_f = unpack(in1[31:0]);
|
||||
Float in2_f = unpack(in2[31:0]);
|
||||
// nirajns: interpret the inputs as floats. Observe that this function
|
||||
// receives raw bits.
|
||||
Float in1_f = fv_unbox(in1);
|
||||
Float in2_f = fv_unbox(in2);
|
||||
Bit #(64) in1_f_packed = fv_nanbox (zeroExtend(pack(in1_f)));
|
||||
Bit #(64) in2_f_packed = fv_nanbox (zeroExtend(pack(in2_f)));
|
||||
|
||||
Float nan_f = qnan(); // canonical NAN
|
||||
FpuException e = unpack(0);
|
||||
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
|
||||
// nirajns: TEST 21 failure on fmin ISA tests
|
||||
// e.invalid_op should only be signalled only if either operand is a sNaN
|
||||
// as the fmin and fmax are quiet comparison
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
|
||||
e.invalid_op = True;
|
||||
return tuple2(zeroExtend(pack(nan_f)), e);
|
||||
end
|
||||
if (isNaN(in1_f) && isNaN(in2_f)) begin
|
||||
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
|
||||
end else if (isNaN(in2_f)) begin
|
||||
return tuple2(in1, e);
|
||||
return tuple2(in1_f_packed, e);
|
||||
end else if (isNaN(in1_f)) begin
|
||||
return tuple2(in2, e);
|
||||
return tuple2(in2_f_packed, e);
|
||||
end else begin
|
||||
let signLT = (in1_f.sign && !in2_f.sign);
|
||||
let signEQ = in1_f.sign == in2_f.sign;
|
||||
let absLT = {in1_f.exp, in1_f.sfd} < {in2_f.exp, in2_f.sfd};
|
||||
if (signLT || (signEQ && (in1_f.sign ? !absLT : absLT))) begin
|
||||
return tuple2(in1, e);
|
||||
return tuple2(in1_f_packed, e);
|
||||
end else begin
|
||||
return tuple2(in2, e);
|
||||
return tuple2(in2_f_packed, e);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
@@ -202,9 +212,14 @@ function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
|
||||
Double nan_f = qnan(); // canonical NAN
|
||||
FpuException e = unpack(0);
|
||||
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
|
||||
// nirajns: TEST 21 failure on fmin ISA tests
|
||||
// e.invalid_op should only be signalled only if either operand is a sNaN
|
||||
// as the fmin and fmax are quiet comparison
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
|
||||
e.invalid_op = True;
|
||||
return tuple2(pack(nan_f), e);
|
||||
end
|
||||
if (isNaN(in1_f) && isNaN(in2_f)) begin
|
||||
return tuple2(zeroExtend(pack(nan_f)), e);
|
||||
end else if (isNaN(in2_f)) begin
|
||||
return tuple2(in1, e);
|
||||
end else if (isNaN(in1_f)) begin
|
||||
@@ -222,26 +237,39 @@ function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
|
||||
endfunction
|
||||
|
||||
function Tuple2#(Bit#(64), FpuException) fmax_s(Bit#(64) in1, Bit#(64) in2);
|
||||
Float in1_f = unpack(in1[31:0]);
|
||||
Float in2_f = unpack(in2[31:0]);
|
||||
// nirajns: interpret the inputs as floats. Observe that this function
|
||||
// receives raw bits.
|
||||
// If the raw bits are nan-boxed, the fv_nanbox(fv_unbox) are identity
|
||||
// functions. However, if the raw input was not properly nanboxed, then
|
||||
// the output would be a canonical NaN
|
||||
Float in1_f = fv_unbox(in1);
|
||||
Float in2_f = fv_unbox(in2);
|
||||
Bit #(64) in1_f_packed = fv_nanbox (zeroExtend(pack(in1_f)));
|
||||
Bit #(64) in2_f_packed = fv_nanbox (zeroExtend(pack(in2_f)));
|
||||
|
||||
Float nan_f = qnan(); // canonical NAN
|
||||
FpuException e = unpack(0);
|
||||
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
|
||||
// nirajns: TEST 21 failure on fmin ISA tests
|
||||
// e.invalid_op should only be signalled only if either operand is a sNaN
|
||||
// as the fmin and fmax are quiet comparison
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
|
||||
e.invalid_op = True;
|
||||
return tuple2(zeroExtend(pack(nan_f)), e);
|
||||
end
|
||||
if (isNaN(in1_f) && isNaN(in2_f)) begin
|
||||
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
|
||||
end else if (isNaN(in2_f)) begin
|
||||
return tuple2(in1, e);
|
||||
return tuple2(in1_f_packed, e);
|
||||
end else if (isNaN(in1_f)) begin
|
||||
return tuple2(in2, e);
|
||||
return tuple2(in2_f_packed, e);
|
||||
end else begin
|
||||
let signGT = (!in1_f.sign && in2_f.sign);
|
||||
let signEQ = in1_f.sign == in2_f.sign;
|
||||
let absGT = {in1_f.exp, in1_f.sfd} > {in2_f.exp, in2_f.sfd};
|
||||
if (signGT || (signEQ && (in1_f.sign ? !absGT : absGT))) begin
|
||||
return tuple2(in1, e);
|
||||
return tuple2(in1_f_packed, e);
|
||||
end else begin
|
||||
return tuple2(in2, e);
|
||||
return tuple2(in2_f_packed, e);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
@@ -252,9 +280,14 @@ function Tuple2#(Bit#(64), FpuException) fmax_d(Bit#(64) in1, Bit#(64) in2);
|
||||
Double nan_f = qnan(); // canonical NAN
|
||||
FpuException e = unpack(0);
|
||||
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
|
||||
// nirajns: TEST 21 failure on fmin ISA tests
|
||||
// e.invalid_op should only be signalled only if either operand is a sNaN
|
||||
// as the fmin and fmax are quiet comparison
|
||||
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
|
||||
e.invalid_op = True;
|
||||
return tuple2(pack(nan_f), e);
|
||||
end
|
||||
if (isNaN(in1_f) && isNaN(in2_f)) begin
|
||||
return tuple2(zeroExtend(pack(nan_f)), e);
|
||||
end else if (isNaN(in2_f)) begin
|
||||
return tuple2(in1, e);
|
||||
end else if (isNaN(in1_f)) begin
|
||||
@@ -417,8 +450,9 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
|
||||
|
||||
if (fpu_inst.precision == Single) begin
|
||||
// single precision
|
||||
Float in1 = unpack(rVal1[31:0]);
|
||||
Float in2 = unpack(rVal2[31:0]);
|
||||
// nirajns: interpret them as floats
|
||||
Float in1 = fv_unbox(rVal1);
|
||||
Float in2 = fv_unbox(rVal2);
|
||||
Float dst = unpack(0);
|
||||
Maybe#(Data) full_dst = Invalid;
|
||||
FpuException e = unpack(0);
|
||||
@@ -436,18 +470,33 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
|
||||
{x, e} = fmax_s(rVal1, rVal2);
|
||||
full_dst = tagged Valid x;
|
||||
end
|
||||
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
|
||||
FEq: begin
|
||||
// nirajns: TEST 10 failure on fcmp ISA tests
|
||||
Data x;
|
||||
if (isNaN (in1) || isNaN (in2)) x = 0;
|
||||
else x = zeroExtend(pack(compareFP(in1, in2) == EQ));
|
||||
if (isSNaN(in1) || isSNaN(in2)) begin
|
||||
e.invalid_op = True;
|
||||
end
|
||||
full_dst = tagged Valid x;
|
||||
end
|
||||
FLt: begin
|
||||
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
|
||||
Data x;
|
||||
if (isNaN (in1) || isNaN (in2)) x = 0;
|
||||
else x = zeroExtend(pack(compareFP(in1, in2) == LT));
|
||||
if (isNaN(in1) || isNaN(in2)) begin
|
||||
e.invalid_op = True;
|
||||
end
|
||||
full_dst = tagged Valid x;
|
||||
end
|
||||
FLe: begin
|
||||
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
|
||||
Data x;
|
||||
if (isNaN (in1) || isNaN (in2)) x = 0;
|
||||
else x = zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ)));
|
||||
if (isNaN(in1) || isNaN(in2)) begin
|
||||
e.invalid_op = True;
|
||||
end
|
||||
full_dst = tagged Valid x;
|
||||
end
|
||||
// CLASS functions
|
||||
FClass: begin
|
||||
@@ -481,9 +530,11 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
|
||||
dst.sign = unpack(pack(in1.sign) ^ pack(in2.sign));
|
||||
end
|
||||
// Float -> Bits
|
||||
FMv_XF: full_dst = tagged Valid signExtend(pack(in1));
|
||||
// nirajns: don't interpret the bits - use raw bits rVal1
|
||||
FMv_XF: full_dst = tagged Valid signExtend(pack(rVal1[31:0]));
|
||||
// Bits -> Float
|
||||
FMv_FX: full_dst = tagged Valid zeroExtend(pack(in1));
|
||||
// nirajns: don't interpret the bits - use raw bits rVal1
|
||||
FMv_FX: full_dst = tagged Valid fv_nanbox (zeroExtend(pack(rVal1[31:0])));
|
||||
// Float -> Float
|
||||
FCvt_FF: begin
|
||||
Double in1_double = unpack(rVal1);
|
||||
@@ -529,7 +580,7 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
|
||||
if (isNaN(dst)) dst = canonicalNaN;
|
||||
end
|
||||
endcase
|
||||
fpu_result.data = (full_dst matches tagged Valid .data ? data : zeroExtend(pack(dst)));
|
||||
fpu_result.data = (full_dst matches tagged Valid .data ? data : fv_nanbox(zeroExtend(pack(dst))));
|
||||
fpu_result.fflags = pack(e);
|
||||
end else if (fpu_inst.precision == Double) begin
|
||||
// double precision
|
||||
@@ -552,15 +603,24 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
|
||||
{x, e} = fmax_d(rVal1, rVal2);
|
||||
full_dst = tagged Valid x;
|
||||
end
|
||||
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
|
||||
FEq: begin
|
||||
// nirajns: TEST 10 failure on fcmp ISA tests
|
||||
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
|
||||
else dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
|
||||
if (isSNaN(in1) || isSNaN(in2)) begin
|
||||
e.invalid_op = True;
|
||||
end
|
||||
end
|
||||
FLt: begin
|
||||
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
|
||||
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
|
||||
else dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
|
||||
if (isNaN(in1) || isNaN(in2)) begin
|
||||
e.invalid_op = True;
|
||||
end
|
||||
end
|
||||
FLe: begin
|
||||
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
|
||||
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
|
||||
else dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
|
||||
if (isNaN(in1) || isNaN(in2)) begin
|
||||
e.invalid_op = True;
|
||||
end
|
||||
@@ -720,7 +780,7 @@ module mkFpuExecPipeline(FpuExec);
|
||||
// canonicalize NaN
|
||||
out_f = isNaN(out_f) ? canonicalNaN : out_f;
|
||||
res = FpuResult {
|
||||
data: zeroExtend(pack(out_f)),
|
||||
data: fv_nanbox (zeroExtend(pack(out_f))),
|
||||
fflags: pack(info.exc_conv_in | exc_op | exc_conv_out)
|
||||
};
|
||||
end
|
||||
@@ -778,9 +838,10 @@ module mkFpuExecPipeline(FpuExec);
|
||||
Double in3 = unpack(rVal3);
|
||||
if (fpu_inst.precision == Single) begin
|
||||
// conver single to double
|
||||
Float f1 = unpack(rVal1[31:0]);
|
||||
Float f2 = unpack(rVal2[31:0]);
|
||||
Float f3 = unpack(rVal3[31:0]);
|
||||
// nirajns: interpret the raw bits as floats first
|
||||
Float f1 = fv_unbox(rVal1);
|
||||
Float f2 = fv_unbox(rVal2);
|
||||
Float f3 = fv_unbox(rVal3);
|
||||
let {d1, exc1} = fcvt_d_s(f1, fpu_rm);
|
||||
let {d2, exc2} = fcvt_d_s(f2, fpu_rm);
|
||||
let {d3, exc3} = fcvt_d_s(f3, fpu_rm);
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Portions Copyright (c) 2019-2020 Bluespec, Inc.
|
||||
|
||||
// This file is a modified version of: RISCY_OOO/procs/lib/LLCDmaConnect.bsv
|
||||
// Bluespec: this file is has many modifications.
|
||||
|
||||
// The original module had 3 params and had an Empty interface.
|
||||
// The 2nd param was: MemLoaderMemClient memLoader
|
||||
// which issued only write-transactions (to load memory).
|
||||
// The module discarded write responses, and ignored read-requests.
|
||||
|
||||
// Here, that module parameter is removed and, instead, the module has an
|
||||
// AXI4_Slave interface, to be connected to the AXI4_Master of the
|
||||
// Debug Module. This axi4_slave accepts, processes and responds
|
||||
// to both read and write transactions.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
@@ -21,12 +34,31 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import FShow::*;
|
||||
import GetPut::*;
|
||||
import Vector::*;
|
||||
import BuildVector::*;
|
||||
import FIFO::*;
|
||||
import Assert::*;
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
import FShow :: *;
|
||||
import GetPut :: *;
|
||||
import Vector :: *;
|
||||
import BuildVector :: *;
|
||||
import FIFO :: *;
|
||||
import Assert :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import EdgeFIFOFs :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From RISCY-OOO
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
@@ -37,6 +69,15 @@ import MemLoader::*;
|
||||
import CrossBar::*;
|
||||
import MemLoader::*;
|
||||
|
||||
// ----------------
|
||||
// From Toooba
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
typedef struct {
|
||||
CoreId core;
|
||||
TlbMemReqId id;
|
||||
@@ -48,15 +89,277 @@ typedef union tagged {
|
||||
TlbDmaReqId Tlb;
|
||||
} LLCDmaReqId deriving(Bits, Eq, FShow);
|
||||
|
||||
module mkLLCDmaConnect#(
|
||||
// ================================================================
|
||||
// Help functions for read-modify-writes of 4-Byte values on a 64-Byte Cache Line
|
||||
|
||||
typedef enum {CACHELINE_CACHE_INVALID,
|
||||
CACHELINE_CACHE_WRITING_BACK,
|
||||
CACHELINE_CACHE_RELOADING,
|
||||
CACHELINE_CACHE_CLEAN,
|
||||
CACHELINE_CACHE_DIRTY
|
||||
} Cacheline_Cache_State
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
function Addr fn_align_addr_to_line (Addr addr);
|
||||
Addr line_addr = { addr [63:6], 6'b0 };
|
||||
return line_addr;
|
||||
endfunction
|
||||
|
||||
function Bool fn_addr_is_in_line (Addr addr, Addr line_addr);
|
||||
return (fn_align_addr_to_line (addr) == line_addr);
|
||||
endfunction
|
||||
|
||||
function Bit #(64) fn_expand_strb_to_mask (Bit #(8) strb);
|
||||
function Bit #(8) fn_bit_to_byte (Integer j);
|
||||
return ((strb [j] == 1'b1) ? 8'hFF : 8'h00);
|
||||
endfunction
|
||||
|
||||
Vector #(8, Bit #(8)) v = genWith (fn_bit_to_byte);
|
||||
|
||||
return pack (v);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
module mkLLCDmaConnect #(
|
||||
DmaServer#(LLCDmaReqId) llc,
|
||||
MemLoaderMemClient memLoader,
|
||||
// MemLoaderMemClient memLoader, // REPLACED BY AXI4_Slave_interface
|
||||
Vector#(CoreNum, TlbMemClient) tlb
|
||||
)(Empty) provisos (
|
||||
)(AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User)) provisos (
|
||||
Alias#(dmaRqT, DmaRq#(LLCDmaReqId))
|
||||
);
|
||||
Bool verbose = False;
|
||||
|
||||
Integer verbosity = 0;
|
||||
|
||||
// When debugger reads a word, request a line from LLC, and remember dword-in-line here
|
||||
FIFOF #(Bit #(3)) f_dword_in_line <- mkFIFOF;
|
||||
|
||||
// Slave transactor for requests from Debug Module
|
||||
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) axi4_slave_xactor <- mkAXI4_Slave_Xactor;
|
||||
|
||||
// ================================================================
|
||||
// These regs are a 1-location local cache for an LLC Cache Line,
|
||||
// to avoid doing a full read-modify-write to the LLC on each transaction.
|
||||
|
||||
Reg #(Cacheline_Cache_State) rg_cacheline_cache_state <- mkReg (CACHELINE_CACHE_CLEAN);
|
||||
Reg #(Addr) rg_cacheline_cache_addr <- mkReg (1); // never matches an LLC line addr
|
||||
Reg #(Line) rg_cacheline_cache_data <- mkRegU;
|
||||
|
||||
// Writeback dirty cacheline_cache if no new store requests within n-cycles
|
||||
Reg #(Bit #(10)) rg_cacheline_cache_dirty_delay <- mkReg (0);
|
||||
|
||||
// ================================================================
|
||||
// Write transactions from the external client (e.g., Debug Module)
|
||||
|
||||
// Respond to store-requests from the external client on store-hit
|
||||
rule rl_handle_MemLoader_st_req ( ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
|
||||
|| (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY))
|
||||
&& (fn_addr_is_in_line (axi4_slave_xactor.o_wr_addr.first.awaddr,
|
||||
rg_cacheline_cache_addr)));
|
||||
let wr_addr <- pop_o (axi4_slave_xactor.o_wr_addr);
|
||||
let wr_data <- pop_o (axi4_slave_xactor.o_wr_data);
|
||||
Addr addr = wr_addr.awaddr;
|
||||
Bit #(64) data = wr_data.wdata;
|
||||
Bit #(64) mask = fn_expand_strb_to_mask (wr_data.wstrb);
|
||||
|
||||
// Read rg_cacheline_cache_data as 64-bit words
|
||||
Vector #(8, Bit #(64)) line_dwords = unpack (pack (rg_cacheline_cache_data));
|
||||
// Modify relevant bytes of relevant dword
|
||||
Bit #(3) dword_in_line = addr [5:3];
|
||||
Bit #(64) old_dword = line_dwords [dword_in_line];
|
||||
Bit #(64) new_dword = ((old_dword & (~ mask)) | (data & mask));
|
||||
line_dwords [dword_in_line] = new_dword;
|
||||
// Save it
|
||||
rg_cacheline_cache_data <= unpack (pack (line_dwords));
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_DIRTY;
|
||||
rg_cacheline_cache_dirty_delay <= '1; // start write-back delay countdown
|
||||
|
||||
// Send response to external client
|
||||
AXI4_Wr_Resp #(Wd_Id, Wd_User)
|
||||
wr_resp = AXI4_Wr_Resp {bid: 0, // TODO: change uniformly to Fabric_id
|
||||
bresp: axi4_resp_okay,
|
||||
buser: ?};
|
||||
axi4_slave_xactor.i_wr_resp.enq (wr_resp);
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_handle_MemLoader_st_req: addr %0h data %0h strb %0h",
|
||||
cur_cycle, wr_addr.awaddr, wr_data.wdata, wr_data.wstrb);
|
||||
$display (" old_dword: %0h", old_dword);
|
||||
$display (" new_dword: %0h", old_dword);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Read transactions from the external memory client (e.g., Debug Module)
|
||||
|
||||
// Responds to load-requests from the external client on load-hit
|
||||
rule rl_handle_MemLoader_ld_req ( ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
|
||||
|| (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY))
|
||||
&& (fn_addr_is_in_line (axi4_slave_xactor.o_rd_addr.first.araddr,
|
||||
rg_cacheline_cache_addr)));
|
||||
let rd_addr <- pop_o (axi4_slave_xactor.o_rd_addr);
|
||||
Addr addr = rd_addr.araddr;
|
||||
|
||||
// Read rg_cacheline_cache as 64-bit words
|
||||
Vector #(8, Bit #(64)) line_dwords = unpack (pack (rg_cacheline_cache_data));
|
||||
Bit #(3) dword_in_line = addr [5:3];
|
||||
Bit #(64) dword = line_dwords [dword_in_line];
|
||||
|
||||
// Send response to external client
|
||||
AXI4_Rd_Data #(Wd_Id, Wd_Data, Wd_User)
|
||||
rd_data = AXI4_Rd_Data {rid: 0, // TODO: fixup
|
||||
rdata: dword,
|
||||
rresp: axi4_resp_okay,
|
||||
rlast: True,
|
||||
ruser: ?};
|
||||
axi4_slave_xactor.i_rd_data.enq (rd_data);
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_handle_MemLoader_ld_req: addr %0h", cur_cycle, rd_addr.araddr);
|
||||
$display (" dword: %0h", dword);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Miss and writeback processing
|
||||
|
||||
// Maintain dirty delay countdown
|
||||
rule rl_cacheline_cache_writeback_dirty_delay ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
|
||||
&& (rg_cacheline_cache_dirty_delay != 0));
|
||||
rg_cacheline_cache_dirty_delay <= rg_cacheline_cache_dirty_delay - 1;
|
||||
endrule
|
||||
|
||||
function Action fa_writeback;
|
||||
action
|
||||
dmaRqT req = DmaRq {addr: rg_cacheline_cache_addr,
|
||||
byteEn: replicate (True), // Write all bytes
|
||||
data: rg_cacheline_cache_data,
|
||||
id: tagged MemLoader (?) // TODO: use wr_addr.awid?
|
||||
};
|
||||
llc.memReq.enq (req);
|
||||
// $display ("%0d: %m.fa_writeback line at %0h", cur_cycle, rg_cacheline_cache_addr);
|
||||
// $display (" data %0128h", rg_cacheline_cache_data);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// Initiate writeback if dirty for full delay
|
||||
rule rl_cacheline_cache_writeback_dirty_aged ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
|
||||
&& (rg_cacheline_cache_dirty_delay == 0));
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_writeback_dirty_aged.", cur_cycle);
|
||||
$display (" Old line addr %0h", rg_cacheline_cache_addr);
|
||||
end
|
||||
|
||||
fa_writeback;
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_WRITING_BACK;
|
||||
endrule
|
||||
|
||||
// Initiate writeback if dirty and next request is store-miss
|
||||
rule rl_cacheline_cache_writeback_st_miss ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
|
||||
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_wr_addr.first.awaddr,
|
||||
rg_cacheline_cache_addr)));
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_writeback_st_miss.", cur_cycle);
|
||||
$display (" Old line addr %0h", rg_cacheline_cache_addr);
|
||||
$display (" New addr %0h", axi4_slave_xactor.o_wr_addr.first.awaddr);
|
||||
end
|
||||
|
||||
fa_writeback;
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_WRITING_BACK;
|
||||
endrule
|
||||
|
||||
// Initiate writeback if dirty and next request is load-miss
|
||||
rule rl_cacheline_cache_writeback_ld_miss ( (rg_cacheline_cache_state == CACHELINE_CACHE_DIRTY)
|
||||
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_rd_addr.first.araddr,
|
||||
rg_cacheline_cache_addr)));
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_writeback_ld_miss.", cur_cycle);
|
||||
$display (" Old line addr %0h", rg_cacheline_cache_addr);
|
||||
$display (" New addr %0h", axi4_slave_xactor.o_wr_addr.first.awaddr);
|
||||
end
|
||||
|
||||
fa_writeback;
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_WRITING_BACK;
|
||||
endrule
|
||||
|
||||
// Finish writeback
|
||||
rule rl_cacheline_cache_writeback_finish (llc.respSt.first matches tagged MemLoader .id
|
||||
&&& (rg_cacheline_cache_state == CACHELINE_CACHE_WRITING_BACK));
|
||||
let resp = llc.respSt.first;
|
||||
llc.respSt.deq;
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_CLEAN;
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_writeback_finish. Line addr %0h",
|
||||
cur_cycle, rg_cacheline_cache_addr);
|
||||
$display (" Line data %0h", rg_cacheline_cache_data);
|
||||
end
|
||||
endrule
|
||||
|
||||
function Action fa_initiate_reload (Addr addr);
|
||||
action
|
||||
let line_addr = fn_align_addr_to_line (addr);
|
||||
dmaRqT req = DmaRq {addr: line_addr,
|
||||
byteEn: replicate (False), // all False means 'read'
|
||||
data: ?,
|
||||
id: tagged MemLoader (?)}; // TODO: change uniformly to wr_addr.awid
|
||||
llc.memReq.enq (req);
|
||||
rg_cacheline_cache_addr <= line_addr;
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display (" fa_initiate_reload: line_addr %0h", line_addr);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// Initiate reload when cacheline_cache is clean on store-miss
|
||||
rule rl_cacheline_cache_reload_req_st ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
|
||||
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_wr_addr.first.awaddr,
|
||||
rg_cacheline_cache_addr)));
|
||||
let addr = axi4_slave_xactor.o_wr_addr.first.awaddr;
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_reload_req_st for addr %0h", cur_cycle, addr);
|
||||
end
|
||||
|
||||
fa_initiate_reload (addr);
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_RELOADING;
|
||||
endrule
|
||||
|
||||
// Initiate reload when cacheline_cache is clean on load-miss
|
||||
rule rl_cacheline_cache_reload_req_ld ( (rg_cacheline_cache_state == CACHELINE_CACHE_CLEAN)
|
||||
&& (! fn_addr_is_in_line (axi4_slave_xactor.o_rd_addr.first.araddr,
|
||||
rg_cacheline_cache_addr)));
|
||||
let addr = axi4_slave_xactor.o_rd_addr.first.araddr;
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_reload_req_ld for addr %0h", cur_cycle, addr);
|
||||
end
|
||||
|
||||
fa_initiate_reload (addr);
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_RELOADING;
|
||||
endrule
|
||||
|
||||
// Finish reload
|
||||
rule rl_cacheline_cache_reload_finish (llc.respLd.first.id matches tagged MemLoader .id
|
||||
&&& (rg_cacheline_cache_state == CACHELINE_CACHE_RELOADING));
|
||||
let resp = llc.respLd.first;
|
||||
llc.respLd.deq;
|
||||
rg_cacheline_cache_state <= CACHELINE_CACHE_CLEAN;
|
||||
rg_cacheline_cache_data <= resp.data;
|
||||
|
||||
if (verbosity >= 2) begin
|
||||
$display ("%0d: %m.rl_cacheline_cache_reload_finish. Line addr %0h", cur_cycle, rg_cacheline_cache_addr);
|
||||
$display (" Line data %0h", resp.data);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Transactions from the TLB
|
||||
// Expecting only LOAD requests from TLB
|
||||
// This section is unchanged from the original riscy-ooo module.
|
||||
|
||||
// helper functions for cross bar
|
||||
function XBarDstInfo#(Bit#(0), Tuple2#(CoreId, TlbMemReq)) getTlbDst(CoreId core, TlbMemReq r);
|
||||
return XBarDstInfo {idx: 0, data: tuple2(core, r)};
|
||||
@@ -83,24 +386,13 @@ module mkLLCDmaConnect#(
|
||||
};
|
||||
endfunction
|
||||
|
||||
// send req to LLC
|
||||
rule sendMemLoaderReqToLLC;
|
||||
memLoader.memReq.deq;
|
||||
let r = memLoader.memReq.first;
|
||||
dmaRqT req = DmaRq {
|
||||
addr: r.addr,
|
||||
byteEn: r.byteEn,
|
||||
data: r.data,
|
||||
id: MemLoader (r.id)
|
||||
};
|
||||
llc.memReq.enq(req);
|
||||
if(verbose) begin
|
||||
$display("[LLCDmaConnnect sendMemLoaderReqToLLC] ",
|
||||
fshow(r), " ; ", fshow(req));
|
||||
end
|
||||
endrule
|
||||
// Prioritize external mem client over Tlb
|
||||
(* descending_urgency = "rl_cacheline_cache_writeback_dirty_aged, sendTlbReqToLLC" *)
|
||||
(* descending_urgency = "rl_cacheline_cache_writeback_st_miss, sendTlbReqToLLC" *)
|
||||
(* descending_urgency = "rl_cacheline_cache_writeback_ld_miss, sendTlbReqToLLC" *)
|
||||
(* descending_urgency = "rl_cacheline_cache_reload_req_st, sendTlbReqToLLC" *)
|
||||
(* descending_urgency = "rl_cacheline_cache_reload_req_ld, sendTlbReqToLLC" *)
|
||||
|
||||
(* descending_urgency = "sendMemLoaderReqToLLC, sendTlbReqToLLC" *)
|
||||
rule sendTlbReqToLLC;
|
||||
let {c, r} <- toGet(tlbQ).get;
|
||||
let req = getTlbDmaReq(c, r);
|
||||
@@ -110,16 +402,6 @@ module mkLLCDmaConnect#(
|
||||
end
|
||||
endrule
|
||||
|
||||
// send Ld resp from LLC
|
||||
rule sendLdRespToMemLoader(llc.respLd.first.id matches tagged MemLoader .id);
|
||||
llc.respLd.deq;
|
||||
if(verbose) begin
|
||||
$display("[LLCDmaConnect sendLdRespToMemLoader] ",
|
||||
fshow(llc.respLd.first));
|
||||
end
|
||||
doAssert(False, "No mem loader ld");
|
||||
endrule
|
||||
|
||||
rule sendLdRespToTlb(llc.respLd.first.id matches tagged Tlb .id);
|
||||
llc.respLd.deq;
|
||||
let resp = llc.respLd.first;
|
||||
@@ -133,16 +415,6 @@ module mkLLCDmaConnect#(
|
||||
end
|
||||
endrule
|
||||
|
||||
// send St resp from LLC
|
||||
rule sendStRespToMemLoader(llc.respSt.first matches tagged MemLoader .id);
|
||||
llc.respSt.deq;
|
||||
memLoader.respSt.enq(id);
|
||||
if(verbose) begin
|
||||
$display("[LLCDmaConnect sendStRespToMemLoader] ",
|
||||
fshow(llc.respSt.first));
|
||||
end
|
||||
endrule
|
||||
|
||||
rule sendStRespToTlb(llc.respSt.first matches tagged Tlb .id);
|
||||
llc.respSt.deq;
|
||||
if(verbose) begin
|
||||
@@ -150,4 +422,9 @@ module mkLLCDmaConnect#(
|
||||
end
|
||||
doAssert(False, "No TLB st");
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
return axi4_slave_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2018 Massachusetts Institute of Technology
|
||||
// Portions copyright (c) 2019-2020 Bluespec, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
// Portions (c) 2020 Bluespec, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
@@ -275,8 +276,22 @@ typedef enum {
|
||||
// sanctum user CSR
|
||||
CSRtrng = 12'hcc0, // random number for secure boot
|
||||
`endif
|
||||
|
||||
CSRtselect = 12'h7A0, // Debug/trace tselect
|
||||
CSRtdata1 = 12'h7A1, // Debug/trace tdata1
|
||||
CSRtdata2 = 12'h7A2, // Debug/trace tdata2
|
||||
CSRtdata3 = 12'h7A3, // Debug/trace tdata3
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
CSRdcsr = 12'h7B0, // Debug control and status
|
||||
CSRdpc = 12'h7B1, // Debug PC
|
||||
CSRdscratch0 = 12'h7B2, // Debug scratch0
|
||||
CSRdscratch1 = 12'h7B3, // Debug scratch1
|
||||
`endif
|
||||
|
||||
// CSR that catches all the unimplemented CSRs. To avoid exception on this,
|
||||
// make it a user non-standard read/write CSR.
|
||||
// Bluespec: in RenameStage.getTrap(), we force this to be a csr_access_trap
|
||||
CSRnone = 12'h8ff
|
||||
} CSR deriving(Bits, Eq, FShow);
|
||||
|
||||
@@ -332,6 +347,19 @@ function CSR unpackCSR(Bit#(12) x);
|
||||
pack(CSR'(CSRmspec )): (CSRmspec );
|
||||
pack(CSR'(CSRtrng )): (CSRtrng );
|
||||
`endif
|
||||
|
||||
pack(CSR'(CSRtselect )): (CSRtselect );
|
||||
pack(CSR'(CSRtdata1 )): (CSRtdata1 );
|
||||
pack(CSR'(CSRtdata2 )): (CSRtdata2 );
|
||||
pack(CSR'(CSRtdata3 )): (CSRtdata3 );
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
pack(CSR'(CSRdcsr )): (CSRdcsr );
|
||||
pack(CSR'(CSRdpc )): (CSRdpc );
|
||||
pack(CSR'(CSRdscratch0 )): (CSRdscratch0 );
|
||||
pack(CSR'(CSRdscratch1 )): (CSRdscratch1 );
|
||||
`endif
|
||||
|
||||
default : (CSRnone );
|
||||
endcase);
|
||||
endfunction
|
||||
@@ -452,13 +480,20 @@ typedef enum {
|
||||
MachineTimer = 4'd7,
|
||||
UserExternal = 4'd8,
|
||||
SupervisorExternel = 4'd9,
|
||||
MachineExternal = 4'd11,
|
||||
MachineExternal = 4'd11
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
, DebugHalt = 4'd14, // Debugger halt command (^C in GDB)
|
||||
DebugStep = 4'd15 // dcsr.step is set and 1 instr has been processed
|
||||
`endif
|
||||
|
||||
DebugExternal = 4'd14 // Bluespec: for debug mode
|
||||
} Interrupt deriving(Bits, Eq, FShow);
|
||||
|
||||
// typedef 12 InterruptNum;
|
||||
typedef 15 InterruptNum; // Bluespec: extended to 15 bits for debug interrupt
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
typedef 16 InterruptNum; // With debugger
|
||||
`else
|
||||
typedef 12 InterruptNum; // Without debugger
|
||||
`endif
|
||||
|
||||
// Traps are either an exception or an interrupt
|
||||
typedef union tagged {
|
||||
|
||||
@@ -34,6 +34,8 @@ import RevertingVirtualReg::*;
|
||||
import RVFI_DII_Types::*;
|
||||
`endif
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// right after execution, full_result has more up-to-date data (e.g. ppc of mispredicted branch)
|
||||
// some parts of full_result are for verification
|
||||
// but some are truly used for execution
|
||||
@@ -58,6 +60,13 @@ typedef struct {
|
||||
Addr pc;
|
||||
Bit #(32) orig_inst; // original 16b or 32b instruction ([1:0] will distinguish 16b or 32b)
|
||||
IType iType;
|
||||
Maybe#(ArchRIndx) dst; // Invalid, GPR or FPR destination ("Rd")
|
||||
Data dst_data; // Output of instruction into destination register
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// Store-data, for those mem instrs that store data
|
||||
Data store_data;
|
||||
ByteEn store_data_BE;
|
||||
`endif
|
||||
Maybe#(CSR) csr;
|
||||
Bool claimed_phy_reg; // whether we need to commmit renaming
|
||||
Maybe#(Trap) trap;
|
||||
@@ -100,6 +109,7 @@ typedef enum {
|
||||
|
||||
interface Row_setExecuted_doFinishAlu;
|
||||
method Action set(
|
||||
Data dst_data,
|
||||
Maybe#(Data) csrData,
|
||||
ControlFlow cf
|
||||
`ifdef RVFI
|
||||
@@ -109,7 +119,7 @@ interface Row_setExecuted_doFinishAlu;
|
||||
endinterface
|
||||
|
||||
interface Row_setExecuted_doFinishFpuMulDiv;
|
||||
method Action set(Bit#(5) fflags);
|
||||
method Action set(Data dst_data, Bit#(5) fflags);
|
||||
endinterface
|
||||
|
||||
interface ReorderBufferRowEhr#(numeric type aluExeNum, numeric type fpuMulDivExeNum);
|
||||
@@ -130,11 +140,20 @@ interface ReorderBufferRowEhr#(numeric type aluExeNum, numeric type fpuMulDivExe
|
||||
// faulting inst cannot have this set, since there is no access to
|
||||
// perform), and non-MMIO St can become Executed (NOTE faulting
|
||||
// instructions are not Executed, they are set at deqLSQ time)
|
||||
method Action setExecuted_doFinishMem(Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
|
||||
|
||||
method Action setExecuted_doFinishMem(Addr vaddr,
|
||||
Data store_data, ByteEn store_data_BE,
|
||||
Bool access_at_commit, Bool non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, ExtraTraceBundle tb
|
||||
, ExtraTraceBundle tb
|
||||
`endif
|
||||
);
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// Used after a Ld, Lr, Sc, Amo to record reg data
|
||||
method Action setExecuted_doFinishMem_RegData (Data dst_data);
|
||||
`endif
|
||||
|
||||
`ifdef INORDER_CORE
|
||||
// in-order core sets LSQ tag after getting out of issue queue
|
||||
method Action setLSQTag(LdStQTag t, Bool isFence);
|
||||
@@ -201,6 +220,12 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
Reg#(Addr) pc <- mkRegU;
|
||||
Reg #(Bit #(32)) orig_inst <- mkRegU;
|
||||
Reg#(IType) iType <- mkRegU;
|
||||
Reg #(Maybe #(ArchRIndx)) rg_dst_reg <- mkRegU;
|
||||
Reg #(Data) rg_dst_data <- mkRegU;
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
Reg #(Data) rg_store_data <- mkRegU;
|
||||
Reg #(ByteEn) rg_store_data_BE <- mkRegU;
|
||||
`endif
|
||||
Reg#(Maybe#(CSR)) csr <- mkRegU;
|
||||
Reg#(Bool) claimed_phy_reg <- mkRegU;
|
||||
Ehr#(3, Maybe#(Trap)) trap <- mkEhr(?);
|
||||
@@ -235,6 +260,7 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
for(Integer i = 0; i < valueof(aluExeNum); i = i+1) begin
|
||||
aluSetExe[i] = (interface Row_setExecuted_doFinishAlu;
|
||||
method Action set(
|
||||
Data dst_data,
|
||||
Maybe#(Data) csrData,
|
||||
ControlFlow cf
|
||||
`ifdef RVFI
|
||||
@@ -242,7 +268,10 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
`endif
|
||||
);
|
||||
// inst is done
|
||||
rob_inst_state[state_finishAlu_port(i)] <= Executed;
|
||||
rob_inst_state[state_finishAlu_port(i)] <= Executed;
|
||||
// Destination register data, for Tandem Verification
|
||||
rg_dst_data <= dst_data;
|
||||
|
||||
// update PPC or csrData (vaddr is always useless for ALU results)
|
||||
if(csrData matches tagged Valid .d) begin
|
||||
ppc_vaddr_csrData[pvc_finishAlu_port(i)] <= CSRData (d);
|
||||
@@ -262,9 +291,10 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
Vector#(fpuMulDivExeNum, Row_setExecuted_doFinishFpuMulDiv) fpuMulDivExe;
|
||||
for(Integer i = 0; i < valueof(fpuMulDivExeNum); i = i+1) begin
|
||||
fpuMulDivExe[i] = (interface Row_setExecuted_doFinishFpuMulDiv;
|
||||
method Action set(Bit#(5) new_fflags);
|
||||
method Action set(Data dst_data, Bit#(5) new_fflags);
|
||||
// inst is done
|
||||
rob_inst_state[state_finishFpuMulDiv_port(i)] <= Executed;
|
||||
rg_dst_data <= dst_data;
|
||||
// update fflags
|
||||
fflags[fflags_finishFpuMulDiv_port(i)] <= new_fflags;
|
||||
endmethod
|
||||
@@ -279,9 +309,11 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
|
||||
interface setExecuted_doFinishFpuMulDiv = fpuMulDivExe;
|
||||
|
||||
method Action setExecuted_doFinishMem(Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
|
||||
method Action setExecuted_doFinishMem(Addr vaddr,
|
||||
Data store_data, ByteEn store_data_BE,
|
||||
Bool access_at_commit, Bool non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, ExtraTraceBundle tb
|
||||
, ExtraTraceBundle tb
|
||||
`endif
|
||||
);
|
||||
doAssert(!(access_at_commit && non_mmio_st_done),
|
||||
@@ -296,6 +328,11 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
`ifdef RVFI
|
||||
//$display("%t : traceBundle = ", $time(), fshow(tb), " in setExecuted_doFinishMem for %x", pc);
|
||||
traceBundle[pvc_finishMem_port] <= tb;
|
||||
`endif
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// Store-data (for mem instrs that store data)
|
||||
rg_store_data <= store_data;
|
||||
rg_store_data_BE <= store_data_BE;
|
||||
`endif
|
||||
// update access at commit
|
||||
memAccessAtCommit[accessCom_finishMem_port] <= access_at_commit;
|
||||
@@ -303,6 +340,13 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
nonMMIOStDone[nonMMIOSt_finishMem_port] <= non_mmio_st_done;
|
||||
endmethod
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// Used after a Ld, Lr, Sc, Amo to record reg data
|
||||
method Action setExecuted_doFinishMem_RegData (Data dst_data);
|
||||
rg_dst_data <= dst_data;
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
`ifdef INORDER_CORE
|
||||
method Action setLSQTag(LdStQTag t, Bool isFence);
|
||||
lsqTag <= t;
|
||||
@@ -315,6 +359,10 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
pc <= x.pc;
|
||||
orig_inst <= x.orig_inst;
|
||||
iType <= x.iType;
|
||||
rg_dst_reg <= x.dst;
|
||||
// rg_dst_data will be written after inst execution
|
||||
// rg_store_data will be written in Mem pipeline
|
||||
// rg_store_data_BE will be written in Mem pipeline
|
||||
csr <= x.csr;
|
||||
claimed_phy_reg <= x.claimed_phy_reg;
|
||||
trap[trap_enq_port] <= x.trap;
|
||||
@@ -355,6 +403,12 @@ module mkReorderBufferRowEhr(ReorderBufferRowEhr#(aluExeNum, fpuMulDivExeNum)) p
|
||||
pc: pc,
|
||||
orig_inst: orig_inst,
|
||||
iType: iType,
|
||||
dst: rg_dst_reg,
|
||||
dst_data: rg_dst_data,
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
store_data: rg_store_data,
|
||||
store_data_BE: rg_store_data_BE,
|
||||
`endif
|
||||
csr: csr,
|
||||
claimed_phy_reg: claimed_phy_reg,
|
||||
trap: trap[trap_deq_port],
|
||||
@@ -466,6 +520,7 @@ endinterface
|
||||
|
||||
interface ROB_setExecuted_doFinishAlu;
|
||||
method Action set(InstTag x,
|
||||
Data dst_data,
|
||||
Maybe#(Data) csrData,
|
||||
ControlFlow cf
|
||||
`ifdef RVFI
|
||||
@@ -475,7 +530,7 @@ interface ROB_setExecuted_doFinishAlu;
|
||||
endinterface
|
||||
|
||||
interface ROB_setExecuted_doFinishFpuMulDiv;
|
||||
method Action set(InstTag x, Bit#(5) fflags);
|
||||
method Action set(InstTag x, Data dst_data, Bit#(5) fflags);
|
||||
endinterface
|
||||
|
||||
interface ROB_getOrigPC;
|
||||
@@ -508,11 +563,20 @@ interface SupReorderBuffer#(numeric type aluExeNum, numeric type fpuMulDivExeNum
|
||||
interface Vector#(aluExeNum, ROB_setExecuted_doFinishAlu) setExecuted_doFinishAlu;
|
||||
interface Vector#(fpuMulDivExeNum, ROB_setExecuted_doFinishFpuMulDiv) setExecuted_doFinishFpuMulDiv;
|
||||
// doFinishMem, after addr translation
|
||||
method Action setExecuted_doFinishMem(InstTag x, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
|
||||
method Action setExecuted_doFinishMem(InstTag x,
|
||||
Addr vaddr,
|
||||
Data store_data, ByteEn store_data_BE,
|
||||
Bool access_at_commit, Bool non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, ExtraTraceBundle tb
|
||||
, ExtraTraceBundle tb
|
||||
`endif
|
||||
);
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// Used after a Ld, Lr, Sc, Amo to record reg data
|
||||
method Action setExecuted_doFinishMem_RegData (InstTag x, Data dst_data);
|
||||
`endif
|
||||
|
||||
`ifdef INORDER_CORE
|
||||
// in-order core sets LSQ tag after getting out of issue queue
|
||||
method Action setLSQTag(InstTag x, LdStQTag t, Bool isFence);
|
||||
@@ -992,6 +1056,7 @@ module mkSupReorderBuffer#(
|
||||
aluSetExeIfc[i] = (interface ROB_setExecuted_doFinishAlu;
|
||||
method Action set(
|
||||
InstTag x,
|
||||
Data dst_data,
|
||||
Maybe#(Data) csrData,
|
||||
ControlFlow cf
|
||||
`ifdef RVFI
|
||||
@@ -1001,6 +1066,7 @@ module mkSupReorderBuffer#(
|
||||
all(id, readVReg(setExeAlu_SB_enq)) // ordering: < enq
|
||||
);
|
||||
row[x.way][x.ptr].setExecuted_doFinishAlu[i].set(
|
||||
dst_data,
|
||||
csrData,
|
||||
cf
|
||||
`ifdef RVFI
|
||||
@@ -1015,11 +1081,11 @@ module mkSupReorderBuffer#(
|
||||
for(Integer i = 0; i < valueof(fpuMulDivExeNum); i = i+1) begin
|
||||
fpuMulDivSetExeIfc[i] = (interface ROB_setExecuted_doFinishFpuMulDiv;
|
||||
method Action set(
|
||||
InstTag x, Bit#(5) fflags
|
||||
InstTag x, Data dst_data, Bit#(5) fflags
|
||||
) if(
|
||||
all(id, readVReg(setExeFpuMulDiv_SB_enq)) // ordering: < enq
|
||||
);
|
||||
row[x.way][x.ptr].setExecuted_doFinishFpuMulDiv[i].set(fflags);
|
||||
row[x.way][x.ptr].setExecuted_doFinishFpuMulDiv[i].set(dst_data, fflags);
|
||||
endmethod
|
||||
endinterface);
|
||||
end
|
||||
@@ -1095,20 +1161,29 @@ module mkSupReorderBuffer#(
|
||||
interface setExecuted_doFinishFpuMulDiv = fpuMulDivSetExeIfc;
|
||||
|
||||
method Action setExecuted_doFinishMem(
|
||||
InstTag x, Addr vaddr, Bool access_at_commit, Bool non_mmio_st_done
|
||||
InstTag x, Addr vaddr, Data store_data, ByteEn store_data_BE, Bool access_at_commit, Bool non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, tb
|
||||
`endif
|
||||
) if(
|
||||
all(id, readVReg(setExeMem_SB_enq)) // ordering: < enq
|
||||
);
|
||||
row[x.way][x.ptr].setExecuted_doFinishMem(vaddr, access_at_commit, non_mmio_st_done
|
||||
row[x.way][x.ptr].setExecuted_doFinishMem(vaddr,
|
||||
store_data, store_data_BE,
|
||||
access_at_commit, non_mmio_st_done
|
||||
`ifdef RVFI
|
||||
, tb
|
||||
, tb
|
||||
`endif
|
||||
);
|
||||
endmethod
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// Used after a Ld, Lr, Sc, Amo to record reg data
|
||||
method Action setExecuted_doFinishMem_RegData (InstTag x, Data dst_data);
|
||||
row[x.way][x.ptr].setExecuted_doFinishMem_RegData (dst_data);
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
`ifdef INORDER_CORE
|
||||
method Action setLSQTag(InstTag x, LdStQTag t, Bool isFence);
|
||||
row[x.way][x.ptr].setLSQTag(t, isFence);
|
||||
|
||||
@@ -33,6 +33,7 @@ import HasSpecBits::*;
|
||||
import SpecFifo::*;
|
||||
import StoreBuffer::*;
|
||||
import Exec::*;
|
||||
import FP_Utils::*;
|
||||
|
||||
// I don't want to export auxiliary functions, so manually export all types
|
||||
export LdQMemFunc(..);
|
||||
@@ -279,6 +280,9 @@ typedef struct {
|
||||
typedef struct {
|
||||
Bool wrongPath;
|
||||
Maybe#(PhyDst) dst;
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
InstTag instTag; // For recording Ld data in ROB
|
||||
`endif
|
||||
Data data;
|
||||
} LSQRespLdResult deriving(Bits, Eq, FShow);
|
||||
|
||||
@@ -1974,6 +1978,9 @@ module mkSplitLSQ(SplitLSQ);
|
||||
let res = LSQRespLdResult {
|
||||
wrongPath: False,
|
||||
dst: Invalid,
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
instTag: ld_instTag [t], // For recording Ld data in ROB
|
||||
`endif
|
||||
data: ?
|
||||
};
|
||||
if(ld_waitWPResp_resp[t]) begin
|
||||
@@ -1993,9 +2000,20 @@ module mkSplitLSQ(SplitLSQ);
|
||||
// mark load as done, and shift resp
|
||||
ld_done_resp[t] <= True;
|
||||
res.wrongPath = False;
|
||||
|
||||
// nirajns: checking if this is a 32-bit load response to a FPR
|
||||
// In that case, the data needs to be nanboxed before writing to
|
||||
// the register files as the Toooba FPR is 64-bit
|
||||
let bEn = ld_byteEn[t];
|
||||
let dst = ld_dst[t];
|
||||
let is32BitLd = (bEn[3] && !bEn[7]);
|
||||
res.dst = ld_dst[t];
|
||||
res.data = gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
|
||||
ld_unsigned[t], alignedData);
|
||||
if (dst.Valid.isFpuReg && is32BitLd)
|
||||
res.data = fv_nanbox (gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
|
||||
ld_unsigned[t], alignedData));
|
||||
else
|
||||
res.data = gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
|
||||
ld_unsigned[t], alignedData);
|
||||
end
|
||||
if(verbose) begin
|
||||
$display("[LSQ - respLd] ", fshow(t), "; ", fshow(alignedData),
|
||||
|
||||
Reference in New Issue
Block a user