Skip to content

Commit

Permalink
Merge pull request #2537 from fanoush/f-swdcon-pr
Browse files Browse the repository at this point in the history
SWD console via RTT
  • Loading branch information
gfwilliams authored Dec 9, 2024
2 parents abf0fd9 + b629fab commit 6020229
Show file tree
Hide file tree
Showing 10 changed files with 3,276 additions and 4 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ ifeq ($(USE_TERMINAL),1)
WRAPPERSOURCES += libs/graphics/jswrap_terminal.c
endif

ifeq ($(USE_SWDCON),1)
DEFINES += -DUSE_SWDCON
WRAPPERSOURCES += libs/swdcon/jswrap_swdcon.c
# directly included so not needed SOURCES += libs/swdcon/SEGGER_RTT_custom.c
endif

endif

ifeq ($(USE_USB_HID),1)
Expand Down
1 change: 1 addition & 0 deletions boards/BANGLEJS2.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
'AES_CCM',
'LCD_MEMLCD',
'TENSORFLOW',
'SWDCON', # RTT console over SWD
'JIT' # JIT compiler enabled
],
'makefile' : [
Expand Down
97 changes: 97 additions & 0 deletions libs/swdcon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# SWDCON - console over Segger RTT

This is Espruino console over [Segger RTT](https://wiki.segger.com/RTT) technology. Can be used with [BANGLEJS2](https://www.espruino.com/Bangle.js2#hardware-swd) over charging cable or with any other device that has SWD pins available and no extra UART or USB. Also could work as initial console when porting to new devices before other drivers are working.

The console can be used via Segger RTT Viewer (if you use J-Link debugger probe) or it can be also used with OpenOCD with any debugger probe like e.g. cheap CMSIS-DAP dongle.

Another way instead of running OpenOCD is dapjs over WebUSB supported in Chrome browser.

In future the console can be also used with any Espruino device with 2 free GPIOs acting as serial to SWD/RTT bridge.

## dapjs over WebUSB

There is a project that allows using CMSIS-DAP debug probe directly from web browser https://armmbed.github.io/dapjs/docs/index.html

You can try customized example here https://fanoush.github.io/dapjs/examples/rtt/web.html

Best is to enable character mode, then connect to your probe via 'start RTT', then press enter and then click the 'trigger IRQ' button and you should get console output - see also Known issues below.

Unfortunately only newer CMSIS-DAP probes that support V2 protocol work over WebUSB in browser, older v1 probes that work over HID protocol do not work. If your probe is not working the easiest is to get Raspberry Pico or any other RP2040 board and flash it with debugprobe firmware https://github.com/raspberrypi/debugprobe which does support V2 protocol

## OpenOCD

OpenOCD gives best compatibility with many debug probes.
Attach openocd to device, for nrf52 and cmsis-dap probe it is something like `openocd -d2 -f interface/cmsis-dap.cfg -f target/nrf52.cfg`. Then connect
to openocd interactive console via `telnet localhost 4444` (Or you can use e.g. Putty for making Telnet connection) and continue with some OpenOCD commands below:

`rtt setup 0x20000000 262144 "SEGGER RTT"` - with 256KB RAM size for nrf52840 like BANGLEJS2

`rtt setup 0x20000000 65535 "SEGGER RTT"` - with 64KB RAM size for nrf52832, newer OpenOCD may not need third "SEGGER RTT" parameter

`rtt start` - should search and find the buffer

`rtt polling_interval 30` - optional, make console I/O faster (try to run e.g. `E.dumpVariables()` with default value)

`rtt server start 9090 0` - start telnet server on port 9090 for channel 0 = SWDCON, use any free port number you wish, 9090 is typical for RTT channel 0

You can also run all commands directly when starting OpenOCD

`openocd -d2 -f interface/cmsis-dap.cfg -f target/nrf52.cfg -c "adapter speed 8000" -c "init" -c "rtt setup 0x20000000 262144" -c "rtt start ; rtt server start 9090 0" -c "rtt polling_interval 30"`

Many good probes including RP2040 debugprobe support higher adapter speeds, for nrf52 chips maximum supported is 8MHz, if you have issues skip `-c "adapter speed 8000"` or go lower to 4 or 2 MHz


## EspruinoTools

When the openocd rtt server is running on TCP port you can use the `espruino` command line tool available from https://github.com/espruino/EspruinoTools like this:

```
espruino --ide 8080 --port tcp://127.0.0.1:9090
```
Then the interactive console is available and you can also use Web IDE from web browser on http://localhost:8080
```
Espruino Command-line Tool 0.1.47
-----------------------------------
Connecting to 'tcp://127.0.0.1:9090'
Connected
Web IDE is now available on http://localhost:8080
>
```

Beware that by default device is in deep sleep and nothing happens after first text entry, you need to wake it up to notice the input and switch to RTT console - see Known issues below.


## telnet client

While espruino command line tool is easier, you may also use telnet client directly via `telnet localhost 9090`

By default telnet is in line mode if there is no rtt server inital string. For espruino console we need raw character mode,
type ctrl+],Enter and then type `mode char` to switch to raw mode
or you can use netcat https://unix.stackexchange.com/questions/767170/using-telnet-command-without-protocol-negotiation

Now press ctrl+c to clear espruino console and/or press enter few times to see some initial errors from telnet garbage

`rtt server start 9090 0 "\377\375\042\377\373\001"` - newer OpenOCD versions can send initial string to telnet client, this switches it to raw mode automatically but adds some extra initial garbage also to Espruino console as telnet client sends some stuff back, more info https://stackoverflow.com/questions/273261/force-telnet-client-into-character-mode


## OpenOCD stop

`rtt stop` - stop polling data, do this also before flashing new version of espruino (server can stay running but rttt stop, setup and start is needed to run after firmware update)

`rtt server stop 9090` - optional

`nrf52.dap dpreg 4 0 ; shutdown` - with cmsis-dap this powers down nrf52 debug hardware (`dap dpreg 4 0`) and disconnects, if you would just close/kill openocd and nrf5x stays in debug mode it drains battery and needs reboot to clear this state

## Known issues

- clipboard paste drops data when buffer is full, this is [openocd issue](https://review.openocd.org/c/openocd/+/8360) - it only writes first part that fits into buffer and does not retry or wait for data to go out, quick fix in branch here https://github.com/fanoush/openocd/tree/f-rtt-server-write-retry with Windows build available here https://github.com/fanoush/openocd/releases

- if device is in deep sleep it needs to be woken up to activate the console - press button, press enter in old console, for nrf52 also triggering TIMER1_IRQn interrupt via STIR register write in openocd works `mww 0xE000EF00 9`

## TODO

- disable/enable and allocate buffers for SWDCON dynamically at runtime

- our own SWD RTT host code instead of openocd/telnet, then any Espruino device could redirect its serial/usb/bluetooth console to SWDCON of another device, which would allow WebIDE to be used easily with target device (so e.g. Bangle.js 2 with dead bluetooth could be used with App Loader over cable)

210 changes: 210 additions & 0 deletions libs/swdcon/SEGGER_RTT.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
--- SEGGER_RTT.h 2024-08-28 15:41:15.273606346 +0200
+++ SEGGER_RTT_custom.h 2024-08-08 17:02:06.286067463 +0200
@@ -53,7 +53,9 @@
#ifndef SEGGER_RTT_H
#define SEGGER_RTT_H

+#ifndef CUSTOM_RTT
#include "SEGGER_RTT_Conf.h"
+#endif

/*********************************************************************
*
@@ -413,6 +415,7 @@

#define SEGGER_RTT_HASDATA_UP(n) (((SEGGER_RTT_BUFFER_UP*)((uintptr_t)&_SEGGER_RTT.aUp[n] + SEGGER_RTT_UNCACHED_OFF))->WrOff - ((SEGGER_RTT_BUFFER_UP*)((uintptr_t)&_SEGGER_RTT.aUp[n] + SEGGER_RTT_UNCACHED_OFF))->RdOff) // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly

+#ifndef CUSTOM_RTT
/*********************************************************************
*
* RTT "Terminal" API functions
@@ -430,7 +433,7 @@
*/
int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...);
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
-
+#endif // CUSTOM_RTT
#ifdef __cplusplus
}
#endif
--- SEGGER_RTT.c 2024-08-28 15:41:12.163607554 +0200
+++ SEGGER_RTT_custom.c 2024-08-08 17:02:06.286067463 +0200
@@ -1,3 +1,18 @@
+/*
+ customized and cut down (=#ifdef-ed out) minimal version of SEGGER RTT
+ Version V7.94a (2023-12-06)
+ https://github.com/adfernandes/segger-rtt/tree/0022265202e4f6e7a44cf0e15e447d75b573c371
+
+ - no default allocation of channel 0 with name "Terminal"
+ - customizable "SEGGER RTT" header in memory (to possibly coexist with full version)
+ - removed some Terminal static deslarations + related Terminal methods
+ - INIT() macro used in most calls is empty, now needs SEGGER_RTT_Init() to work
+ - no extra _Conf.h header included
+*/
+#define CUSTOM_RTT // used for ifdefs with changes
+#ifndef CUSTOM_RTT_HEADER_BACKWARDS
+#define CUSTOM_RTT_HEADER_BACKWARDS "TTR REGGES"
+#endif
/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
@@ -68,9 +83,11 @@

----------------------------------------------------------------------
*/
-
+#ifdef CUSTOM_RTT
+#include "SEGGER_RTT_custom.h"
+#else
#include "SEGGER_RTT.h"
-
+#endif
#include <string.h> // for memcpy

/*********************************************************************
@@ -244,7 +261,10 @@
**********************************************************************
*/

+#ifndef CUSTOM_RTT
static const unsigned char _aTerminalId[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+#endif
+

/*********************************************************************
*
@@ -259,11 +279,22 @@
#if SEGGER_RTT_CPU_CACHE_LINE_SIZE
#if ((defined __GNUC__) || (defined __clang__))
SEGGER_RTT_CB _SEGGER_RTT __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
- static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
- static char _acDownBuffer[SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_DOWN)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
#elif (defined __ICCARM__)
#pragma data_alignment=SEGGER_RTT_CPU_CACHE_LINE_SIZE
SEGGER_RTT_CB _SEGGER_RTT;
+ #else
+ #error "Don't know how to place _SEGGER_RTT, _acUpBuffer, _acDownBuffer cache-line aligned"
+ #endif
+#else
+ SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT));
+#endif
+
+#ifndef CUSTOM_RTT
+#if SEGGER_RTT_CPU_CACHE_LINE_SIZE
+ #if ((defined __GNUC__) || (defined __clang__))
+ static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
+ static char _acDownBuffer[SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_DOWN)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
+ #elif (defined __ICCARM__)
#pragma data_alignment=SEGGER_RTT_CPU_CACHE_LINE_SIZE
static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)];
#pragma data_alignment=SEGGER_RTT_CPU_CACHE_LINE_SIZE
@@ -272,13 +303,12 @@
#error "Don't know how to place _SEGGER_RTT, _acUpBuffer, _acDownBuffer cache-line aligned"
#endif
#else
- SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT));
SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acUpBuffer [BUFFER_SIZE_UP]));
SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acDownBuffer[BUFFER_SIZE_DOWN]));
#endif

static unsigned char _ActiveTerminal;
-
+#endif
/*********************************************************************
*
* Static functions
@@ -297,6 +327,9 @@
* (1) May only be called via INIT() to avoid overriding settings.
* The only exception is SEGGER_RTT_Init(), to make an intentional override possible.
*/
+#ifdef CUSTOM_RTT
+#define INIT()
+#else
#define INIT() \
do { \
volatile SEGGER_RTT_CB* pRTTCBInit; \
@@ -305,10 +338,11 @@
_DoInit(); \
} \
} while (0)
+#endif

static void _DoInit(void) {
volatile SEGGER_RTT_CB* p; // Volatile to make sure that compiler cannot change the order of accesses to the control block
- static const char _aInitStr[] = "\0\0\0\0\0\0TTR REGGES"; // Init complete ID string to make sure that things also work if RTT is linked to a no-init memory area
+ static const char _aInitStr[] = "\0\0\0\0\0\0"CUSTOM_RTT_HEADER_BACKWARDS; // Init complete ID string to make sure that things also work if RTT is linked to a no-init memory area
unsigned i;
//
// Initialize control block
@@ -317,6 +351,7 @@
memset((SEGGER_RTT_CB*)p, 0, sizeof(_SEGGER_RTT)); // Make sure that the RTT CB is always zero initialized.
p->MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS;
p->MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS;
+#ifndef CUSTOM_RTT
//
// Initialize up buffer 0
//
@@ -335,6 +370,7 @@
p->aDown[0].RdOff = 0u;
p->aDown[0].WrOff = 0u;
p->aDown[0].Flags = SEGGER_RTT_MODE_DEFAULT;
+#endif
//
// Finish initialization of the control block.
// Copy Id string backwards to make sure that "SEGGER RTT" is not found in initializer memory (usually flash),
@@ -483,6 +519,7 @@
}
}

+#ifndef CUSTOM_RTT
/*********************************************************************
*
* _PostTerminalSwitch()
@@ -503,6 +540,7 @@
ac[1] = _aTerminalId[TerminalId]; // Caller made already sure that TerminalId does not exceed our terminal limit
_WriteBlocking(pRing, (const char*)ac, 2u);
}
+#endif

/*********************************************************************
*
@@ -1672,7 +1710,11 @@
if (BufferIndex < SEGGER_RTT_MAX_NUM_UP_BUFFERS) {
SEGGER_RTT_LOCK();
pUp = &pRTTCB->aUp[BufferIndex];
+#ifdef CUSTOM_RTT
+ if (1) {
+#else
if (BufferIndex) {
+#endif
pUp->sName = sName;
pUp->pBuffer = (char*)pBuffer;
pUp->SizeOfBuffer = BufferSize;
@@ -1724,7 +1766,11 @@
if (BufferIndex < SEGGER_RTT_MAX_NUM_DOWN_BUFFERS) {
SEGGER_RTT_LOCK();
pDown = &pRTTCB->aDown[BufferIndex];
+#ifdef CUSTOM_RTT
+ if (1) {
+#else
if (BufferIndex) {
+#endif
pDown->sName = sName;
pDown->pBuffer = (char*)pBuffer;
pDown->SizeOfBuffer = BufferSize;
@@ -1896,6 +1942,7 @@
_DoInit();
}

+#ifndef CUSTOM_RTT
/*********************************************************************
*
* SEGGER_RTT_SetTerminal
@@ -2036,6 +2083,7 @@
}
return Status;
}
+#endif // CUSTOM_RTT

/*********************************************************************
*
Loading

0 comments on commit 6020229

Please sign in to comment.