CDL Modules
Data Structures | Namespaces | Enumerations | Variables
riscv_i32_debug.cdl File Reference

RISC-V debug module with APB interface. More...

Detailed Description

RISC-V debug module with APB interface.

CDL implementation of a RISC-V debug module supporting up to 64 HARTs using PDMs

Implementation of Minimalist Debug Module

Supported features (section 1.4)

The specification indicates that "The debug interface described in this specification supports the following features", with a bullet list of features. It does not state that the list of features are required, but perhaps it should, since that would appear to be the purpose of a standardized debug interface.

So, assuming that the supported features are required, the implementation must provide:

RV32 support (not RV64 or RV128 if the harts have to be RV32...)

Debug of any individual hart attached to the debugger

Debug of harts from the first instruction out of reset

Halt of hart on software breakpoint execution

Single-step of hart

EITHER 'a mechanism to execute arbitrary instructions' or 'a system bus master'

Abstract commands (section 3.5.1) can be either 'Access Register Command' or 'Quick Access'. The first must be supported; this has to copy data from a dataX register (if write=0 and transfer=1); copy data in to a dataX register (if write=1 and trasfer=1); execute the program buffer.

It is not quite clear where the data is copied to and from.

Can we add another abstract command that is 'force debug trap to PC x'? This would set dpc to be the PC being fetched, and would replace the fetch PC with x and the fetch privilege mode to be debug.

Overview of design choices

Only RV32 and RV32C are supported.

Debug of a single hart at any one time is supported.

Debug of many harts at any one time is not required.

Insertion of a single RV32 or RV32C instruction to the pipeline is supported, with result of an instruction execution being available (to support reading GPRs, CSRs and memory; to test execution of individual instructions). (What PC is used for this instruction? We will run with 0xffffff00, and if this is a JALR then execution continues until an ebreak OR it returns to 0xffffffNN)

A system bus master is not supported.

Detailed design choices

No support for multiple hart select +++++++++++++++++++++++++++++++++++

There is no need for a hart array matrix (sections 3.3.1 and 3.3.2), just a single selector. This requires hawindowsel (3.11.4) and hawindow (3.11.5) to be read-only of 0.

The other impact is that the 'all' and 'any' variants in dmstatus will return the same value.

Reset +++++

ndmreset has to be provided from the DM to the PDM, which presents it as an output. In many instances this will do nothing except reset the pipeline, since it does not make sense to have the DM reset a swathe of other system that has to keep running. But it may be used in other instances, in which case the signal must not be fed back to reset any part of the DM or PDM.

System bus access +++++++++++++++++

System bus access is not supported.

Hence sbcs (section 3.11.19) and the sbaddress registers (sections 3.11.18/20/21/22) and sbdata (section 3.11.23/24/25/26) are zero.

Authentication ++++++++++++++

Authentication is not supported (other means in the system must prevent access to the debugger; for many of the known target systems there is no necessity of authentication, and for others there have to be more secure mechanisms than the DM can provide).

Hence authdata (section 3.11.13) is not supported.

Other registers +++++++++++++++

Device tree (section 3.11.9) is 0.

Next debug module (section 3.11.10) is 0.

Haltsum +++++++

The halt summary registers provide indications as to which sets of harts are halted.

The harts are known to be halted when they report their state.

haltsum1 (section 3.11.15) may be required, if the DM is configured for >32 harts. It reflects with 'halted[32;0]' or 'halted[32;32]', if 64 harts are configured.

haltsum2 (section 3.11.16) is not required for systems with fewer than 1k harts, hence this is not supported.

haltsum3 (section 3.11.17) is not required for systems with fewer than 32k harts, hence this is not supported.

Debug instruction execution +++++++++++++++++++++++++++

The abstract command 'Access Register Command' requires copying data to a CSR or GPR from data0 (note this is only data0, since that is all that is supported); it also requires copying of data back from a CSR or GPR to data0.

There must therefore be a method of executing a CSR read or write without changing the PC*; there must be a way to read or write a GPR without changing the PC*.

Hence the DM must be able to pass instructions to a halted hart that execute in debug mode (with what privilege?), and which provide an acknowledgement when the hart has completed the instruction, returning the data passed to rfw.

An instruction to read a GPR would be (for example) 'ori rN, rN, 0'.

Two instructions are required to write a GPR: 'lui rN, 0x12345' and 'ori rN, rN, 0x678'.

Reading the PC can be performed with 'auipc r0, 0'

Reading a CSR can be performed with 'csrrsi r0, 0, CSR' Note that reading the PC can be performed (how?) through reading dpc

Writing a CSR is a pain - it requires a GPR (rN) to be preserved, then 'CSRRW x0, csr, rN' to be performed, then rN to be recovered. Potentially dscratch0 can be used for this; this forces dscratch0 to be supported sigh. This might also not fit the requirement of the debug specification? Note that reading the PC can be performed (how?) through writing dpc?

Given the issues here, it seems sensible for the PDM to have a concept of a debug instruction, which has a different (but simple) decoder to rv32i/c.

A method must also be supported to provide for an instruction to be executed

Abstract commands (section 3.5.1) can be either or 'Quick Access'. The first must be supported; this has to copy data from a dataX register (if write=0 and transfer=1); copy data in to a dataX register (if write=1 and trasfer=1); execute the program buffer.

Program buffer (section 3.6) ++++++++++++++++++++++++++++

Although the program buffer is not required, the implementation supports a program buffer size of 1.

From section 3.6 and 3.11.1 the choice of progbufsize of 1 implies impebreak is 1.

From section 3.11.12, progbuf0 must be supported as read/write; reading or writing this register during an abstrct command causes cmderr to be set. Writing when busy is set does not effect the register.

The instruction in progbuf0 can be executed by a using the 'Access Register Abstract Command'. The instruction is fed to a pipeline, and executed with a program counter of 0xffffff00. If the instruction generates an exception then the exception is NOT taken but execution halts and cmderr is set to 3. If the instruction changes the PC then execution must continue at that PC, in debug mode, until (if ever) a software breakpoint is hit or it returns to 0xffffffNN.

Debug mode can be exited using a dret in progbuf0.

Data access +++++++++++

A single data register, data0, is supported. Hence datacount will be 1.

From section 3.11.11, data0 must be supported as read/write; reading or writing this register during an abstrct command causes cmderr to be set. Writing when busy is set does not effect the register.

The harts may support dscratch registers, but the architecture does not permit the data0 register to map to a dscratch register; hence hartinfo (section 3.11.3) must be 0 except for the number of dscratch registers; this must be the same (in this implementation) for all the harts, and has to be a configuration constant.

Abstract commands +++++++++++++++++

Writing to command (section 3.11.7) can cause an abstract command to be performed.

If the abstract command is busy then it sets cmderr and ignores the write

If cmderr is already set then it ignores the write (without changing cmderr)

This is actually a writable register that, as a side effect of write, causes it to be executed.

The abstractauto register (section 3.11.8) has a read/write of bit 0 of autoexecprobuf and read/write of bit 0 of autoexecdata, so that read/writes of data0 or may foce abstract command execution Or hardwire to 0?

Debug CSRs ++++++++++

dpc is set to the current pc on an ebreak; it is set to the current pc if an instruction is flagged as a 'debug ebreak'; if a halt request is received by the PDM then instruction input feed is flagged as a 'debug ebreak'.

The PDM can present a 'debug resume' in which case the pipeline will set itself to fetch 'dpc'. (This is 'execute a dret'?)

dcsr has 'prv' set to the privilege level any time dpc is set. It may be written.

Halting execution +++++++++++++++++

The DM may request that a PDM halt.

This makes the PDM flag an instruction fetch as 'debug ebreak', which forces the pipeline to enter debug mode, set dcsr.prv, set dpc, and (as it is requesting instructions in debug mode) the PDM kills instruction fetching. The PDM can monitor the instruction retirement to know that the halt has been achieved.

Resuming execution +++++++++++++++++

The DM may request that a PDM resume, if the pipeline is in debug mode. This is handled by the DM issuing an 'execute dret' control.

The DM

Single step +++++++++++

Single step is performed by the PDM by allowing a single instruction fetch to go through, and for a following instruction fetch to be passed to the pipeline with 'debug ebreak' - which forces it to be interpreted as an ebreak. Is this 'execute dret' then 'ebreak after next ifetch'?

Step 'n' ++++++++

The PDM could have a 'count down by N' rather than single step counter.

Reading CSRs

Reading memory

Trace, software breakpoint +++++++++++++++++++++++++++

A hart needs to be able to indicate that it has 'status'; such as a software breakpoint has occurred or a hardware trace point has forced a halt.

Extensions

In theory a larger progbufsize can be supported.

Then one MIGHT be able to just request progbuf run on a hart

In this case the DM would have to:

Request quick-access halt of the hart (dpc <= pc of instruction that was stopped from execution)

Request execution of each instruction individually from progbuf

Perhaps a better approach would be to allow a 'force debug trap', which would enter debug mode, set dpc to the next PC, and set PC to a known value. )

Data Structures

struct  riscv_i32_debug::t_apb_state
 
struct  riscv_i32_debug::t_debug_state
 
struct  riscv_i32_debug::t_dmstatus
 
struct  riscv_i32_debug::t_debug_combs
 

Namespaces

 riscv_i32_debug
 

Enumerations

enum  riscv_i32_debug::t_write_action {
  riscv_i32_debug::write_action_none,
  riscv_i32_debug::write_action_data0,
  riscv_i32_debug::write_action_progbuf0,
  riscv_i32_debug::write_action_control,
  riscv_i32_debug::write_action_abstract_cs,
  riscv_i32_debug::write_action_abstract_cmd
}
 
enum  riscv_i32_debug::t_read_select {
  riscv_i32_debug::read_select_zero,
  riscv_i32_debug::read_select_dmstatus,
  riscv_i32_debug::read_select_dmcontrol,
  riscv_i32_debug::read_select_abstractcs,
  riscv_i32_debug::read_select_data0,
  riscv_i32_debug::read_select_progbuf0,
  riscv_i32_debug::read_select_haltsum0,
  riscv_i32_debug::read_select_haltsum1
}
 
enum  riscv_i32_debug::t_apb_address {
  riscv_i32_debug::dm_addr_dmcontrol = 0x10,
  riscv_i32_debug::dm_addr_dmstatus = 0x11,
  riscv_i32_debug::dm_addr_hart_info = 0x12,
  riscv_i32_debug::dm_addr_hart_window_sel = 0x14,
  riscv_i32_debug::dm_addr_hart_window = 0x15,
  riscv_i32_debug::dm_addr_abstract_cs = 0x16,
  riscv_i32_debug::dm_addr_abstract_cmd = 0x17,
  riscv_i32_debug::dm_addr_abstract_cmd_autoexec = 0x18,
  riscv_i32_debug::dm_addr_dev_tree_addr0 = 0x19,
  riscv_i32_debug::dm_addr_next_dm = 0x1a,
  riscv_i32_debug::dm_addr_data0 = 0x04,
  riscv_i32_debug::dm_addr_progbuf0 = 0x20,
  riscv_i32_debug::dm_addr_authdata = 0x30,
  riscv_i32_debug::dm_addr_haltsum0 = 0x40,
  riscv_i32_debug::dm_addr_haltsum1 = 0x13
}
 

Variables

constant integer riscv_i32_debug::max_hart_minus_one =63
 
constant bit[64] riscv_i32_debug::valid_hart_mask = 64hffffffffffffffff