Work in progress: updates to handle stop/step/run from Debug Module
This commit is contained in:
@@ -84,6 +84,8 @@ import Bypass::*;
|
||||
|
||||
import CsrFile :: *;
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
interface CoreReq;
|
||||
method Action start(
|
||||
Addr startpc,
|
||||
@@ -138,6 +140,19 @@ interface Core;
|
||||
|
||||
// Bluespec: external interrupt to enter debug mode
|
||||
method Action setDEIP (Bit #(1) v);
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action halt_to_debug_mode_req;
|
||||
|
||||
(* always_ready *)
|
||||
method Bool is_debug_halted;
|
||||
|
||||
method Action resume_from_debug_mode;
|
||||
|
||||
method Data csr_read (Bit #(12) csr_addr);
|
||||
method Action csr_write (Bit #(12) csr_addr, Data data);
|
||||
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// fixpoint to instantiate modules
|
||||
@@ -158,7 +173,11 @@ module mkCore#(CoreId coreId)(Core);
|
||||
outOfReset <= True;
|
||||
endrule
|
||||
|
||||
Reg#(Bool) started <- mkReg(False);
|
||||
Reg#(Bool) started <- mkReg(False); // only used for deadlock check
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
Reg#(Bool) rg_debug_halted <- mkReg (False);
|
||||
`endif
|
||||
|
||||
// front end
|
||||
FetchStage fetchStage <- mkFetchStage;
|
||||
@@ -512,6 +531,21 @@ module mkCore#(CoreId coreId)(Core);
|
||||
endinterface);
|
||||
CommitStage commitStage <- mkCommitStage(commitInput);
|
||||
|
||||
(* mutually_exclusive = "coreFix.aluExe_0.doRegReadAlu, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.aluExe_1.doRegReadAlu, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.aluExe_0.doDispatchAlu, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.aluExe_1.doDispatchAlu, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.fpuMulDivExe_0.doFinishIntMul, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.fpuMulDivExe_0.doFinishIntDiv, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.fpuMulDivExe_0.doFinishFpFma, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.fpuMulDivExe_0.doFinishFpDiv, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.fpuMulDivExe_0.doFinishFpSqrt, commitStage.rl_enter_debug_mode_flush" *)
|
||||
(* mutually_exclusive = "coreFix.fpuMulDivExe_0.doFinishFpSqrt, commitStage.rl_enter_debug_mode_flush" *)
|
||||
|
||||
rule rl_bogus_dummy (False);
|
||||
// Just to allow the scheduling attributes above
|
||||
endrule
|
||||
|
||||
// send rob enq time to reservation stations
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule sendRobEnqTime;
|
||||
@@ -903,6 +937,19 @@ module mkCore#(CoreId coreId)(Core);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ================================================================
|
||||
// Stopping into debug mode
|
||||
|
||||
rule rl_debug_halt_actions ((! rg_debug_halted) && commitStage.is_debug_halted);
|
||||
$display ("%0d: %m.rl_debug_halt_actions", cur_cycle);
|
||||
rg_debug_halted <= True;
|
||||
endrule
|
||||
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface CoreReq coreReq;
|
||||
method Action start(
|
||||
Bit#(64) startpc,
|
||||
@@ -910,6 +957,9 @@ module mkCore#(CoreId coreId)(Core);
|
||||
);
|
||||
fetchStage.start(startpc);
|
||||
started <= True;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
rg_debug_halted <= False;
|
||||
`endif
|
||||
mmio.setHtifAddrs(toHostAddr, fromHostAddr);
|
||||
// start rename debug
|
||||
commitStage.startRenameDebug;
|
||||
@@ -980,5 +1030,38 @@ module mkCore#(CoreId coreId)(Core);
|
||||
|
||||
// Bluespec: external interrupt to enter debug mode
|
||||
method Action setDEIP (v) = csrf.setDEIP (v);
|
||||
endmodule
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action halt_to_debug_mode_req () if (! rg_debug_halted);
|
||||
$display ("%0d: %m.halt_to_debug_mode_req", cur_cycle);
|
||||
started <= False;
|
||||
fetchStage.stop;
|
||||
commitStage.halt_to_debug_mode_req;
|
||||
endmethod
|
||||
|
||||
method Bool is_debug_halted;
|
||||
return rg_debug_halted;
|
||||
endmethod
|
||||
|
||||
method Action resume_from_debug_mode if (rg_debug_halted);
|
||||
let startpc = csrf.dpc_read;
|
||||
fetchStage.resume_from_debug_mode (startpc);
|
||||
commitStage.resume_from_debug_mode;
|
||||
started <= True;
|
||||
rg_debug_halted <= False;
|
||||
|
||||
$display ("%0d: %m.resume_from_debug_mode, dpc = 0x%0h", cur_cycle, startpc);
|
||||
endmethod
|
||||
|
||||
// TODO_DEBUG: was part of method cond: commitStage.is_debug_halted &&
|
||||
method Data csr_read (Bit #(12) csr_addr) if (rg_debug_halted);
|
||||
return csrf.rd (unpack (csr_addr));
|
||||
endmethod
|
||||
|
||||
// TODO_DEBUG: was part of method cond: commitStage.is_debug_halted &&
|
||||
method Action csr_write (Bit #(12) csr_addr, Data data) if (rg_debug_halted);
|
||||
csrf.csrInstWr (unpack (csr_addr), data);
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -37,6 +37,8 @@ import GetPut::*;
|
||||
import BuildVector::*;
|
||||
//import TRNG::*;
|
||||
|
||||
import SoC_Map :: *;
|
||||
|
||||
interface CsrFile;
|
||||
// Read
|
||||
method Data rd(CSR csr);
|
||||
@@ -90,6 +92,24 @@ 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 Bool dcsr_stop_for_break;
|
||||
|
||||
// Check whether to enter Debug Mode based on dcsr.step
|
||||
method Bool dcsr_stop_for_step;
|
||||
|
||||
// Update 'cause' in DCSR
|
||||
(* always_ready *)
|
||||
method Action dcsr_cause_write (Bit #(3) dcsr_cause);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// Fancy Reg functions
|
||||
@@ -501,6 +521,30 @@ module mkCsrFile #(Data hartid)(CsrFile);
|
||||
StatsCsr stats_module <- mkStatsCsr;
|
||||
Reg#(Data) stats_csr = stats_module.reg_ifc;
|
||||
|
||||
`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:7] 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)
|
||||
|
||||
Reg #(Data) rg_dcsr <- mkReg (zeroExtend (dcsr_reset_value));
|
||||
Reg #(Data) rg_dpc <- mkReg (truncate (soc_map_struct.pc_reset_value));
|
||||
Reg #(Data) rg_dscratch0 <- mkRegU;
|
||||
Reg #(Data) rg_dscratch1 <- mkRegU;
|
||||
`endif
|
||||
|
||||
`ifdef SECURITY
|
||||
// sanctum machine CSRs
|
||||
|
||||
@@ -607,6 +651,14 @@ module mkCsrFile #(Data hartid)(CsrFile);
|
||||
CSRmspec: mspec_csr;
|
||||
CSRtrng: trng_csr;
|
||||
`endif
|
||||
|
||||
`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
|
||||
@@ -856,4 +908,40 @@ module mkCsrFile #(Data hartid)(CsrFile);
|
||||
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 Bool dcsr_stop_for_break;
|
||||
return case (prv_reg)
|
||||
prvM: (rg_dcsr [15] == 1'b1);
|
||||
prvS: (rg_dcsr [13] == 1'b1);
|
||||
prvU: (rg_dcsr [12] == 1'b1);
|
||||
endcase;
|
||||
endmethod
|
||||
|
||||
// Check whether to enter Debug Mode based on dcsr.step
|
||||
method Bool dcsr_stop_for_step;
|
||||
return (rg_dcsr [2] == 1'b1);
|
||||
endmethod
|
||||
|
||||
// Update 'cause' in DCSR
|
||||
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 };
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -92,6 +92,31 @@ import TV_Info :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
// Major States of CPU
|
||||
|
||||
typedef enum {CPU_RESET1,
|
||||
CPU_RESET2,
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
CPU_GDB_PAUSING, // On GDB breakpoint, while waiting for fence completion
|
||||
`endif
|
||||
CPU_DEBUG_MODE, // Stopped (normally for debugger)
|
||||
CPU_RUNNING // Normal operation
|
||||
} CPU_State
|
||||
deriving (Eq, Bits, FShow);
|
||||
|
||||
function Bool fn_is_running (CPU_State cpu_state);
|
||||
return ( (cpu_state != CPU_RESET1)
|
||||
&& (cpu_state != CPU_RESET2)
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
&& (cpu_state != CPU_GDB_PAUSING)
|
||||
&& (cpu_state != CPU_DEBUG_MODE)
|
||||
`endif
|
||||
);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkProc (Proc_IFC);
|
||||
|
||||
@@ -108,6 +133,11 @@ module mkProc (Proc_IFC);
|
||||
// Verbosity: 0=quiet; 1=instruction trace; 2=more detail
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
// ----------------
|
||||
// Major CPU states
|
||||
|
||||
Reg #(CPU_State) rg_state <- mkReg (CPU_RESET1);
|
||||
|
||||
// ----------------
|
||||
// Reset requests and responses (TODO: to be implemented)
|
||||
|
||||
@@ -258,6 +288,8 @@ module mkProc (Proc_IFC);
|
||||
mmio_axi4_adapter.reset;
|
||||
|
||||
f_reset_rsps.enq (?);
|
||||
|
||||
rg_state <= CPU_RUNNING;
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
@@ -287,6 +319,122 @@ module mkProc (Proc_IFC);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// DEBUGGER ACCESS
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
|
||||
// ----------------
|
||||
// Debug Module Run (resume) control
|
||||
|
||||
// Run command when in debug mode
|
||||
rule rl_debug_run ((f_run_halt_reqs.first == True)
|
||||
&& (! f_gpr_reqs.notEmpty)
|
||||
&& (! f_fpr_reqs.notEmpty)
|
||||
&& (! f_csr_reqs.notEmpty)
|
||||
&& (rg_state == CPU_DEBUG_MODE));
|
||||
// if (cfg_verbosity > 1)
|
||||
$display ("%0d: %m.rl_debug_run", cur_cycle);
|
||||
|
||||
f_run_halt_reqs.deq;
|
||||
core[0].resume_from_debug_mode;
|
||||
rg_state <= CPU_RUNNING;
|
||||
|
||||
// Notify debugger that we've started running
|
||||
f_run_halt_rsps.enq (True);
|
||||
endrule
|
||||
|
||||
// Run command when already running
|
||||
rule rl_debug_run_redundant ((f_run_halt_reqs.first == True)
|
||||
&& (! f_gpr_reqs.notEmpty)
|
||||
&& (! f_fpr_reqs.notEmpty)
|
||||
&& (! f_csr_reqs.notEmpty)
|
||||
&& fn_is_running (rg_state));
|
||||
// if (cfg_verbosity > 1)
|
||||
$display ("%0d: %m.rl_debug_run_redundant", cur_cycle);
|
||||
|
||||
f_run_halt_reqs.deq;
|
||||
|
||||
// Notify debugger that we're running
|
||||
f_run_halt_rsps.enq (True);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Debug Module Halt control
|
||||
|
||||
rule rl_debug_halt ((f_run_halt_reqs.first == False) && fn_is_running (rg_state));
|
||||
// if (cfg_verbosity > 1)
|
||||
$display ("%0d: %m.rl_debug_halt", cur_cycle);
|
||||
|
||||
f_run_halt_reqs.deq;
|
||||
|
||||
// Debugger 'halt' request (e.g., GDB '^C' command)
|
||||
core[0].halt_to_debug_mode_req;
|
||||
|
||||
rg_state <= CPU_GDB_PAUSING;
|
||||
endrule
|
||||
|
||||
rule rl_debug_halted ((rg_state == CPU_GDB_PAUSING) && core [0].is_debug_halted);
|
||||
// Notify debugger that we've halted
|
||||
f_run_halt_rsps.enq (False);
|
||||
// Stop executing rules until ready to restart from debugger
|
||||
rg_state <= CPU_DEBUG_MODE;
|
||||
|
||||
// if (cfg_verbosity > 1)
|
||||
$display ("%0d: %m.rl_debug_halted", cur_cycle);
|
||||
endrule
|
||||
|
||||
rule rl_debug_halt_redundant ((f_run_halt_reqs.first == False) && (! fn_is_running (rg_state)));
|
||||
// if (cfg_verbosity > 1)
|
||||
$display ("%0d: %m.rl_debug_halt_redundant", cur_cycle);
|
||||
|
||||
f_run_halt_reqs.deq;
|
||||
|
||||
// Notify debugger that we've 'halted'
|
||||
f_run_halt_rsps.enq (False);
|
||||
|
||||
$display ("%0d: %m.rl_debug_halt_redundant: CPU already halted; state = ", cur_cycle, fshow (rg_state));
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Debug Module CSR read/write
|
||||
|
||||
rule rl_debug_read_csr ((rg_state == CPU_DEBUG_MODE) && (! f_csr_reqs.first.write));
|
||||
let req <- pop (f_csr_reqs);
|
||||
Bit #(12) csr_addr = req.address;
|
||||
let data = core [0].csr_read (csr_addr);
|
||||
let rsp = DM_CPU_Rsp {ok: True, data: data};
|
||||
f_csr_rsps.enq (rsp);
|
||||
// if (cur_verbosity > 1)
|
||||
$display ("%m.rl_debug_read_csr: csr %0d => 0x%0h",
|
||||
csr_addr, data);
|
||||
endrule
|
||||
|
||||
rule rl_debug_write_csr ((rg_state == CPU_DEBUG_MODE) && f_csr_reqs.first.write);
|
||||
let req <- pop (f_csr_reqs);
|
||||
Bit #(12) csr_addr = req.address;
|
||||
let data = req.data;
|
||||
core [0].csr_write (csr_addr, data);
|
||||
let rsp = DM_CPU_Rsp {ok: True, data: ?};
|
||||
f_csr_rsps.enq (rsp);
|
||||
|
||||
// if (cur_verbosity > 1)
|
||||
$display ("%m.rl_debug_write_csr: csr 0x%0h <= 0x%0h", csr_addr, data);
|
||||
endrule
|
||||
|
||||
rule rl_debug_csr_access_busy (rg_state != CPU_DEBUG_MODE);
|
||||
let req <- pop (f_csr_reqs);
|
||||
let rsp = DM_CPU_Rsp {ok: False, data: ?};
|
||||
f_csr_rsps.enq (rsp);
|
||||
|
||||
// if (cur_verbosity > 1)
|
||||
$display ("%m.rl_debug_csr_access_busy");
|
||||
endrule
|
||||
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
@@ -305,7 +453,7 @@ module mkProc (Proc_IFC);
|
||||
|
||||
mmioPlatform.start (tohostAddr, fromhostAddr);
|
||||
|
||||
$display ("Proc.start: startpc = 0x%0h, tohostAddr = 0x%0h, fromhostAddr = %0h",
|
||||
$display ("%m.start: startpc = 0x%0h, tohostAddr = 0x%0h, fromhostAddr = %0h",
|
||||
startpc, tohostAddr, fromhostAddr);
|
||||
endmethod
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ import StoreBuffer::*;
|
||||
import VerificationPacket::*;
|
||||
import RenameDebugIF::*;
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
typedef struct {
|
||||
// info about the inst blocking at ROB head
|
||||
Addr pc;
|
||||
@@ -112,6 +114,18 @@ interface CommitStage;
|
||||
// rename debug
|
||||
method Action startRenameDebug;
|
||||
interface Get#(RenameErrInfo) renameErr;
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Request halt into debug mode
|
||||
method Action halt_to_debug_mode_req;
|
||||
|
||||
(* always_ready *)
|
||||
// Becomes true when pipeline is halted
|
||||
method Bool is_debug_halted;
|
||||
|
||||
method Action resume_from_debug_mode;
|
||||
`endif
|
||||
|
||||
endinterface
|
||||
|
||||
// we apply actions the end of commit rule
|
||||
@@ -122,6 +136,17 @@ typedef struct {
|
||||
Trap trap;
|
||||
} CommitTrap deriving(Bits, Eq, FShow);
|
||||
|
||||
// Bluespec: for debugger run-control
|
||||
typedef enum {
|
||||
Run_State_RUNNING // Normal state
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
, Run_State_DEBUGGER_HALT // When ready to halt for debugger after stop/step
|
||||
, Run_State_DEBUGGER_HALT_FLUSH // When flushing state during debugger halt
|
||||
, Run_State_DEBUGGER_HALTED // When halted for debugger
|
||||
`endif
|
||||
} Run_State
|
||||
deriving (Eq, FShow, Bits);
|
||||
|
||||
module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
Bool verbose = False;
|
||||
|
||||
@@ -129,6 +154,17 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
Integer verbosity = 1;
|
||||
Reg #(Bit #(64)) rg_instret <- mkReg (0);
|
||||
|
||||
// Bluespec: for debugger run-control
|
||||
Reg #(Run_State) rg_run_state <- mkReg (Run_State_RUNNING);
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// rg_stop_req is set True when debugger requests a stop (e.g., GDB ^C)
|
||||
Reg #(Bool) rg_stop_req <- mkReg (False);
|
||||
// When going into debug mode, these hold the cause for the break and the resume-PC
|
||||
Reg #(Bit #(3)) rg_debug_cause <- mkRegU;
|
||||
Reg #(Addr) rg_debug_pc <- mkRegU;
|
||||
`endif
|
||||
|
||||
// func units
|
||||
ReorderBufferSynth rob = inIfc.robIfc;
|
||||
RegRenamingTable regRenamingTable = inIfc.rtIfc;
|
||||
@@ -338,15 +374,57 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ================================================================
|
||||
// Bluespec: debugger run-control
|
||||
// Every time an instruction commits, we check this boolean
|
||||
// to decide whether we continue running of stop.
|
||||
|
||||
// We halt after committing an instruction if:
|
||||
// the debugger requested a stop
|
||||
// or dcsr.step is True
|
||||
|
||||
Maybe #(Bit #(3)) m_debug_stop_step_cause = ( rg_stop_req
|
||||
? tagged Valid 3 // Debug Module haltreq
|
||||
: (csrf.dcsr_stop_for_step
|
||||
? tagged Valid 4 // dcsr.step
|
||||
: tagged Invalid));
|
||||
|
||||
// ================================================================
|
||||
|
||||
/*
|
||||
// Bluespec: debugger run-control
|
||||
// Rule cond should be identical to doCommitTrap_flush's cond
|
||||
// except for fn_ebreak_to_debug_mode()
|
||||
rule doCommitTrap_ebreak_to_debug_mode(
|
||||
(rg_run_state == Run_State_RUNNING) &&& // Bluespec: debugger run-control
|
||||
!isValid(commitTrap) &&&
|
||||
rob.deqPort[0].deq_data.trap matches tagged Valid .trap &&&
|
||||
fn_ebreak_to_debug_mode (trap) // Bluespec: debugger run-control
|
||||
);
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
rg_run_state <= Run_State_DEBUGGER_HALT;
|
||||
rg_debug_pc <= x.pc;
|
||||
rg_debug_cause <= 1; // cause: EBREAK
|
||||
endrule
|
||||
*/
|
||||
`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) &&& // Bluespec: debugger run-control
|
||||
`endif
|
||||
!isValid(commitTrap) &&&
|
||||
rob.deqPort[0].deq_data.trap matches tagged Valid .trap
|
||||
);
|
||||
$display ("%0d: %m.CommitStage.rule_doCommitTrap_flush", cur_cycle);
|
||||
rob.deqPort[0].deq;
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
if(verbose) $display("[doCommitTrap] ", fshow(x));
|
||||
@@ -404,7 +482,15 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
doAssert(x.spec_bits == 0, "cannot have spec bits");
|
||||
endrule
|
||||
|
||||
rule doCommitTrap_handle(commitTrap matches tagged Valid .trap);
|
||||
// This rule's condition is enabled only by the previous rule, doCommitTrap_flush
|
||||
rule doCommitTrap_handle(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == Run_State_RUNNING) &&& // Bluespec: debugger run-control
|
||||
`endif
|
||||
commitTrap matches tagged Valid .trap);
|
||||
|
||||
$display ("%0d: %m.CommitStage.rule_doCommitTrap_handle", cur_cycle);
|
||||
|
||||
// reset commitTrap
|
||||
commitTrap <= Invalid;
|
||||
|
||||
@@ -413,9 +499,31 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
inIfc.commitCsrInstOrInterrupt;
|
||||
end
|
||||
|
||||
// trap handling & redirect
|
||||
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
|
||||
inIfc.redirectPc(new_pc);
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
if ((trap.trap == tagged Exception Breakpoint) && csrf.dcsr_stop_for_break) begin
|
||||
// Don't handle the trap
|
||||
rg_run_state <= Run_State_DEBUGGER_HALTED;
|
||||
// Record debug CSRs for later Debugger query and later resumption
|
||||
csrf.dcsr_cause_write (1); // EBREAK dcsr.cause
|
||||
csrf.dpc_write (trap.pc); // Where we'll resume on 'continue'
|
||||
end
|
||||
else begin
|
||||
// Handle the trap and redirect
|
||||
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
|
||||
inIfc.redirectPc (new_pc);
|
||||
|
||||
if (m_debug_stop_step_cause matches tagged Valid .cause) begin
|
||||
rg_run_state <= Run_State_DEBUGGER_HALTED;
|
||||
// Record debug CSRs for later Debugger query and later resumption
|
||||
csrf.dcsr_cause_write (cause);
|
||||
csrf.dpc_write (new_pc); // Where we'll resume on 'continue'
|
||||
end
|
||||
end
|
||||
`else
|
||||
// trap handling & redirect
|
||||
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
|
||||
inIfc.redirectPc(new_pc);
|
||||
`endif
|
||||
|
||||
// system consistency
|
||||
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
|
||||
@@ -427,6 +535,9 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
// commit misspeculated load
|
||||
rule doCommitKilledLd(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == Run_State_RUNNING) &&& // Bluespec: debugger run-control
|
||||
`endif
|
||||
!isValid(commitTrap) &&&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&&
|
||||
rob.deqPort[0].deq_data.ldKilled matches tagged Valid .killBy
|
||||
@@ -437,7 +548,19 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
// kill everything, redirect, and increment epoch
|
||||
inIfc.killAll;
|
||||
inIfc.redirectPc(x.pc);
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Bluespec: debug run-control
|
||||
doAssert (x.iType != Ebreak, "CommitStage.rule_doCommitKilledLd iType should not be Ebreak");
|
||||
if (m_debug_stop_step_cause matches tagged Valid .cause) begin
|
||||
rg_run_state <= Run_State_DEBUGGER_HALT_FLUSH;
|
||||
rg_debug_pc <= x.pc;
|
||||
rg_debug_cause <= cause;
|
||||
end
|
||||
else
|
||||
`endif
|
||||
inIfc.redirectPc(x.pc); // original (no debugger)
|
||||
|
||||
inIfc.incrementEpoch;
|
||||
|
||||
// the killed Ld should have claimed phy reg, we should not commit it;
|
||||
@@ -461,6 +584,9 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
// commit system inst
|
||||
rule doCommitSystemInst(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == Run_State_RUNNING) && // Bluespec: debugger run-control
|
||||
`endif
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
@@ -510,7 +636,18 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
else if(x.iType == Mret) begin
|
||||
next_pc <- csrf.mret;
|
||||
end
|
||||
inIfc.redirectPc(next_pc);
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Bluespec: debug run-control
|
||||
doAssert (x.iType != Ebreak, "CommitStage.rule_doCommitSystemInst iType should not be Ebreak");
|
||||
if (m_debug_stop_step_cause matches tagged Valid .cause) begin
|
||||
rg_run_state <= Run_State_DEBUGGER_HALT_FLUSH;
|
||||
rg_debug_pc <= next_pc;
|
||||
rg_debug_cause <= cause;
|
||||
end
|
||||
else
|
||||
`endif
|
||||
inIfc.redirectPc(next_pc); // original (no debugger)
|
||||
|
||||
// rename stage only sends out system inst when ROB is empty, so no
|
||||
// need to flush ROB again
|
||||
@@ -567,6 +704,9 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
// Lr/Sc/Amo/MMIO cannot proceed to executed until we notify LSQ that it
|
||||
// has reached the commit stage
|
||||
rule notifyLSQCommit(
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
(rg_run_state == Run_State_RUNNING) && // Bluespec: debugger run-control
|
||||
`endif
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
@@ -585,6 +725,9 @@ 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) && // Bluespec: debugger run-control
|
||||
`endif
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
@@ -596,6 +739,10 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
// 2. inst is not ready to commit
|
||||
Bool stop = False;
|
||||
|
||||
// Bluespec: debugger run-control
|
||||
// 3. one instr is committed and debugger halt is requested (due to stop or step)
|
||||
Bool stop_for_debugger = False;
|
||||
|
||||
// We merge writes on FPU csr and apply writes at the end of the rule
|
||||
Bit#(5) fflags = 0;
|
||||
Bool will_dirty_fpu_state = False;
|
||||
@@ -620,7 +767,8 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
|
||||
// compute what actions to take
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
if(!stop && rob.deqPort[i].canDeq) begin
|
||||
|
||||
if(!stop && (! stop_for_debugger) && rob.deqPort[i].canDeq) begin
|
||||
let x = rob.deqPort[i].deq_data;
|
||||
let inst_tag = rob.deqPort[i].getDeqInstTag;
|
||||
|
||||
@@ -673,6 +821,35 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
comUserInstCnt = comUserInstCnt + 1; // user space inst
|
||||
end
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
if (m_debug_stop_step_cause matches tagged Valid .cause
|
||||
&&& (comInstCnt != 0))
|
||||
begin
|
||||
$display ("%0d: %m.CommitStage.rule_doCommitNormalInst: Stopping for debugger", cur_cycle);
|
||||
stop_for_debugger = True;
|
||||
|
||||
// Compute next PC in case of debugger stop.
|
||||
// Note: AluExePipeline.rule_doFinishAlu does inIfc.rob_setExecuted (... x.controlFlow)
|
||||
// which updates the rob entry's ppc_vaddr_csrData field with actual
|
||||
// branch/jump targets, which we retrieve here.
|
||||
Addr next_pc = ?;
|
||||
if (x.ppc_vaddr_csrData matches tagged PPC .addr
|
||||
&&& ((x.iType == J) || (x.iType == Jr) || (x.iType == Br)))
|
||||
// JAL, JALR, BRANCH
|
||||
next_pc = addr;
|
||||
else if (x.orig_inst [1:0] == 2'b11)
|
||||
// RV32I RV64I
|
||||
next_pc = x.pc + 4;
|
||||
else
|
||||
// RVC
|
||||
next_pc = x.pc + 2;
|
||||
|
||||
rg_run_state <= Run_State_DEBUGGER_HALT_FLUSH;
|
||||
rg_debug_pc <= next_pc;
|
||||
rg_debug_cause <= cause;
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter
|
||||
case(x.iType)
|
||||
@@ -688,7 +865,7 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
`endif
|
||||
end
|
||||
end
|
||||
end
|
||||
end // for-loop for superscalar width
|
||||
rg_instret <= rg_instret + instret;
|
||||
|
||||
// write FPU csr
|
||||
@@ -736,6 +913,47 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ================================================================
|
||||
// Rules to move into debug mode
|
||||
|
||||
// This rule is like rule doCommitTrap_flush, i.e., a debugger-halt is like an interrupt
|
||||
rule rl_enter_debug_mode_flush (rg_run_state == Run_State_DEBUGGER_HALT_FLUSH);
|
||||
$write ("%0d: %m.commitStage.rl_enter_debug_mode:", cur_cycle);
|
||||
if (rg_debug_cause == 1) $display (" EBREAK");
|
||||
else if (rg_debug_cause == 3) $display (" Debugger halt request");
|
||||
else if (rg_debug_cause == 4) $display (" Halt after step");
|
||||
else $display (" Unknown cause %0d", rg_debug_cause);
|
||||
|
||||
// flush everything. Only increment epoch and stall fetch when we haven
|
||||
// not done it yet (we may have already done them at rename stage)
|
||||
inIfc.killAll;
|
||||
inIfc.incrementEpoch;
|
||||
inIfc.setFetchWaitRedirect;
|
||||
|
||||
rg_run_state <= Run_State_DEBUGGER_HALT;
|
||||
endrule
|
||||
|
||||
// This rule is like rule doCommitTrap_handle, except we don't redirect
|
||||
rule rl_enter_debug_mode_halt (rg_run_state == Run_State_DEBUGGER_HALT);
|
||||
// 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);
|
||||
|
||||
// Record debug CSRs for later Debugger query and later resumption
|
||||
csrf.dcsr_cause_write (rg_debug_cause); // reason for entering debug mode
|
||||
csrf.dpc_write (rg_debug_pc); // Where we'll resume on 'continue'
|
||||
|
||||
// Go to HALTED state where no rule fires.
|
||||
// To re-start, mkCore must call restart method.
|
||||
rg_run_state <= Run_State_DEBUGGER_HALTED;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
`endif
|
||||
|
||||
method Data getPerf(ComStagePerfType t);
|
||||
return (case(t)
|
||||
@@ -785,4 +1003,24 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
endmethod
|
||||
interface renameErr = nullGet;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Request halt into debug mode
|
||||
method Action halt_to_debug_mode_req () if (rg_run_state == Run_State_RUNNING);
|
||||
$display ("%0d: %m.commitStage.halt_to_debug_mode_req", cur_cycle);
|
||||
rg_stop_req <= True;
|
||||
endmethod
|
||||
|
||||
// Becomes true when pipeline is halted
|
||||
method Bool is_debug_halted;
|
||||
return (rg_run_state == Run_State_DEBUGGER_HALTED);
|
||||
endmethod
|
||||
|
||||
method Action resume_from_debug_mode () if (rg_run_state == Run_State_DEBUGGER_HALTED);
|
||||
$display ("%0d: %m.commitStage.resume_from_debug_mode", cur_cycle);
|
||||
rg_stop_req <= False;
|
||||
rg_run_state <= Run_State_RUNNING;
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -50,6 +50,8 @@ import CCTypes::*;
|
||||
import L1CoCache::*;
|
||||
import MMIOInst::*;
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ================================================================
|
||||
// For fv_decode_C function and related types and definitions
|
||||
|
||||
@@ -90,6 +92,10 @@ interface FetchStage;
|
||||
|
||||
// performance
|
||||
interface Perf#(DecStagePerfType) perf;
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action resume_from_debug_mode (Addr new_pc);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
typedef struct {
|
||||
@@ -984,6 +990,7 @@ module mkFetchStage(FetchStage);
|
||||
interface mmioIfc = mmio.toCore;
|
||||
|
||||
method Action start(Addr start_pc);
|
||||
$display ("%0d: %m.start (start_pc = 0x%0h)", cur_cycle, start_pc);
|
||||
pc_reg[0] <= start_pc;
|
||||
started <= True;
|
||||
waitForRedirect <= False;
|
||||
@@ -991,6 +998,7 @@ module mkFetchStage(FetchStage);
|
||||
endmethod
|
||||
method Action stop();
|
||||
started <= False;
|
||||
$display ("%0d: %m.stop", cur_cycle);
|
||||
endmethod
|
||||
|
||||
method Action setWaitRedirect;
|
||||
@@ -1095,5 +1103,24 @@ module mkFetchStage(FetchStage);
|
||||
method Bool respValid = perfReqQ.notEmpty;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
method Action resume_from_debug_mode (Addr new_pc);
|
||||
if (verbose)
|
||||
$display("resume_from_debug_mode: newpc %h, old f_main_epoch %d, new f_main_epoch %d",
|
||||
new_pc, f_main_epoch, f_main_epoch + 1);
|
||||
pc_reg[pc_redirect_port] <= new_pc;
|
||||
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
|
||||
ehr_pending_straddle[1] <= tagged Invalid;
|
||||
// redirect comes, stop stalling for redirect
|
||||
waitForRedirect <= False;
|
||||
setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
|
||||
|
||||
// this redirect may be caused by a trap/system inst in commit stage
|
||||
// we conservatively set wait for flush TODO make this an input parameter
|
||||
// TODO: do we need this? waitForFlush <= True;
|
||||
endmethod
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
@@ -269,6 +269,14 @@ typedef enum {
|
||||
// sanctum user CSR
|
||||
CSRtrng = 12'hcc0, // random number for secure boot
|
||||
`endif
|
||||
|
||||
`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.
|
||||
CSRnone = 12'h8ff
|
||||
|
||||
Reference in New Issue
Block a user