From ab2b4fdf732ee48bee76d06a98d613a31312378a Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sun, 19 Dec 2021 15:47:13 +0000 Subject: [PATCH] add DMI STOPADDR register and use it in HDLRunner to halt simulations at exactly the right point. very useful also for gdb hardware-level breakpoints --- src/soc/debug/dmi.py | 16 ++++++++++++++-- src/soc/simple/issuer.py | 14 ++++++++++++-- src/soc/simple/test/test_runner.py | 7 ++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/soc/debug/dmi.py b/src/soc/debug/dmi.py index 8a6686df..8a3aaea5 100644 --- a/src/soc/debug/dmi.py +++ b/src/soc/debug/dmi.py @@ -26,6 +26,7 @@ class DBGCore: CR = 0b1000 # CR (read only) XER = 0b1001 # XER (read only) - note this is a TEMPORARY hack SVSTATE = 0b1010 # SVSTATE register (read only for now) + STOPADDR = 0b1011 # Address at which the core automatically stops # CTRL register (direct actions, write 1 to act, read back 0) @@ -120,6 +121,10 @@ class CoreDebug(Elaboratable): self.log_read_data_o = Signal(64) self.log_write_addr_o = Signal(32) + # address at which the processor stops automatically + # set to 0xffffffffffffffff by default (impossible to reach) + self.stop_addr_o = Signal(64, reset=-1) + # Misc self.terminated_o = Signal() @@ -191,6 +196,8 @@ class CoreDebug(Elaboratable): comb += dmi.dout.eq(d_cr.data) with m.Case(DBGCore.XER): comb += dmi.dout.eq(d_xer.data) + with m.Case(DBGCore.STOPADDR): # Halt PC + comb += dmi.dout.eq(self.stop_addr_o) # DMI writes # Reset the 1-cycle "do" signals @@ -231,6 +238,11 @@ class CoreDebug(Elaboratable): with m.Elif(dmi.addr_i == DBGCore.LOG_ADDR): sync += log_dmi_addr.eq(dmi.din) sync += do_dmi_log_rd.eq(1) + + # set PC Halt address + with m.Elif(dmi.addr_i == DBGCore.STOPADDR): + sync += self.stop_addr_o.eq(dmi.din) + with m.Else(): # sync += Display("DMI read from " & to_string(dmi_addr)) pass @@ -255,10 +267,10 @@ class CoreDebug(Elaboratable): comb += d_gpr.addr.eq(gspr_index) # Core control signals generated by the debug module - comb += self.core_stop_o.eq(stopping & ~do_step) + comb += self.core_stop_o.eq((stopping & ~do_step) | self.terminate_i) comb += self.core_rst_o.eq(do_reset) comb += self.icache_rst_o.eq(do_icreset) - comb += self.terminated_o.eq(terminated) + comb += self.terminated_o.eq(terminated | self.terminate_i) # Logging RAM (none) diff --git a/src/soc/simple/issuer.py b/src/soc/simple/issuer.py index 7c69eef9..15391fe8 100644 --- a/src/soc/simple/issuer.py +++ b/src/soc/simple/issuer.py @@ -525,6 +525,15 @@ class TestIssuerBase(Elaboratable): with m.If(core_rst): m.d.sync += self.cur_state.eq(0) + # check halted condition: requested PC to execute matches DMI stop addr + # and immediately stop. address of 0xffff_ffff_ffff_ffff can never + # match + halted = Signal() + comb += halted.eq(dbg.stop_addr_o == dbg.state.pc) + with m.If(halted): + comb += dbg.core_stopped_i.eq(1) + comb += dbg.terminate_i.eq(1) + # PC and instruction from I-Memory comb += self.pc_o.eq(cur_state.pc) self.pc_changed = Signal() # note write to PC @@ -710,9 +719,10 @@ class FetchFSM(ControlBase): # waiting (zzz) with m.State("IDLE"): - with m.If(~dbg.stopping_o & ~fetch_failed): + with m.If(~dbg.stopping_o & ~fetch_failed & ~dbg.core_stop_o): comb += fetch_pc_o_ready.eq(1) - with m.If(fetch_pc_i_valid & ~pdecode2.instr_fault): + with m.If(fetch_pc_i_valid & ~pdecode2.instr_fault + & ~dbg.core_stop_o): # instruction allowed to go: start by reading the PC # capture the PC and also drop it into Insn Memory # we have joined a pair of combinatorial memory diff --git a/src/soc/simple/test/test_runner.py b/src/soc/simple/test/test_runner.py index 3f637059..e83a39b7 100644 --- a/src/soc/simple/test/test_runner.py +++ b/src/soc/simple/test/test_runner.py @@ -265,6 +265,12 @@ class HDLRunner(StateRunner): print("instructions", instructions) + # before starting the simulation, set the core stop address to be + # just after the last instruction. if a load of an instruction is + # requested at this address, the core is immediately put into "halt" + # XXX: keep an eye out for in-order problems + yield from set_dmi(dmi, DBGCore.STOPADDR, len(instructions)*4) + # run the loop of the instructions on the current test index = (yield self.issuer.cur_state.pc) // 4 while index < len(instructions): @@ -320,7 +326,6 @@ class HDLRunner(StateRunner): yield break - terminated = yield self.issuer.dbg.terminated_o print("terminated(2)", terminated) if terminated: -- 2.30.2