neorv32/docs/datasheet/on_chip_debugger.adoc

787 lines
36 KiB
Plaintext
Raw Permalink Normal View History

2024-02-24 08:25:27 +00:00
<<<
:sectnums:
== On-Chip Debugger (OCD)
The NEORV32 Processor features an _on-chip debugger_ (OCD) implementing the **execution-based debugging** scheme,
which is compatible to the **Minimal RISC-V Debug Specification**. A copy of the specification is
available in `docs/references`.
**Key Features**
* standard JTAG access port
* full control of the CPU: halting, single-stepping and resuming
* indirect access to all core registers (via program buffer)
* indirect access to the whole processor address space (via program buffer)
* trigger module for hardware breakpoints
* compatible with upstream OpenOCD and GDB
**Section Structure**
* <<_debug_transport_module_dtm>>
* <<_debug_module_dm>>
* <<_cpu_debug_mode>>
* <<_trigger_module>>
.GDB + SVD
[TIP]
Together with a third-party plugin the processor's SVD file can be imported right into GDB to allow comfortable
debugging of peripheral/IO devices (see https://github.com/stnolting/neorv32/discussions/656).
.Hands-On Tutorial
[TIP]
A simple example on how to use NEORV32 on-chip debugger in combination with OpenOCD and the GNU debugger is shown in
section https://stnolting.github.io/neorv32/ug/#_debugging_using_the_on_chip_debugger[Debugging using the On-Chip Debugger]
of the User Guide.
.OCD Security Note
[NOTE]
JTAG access via the OCD is **always authenticated** (`dmstatus.authenticated = 1`). Hence, the entire system can always
be accessed via the on-chip debugger.
The NEORV32 on-chip debugger complex is based on four hardware modules:
.NEORV32 on-chip debugger complex
image::neorv32_ocd_complex.png[align=center]
[start=1]
. <<_debug_transport_module_dtm>> (`rtl/core/neorv32_debug_dtm.vhd`): JTAG access tap to allow an external
adapter to interface with the _debug module (DM)_ using the _debug module interface (dmi)_.
. <<_debug_module_dm>> (`rtl/core/neorv32_debug_tm.vhd`): RISC-V debug module that is configured by the DTM via the _dmi_.
From the CPU's "point of view" this module behaves as another memory-mapped "peripheral" that can be accessed via the
processor-internal bus. The memory-mapped registers provide an internal _data buffer_ for data transfer from/to the DM, a
_code ROM_ containing the "park loop" code, a _program buffer_ to allow the debugger to execute small programs defined by the
DM and a _status register_ that is used to communicate _exception, _halt_, _resume_ and _execute_ requests/acknowledges from/to the DM.
. CPU <<_cpu_debug_mode>> extension (part of `rtl/core/neorv32_cpu_control.vhd`): This extension provides the "debug execution mode",
which executes the park loop code from the DM. The mode also provides additional CSRs and instructions.
. CPU <<_trigger_module>> (also part of `rtl/core/neorv32_cpu_control.vhd`): This module provides a single _hardware_ breakpoint,
which allows to debug code executed from ROM.
**Theory of Operation**
When debugging the system using the OCD, the debugger issues a halt request to the CPU (via the CPU's
`db_halt_req_i` signal) to make the CPU enter _debug mode_. In this state, the application-defined architectural
state of the system/CPU is "frozen" so the debugger can monitor if without interfering with the actual application.
However, the OCD can also modify the entire architectural state at any time. While in debug mode, the debugger has
full control over the entire CPU and processor.
While in debug mode, the CPU executes the "park loop" code from the code ROM of the DM.
This park loop implements an endless loop, where the CPU polls the memory-mapped _status register_ that is
controlled by the debug module (DM). The flags in this register are used to communicate requests from
the DM and to acknowledge them by the CPU: trigger execution of the program buffer or resume the halted
application. Furthermore, the CPU uses this register to signal that the CPU has halted after a halt request
and to signal that an exception has been triggered while being in debug mode.
<<<
// ####################################################################################################################
:sectnums:
=== Debug Transport Module (DTM)
The debug transport module (VHDL module: `rtl/core/neorv32_debug_dtm.vhd`) provides a JTAG test access port (TAP).
External JTAG access is provided by the following top-level ports.
.JTAG top level signals
[cols="^2,^2,^2,<8"]
[options="header",grid="rows"]
|=======================
| Name | Width | Direction | Description
| `jtag_trst_i` | 1 | in | TAP reset (low-active); this signal is optional, make sure to pull it **high** if not used
| `jtag_tck_i` | 1 | in | serial clock
| `jtag_tdi_i` | 1 | in | serial data input
| `jtag_tdo_o` | 1 | out | serial data output
| `jtag_tms_i` | 1 | in | mode select
|=======================
.Maximum JTAG Clock
[IMPORTANT]
All JTAG signals are synchronized to the processor's clock domain. Hence, no additional clock domain is required for the DTM.
However, this constraints the maximal JTAG clock frequency (`jtag_tck_i`) to be less than or equal to **1/5** of the processor
clock frequency (`clk_i`).
.Maintaining JTAG Chain
[NOTE]
If the on-chip debugger is disabled the JTAG serial input `jtag_tdi_i` is directly
connected to the JTAG serial output `jtag_tdo_o` to maintain the JTAG chain.
JTAG accesses are based on a single _instruction register_ `IR`, which is 5 bit wide, and several _data registers_ `DR`
with different sizes. The individual data registers are accessed by writing the according address to the instruction
register. The following table shows the available data registers and their addresses:
.JTAG TAP registers
[cols="^2,^2,^2,<8"]
[options="header",grid="rows"]
|=======================
| Address (via `IR`) | Name | Size (bits) | Description
| `00001` | `IDCODE` | 32 | identifier, hardwired to `0x00000001`
| `10000` | `DTMCS` | 32 | debug transport module control and status register
| `10001` | `DMI` | 41 | debug module interface (_dmi_); 7-bit address, 32-bit read/write data, 2-bit operation (`00` = NOP; `10` = write; `01` = read)
| others | `BYPASS` | 1 | default JTAG bypass register
|=======================
.`DTMCS` - DTM Control and Status Register
[cols="^2,^3,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit(s) | Name | R/W | Description
| 31:18 | - | r/- | _reserved_, hardwired to zero
| 17 | `dmihardreset` | r/w | setting this bit will reset the debug module interface; this bit auto-clears
| 16 | `dmireset` | r/w | setting this bit will clear the sticky error state; this bit auto-clears
| 15 | - | r/- | _reserved_, hardwired to zero
| 14:12 | `idle` | r/- | recommended idle states (= 0, no idle states required)
| 11:10 | `dmistat` | r/- | DMI status: `00` = no error, `01` = reserved, `10` = operation failed, `11` = failed operation during pending DMI operation
| 9:4 | `abits` | r/- | number of address bits in `DMI` register (= 6)
| 3:0 | `version` | r/- | `0001` = DTM is compatible to spec. versions v0.13 and v1.0
|=======================
<<<
// ####################################################################################################################
:sectnums:
=== Debug Module (DM)
The debug module "DM" (VHDL module: `rtl/core/neorv32_debug_dm.vhd`) acts as a translation interface between abstract
operations issued by the debugger (application) and the platform-specific debugger (hardware) implementation.
It supports the following features:
* Gives the debugger necessary information about the implementation.
* Allows the hart to be halted/resumed and provides status of the current state.
* Provides abstract read and write access to the halted hart's GPRs.
* Provides access to a reset signal that allows debugging from the very first instruction after reset.
* Provides a Program Buffer to force the hart to execute arbitrary instructions.
* Allows memory access from a hart's point of view.
The NEORV32 DM follows the "Minimal RISC-V External Debug Specification" to provide full debugging capabilities while
keeping resource/area requirements at a minimum. It implements the **execution based debugging scheme** for a
single hart and provides the following hardware features:
* program buffer with 2 entries and implicit `ebreak` instruction afterwards
* no _direct_ bus access; indirect bus access via the CPU using the program buffer
* abstract commands: "access register" plus auto-execution
* no dedicated halt-on-reset capabilities yet (but can be emulated)
.DM Spec. Version
[TIP]
By default, the OCD's debug module supports version 1.0 of the RISC-V debug spec. For backwards compatibility, the DM
can be "downgraded" back to version 0.13 via the `DM_LEGACY_MODE` generic (see <<_processor_top_entity_generics>>).
The DM provides two access "point of views": accesses from the DTM via the _debug module interface (dmi)_ and
accesses from the CPU via the processor-internal bus. From the DTM's point of view, the DM implements a set of
<<_dm_registers>> that are used to control and monitor the actual debugging. From the CPU's point of view, the
DM implements several memory-mapped registers (within the _normal_ address space) that are used for communicating
debugging control and status (<<_dm_cpu_access>>).
:sectnums:
==== DM Registers
The DM is controlled via a set of registers that are accessed via the DTM's _debug module interface_.
The following registers are implemented:
[NOTE]
Write accesses to registers that are not implemented are simply ignored and read accesses will always return zero.
.Available DM registers
[cols="^2,^3,<7"]
[options="header",grid="rows"]
|=======================
| Address | Name | Description
| `0x04` | <<_data0>> | Abstract data 0, used for data transfer between debugger and processor
| `0x10` | <<_dmcontrol>> | Debug module control
| `0x11` | <<_dmstatus>> | Debug module status
| `0x12` | <<_hartinfo>> | Hart information
| `0x16` | <<_abstracts>> | Abstract control and status
| `0x17` | <<_command>> | Abstract command
| `0x18` | <<_abstractauto>> | Abstract command auto-execution
| `0x1d` | `nextdm` | Base address of next DM; reads as zero to indicate there is only one DM
| `0x20` | <<_progbuf, `progbuf0`>> | Program buffer 0
| `0x21` | <<_progbuf, `progbuf1`>> | Program buffer 1
| `0x38` | `sbcs` | System bus access control and status; reads as zero to indicate there is **no** direct system bus access
| `0x40` | <<_haltsum0>> | Halted harts
|=======================
:sectnums!:
===== **`data0`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x04 | **Abstract data 0** | `data0`
3+| Reset value: `0x00000000`
3+| Basic read/write data exchange register to be used with abstract commands (for example to read/write data from/to CPU GPRs).
|======
:sectnums!:
===== **`dmcontrol`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x10 | **Debug module control register** | `dmcontrol`
3+| Reset value: `0x00000000`
3+| Control of the overall debug module and the hart. The following table shows all implemented bits. All remaining bits/bit-fields
are configured as "zero" and are read-only. Writing '1' to these bits/fields will be ignored.
|======
.`dmcontrol` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 31 | `haltreq` | -/w | set/clear hart halt request
| 30 | `resumereq` | -/w | request hart to resume
| 28 | `ackhavereset` | -/w | write `1` to clear `*havereset` flags
| 1 | `ndmreset` | r/w | put whole system (except OCD) into reset state when `1`
| 0 | `dmactive` | r/w | DM enable; writing `0`-`1` will reset the DM
|=======================
:sectnums!:
===== **`dmstatus`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x11 | **Debug module status register** | `dmstatus`
3+| Reset value: `0x00400083`
3+| Current status of the overall debug module and the hart. The entire register is read-only.
|======
.`dmstatus` Register Bits
[cols="^1,^2,<10"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | Description
| 31:23 | _reserved_ | reserved; always zero
| 22 | `impebreak` | always `1`; indicates an implicit `ebreak` instruction after the last program buffer entry
| 21:20 | _reserved_ | reserved; always zero
| 19 | `allhavereset` .2+| `1` when the hart is in reset
| 18 | `anyhavereset`
| 17 | `allresumeack` .2+| `1` when the hart has acknowledged a resume request
| 16 | `anyresumeack`
| 15 | `allnonexistent` .2+| always zero to indicate the hart is always existent
| 14 | `anynonexistent`
| 13 | `allunavail` .2+| `1` when the DM is disabled to indicate the hart is unavailable
| 12 | `anyunavail`
| 11 | `allrunning` .2+| `1` when the hart is running
| 10 | `anyrunning`
| 9 | `allhalted` .2+| `1` when the hart is halted
| 8 | `anyhalted`
| 7 | `authenticated` | always `1`; there is no authentication
| 6 | `authbusy` | always `0`; there is no authentication
| 5 | `hasresethaltreq` | always `0`; halt-on-reset is not supported (directly)
| 4 | `confstrptrvalid` | always `0`; no configuration string available
| 3:0 | `version` | debug spec. version; `0011` (v1.0) or `0010` (v0.13); configured via the `DM_LEGACY_MODE` <<_processor_top_entity_generics>>
|=======================
:sectnums!:
===== **`hartinfo`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x12 | **Hart information** | `hartinfo`
3+| Reset value: _see below_
3+| This register gives information about the hart. The entire register is read-only.
|======
.`hartinfo` Register Bits
[cols="^1,^2,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | Description
| 31:24 | _reserved_ | reserved; always zero
| 23:20 | `nscratch` | `0001`, number of `dscratch*` CPU registers = 1
| 19:17 | _reserved_ | reserved; always zero
| 16 | `dataccess` | `0`, the `data` registers are shadowed in the hart's address space
| 15:12 | `datasize` | `0001`, number of 32-bit words in the address space dedicated to shadowing the `data` registers (1 register)
| 11:0 | `dataaddr` | = `dm_data_base_c(11:0)`, signed base address of `data` words (see address map in <<_dm_cpu_access>>)
|=======================
:sectnums!:
===== **`abstracts`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x16 | **Abstract control and status** | `abstracts`
3+| Reset value: `0x02000801`
3+| Command execution info and status.
|======
.`abstracts` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 31:29 | _reserved_ | r/- | reserved; always zero
| 28:24 | `progbufsize` | r/- | always `0010`: size of the program buffer (`progbuf`) = 2 entries
| 23:11 | _reserved_ | r/- | reserved; always zero
| 12 | `busy` | r/- | `1` when a command is being executed
| 11 | `relaxedpriv` | r/- | always `1`: PMP rules are ignored when in debug mode
| 10:8 | `cmderr` | r/w | error during command execution (see below); has to be cleared by writing `111`
| 7:4 | _reserved_ | r/- | reserved; always zero
| 3:0 | `datacount` | r/- | always `0001`: number of implemented `data` registers for abstract commands = 1
|=======================
Error codes in `cmderr` (highest priority first):
* `000` - no error
* `100` - command cannot be executed since hart is not in expected state
* `011` - exception during command execution
* `010` - unsupported command
* `001` - invalid DM register read/write while command is/was executing
:sectnums!:
===== **`command`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x17 | **Abstract command** | `command`
3+| Reset value: `0x00000000`
3+| Writing this register will trigger the execution of an abstract command. New command can only be executed if
`cmderr` is zero. The entire register in write-only (reads will return zero).
|======
[NOTE]
The NEORV32 DM only supports **Access Register** abstract commands. These commands can only access the
hart's GPRs (abstract command register index `0x1000` - `0x101f`).
.`command` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description / required value
| 31:24 | `cmdtype` | -/w | `00000000` to indicate "access register" command
| 23 | _reserved_ | -/w | reserved, has to be `0` when writing
| 22:20 | `aarsize` | -/w | `010` to indicate 32-bit accesses
| 21 | `aarpostincrement` | -/w | `0`, post-increment is not supported
| 18 | `postexec` | -/w | if set the program buffer is executed _after_ the command
| 17 | `transfer` | -/w | if set the operation in `write` is conducted
| 16 | `write` | -/w | `1`: copy `data0` to `[regno]`, `0`: copy `[regno]` to `data0`
| 15:0 | `regno` | -/w | GPR-access only; has to be `0x1000` - `0x101f`
|=======================
:sectnums!:
===== **`abstractauto`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x18 | **Abstract command auto-execution** | `abstractauto`
3+| Reset value: `0x00000000`
3+| Register to configure when a read/write access to a DM repeats execution of the last abstract command.
|======
.`abstractauto` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 17 | `autoexecprogbuf[1]` | r/w | when set reading/writing from/to `progbuf1` will execute `command` again
| 16 | `autoexecprogbuf[0]` | r/w | when set reading/writing from/to `progbuf0` will execute `command` again
| 0 | `autoexecdata[0]` | r/w | when set reading/writing from/to `data0` will execute `command` again
|=======================
:sectnums!:
===== **`progbuf`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x20 | **Program buffer 0** | `progbuf0`
| 0x21 | **Program buffer 1** | `progbuf1`
3+| Reset value: `0x00000013` ("NOP")
3+| Program buffer (two entries) for the DM.
|======
:sectnums!:
===== **`haltsum0`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x408 | **Halted harts status** | `haltsum0`
3+| Reset value: `0x00000000`
3+| Hart has halted when according bit is set.
|======
.`haltsum0` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 0 | `haltsum0[0]` | r/- | Hart is halted when set.
|=======================
:sectnums:
==== DM CPU Access
From the CPU's perspective, the DM behaves as a memory-mapped peripheral. It occupies 256 bytes of the CPU's address
space starting at address `dm_base_c` (see table below). This address space is divided into four sections of 64 bytes
each to provide access to the _park loop code ROM_, the _program buffer_, the _data buffer_ and the _status register_.
The program buffer, the data buffer and the status register do not fully occupy the 64-byte-wide sections and are
mirrored to fill the entire section.
.DM CPU Access - Address Map
[cols="^2,^2,<5"]
[options="header",grid="rows"]
|=======================
| Base address | Actual size | Description
| `0xffffff00` | 64 bytes | ROM for the "park loop" code
| `0xffffff40` | 16 bytes | Program buffer (<<_progbuf>>)
| `0xffffff80` | 4 bytes | Data buffer (<<_data0>>)
| `0xffffffc0` | 4 bytes | Control and <<_status_register>>
|=======================
.DM Register Access
[IMPORTANT]
All memory-mapped registers of the DM can only be accessed by the CPU if it is actually in debug mode.
Hence, the DM registers are not "visible" for normal CPU operations.
Any CPU access outside of debug mode will raise a bus access fault exception.
.Park Loop Code Sources ("OCD Firmware")
[NOTE]
The assembly sources of the **park loop code** are available in `sw/ocd-firmware/park_loop.S`. Please note that
these sources are not intended to be changed by the user.
:sectnums:
===== Code ROM Entry Points
The park loop code provides two entry points, where the actual code execution can start. These are used to enter
the park loop either when an explicit request has been issued (for example a halt request) or when an exception
has occurred _while executing_ the park loop code itself.
.Park Loop Entry Points
[cols="^6,<4"]
[options="header",grid="rows"]
|=======================
| Address | Description
| `dm_exc_entry_c` (`dm_base_c` + 0) | Exception entry address
| `dm_park_entry_c` (`dm_base_c` + 8) | Normal entry address
|=======================
When the CPU enters or re-enters debug mode (for example via an `ebreak` in the DM's program buffer), it jumps to
the _normal entry point_ that is configured via the `CPU_DEBUG_PARK_ADDR` generic
(<<_cpu_top_entity_generics>>). By default, this generic is set to `dm_park_entry_c`, which is defined in main
package file. If an exception is encountered during debug mode, the CPU jumps to the address of the _exception
entry point_ configured via the `CPU_DEBUG_EXC_ADDR` generic (<<_cpu_top_entity_generics>>). By default, this generic
is set to `dm_exc_entry_c`, which is also defined in main package file.
:sectnums:
===== Status Register
The status register provides a direct communication channel between the CPU's debug mode executing the park loop
and the debugger-controlled debug module. This register is used to communicate _requests_, which are issued by the
DM and the according _acknowledges_, which are generated by the CPU.
There are only 4 bits in this register that are used to implement the requests/acknowledges. Each bit is left-aligned
in one sub-byte of the entire 32-bit register. Thus, the CPU can access each bit individually using _store-byte_ and
_load-byte_ instructions. This eliminates the need to perform bit-masking in the park loop code leading to less code size
and faster execution.
.DM Status Register - CPU Access
[cols="^1,^3,^2,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name | CPU access <| Description
.2+| 0 | `sreg_halt_ack` | read <| -
| - | write <| Set by the CPU while it is halted (and executing the park loop).
.2+| 8 | `sreg_resume_req` | read <| Set by the DM to request the CPU to resume normal operation.
| `sreg_resume_ack` | write <| Set by the CPU before it starts resuming.
.2+| 16 | `sreg_execute_req` | read <| Set by the DM to request execution of the program buffer.
| `sreg_execute_ack` | write <| Set by the CPU before it starts executing the program buffer.
.2+| 24 | - | read <| -
| `sreg_execute_ack` | write <| Set by the CPU if an exception occurs while being in debug mode.
|=======================
<<<
// ####################################################################################################################
:sectnums:
=== CPU Debug Mode
The NEORV32 CPU Debug Mode is compatible to the **Minimal RISC-V Debug Specification 1.0**
`Sdext` (external debug) ISA extension. When enabled via the <<_sdext_isa_extension>> generic (CPU) and/or
the `ON_CHIP_DEBUGGER_EN` (Processor) it adds a new CPU operation mode ("debug mode"), three additional CSRs
(section <<_cpu_debug_mode_csrs>>) and one additional instruction (`dret`) to the core.
.ISA Requirements
[IMPORTANT]
The CPU debug mode requires the `Zicsr` and `Zifencei` CPU extension to be implemented.
The CPU debug-mode is entered on any of the following events:
[start=1]
. The CPU executes a `ebreak` instruction (when in machine-mode and `ebreakm` in <<_dcsr>> is set OR when in user-mode and `ebreaku` in <<_dcsr>> is set).
. A debug halt request is issued by the DM (via CPU signal `db_halt_req_i`, high-active, triggering on rising-edge).
. The CPU completes executing of a single instruction while being single-step debugging mode (enabled if `step` in <<_dcsr>> is set).
. A hardware trigger from the <<_trigger_module>> fires (if `action` in <<_tdata1>> / `mcontrol` is set).
[NOTE]
From a hardware point of view these entry conditions are special traps that are handled transparently by
the control logic.
**Whenever the CPU enters debug-mode it performs the following operations:**
* wake-up CPU if it was send to sleep mode by the `wfi` instruction
* move the current program counter to <<_dpc>>
* copy the hart's current privilege level to the `prv` flags in <<_dcsr>>
* set `cause` in <<_dcrs>> according to the cause why debug mode is entered
* **no update** of `mtval`, `mcause`, `mtval` and `mstatus` CSRs
* load the address configured via the CPU's `CPU_DEBUG_PARK_ADDR` (<<_cpu_top_entity_generics>>) generic to the program counter jumping to the
"debugger park loop" code stored in the debug module (DM)
**When the CPU is in debug-mode the following things are important:**
* while in debug mode, the CPU executes the parking loop and - if requested by the DM - the program buffer
* effective CPU privilege level is `machine` mode; any active physical memory protection (PMP) configuration is bypassed
* the `wfi` instruction acts as a `nop` (also during single-stepping)
* if an exception occurs while being in debug mode:
** if the exception was caused by any debug-mode entry action the CPU jumps to the normal entry point (defined by `CPU_DEBUG_PARK_ADDR` generic of the <<_cpu_top_entity_generics>>) of the park loop again (for example when executing `ebreak` while in debug-mode)
** for all other exception sources the CPU jumps to the exception entry point (defined by `CPU_DEBUG_EXC_ADDR` generic of the <<_cpu_top_entity_generics>>) to signal an exception to the DM; the CPU restarts the park loop again afterwards
* interrupts are disabled; however, they will remain pending and will get executed after the CPU has left debug mode
* if the DM makes a resume request, the park loop exits and the CPU leaves debug mode (executing `dret`)
* the standard counters <<_machine_counter_and_timer_csrs>> `[m]cycle[h]` and `[m]instret[h]` are stopped
* all <<_hardware_performance_monitors_hpm_csrs>> are stopped
Debug mode is left either by executing the `dret` instruction or by performing a hardware reset of the CPU.
Executing `dret` outside of debug mode will raise an illegal instruction exception.
**Whenever the CPU leaves debug mode it performs the following operations:**
* set the hart's current privilege level according to the `prv` flags of <<_dcsr>>
* restore the original program counter from <<_dpcs>> resuming normal operation
:sectnums:
==== CPU Debug Mode CSRs
Two additional CSRs are required by the _Minimal RISC-V Debug Specification_: the debug mode control and status register
`dcsr` and the debug program counter `dpc`. An additional general purpose scratch register for debug mode only
(`dscratch0`) allows faster execution by having a fast-accessible backup register.
[NOTE]
The debug-mode CSRs are only accessible when the CPU is _in_ debug mode. If these CSRs are accessed outside of debug mode
an illegal instruction exception is raised.
:sectnums!:
===== **`dcsr`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Debug control and status register
| Address | `0x7b0`
| Reset value | `0x40000413`
| ISA | `Zicsr` & `Sdext`
| Description | This register is used to configure the debug mode environment and provides additional status information.
|=======================
.Debug control and status register `dcsr` bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 31:28 | `xdebugver` | r/- | `0100` - CPU debug mode is compatible to spec. version 1.0
| 27:16 | - | r/- | `000000000000` - _reserved_
| 15 | `ebereakm` | r/w | `ebreak` instructions in `machine` mode will _enter_ debug mode when set
| 14 | `ebereakh` | r/- | `0` - hypervisor mode not supported
| 13 | `ebereaks` | r/- | `0` - supervisor mode not supported
| 12 | `ebereaku` | r/w | `ebreak` instructions in `user` mode will _enter_ debug mode when set
| 11 | `stepie` | r/- | `0` - IRQs are disabled during single-stepping
| 10 | `stopcount` | r/- | `1` - standard counters and HPMs are stopped when in debug mode
| 9 | `stoptime` | r/- | `0` - timers increment as usual
| 8:6 | `cause` | r/- | cause identifier - why debug mode was entered (see below)
| 5 | - | r/- | `0` - _reserved_
| 4 | `mprven` | r/- | `1` - <<_mstatus>>.mprv is also evaluated when in debug mode
| 3 | `nmip` | r/- | `0` - non-maskable interrupt is pending
| 2 | `step` | r/w | enable single-stepping when set
| 1:0 | `prv` | r/w | CPU privilege level before/after debug mode
|=======================
Cause codes in `dcsr.cause` (highest priority first):
* `010` - triggered by hardware <<_trigger_module>>
* `001` - executed `EBREAK` instruction
* `011` - external halt request (from DM)
* `100` - return from single-stepping
:sectnums!:
===== **`dpc`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Debug program counter
| Address | `0x7b1`
| Reset value | `CPU_BOOT_ADDR`, CPU boot address, 4-byte aligned (see <<_cpu_top_entity_generics>> and <<_address_space>>)
| ISA | `Zicsr` & `Sdext`
| Description | The register is used to store the current program counter when debug mode is entered. The `dret` instruction will
return to the address stored in `dpc` by automatically moving `dpc` to the program counter.
|=======================
[NOTE]
`dpc[0]` is hardwired to zero. If IALIGN = 32 (i.e. <<_c_isa_extension>> is disabled) then `dpc[1]` is also hardwired to zero.
:sectnums!:
===== **`dscratch0`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Debug scratch register 0
| Address | `0x7b2`
| Reset value | `0x00000000`
| ISA | `Zicsr` & `Sdext`
| Description | The register provides a general purpose debug mode-only scratch register.
|=======================
<<<
// ####################################################################################################################
:sectnums:
=== Trigger Module
"Normal" _software_ breakpoints (using GDB's `b`/`break` command) are implemented by temporarily replacing the according
instruction word by an `[c.]ebreak` instruction. However, this is not possible when debugging code that is executed from
read-only memory (for example when debugging programs that are executed via the <<_execute_in_place_module_xip>>).
To circumvent this limitation a hardware trigger logic allows to (re-)enter debug-mode when instruction execution
reaches a programmable address. These "hardware-assisted breakpoints" are used by GDB's `hb`/`hbreak` commands.
The RISC-V `Sdtrig` ISA extension adds a programmable _trigger module_ to the CPU core that is enabled via the
<<_sdtrig_isa_extension>> generic. The trigger module implements a subset of the features described in the
"RISC-V Debug Specification / Trigger Module" and complies to version v1.0 of the `Sdtrig` spec.
The NEORV32 trigger module features only a _single_ trigger implementing a "type 6 - instruction address match" trigger.
This limitation is granted by the RISC-V debug spec and is sufficient to **debug code executed from read-only memory (ROM)**.
The trigger module can also be used independently of the CPU debug-mode / `Sdext` ISA extension.
Machine-mode software can use the trigger module to raise a breakpoint exception when instruction execution
reaches a programmed address.
.Trigger Timing
[NOTE]
When enabled the address match trigger will fire **BEFORE** the instruction at the programmed address gets executed.
.MEPC & DPC CSRs
[WARNING]
The breakpoint exception when raised by the trigger module behaves different then the "normal" trapping (see
<<_neorv32_trap_listing>>): <<_mepc>> / <<_dpc>> is set to the address of the next instruction that needs to be
executed to preserve the program flow. A "normal" breakpoint exception would set <<_mepc>> / <<_dpc>> to the address
of the actual `ebreak` instruction itself.
:sectnums:
==== Trigger Module CSRs
The `Sdtrig` ISA extension adds 4 additional CSRs that are accessible from debug-mode and also from machine-mode.
Machine-mode write accesses can be ignored by setting ´dmode´ in <<_tdata1>>. This is automatically done by the debugger
if it uses the trigger module for implementing a "hardware breakpoint"
:sectnums!:
===== **`tselect`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Trigger select register
| Address | `0x7a0`
| Reset value | `0x00000000`
| ISA | `Zicsr` & `Sdtrig`
| Description | This CSR is hardwired to zero indicating there is only one trigger available. Any write access is ignored.
|=======================
:sectnums!:
===== **`tdata1`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Trigger data register 1, visible as trigger "type 6 match control" (`mcontrol6`)
| Address | `0x7a1`
| Reset value | `0x60000048`
| ISA | `Zicsr` & `Sdtrig`
| Description | This CSR is used to configure the address match trigger using the "type 6" format.
|=======================
.Match Control CSR (`tdata1`) Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 31:28 | `type` | r/- | `0100` - address match trigger type 6
| 27 | `dmode` | r/w | set to ignore write accesses to <<_tdata1>> and <<_tdata2>> from machine-mode; writable from debug-mode only
| 26 | `uncertain` | r/- | `0` - trigger satisfies the configured conditions
| 25 | `hit1` | r/- | `0` - hardwired to zero, only `hit0` is used
| 24 | `vs` | r/- | `0` - VS-mode not supported
| 23 | `vu` | r/- | `0` - VU-mode not supported
| 22 | `hit0` | r/c | set when trigger has fired (**BEFORE** executing the triggering address); must be explicitly cleared by writing zero
| 21 | `select` | r/- | `0` - only address matching is supported
| 20:19 | reserved | r/- | `00` - hardwired to zero
| 18:16 | `size` | r/- | `000` - match accesses of any size
| 15:12 | `action` | r/w | `0000` = breakpoint exception on trigger match, `0001` = enter debug-mode on trigger match
| 11 | `chain` | r/- | `0` - chaining is not supported as there is only one trigger
| 10:6 | `match` | r/- | `0000` - equal-match only
| 6 | `m` | r/- | `1` - trigger enabled when in machine-mode
| 5 | `uncertainen` | r/- | `0` - feature not supported, hardwired to zero
| 4 | `s` | r/- | `0` - supervisor-mode not supported
| 3 | `u` | r/- | `0`/`1` - trigger enabled when in user-mode, set if `U` ISA extension is enabled
| 2 | `execute` | r/w | set to enable trigger matching on instruction address
| 1 | `store` | r/- | `0` - store address/data matching not supported
| 0 | `load` | r/- | `0` - load address/data matching not supported
|=======================
:sectnums!:
===== **`tdata2`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Trigger data register 2
| Address | `0x7a2`
| Reset value | `0x00000000`
| ISA | `Zicsr` & `Sdtrig`
| Description | Since only the "address match trigger" type is supported, this r/w CSR is used to configure the address of the triggering instruction.
Note that the trigger module will fire **before** the instruction at the programmed address gets executed.
|=======================
:sectnums!:
===== **`tinfo`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Trigger information register
| Address | `0x7a4`
| Reset value | `0x01000006`
| ISA | `Zicsr` & `Sdtrig`
| Description | The CSR shows global trigger information (see below). Any write access is ignored.
|=======================
.Trigger Info CSR (`tinfo`) Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 31:24 | `version` | r/- | `0x01` - compatible to spec. version v1.0
| 23:15 | reserved | r/- | `0x00` - hardwired to zero
| 15:0 | `info` | r/- | `0x0006` - only type 6 trigger is supported
|=======================