[Open SoC Debug] CDM-OR1K implementation

Philipp Wagner philipp.wagner at tum.de
Thu May 24 18:37:21 CEST 2018


Hi,

this thread got rather long and the introduction of OpenOCD into the 
picture confuses me. So I'm taking the chance to summarize the my 
current understanding.

To make it easier to follow, I created a modified figure here: 
http://pasteall.org/pic/show.php?id=91948fcf0633eef6e4aa0b0d9c0a58d3 
(it's not pretty, but hopefully illustrative).

What important components do we have in the system?

1) The Core Debug Module (CDM). This module connects to a single CPU and 
has two tasks:
   a) Forward the accesses to SPRs from GDB to the core. The data can be 
forwarded unchanged, but the address needs to be translated.
   b) Inform GDB about a status change to the CPU. The only status 
change we're interested in is a change between stalled and unstalled.

2) The MAM. This (already existing) module can read and write arbitrary 
memory addresses.

3) The OSD-GDB Server Bridge. This software module speaks the gdbserver 
protocol and forwards the GDB requests to the MAM and CDM modules.

Notably absent in this picture are adv_dbg_system and OpenOCD. I don't 
see how these two pieces fit into the picture (expect for taking 
inspiration from their implementation).




Now let's have a closer look at the CDM module.

- Every OSD module has registers which can be accessed from the host. 
I'm calling them "OSD registers" for now.

- OSD Register addresses are 16 bit wide, values can (according to the 
spec) be 16, 32, 64 or 128 bit wide. 
(http://opensocdebug.readthedocs.io/en/latest/02_spec/03_dataformats.html#register-access-type-reg)

- OSD register addresses between 0x0000 and 0x01FF are always used by 
OSD itself and have fixed meaning. All other register addresses can be 
used for any purpose by the debug module, i.e. by the CDM module in this 
case.

- To enable GDB to access any SPR of the core, we want to use this 
functionality to "forward" register accesses from GDB through the OSD 
system to the core.

- Side note: GPRs can be also be accesses through a SPR address, so 
there's no need have dedicated support for GPRs.

- or1k SPR addresses in or1k are 16 bit wide.

- As we don't have 16 bit available for a OSD register address, given 
the fixed registers between 0..0x200, we need to perform a address 
mapping. The address mapping I'm proposing is outlined below.

- Register accesses require polling from the host. To notify the host 
about something interesting OSD provides a concept of "event packets". 
These packets can be sent by the CDM like an interrupt at any time and 
the host can then react on it. We can use this functionality to inform 
the host about a change in processor state: the core is stalled, or not 
stalled any more.


SPR to CDM register address mapping

- Map SPRs to CDM address 0x8000 to 0xFFFF (msb set). This gives 15 bit 
of address space.

- Have a separate register called CORE_REG_UPPER which contains the msb 
of the address.

- The SPR register address is then calculated as
   spr_reg_addr = CORE_REG_UPPER << 15 | cdm_reg_addr - 0x8000

- As long as the msb of a SPR register address (spr_reg_addr[15]) does 
not change the CORE_REG_UPPER register does not need to be modified, 
nicely generating one (1) OSD operation for a one (1) SPR register access.


Finally, let's look at the OSD-GDB Server bridge.

- In OSD-speak, this component is a "host module". It connects to the 
"host controller" over (most of the time) TCP and has functionality to 
read and write OSD registers from any other module, and to receive and 
send OSD event packets, just like a debug module on the chip. An example 
for such a host module can be found here: 
https://github.com/opensocdebug/osd-sw/blob/master/src/libosd/systracelogger.c
In our case, the OSD-GDB server bridge will need to communicate with the 
CDM and the MAM modules.

- The OSD-GDB Server bridge also needs to be able to talk to a GDB 
instance. For this purpose it implements the gdbserver protocol. GDB 
instances can then connect to this gdbserver over TCP and communicate 
with the target.

- Most "intelligence" is within gdb itself. gdb knows which SPRs to read 
and write at what time, it knows how to to translate a "set breakpoint" 
request from the user into a memory write inserting a l.trap, etc.

- Looking at the OpenOCD implementation I'm not fully sure what 
additional functionality, beyond transferring register and memory 
accesses between GDB and the core/memory we need to perform within the 
OSD-GDB Server bridge.


Stafford and Shivam, is this matching your understanding as well? If 
there are significant differences in understanding maybe we should 
schedule a video call to get this sorted out quickly.

Best,

Philipp







On 05/24/2018 10:24 AM, SHIVAM AGGARWAL wrote:
> 
> 
> On Thu, 24 May 2018, 02:41 Stafford Horne, <shorne at gmail.com 
> <mailto:shorne at gmail.com>> wrote:
> 
>     On Wed, May 23, 2018 at 02:06:57PM +0530, SHIVAM AGGARWAL wrote:
>      > Hi all,
>      >
>      > I spend last two days reading about GDB and its architecture.
>      >
>      > As a program that works directly with the instructions of the
>     target CPU,
>      > GDB needs in-depth knowledge about the details of the CPU core.
>     GDB needs
>      > information of all registers (SPRs and GPRs) within the target
>     system CPU;
> 
>     GDB Only really needs information about the GPRs:
>        See:
>     https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/features/or1k-core.xml;h=6fe9765150575464bf11a9752e3c3276ff4ab8fa;hb=HEAD
> 
>     This is the base register map that GCC needs.
> 
>     For openOCD there are more provided which we can see here:
>     http://repo.or.cz/openocd.git/blob/HEAD:/src/target/openrisc/or1k.c#l53
> 
>     The register maps are transferred from the debug server to GDB via
>     an xml file.
> 
>      > this allows it to read or write CPU registers by address. It also
>     needs to
>      > know about all the the sizes of the different kinds of data, the
>     size and
>      > shape of the address space, how the calling convention works, what
>      > instruction will cause a trap exception, and so on.
> 
>     Yes, GDB has that all defined within it.  It doesnt need to be
>     provided by OSD.
> 
> 
> So, we can include this information on the software side.
> 
> 
>      > To sum up, CDM-OR1K must provides access to each register in the
>     CPU core
>      > as a OSD specific register.
>      >
>      > All SPRs in mor1kx are listed in this verilog code:
>      >
>     https://github.com/openrisc/mor1kx/blob/master/rtl/verilog/mor1kx-sprs.v
>      >
>      >
>      >    1.
>      >
>      >    *Register map for SPRs and GPRs in CDM-OR1K*:
>      >
>      > We can easily map 32-bit wide 32 GPRs as two 16-bit OSD specific
>     registers
>      > in the module. We already have access to the debug unit for setting
>      > watchpoints/breakpoints.
> 
>     *We already have access to the debug unit for reading/setting all
>     registers.
> 
>      > In GDB, there are commands like ‘info spr’ used to show the value
>     of a SPR
>      > or group of SPRs and ‘spr’ to set the value of an individual SPR.
>     There are
>      > about 12 groups (permitted upto 32) with some groups (1, 2 and 3)
>     having
>      > 1000+ SPRs.
> 
>     That list is provided by OpenOCD, or the features XML of the target
>     server not
>     by the debug hardware. See my links above.
> 
>     *Option 1*
>      > We can assign address spaces 0x0200-0xffff (about 65023 registers) as
>      > module specific registers in CDM-OR1K. The space is enough to map
>     each or1k
>      > register into OSD address space.
> 
>     Any mapping makes it specific to a processor this is not needed.
> 
>     *Option 2*
>      > *Alternate solution*: Can we have certain dedicated read/write
>     registers in
>      > the module?
>      >
>      > i.e instead of mapping each and every register, each time GDB
>     asks for a
>      > specific SPR read/write, we can store its address in one of OSD
>     register
>      > and data in two other registers. This information can be
>     translated over
>      > system interface signals to the CPU core. But, for commands like
>     ‘info
>      > spr’, we might need to store such information for all the SPRs.
> 
>     This sounds better.  Have you seen how it is done in Adv_debug_if?
> 
>        - OpenOCD driver:
>     http://repo.or.cz/openocd.git/blob/HEAD:/src/target/openrisc/or1k_du_adv.c#l498
>          Burst read/write for registers is always 4 bytes.
> 
>        - Adv Debug IF:
>     https://github.com/freecores/adv_debug_sys/blob/master/Hardware/adv_dbg_if/doc/AdvancedDebugInterface.pdf
>          RTL:
>     https://github.com/freecores/adv_debug_sys/blob/master/Hardware/adv_dbg_if/rtl/verilog/adbg_or1k_module.v
>          (I think I asked you to look at this before)
> 
> 
> Yes. JTAG is used in adv_dbg_sys. But we don't need any specific protocol.
> 
> 
>          Commands
>            0x0 - NOP
>            0x3 - cpu register burst setup write
>            0x7 - cpu register burst setup read
>            0x9 - module internal register write
>            0xd - module internal register select
> 
>          cpu register burst read/write
>            [ 0 |  Opcode (0x3/0x7) | Address  | Count ]
> 
>            Here Address is the CPU Register Address
> 
>          Read/Write are then not send with opcodes, just data
>            Burst write
>            [ match | crc | status | Data | Start ]
> 
>            Burst read
>            [ CRC  |  Data | Status ]
> 
>           Here Data is the register data
> 
>          Status register Select (There is only a single 2-bit internal
>     register)
>            [ 0    |  Opcode (0xd) | Index 0 ]
> 
>            Status Register Read
>            (Send nop command)
> 
>            Status Register Write
>            [ 0 | Opcode (0x9) | Index (0x0) | Data (2-bits) ]
> 
>            Status Register contents 2 bits.
>            [ Reset | Stall ]
> 
>     So in summary, there are 2 parts here
>        - Reading/Writing registers looks exactly like memory.  All
>     registers are all
>          in the same address space. (Actually in OpenRISC the GPRS can
>     also be
>          accessed via SPR space, that is what is done here)
>        - Reading/Writing the status bit to Stall or Reset the processor
>     or just get
>          the stall status.
> 
>     Note adv_debug_if support burst mode to get around having to send an
>     address and
>     data command for each read/write.  We could think about a burst
>     interface, but
>     currently the software side (OpenOCD) does not use that.
> 
>     Also Note, the interface has nothing OpenRISC specific.  It doesnt
>     really even
>     mention OpenRISC in the spec. It says:
> 
>        "
>        The CPU interface is designed to connect to an OR1200 processor,
>     or any
>        other CPU which uses the same debug ports.
> 
>        "
> 
> 
> As per the above information and my understanding, following changes 
> should be made to Core Debug Module:
> 
> 1. No need for the register map. We can include this information on the 
> software side.
> 
> 2. CDM should focus on:
> a. reading/writing of registers. We can use an interface similar to MAM:
> 
> https://opensocdebug.readthedocs.io/en/latest/02_spec/07_modules/mam/datainterface.html
> 
> It supports both single word and burst read/write access.
> 
> b. stall packet
> 
> 3. As Stafford mentioned above, this implementation will ensure that CDM 
> is core independent.

Sounds reasonable.

> 
> 
> 
>     Next Steps
>       - Read OpenOCD code
>       - Read openrisc target code for GDB (optional)
>       - Adapt the ideas in the adv debug interface to the OSD CDM spec.
> 
> 
> I will surely go through these resources to get a better insight.
> 
> 
>     -Stafford
> 
> 
> - Shivam Aggarwal
> 




More information about the OpenSoCDebug mailing list