Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add semihosting support for nrf52 devices #4137

Merged
merged 9 commits into from
Jun 24, 2024
2 changes: 1 addition & 1 deletion boards/wiscore_rak4631.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"frameworks": ["arduino", "freertos"],
"name": "WisCore RAK4631 Board",
"upload": {
"maximum_ram_size": 248832,
Expand Down
7 changes: 7 additions & 0 deletions pyocd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This is a config file to control pyocd ICE debugger probe options (only used for NRF52 targets with hardware debugging connections)
# for more info see FIXMEURL

# console or telnet
semihost_console_type: telnet
enable_semihosting: True
telnet_port: 4444
8 changes: 8 additions & 0 deletions src/DebugConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@

#include "SerialConsole.h"

// If defined we will include support for ARM ICE "semihosting" for a virtual
// console over the JTAG port (to replace the normal serial port)
// Note: Normally this flag is passed into the gcc commandline by platformio.ini.
// for an example see env:rak4631_dap.
// #ifndef USE_SEMIHOSTING
// #define USE_SEMIHOSTING
// #endif

#define DEBUG_PORT (*console) // Serial debug port

#ifdef USE_SEGGER
Expand Down
32 changes: 31 additions & 1 deletion src/platform/nrf52/main-nrf52.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,43 @@ void nrf52Loop()
checkSDEvents();
}

#ifdef USE_SEMIHOSTING
#include <SemihostingStream.h>

/**
* Note: this variable is in BSS and therfore false by default. But the gdbinit
* file will be installing a temporary breakpoint that changes wantSemihost to true.
*/
bool wantSemihost;

/**
* Turn on semihosting if the ICE debugger wants it.
*/
void nrf52InitSemiHosting()
{
if (wantSemihost) {
static SemihostingStream semiStream;
// We must dynamically alloc because the constructor does semihost operations which
// would crash any load not talking to a debugger
semiStream.open();
semiStream.println("Semihosting starts!");
// Redirect our serial output to instead go via the ICE port
console->setDestination(&semiStream);
}
}
#endif

void nrf52Setup()
{
auto why = NRF_POWER->RESETREAS;
uint32_t why = NRF_POWER->RESETREAS;
// per
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
LOG_DEBUG("Reset reason: 0x%x\n", why);

#ifdef USE_SEMIHOSTING
nrf52InitSemiHosting();
#endif

// Per
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
// This is the recommended setting for Monitor Mode Debugging
Expand Down
91 changes: 79 additions & 12 deletions variants/rak4631/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,100 @@ lib_deps =
debug_tool = jlink



; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds
;upload_protocol = jlink

; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!)
; programming time is about the same as the bootloader version.
; For information on this see the meshtastic developers documentation for "Development on the NRF52"
[env:rak4631_dap]
[env:rak4631_dbg]
extends = env:rak4631
board_level = extra
; pyocd pack --i nrf52840

; if the builtin version of openocd has a buggy version of semihosting, so use the external version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

board_level = extra should stay in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh thanks!  I botched that merge - will fix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geeksville once the board_level = extra is back in, we can safely merge this I think

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thebentern done! thanks!

; platform_packages = platformio/tool-openocd@^3.1200.0

build_flags =
${env:rak4631.build_flags}
-D USE_SEMIHOSTING

lib_deps =
${env:rak4631.lib_deps}
https://github.com/geeksville/Armduino-Semihosting.git#35b538fdf208c3530c1434cd099a08e486672ee4

; NOTE: the pyocd support for semihosting is buggy. So I switched to using the builtin platformio support for the stlink adapter which worked much better.
; However the built in openocd version in platformio has buggy support for TCP to semihosting.
;
; So I'm now trying the external openocd - but the openocd scripts for nrf52.cfg assume you are using a DAP adapter not an STLINK adapter.
; In theory I could change those scripts. But for now I'm trying going back to a DAP adapter but with the external openocd.

upload_protocol = stlink
; eventually use platformio/tool-pyocd@^2.3600.0 instad
upload_protocol = custom
upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE
;upload_protocol = custom
;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE

; We want the initial breakpoint at setup() instead of main(). Also we want to enable semihosting at that point so instead of
; debug_init_break = tbreak setup
; we just turn off the platformio tbreak and do it in .gdbinit (where we have more flexibility for scripting)
; also we use a permanent breakpoint so it gets reused each time we restart the debugging session?
debug_init_break = tbreak setup

; Note: add "monitor arm semihosting_redirect tcp 4444 all" if you want the stdout from the device to go to that port number instead
; (for use by meshtastic command line)
; monitor arm semihosting disable
; monitor debug_level 3
;
; IMPORTANT: fileio must be disabled before using port 5555 - openocd ver 0.12 has a bug where if enabled it never properly parses the special :tt name
; for stdio access.
; monitor arm semihosting_redirect tcp 5555 stdio

; Also note: it is _impossible_ to do non blocking reads on the semihost console port (an oversight when ARM specified the semihost API).
; So we'll neve be able to general purpose bi-directional communication with the device over semihosting.
debug_extra_cmds =
echo Running .gdbinit script
monitor arm semihosting enable
monitor arm semihosting_fileio enable
monitor arm semihosting_redirect disable
commands 1
echo Breakpoint at setup() has semihosting console, connect to it with "telnet localhost 5555"
set wantSemihost = true
end


; Only reprogram the board if the code has changed
debug_load_mode = modified
;debug_load_mode = manual
debug_tool = custom
debug_tool = stlink
;debug_tool = custom
; debug_server =
; openocd
; -f
; /usr/local/share/openocd/scripts/interface/stlink.cfg
; -f
; /usr/local/share/openocd/scripts/target/nrf52.cfg
; $PLATFORMIO_CORE_DIR/packages/tool-openocd/openocd/scripts/interface/cmsis-dap.cfg

; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!)
; programming time is about the same as the bootloader version.
; For information on this see the meshtastic developers documentation for "Development on the NRF52"
; We manually pass in the elf file so that pyocd can reverse engineer FreeRTOS data (running threads, etc...)
debug_server =
pyocd
gdbserver
-t
nrf52840
--elf
${platformio.build_dir}/${this.__env__}/firmware.elf
;debug_server =
; pyocd
; gdbserver
; -j
; ${platformio.workspace_dir}/..
; -t
; nrf52840
; --semihosting
; --elf
; ${platformio.build_dir}/${this.__env__}/firmware.elf

; If you want to debug the semihosting support you can turn on extra logging in pyocd with
; -L
; pyocd.debug.semihost.trace=debug

; The following is not needed because it automatically tries do this
;debug_server_ready_pattern = -.*GDB server started on port \d+.*
;debug_port = localhost:3333
Loading