Skip to content

Commit 6020229

Browse files
authored
Merge pull request #2537 from fanoush/f-swdcon-pr
SWD console via RTT
2 parents abf0fd9 + b629fab commit 6020229

10 files changed

+3276
-4
lines changed

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,12 @@ ifeq ($(USE_TERMINAL),1)
455455
WRAPPERSOURCES += libs/graphics/jswrap_terminal.c
456456
endif
457457

458+
ifeq ($(USE_SWDCON),1)
459+
DEFINES += -DUSE_SWDCON
460+
WRAPPERSOURCES += libs/swdcon/jswrap_swdcon.c
461+
# directly included so not needed SOURCES += libs/swdcon/SEGGER_RTT_custom.c
462+
endif
463+
458464
endif
459465

460466
ifeq ($(USE_USB_HID),1)

boards/BANGLEJS2.py

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
'AES_CCM',
4040
'LCD_MEMLCD',
4141
'TENSORFLOW',
42+
'SWDCON', # RTT console over SWD
4243
'JIT' # JIT compiler enabled
4344
],
4445
'makefile' : [

libs/swdcon/README.md

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# SWDCON - console over Segger RTT
2+
3+
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.
4+
5+
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.
6+
7+
Another way instead of running OpenOCD is dapjs over WebUSB supported in Chrome browser.
8+
9+
In future the console can be also used with any Espruino device with 2 free GPIOs acting as serial to SWD/RTT bridge.
10+
11+
## dapjs over WebUSB
12+
13+
There is a project that allows using CMSIS-DAP debug probe directly from web browser https://armmbed.github.io/dapjs/docs/index.html
14+
15+
You can try customized example here https://fanoush.github.io/dapjs/examples/rtt/web.html
16+
17+
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.
18+
19+
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
20+
21+
## OpenOCD
22+
23+
OpenOCD gives best compatibility with many debug probes.
24+
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
25+
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:
26+
27+
`rtt setup 0x20000000 262144 "SEGGER RTT"` - with 256KB RAM size for nrf52840 like BANGLEJS2
28+
29+
`rtt setup 0x20000000 65535 "SEGGER RTT"` - with 64KB RAM size for nrf52832, newer OpenOCD may not need third "SEGGER RTT" parameter
30+
31+
`rtt start` - should search and find the buffer
32+
33+
`rtt polling_interval 30` - optional, make console I/O faster (try to run e.g. `E.dumpVariables()` with default value)
34+
35+
`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
36+
37+
You can also run all commands directly when starting OpenOCD
38+
39+
`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"`
40+
41+
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
42+
43+
44+
## EspruinoTools
45+
46+
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:
47+
48+
```
49+
espruino --ide 8080 --port tcp://127.0.0.1:9090
50+
```
51+
Then the interactive console is available and you can also use Web IDE from web browser on http://localhost:8080
52+
```
53+
Espruino Command-line Tool 0.1.47
54+
-----------------------------------
55+
56+
Connecting to 'tcp://127.0.0.1:9090'
57+
Connected
58+
Web IDE is now available on http://localhost:8080
59+
>
60+
```
61+
62+
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.
63+
64+
65+
## telnet client
66+
67+
While espruino command line tool is easier, you may also use telnet client directly via `telnet localhost 9090`
68+
69+
By default telnet is in line mode if there is no rtt server inital string. For espruino console we need raw character mode,
70+
type ctrl+],Enter and then type `mode char` to switch to raw mode
71+
or you can use netcat https://unix.stackexchange.com/questions/767170/using-telnet-command-without-protocol-negotiation
72+
73+
Now press ctrl+c to clear espruino console and/or press enter few times to see some initial errors from telnet garbage
74+
75+
`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
76+
77+
78+
## OpenOCD stop
79+
80+
`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)
81+
82+
`rtt server stop 9090` - optional
83+
84+
`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
85+
86+
## Known issues
87+
88+
- 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
89+
90+
- 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`
91+
92+
## TODO
93+
94+
- disable/enable and allocate buffers for SWDCON dynamically at runtime
95+
96+
- 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)
97+

libs/swdcon/SEGGER_RTT.diff

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
--- SEGGER_RTT.h 2024-08-28 15:41:15.273606346 +0200
2+
+++ SEGGER_RTT_custom.h 2024-08-08 17:02:06.286067463 +0200
3+
@@ -53,7 +53,9 @@
4+
#ifndef SEGGER_RTT_H
5+
#define SEGGER_RTT_H
6+
7+
+#ifndef CUSTOM_RTT
8+
#include "SEGGER_RTT_Conf.h"
9+
+#endif
10+
11+
/*********************************************************************
12+
*
13+
@@ -413,6 +415,7 @@
14+
15+
#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
16+
17+
+#ifndef CUSTOM_RTT
18+
/*********************************************************************
19+
*
20+
* RTT "Terminal" API functions
21+
@@ -430,7 +433,7 @@
22+
*/
23+
int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...);
24+
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
25+
-
26+
+#endif // CUSTOM_RTT
27+
#ifdef __cplusplus
28+
}
29+
#endif
30+
--- SEGGER_RTT.c 2024-08-28 15:41:12.163607554 +0200
31+
+++ SEGGER_RTT_custom.c 2024-08-08 17:02:06.286067463 +0200
32+
@@ -1,3 +1,18 @@
33+
+/*
34+
+ customized and cut down (=#ifdef-ed out) minimal version of SEGGER RTT
35+
+ Version V7.94a (2023-12-06)
36+
+ https://github.com/adfernandes/segger-rtt/tree/0022265202e4f6e7a44cf0e15e447d75b573c371
37+
+
38+
+ - no default allocation of channel 0 with name "Terminal"
39+
+ - customizable "SEGGER RTT" header in memory (to possibly coexist with full version)
40+
+ - removed some Terminal static deslarations + related Terminal methods
41+
+ - INIT() macro used in most calls is empty, now needs SEGGER_RTT_Init() to work
42+
+ - no extra _Conf.h header included
43+
+*/
44+
+#define CUSTOM_RTT // used for ifdefs with changes
45+
+#ifndef CUSTOM_RTT_HEADER_BACKWARDS
46+
+#define CUSTOM_RTT_HEADER_BACKWARDS "TTR REGGES"
47+
+#endif
48+
/*********************************************************************
49+
* SEGGER Microcontroller GmbH *
50+
* The Embedded Experts *
51+
@@ -68,9 +83,11 @@
52+
53+
----------------------------------------------------------------------
54+
*/
55+
-
56+
+#ifdef CUSTOM_RTT
57+
+#include "SEGGER_RTT_custom.h"
58+
+#else
59+
#include "SEGGER_RTT.h"
60+
-
61+
+#endif
62+
#include <string.h> // for memcpy
63+
64+
/*********************************************************************
65+
@@ -244,7 +261,10 @@
66+
**********************************************************************
67+
*/
68+
69+
+#ifndef CUSTOM_RTT
70+
static const unsigned char _aTerminalId[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
71+
+#endif
72+
+
73+
74+
/*********************************************************************
75+
*
76+
@@ -259,11 +279,22 @@
77+
#if SEGGER_RTT_CPU_CACHE_LINE_SIZE
78+
#if ((defined __GNUC__) || (defined __clang__))
79+
SEGGER_RTT_CB _SEGGER_RTT __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
80+
- static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
81+
- static char _acDownBuffer[SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_DOWN)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
82+
#elif (defined __ICCARM__)
83+
#pragma data_alignment=SEGGER_RTT_CPU_CACHE_LINE_SIZE
84+
SEGGER_RTT_CB _SEGGER_RTT;
85+
+ #else
86+
+ #error "Don't know how to place _SEGGER_RTT, _acUpBuffer, _acDownBuffer cache-line aligned"
87+
+ #endif
88+
+#else
89+
+ SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT));
90+
+#endif
91+
+
92+
+#ifndef CUSTOM_RTT
93+
+#if SEGGER_RTT_CPU_CACHE_LINE_SIZE
94+
+ #if ((defined __GNUC__) || (defined __clang__))
95+
+ static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
96+
+ static char _acDownBuffer[SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_DOWN)] __attribute__ ((aligned (SEGGER_RTT_CPU_CACHE_LINE_SIZE)));
97+
+ #elif (defined __ICCARM__)
98+
#pragma data_alignment=SEGGER_RTT_CPU_CACHE_LINE_SIZE
99+
static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)];
100+
#pragma data_alignment=SEGGER_RTT_CPU_CACHE_LINE_SIZE
101+
@@ -272,13 +303,12 @@
102+
#error "Don't know how to place _SEGGER_RTT, _acUpBuffer, _acDownBuffer cache-line aligned"
103+
#endif
104+
#else
105+
- SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT));
106+
SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acUpBuffer [BUFFER_SIZE_UP]));
107+
SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acDownBuffer[BUFFER_SIZE_DOWN]));
108+
#endif
109+
110+
static unsigned char _ActiveTerminal;
111+
-
112+
+#endif
113+
/*********************************************************************
114+
*
115+
* Static functions
116+
@@ -297,6 +327,9 @@
117+
* (1) May only be called via INIT() to avoid overriding settings.
118+
* The only exception is SEGGER_RTT_Init(), to make an intentional override possible.
119+
*/
120+
+#ifdef CUSTOM_RTT
121+
+#define INIT()
122+
+#else
123+
#define INIT() \
124+
do { \
125+
volatile SEGGER_RTT_CB* pRTTCBInit; \
126+
@@ -305,10 +338,11 @@
127+
_DoInit(); \
128+
} \
129+
} while (0)
130+
+#endif
131+
132+
static void _DoInit(void) {
133+
volatile SEGGER_RTT_CB* p; // Volatile to make sure that compiler cannot change the order of accesses to the control block
134+
- 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
135+
+ 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
136+
unsigned i;
137+
//
138+
// Initialize control block
139+
@@ -317,6 +351,7 @@
140+
memset((SEGGER_RTT_CB*)p, 0, sizeof(_SEGGER_RTT)); // Make sure that the RTT CB is always zero initialized.
141+
p->MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS;
142+
p->MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS;
143+
+#ifndef CUSTOM_RTT
144+
//
145+
// Initialize up buffer 0
146+
//
147+
@@ -335,6 +370,7 @@
148+
p->aDown[0].RdOff = 0u;
149+
p->aDown[0].WrOff = 0u;
150+
p->aDown[0].Flags = SEGGER_RTT_MODE_DEFAULT;
151+
+#endif
152+
//
153+
// Finish initialization of the control block.
154+
// Copy Id string backwards to make sure that "SEGGER RTT" is not found in initializer memory (usually flash),
155+
@@ -483,6 +519,7 @@
156+
}
157+
}
158+
159+
+#ifndef CUSTOM_RTT
160+
/*********************************************************************
161+
*
162+
* _PostTerminalSwitch()
163+
@@ -503,6 +540,7 @@
164+
ac[1] = _aTerminalId[TerminalId]; // Caller made already sure that TerminalId does not exceed our terminal limit
165+
_WriteBlocking(pRing, (const char*)ac, 2u);
166+
}
167+
+#endif
168+
169+
/*********************************************************************
170+
*
171+
@@ -1672,7 +1710,11 @@
172+
if (BufferIndex < SEGGER_RTT_MAX_NUM_UP_BUFFERS) {
173+
SEGGER_RTT_LOCK();
174+
pUp = &pRTTCB->aUp[BufferIndex];
175+
+#ifdef CUSTOM_RTT
176+
+ if (1) {
177+
+#else
178+
if (BufferIndex) {
179+
+#endif
180+
pUp->sName = sName;
181+
pUp->pBuffer = (char*)pBuffer;
182+
pUp->SizeOfBuffer = BufferSize;
183+
@@ -1724,7 +1766,11 @@
184+
if (BufferIndex < SEGGER_RTT_MAX_NUM_DOWN_BUFFERS) {
185+
SEGGER_RTT_LOCK();
186+
pDown = &pRTTCB->aDown[BufferIndex];
187+
+#ifdef CUSTOM_RTT
188+
+ if (1) {
189+
+#else
190+
if (BufferIndex) {
191+
+#endif
192+
pDown->sName = sName;
193+
pDown->pBuffer = (char*)pBuffer;
194+
pDown->SizeOfBuffer = BufferSize;
195+
@@ -1896,6 +1942,7 @@
196+
_DoInit();
197+
}
198+
199+
+#ifndef CUSTOM_RTT
200+
/*********************************************************************
201+
*
202+
* SEGGER_RTT_SetTerminal
203+
@@ -2036,6 +2083,7 @@
204+
}
205+
return Status;
206+
}
207+
+#endif // CUSTOM_RTT
208+
209+
/*********************************************************************
210+
*

0 commit comments

Comments
 (0)