diff --git a/src_Core/CPU/Core.bsv b/src_Core/CPU/Core.bsv index 7f05d80..e9c5fec 100644 --- a/src_Core/CPU/Core.bsv +++ b/src_Core/CPU/Core.bsv @@ -84,6 +84,25 @@ import Bypass::*; import CsrFile :: *; +// ================================================================ +// Toooba + +import Cur_Cycle :: *; +import FIFOF :: *; +import GetPut_Aux :: *; + +`ifdef INCLUDE_GDB_CONTROL +import DM_CPU_Req_Rsp :: *; +`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, @@ -138,6 +157,15 @@ interface Core; // Bluespec: external interrupt to enter debug mode method Action setDEIP (Bit #(1) v); + +`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 endinterface // fixpoint to instantiate modules @@ -149,6 +177,15 @@ 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; @@ -160,6 +197,11 @@ 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 + // front end FetchStage fetchStage <- mkFetchStage; ITlb iTlb = fetchStage.iTlbIfc; @@ -384,13 +426,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 @@ -477,6 +521,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); @@ -489,16 +536,48 @@ 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; + method setFetchWaitFlush = fetchStage.setWaitFlush; method incrementEpoch = epochManager.incrementEpoch; method commitCsrInstOrInterrupt = csrInstOrInterruptInflight_commit._write(False); method doStats = coreFix.doStatsIfc._read; @@ -541,11 +620,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; @@ -554,10 +635,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. @@ -566,6 +648,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); @@ -578,6 +661,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 @@ -585,6 +669,7 @@ module mkCore#(CoreId coreId)(Core); rule flushBrPred(doFlushBrPred); flush_brpred <= False; fetchStage.flush_predictors; + $display ("%0d: %m.rule flushBrPred", cur_cycle); endrule `endif @@ -629,9 +714,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 @@ -644,8 +732,36 @@ 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; + $display ("%0d: %m.rule readyToFetch: halting for debug mode", cur_cycle); + end +`endif endrule + /* + rule rl_readyToFetch_conds_debug + $display ("%0d: %m.rl_readyToFetch_conds_debug:", cur_cycle); + $display (" !flush_reservation = %0d, !flush_tlbs = %0d, !update_vm_info = %0d", + !flush_reservation, !flush_tlbs, !update_vm_info); + $display (" iTlb.flush_done = %0d, dTlb.flush_done = %0d", iTlb.flush_done, dTlb.flush_done); +`ifdef SECURITY_OR_INCLUDE_GDB_CONTROL + $display (" !flush_caches = %0d !flush_brpred = %0d", !flush_caches, !flush_brpred); + $display (" iMem.flush_done = %0d dMem.flush_done = %0d", iMem.flush_done, dMem.flush_done); + $display (" fetchStage.flush_predictors_done = %0d", fetchStage.flush_predictors_done); +`endif +`ifdef SELF_INV_CACHE + $display (" !reconcile_i = %0d, iMem.reconcide_done = %0d", !reconcile_i, iMem.reconcile_done); +`ifdef SYSTEM_SELF_INV_L1D + $display (" reconcile_d = %0d", reconcile_d); +`endif +`endif + endrule + */ + `ifdef PERF_COUNT // incr cycle count (* fire_when_enabled, no_implicit_conditions *) @@ -903,6 +1019,268 @@ module mkCore#(CoreId coreId)(Core); endrule `endif +`ifdef INCLUDE_GDB_CONTROL + // ================================================================ + // DEBUG MODULE INTERFACE + + Bool show_DM_interactions = True; // 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 %0d => 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 0x%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; + Reg #(Bool) rg_sent_halt_rsp <- mkReg (False); + + // ---------------- + // 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; + rg_sent_halt_rsp <= False; + + 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've 'halted' + 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; + + 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.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 CoreReq coreReq; method Action start( Bit#(64) startpc, @@ -910,6 +1288,9 @@ module mkCore#(CoreId coreId)(Core); ); fetchStage.start(startpc); started <= True; +`ifdef INCLUDE_GDB_CONTROL + rg_core_run_state <= CORE_RUNNING; +`endif mmio.setHtifAddrs(toHostAddr, fromHostAddr); // start rename debug commitStage.startRenameDebug; @@ -980,5 +1361,14 @@ module mkCore#(CoreId coreId)(Core); // 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 + +endmodule diff --git a/src_Core/CPU/CsrFile.bsv b/src_Core/CPU/CsrFile.bsv index 479437c..9b4262b 100644 --- a/src_Core/CPU/CsrFile.bsv +++ b/src_Core/CPU/CsrFile.bsv @@ -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 Bit #(1) dcsr_break_bit; + + // Read dcsr[2], the step bit + method Bit #(1) dcsr_step_bit; + + // Update 'cause' in DCSR + (* always_ready *) + method Action dcsr_cause_write (Bit #(3) dcsr_cause); +`endif endinterface // Fancy Reg functions @@ -501,6 +521,31 @@ 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) + + // 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,6 +652,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 +909,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 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 + 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 diff --git a/src_Core/CPU/Proc.bsv b/src_Core/CPU/Proc.bsv index f485003..3d0ee9f 100644 --- a/src_Core/CPU/Proc.bsv +++ b/src_Core/CPU/Proc.bsv @@ -77,17 +77,12 @@ 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 +`ifdef INCLUDE_GDB_CONTROL +import DM_CPU_Req_Rsp :: *; `endif // ================================================================ @@ -114,37 +109,6 @@ module mkProc (Proc_IFC); 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) @@ -185,16 +149,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); @@ -360,25 +319,23 @@ 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 Server hart0_gpr_mem_server = core[0].hart0_gpr_mem_server; +`ifdef ISA_F + 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; + // mem access goes to LLC (stays coherent with CPU pipeline). + interface debug_module_mem_server = llc_mem_server; + + // We don't implement 'other' functionality interface Put hart0_put_other_req; method Action put (Bit #(4) req); cfg_verbosity <= req; endmethod endinterface - - // GPR access - interface Server hart0_gpr_mem_server = toGPServer (f_gpr_reqs, f_gpr_rsps); - -`ifdef ISA_F - // FPR access - interface Server hart0_fpr_mem_server = toGPServer (f_fpr_reqs, f_fpr_rsps); -`endif - - // CSR access - interface Server hart0_csr_mem_server = toGPServer (f_csr_reqs, f_csr_rsps); `endif endmodule: mkProc diff --git a/src_Core/CPU/Proc_IFC.bsv b/src_Core/CPU/Proc_IFC.bsv index 48e7f49..202394e 100644 --- a/src_Core/CPU/Proc_IFC.bsv +++ b/src_Core/CPU/Proc_IFC.bsv @@ -85,20 +85,16 @@ 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; + interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) debug_module_mem_server; + + // Non-standard + interface Put #(Bit #(4)) hart0_put_other_req; `endif endinterface diff --git a/src_Core/Core/CoreW.bsv b/src_Core/Core/CoreW.bsv index 4272b1b..4bb29f2 100644 --- a/src_Core/Core/CoreW.bsv +++ b/src_Core/Core/CoreW.bsv @@ -206,7 +206,7 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources)); `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 @@ -230,8 +230,8 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources)); rg_fromhost_addr); endrule - rule rl_hart0_server_run_halt; - let tmp <- proc.hart0_server_run_halt.response.get; + rule rl_hart0_run_halt_server; + let tmp <- proc.hart0_run_halt_server.response.get; endrule Reg#(Bool) hart0_halt <- mkReg(False); @@ -409,10 +409,7 @@ module mkCoreW (CoreW_IFC #(N_External_Interrupt_Sources)); // 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); + mkConnection (fabric_2x3.v_to_slaves [near_mem_io_slave_num], proc.debug_module_mem_server); // ================================================================ // Connect external interrupt lines from PLIC to CPU @@ -557,8 +554,8 @@ 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)) + if ( (soc_map.m_mem0_controller_addr_base <= addr) + && (addr < soc_map.m_mem0_controller_addr_lim)) return tuple2 (True, near_mem_io_slave_num); else if ( (soc_map.m_plic_addr_base <= addr) diff --git a/src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv b/src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv index dc142d3..1d80057 100644 --- a/src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv +++ b/src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv @@ -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; @@ -86,6 +88,7 @@ interface CommitInput; method Action killAll; method Action redirectPc(Addr trap_pc); method Action setFetchWaitRedirect; + method Action setFetchWaitFlush; method Action incrementEpoch; // record if we commit a CSR inst or interrupt method Action commitCsrInstOrInterrupt; @@ -112,6 +115,12 @@ interface CommitStage; // rename debug method Action startRenameDebug; interface Get#(RenameErrInfo) renameErr; + +`ifdef INCLUDE_GDB_CONTROL + method Bool is_debug_halted; + method Action debug_resume; +`endif + endinterface // we apply actions the end of commit rule @@ -122,6 +131,16 @@ typedef struct { Trap trap; } CommitTrap deriving(Bits, Eq, FShow); +`ifdef INCLUDE_GDB_CONTROL + +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; @@ -129,6 +148,11 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage); Integer verbosity = 1; Reg #(Bit #(64)) rg_instret <- mkReg (0); + +`ifdef INCLUDE_GDB_CONTROL + Reg #(Run_State) rg_run_state <- mkReg (RUN_STATE_RUNNING); +`endif + // func units ReorderBufferSynth rob = inIfc.robIfc; RegRenamingTable regRenamingTable = inIfc.rtIfc; @@ -338,12 +362,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 ); @@ -404,7 +472,12 @@ 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( +`ifdef INCLUDE_GDB_CONTROL + (rg_run_state == RUN_STATE_RUNNING) &&& +`endif + commitTrap matches tagged Valid .trap); + // reset commitTrap commitTrap <= Invalid; @@ -413,20 +486,59 @@ 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); + Bool debugger_halt = False; - // 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_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; + + $display ("%0d: %m.commitStage.doCommitTrap_handle; debugger halt:", cur_cycle); + end +`endif + + if (! debugger_halt) begin + // trap handling & redirect + let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr); + inIfc.redirectPc(new_pc); + + // 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 @@ -461,6 +573,9 @@ 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) && @@ -585,6 +700,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) && +`endif !isValid(commitTrap) && !isValid(rob.deqPort[0].deq_data.trap) && !isValid(rob.deqPort[0].deq_data.ldKilled) && @@ -736,7 +854,6 @@ module mkCommitStage#(CommitInput inIfc)(CommitStage); `endif endrule - method Data getPerf(ComStagePerfType t); return (case(t) `ifdef PERF_COUNT @@ -785,4 +902,16 @@ 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; + $display ("%0d: %m.commitStage.debug_resume", cur_cycle); + endmethod +`endif + endmodule diff --git a/src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv b/src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv index 635d904..1cc7753 100644 --- a/src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv +++ b/src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv @@ -50,6 +50,8 @@ import CCTypes::*; import L1CoCache::*; import MMIOInst::*; +import Cur_Cycle :: *; + // ================================================================ // For fv_decode_C function and related types and definitions @@ -74,6 +76,9 @@ interface FetchStage; // redirection methods method Action setWaitRedirect; method Action redirect(Addr pc); +`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, @@ -1009,6 +1014,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.FetchStage.setWaitFlush", cur_cycle); + endmethod +`endif + method Action done_flushing() if (waitForFlush); // signal that the pipeline can resume fetching waitForFlush <= False; diff --git a/src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv b/src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv index 8570554..b28d241 100644 --- a/src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv +++ b/src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv @@ -36,6 +36,7 @@ `define a True `define f True `define d True +`define c True //`define NUM_CORES 1 // defined in make file diff --git a/src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv b/src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv index 960edde..dbc89ce 100644 --- a/src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv +++ b/src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv @@ -48,6 +48,8 @@ import ReservationStationMem::*; import ReservationStationFpuMulDiv::*; import SplitLSQ::*; +import Cur_Cycle :: *; + typedef struct { FetchDebugState fetch; EpochDebugState epoch; @@ -75,6 +77,9 @@ interface RenameInput; method Bool checkDeadlock; // performance method Bool doStats; +`ifdef INCLUDE_GDB_CONTROL + method Bool core_is_running; +`endif endinterface interface RenameStage; @@ -83,6 +88,11 @@ 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); @@ -153,6 +163,23 @@ 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; + $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 @@ -234,6 +261,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 +305,19 @@ 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 (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); +`endif let x = fetchStage.pipelines[0].first; let pc = x.pc; let orig_inst = x.orig_inst; @@ -377,8 +422,14 @@ 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; @@ -520,8 +571,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; @@ -677,6 +734,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: @@ -685,6 +745,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 @@ -733,6 +798,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; @@ -986,6 +1058,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); @@ -1026,4 +1103,17 @@ 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; + $display ("%0d: %m.renameStage.renameStage.debug_halt_req", cur_cycle); + endmethod + + method Action debug_resume () if (rg_m_halt_req != tagged Invalid); + rg_m_halt_req <= tagged Invalid; + $display ("%0d: %m.renameStage.renameStage.debug_resume", cur_cycle); + endmethod +`endif + endmodule diff --git a/src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv b/src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv index aaeb447..3c6b3cb 100644 --- a/src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv +++ b/src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv @@ -35,8 +35,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 +65,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; diff --git a/src_Core/RISCY_OOO/procs/lib/LLCDmaConnect.bsv b/src_Core/RISCY_OOO/procs/lib/LLCDmaConnect.bsv index edae865..eb62489 100644 --- a/src_Core/RISCY_OOO/procs/lib/LLCDmaConnect.bsv +++ b/src_Core/RISCY_OOO/procs/lib/LLCDmaConnect.bsv @@ -1,4 +1,16 @@ +// This file is a modified version of: RISCY_OOO/procs/lib/LLCDmaConnect.bsv +// The original module had, as 2nd parameter, MemLoaderMemClient memLoader +// The memLoader assumed only write-transactions (to load memory), and +// discarded load responses. + +// Here, the module instead offers an AXI4_Slave interface to be +// connected to the AXI4_Master of the Debug Module. +// This axi4_slave accepts and responds to both read and write transactions. + +// Modifications Copyright (c) 2020 Bluespec, Inc. + +// Original copyright: // Copyright (c) 2017 Massachusetts Institute of Technology // // Permission is hereby granted, free of charge, to any person @@ -26,6 +38,7 @@ import GetPut::*; import Vector::*; import BuildVector::*; import FIFO::*; +import FIFOF::*; import Assert::*; import Types::*; @@ -37,6 +50,10 @@ import MemLoader::*; import CrossBar::*; import MemLoader::*; +import AXI4_Types :: *; +import Fabric_Defs :: *; +import Semi_FIFOF :: *; + typedef struct { CoreId core; TlbMemReqId id; @@ -48,15 +65,44 @@ typedef union tagged { TlbDmaReqId Tlb; } LLCDmaReqId deriving(Bits, Eq, FShow); -module mkLLCDmaConnect#( +// For writing, position a 4-byte value and 4-bit byte-enable into a 64-byte line and 64-bit line-byte-enable +function Tuple3 #(Addr, Line, LineByteEn) fn_line_and_byteen_from_word (Addr addr, Bit #(64) data); + Vector #(16, Bit #(32)) line_words = replicate (0); + Vector #(16, Bit #(4)) line_word_byteens = replicate (0); + Bit #(4) word_index = addr [5:2]; + line_words [word_index] = ((addr [2] == 0) ? data [31:0] : data [63:32]); + line_word_byteens [word_index] = 4'b1111; + Addr line_addr = { addr [63:6], 6'b0 }; + return tuple3 (line_addr, + unpack (pack (line_words)), + unpack (pack (line_word_byteens))); +endfunction + +// For reading, extract a 4-byte value from a 64-byte line +function Bit #(64) fn_word_from_line (Line line, Bit #(4) word_in_line); + Vector #(16, Bit #(32)) line_words = unpack (pack (line)); + Bit #(32) w = line_words [word_in_line]; + Bit #(64) dw = ((word_in_line [0] == 0) ? { 32'b0, w } : { w, 32'b0 }); + return dw; +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 word-in-line here + FIFOF #(Bit #(4)) f_word_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; + // 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 +129,46 @@ 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 + rule sendMemLoaderReqToLLC_wr; // write requests + let wr_addr <- pop_o (axi4_slave_xactor.o_wr_addr); + let wr_data <- pop_o (axi4_slave_xactor.o_wr_data); + match { .line_addr, .line_data, .line_byteen } = fn_line_and_byteen_from_word (wr_addr.awaddr, wr_data.wdata); + dmaRqT req = DmaRq {addr: line_addr, + byteEn: line_byteen, + data: line_data, + id: tagged MemLoader (?) // TODO: change uniformly to wr_addr.awid + }; + llc.memReq.enq(req); + + if (verbosity != 0) begin + $display("[LLCDmaConnect sendMemLoaderReqToLLC_wr]"); + $display (" ", fshow (wr_addr)); + $display (" ", fshow (wr_data)); + $display (" ", fshow (req)); + end endrule - (* descending_urgency = "sendMemLoaderReqToLLC, sendTlbReqToLLC" *) + rule sendMemLoaderReqToLLC_rd; // read requests + let rd_addr <- pop_o (axi4_slave_xactor.o_rd_addr); + Addr line_addr = { rd_addr.araddr [63:6], 6'b0 }; + dmaRqT req = DmaRq {addr: line_addr, + byteEn: replicate (False), + data: ?, + id: MemLoader (?) // TODO: change uniformly to rd_addr.awid + }; + llc.memReq.enq(req); + Bit #(4) word_in_line = rd_addr.araddr [5:2]; + f_word_in_line.enq (word_in_line); + + if (verbosity != 0) begin + $display("[LLCDmaConnect sendMemLoaderReqToLLC_rd]"); + $display (" ", fshow (rd_addr)); + $display (" ", fshow (req)); + end + endrule + + (* descending_urgency = "sendMemLoaderReqToLLC_wr, sendTlbReqToLLC" *) + (* descending_urgency = "sendMemLoaderReqToLLC_rd, sendTlbReqToLLC" *) rule sendTlbReqToLLC; let {c, r} <- toGet(tlbQ).get; let req = getTlbDmaReq(c, r); @@ -112,12 +180,22 @@ module mkLLCDmaConnect#( // 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"); + let resp = llc.respLd.first; + llc.respLd.deq; + let word_in_line = f_word_in_line.first; + f_word_in_line.deq; + AXI4_Rd_Data #(Wd_Id, Wd_Data, Wd_User) + rd_data = AXI4_Rd_Data {rid: 0, // TODO: change uniformly to Fabric_Id + rdata: fn_word_from_line (resp.data, word_in_line), + rresp: axi4_resp_okay, + rlast: True, + ruser: ?}; + axi4_slave_xactor.i_rd_data.enq (rd_data); + if (verbosity != 0) begin + $display ("[LLCDmaConnect sendLdRespToMemLoader]"); + $display (" ", fshow (resp)); + $display (" ", fshow (rd_data)); + end endrule rule sendLdRespToTlb(llc.respLd.first.id matches tagged Tlb .id); @@ -135,12 +213,19 @@ module mkLLCDmaConnect#( // 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 + let resp = llc.respSt.first; + llc.respSt.deq; + 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 != 0) begin + $display ("[LLCDmaConnect sendStRespToMemLoader]"); + $display (" ", fshow (resp)); + $display (" ", fshow (wr_resp)); + end endrule rule sendStRespToTlb(llc.respSt.first matches tagged Tlb .id); @@ -150,4 +235,6 @@ module mkLLCDmaConnect#( end doAssert(False, "No TLB st"); endrule + + return axi4_slave_xactor.axi_side; endmodule diff --git a/src_Core/RISCY_OOO/procs/lib/ProcTypes.bsv b/src_Core/RISCY_OOO/procs/lib/ProcTypes.bsv index a3ce618..5072908 100644 --- a/src_Core/RISCY_OOO/procs/lib/ProcTypes.bsv +++ b/src_Core/RISCY_OOO/procs/lib/ProcTypes.bsv @@ -99,7 +99,7 @@ typedef struct { instance DefaultValue#(RiscVISASubset); function RiscVISASubset defaultValue = RiscVISASubset { s: True, u: True, - m: `m , a: `a , f: `f , d: `d + m: `m , a: `a , f: `f , d: `d, c: `c }; endinstance @@ -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 @@ -446,13 +454,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 {