rust-embedded / svd2rust

Generate Rust register maps (`struct`s) from SVD files
Apache License 2.0
697 stars 150 forks source link

Device registers cannot be displayed in a debugger #452

Open ryan-summers opened 4 years ago

ryan-summers commented 4 years ago

This issue is being moved after originally posted in https://github.com/rust-embedded/wg/issues/459

Because of the implementation of svd2rust (and some issues with trait implementations in the debug information outlined here), register blocks cannot be printed in a GDB interface. Attempts to do so only result in an output similar to the following:

(gdb) print clocks.rb
$1 = stm32h7x::stm32h743v::RCC {_marker: core::marker::PhantomData<*const ()>}

Similarly, if the register block is correctly type-cast in GDB, the output is equally unhelpful:

(gdb) print *(0x5802_4400 as &stm32h7::stm32h743v::rcc::RegisterBlock)                                                                                                                                     $5 = stm32h7::stm32h743v::rcc::RegisterBlock {cr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 251687333}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_CR>}, _reserved_1_icscr: [242, 6, 0,                                                                                                           
    64], crrcr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CRRCR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 272}}, _marker: core::marker::PhantomData<stm3
2h7::stm32h743v::rcc::_CRRCR>}, csicfgr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CSICFGR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 536871018}}, _marke
r: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_CSICFGR>}, cfgr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CFGR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32
> {value: 27}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_CFGR>}, _reserved5: [0, 0, 0, 
    0], d1cfgr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D1CFGR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 72}}, _marker: core::marker::PhantomData<stm3
2h7::stm32h743v::rcc::_D1CFGR>}, d2cfgr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D2CFGR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 1088}}, _marker: cor
e::marker::PhantomData<stm32h7::stm32h743v::rcc::_D2CFGR>}, d3cfgr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D3CFGR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {
value: 64}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_D3CFGR>}, _reserved8: [0, 0, 0, 
    0], pllckselr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLLCKSELR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 33686016}}, _marker: core::marker::Phan
tomData<stm32h7::stm32h743v::rcc::_PLLCKSELR>}, pllcfgr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLLCFGR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 312
60706}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_PLLCFGR>}, pll1divr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLL1DIVR> {register: vcell::VolatileCell<u32> {value: cor
e::cell::UnsafeCell<u32> {value: 16842951}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_PLL1DIVR>}, pll1fracr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLL1FRACR> {registe
r: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_PLL1FRACR>}, pll2divr: stm32h7::generic::Reg<u32, stm32h7::stm32
h743v::rcc::_PLL2DIVR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 16975559}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_PLL2DIVR>}, pll2fracr: stm
32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLL2FRACR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::
rcc::_PLL2FRACR>}, pll3divr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLL3DIVR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 16843392}}, _marker: core::mar
ker::PhantomData<stm32h7::stm32h743v::rcc::_PLL3DIVR>}, pll3fracr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_PLL3FRACR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32>
 {value: 0}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_PLL3FRACR>}, _reserved16: [0, 0, 0, 
    0], d1ccipr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D1CCIPR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: core::marker::PhantomData<stm
32h7::stm32h743v::rcc::_D1CCIPR>}, d2ccip1r: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D2CCIP1R> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: 
core::marker::PhantomData<stm32h7::stm32h743v::rcc::_D2CCIP1R>}, d2ccip2r: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D2CCIP2R> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCe
ll<u32> {value: 0}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_D2CCIP2R>}, d3ccipr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_D3CCIPR> {register: vcell::VolatileCell<u32> 
{value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: core::marker::PhantomData<stm32h7::stm32h743v::rcc::_D3CCIPR>}, _reserved20: [0, 0, 0, 
    0], cier: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CIER> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: core::marker::PhantomData<stm32h7::
stm32h743v::rcc::_CIER>}, cifr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CIFR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker: core::marker::Pha
ntomData<stm32h7::stm32h743v::rcc::_CIFR>}, cicr: stm32h7::generic::Reg<u32, stm32h7::stm32h743v::rcc::_CICR> {register: vcell::VolatileCell<u32> {value: core::cell::UnsafeCell<u32> {value: 0}}, _marker:
 core::marker::PhantomData<stm32h7::stm32h743v::rcc::_CICR>}, _reserved23: [0, 0, 0, 

<snipped>

The simplest way to visualize the registers is to just examine the raw memory, which is not a great alternative:

(gdb) x/10wx 0x5802_4400
0x58024400:     0x0f0071a5      0x400006f2      0x00000110      0x2000006a
0x58024410:     0x0000001b      0x00000000      0x00000048      0x00000440
0x58024420:     0x00000040      0x00000000

Is there a way that we can provide an interface co cleanly print out the bits (or just bytes) of the registers along with their names in GDB?

In https://github.com/rust-embedded/wg/issues/459, https://github.com/bnahill/PyCortexMDebug was pointed out as an option, but we would ideally not require an external tool and the chip SVD (which may or may not have bugs that have been patched by the PAC maintainer) to visualize the registers.

adamgreig commented 4 years ago

The nicest debug user experience I can imagine is a sort of RawRegisterBlock struct containing plain u32 in repr(C), and a static SPI1: *const RawRegisterBlock = 0x4000abcd; global export, maybe even no_mangle, which would allow GDB to very easily p SPI1.

Unfortunately *const aren't Sync so there's no way to export such a thing. Exporting just the type alone would at least permit manually printing it from a pointer address, e.g. p *(0x40003800 as *const u32 as *const pac::spi::RawRegisterBlock), but it's still pretty wordy. More importantly, if the exported type isn't actually used in the final application, I don't think its layout ends up in the ELF, so gdb can't see it.

(As I'm sure you know,) in C there's often a struct declared and then defined globally in the device header file which permits global access to all registers just through the peripheral name, and makes debug printing (and even setting) from within GDB very easy and ergonomic. I think that's basically the ideal end point, but it might just not be compatible with the type safety we want. It might be that our effect really is better spent on making sure the GDB extensions are as slick as possible: already having it easily print out each field values with the field names and descriptions taken from the SVD is a big win.

Unfortunately arm-none-eabi-gdb shipped by Arm doesn't support Python, which means you can't even use the Python extensions without swapping to a different gdb build.

ryan-summers commented 4 years ago

Unfortunately arm-none-eabi-gdb shipped by Arm doesn't support Python, which means you can't even use the Python extensions without swapping to a different gdb build.

I've been using gdb-multiarch from Ubuntu 20.04 and have played around with the python-interactive interface with a Cortex-M7, so there is a python interface we can use to write a pretty-printer.

Unfortunately *const aren't Sync so there's no way to export such a thing. Exporting just the type alone would at least permit manually printing it from a pointer address, e.g. p *(0x40003800 as *const u32 as *const pac::spi::RawRegisterBlock), but it's still pretty wordy. More importantly, if the exported type isn't actually used in the final application, I don't think its layout ends up in the ELF, so gdb can't see it.

As I noted earlier, RegisterBlock structures aren't always included in the final output ELF. However, if they were, we could generate a GDB pretty-printer to automatically format the structures, which I think is the right approach here. I was playing around with this and discovered that we need the following:

  1. Guarantee that the RawRegisterBlock structures make it into the output ELF file (which would be a C-like definition of the register block)
  2. Guarantee that GDB can access the addresses that are encoded in the Deref trait

In regard to (2), I was previously unable to get at these addresses as it looks like the debug info wasn't present in the ELF file (there was no way to call the ptr() method or get the underlying address through GDB from what I could tell)

I definitely think that a pretty-printer for GDB is the right way to go - we just need to try to provide the necessary debug information in the output ELF for the pretty-printer to do it's job.