-
-
Notifications
You must be signed in to change notification settings - Fork 747
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
SWD console via RTT #2537
base: master
Are you sure you want to change the base?
SWD console via RTT #2537
Conversation
uses customized SEGGER RTT
fix typos/comments
Wow, thanks! This looks really good! My work is a bit on/off during the school holidays so I probably won't get around to merging this for a while though. You mention dynamically allocating the buffers in jswrap_swdcon_init? I had a quick look but I couldn't immediately see - where do you allocate them? As JsVars? Normally I'd say that would be the best idea but if anyone does I'd have thought XON/XOFF could be made to work ok. It's mostly a software thing, but as far as I know it works off So we have to poll to get the received data? So probably just having a bit of code that does the polling and then use Perhaps we could say that we check in Power consumption isn't such a big deal as IIRC SWD itself draws a certain amount of power anyway? |
sorry fo late answer, I'm on vacation, will be back on Sunday. As for sending to host I just copied Telnet code which avoids buffer, but will try to remove that bit and let it fall through to buffering code and test the code you suggested. We don't know if anyone is listening unless we add our custom host side code to set something or trigger interrupt or guess by checking for attached debugger (which does not mean debugger can actually do segger rtt). Currently I wait for first character from host to disable sleep by always returning true from idle method. |
So I tried that and it works at start when typing single characters but then it hangs with any output longer than the RTT buffer. This is probably why the telnet made similar hack. It works when I call the sending code also here Line 242 in ff487e0
jshBusyIdle as that is per platform. So if it is tidier then transmitting the character directy I can do it like that
--- a/src/jsdevices.c
+++ b/src/jsdevices.c
@@ -202,13 +202,6 @@ void jshTransmit(
return;
}
#endif
-#ifdef USE_SWDCON
- if (device==EV_SWDCON) {
- extern void swdconSendChar(char c);
- swdconSendChar((char)data);
- return;
- }
-#endif
#ifndef LINUX
#ifdef USB
if (device==EV_USBSERIAL && !jshIsUSBSERIALConnected()) {
@@ -247,6 +240,10 @@ void jshTransmit(
return;
}
jshBusyIdle();
+#ifdef USE_SWDCON
+ extern bool swdconSend();
+ if (device == EV_SWDCON) swdconSend();
+#endif
#ifdef USB
// just in case USB was unplugged while we were waiting!
if (!jshIsUSBSERIALConnected()) jshTransmitClearDevice(EV_USBSERIAL); Also tried ctrl+c out of
This suggestion produces something like code scheduled via |
I checked how the utility timer is used in bangle HRM code and tried to use it and it seems to work, there is commit in my other branch where I tried it fanoush@baf64ef I kept also code in idle loop if the timer is not running. Timer is started (every 50ms) when first character comes and there is slow 500ms mode after 10 seconds of idle and then it is stopped after 10 minutes of idle. It is started or rescheduled to faster 50ms mode when character comes from either direction (the timer keeps idle counter when nothing happened). Some questions - is the utility timer running from interrupt so can it possibly interrupt the idle code or the jshTransmitChar code where espruino buffer is is full? I added BTW, How heavy the utility timer is - can I keep it running at 50ms always or not stop it after being idle to keep the code shorter/cleaner? |
full diff of my changes with utility timer in other branch compared to code in this PR is now here fanoush/Espruino@baf64ef^...fanoush:Espruino:f-swdcon I changed it so that the polling timer is started when console is activated and stopped only when console is not active (previously it stopped after some longer idle timeout), there is still fast polling and slow polling after being idle for some time I am now more or less happy with it, If you think the utility timer is the way to go I'll add it to this PR too |
Sorry for the delay,
Yes, it's interrupt based so it could interrupt pretty much anything below it.
It's pretty lightweight so wouldn't use much CPU, but it does use a hardware timer which keeps the HSE clock active. But I figure if you're using SWD it's probably not a big deal having that ~1mA extra draw while it's active (but that'll happen at 500 or 50ms). ... so yes, I'd just keep it at 50ms and save having the extra complexity.
You can, yes - the best way to do that I think would actually be to do it from the timer callback function itself though, as then right after it is called the timer reschedules itself with the correct time period. This looks great though. Am I right in thinking that without the util timer you can't ctrl-c out of |
OK I will remove the slower idle polling interval and commit to this PR branch
yes, exactly. and this is needed only when the console is set as active one, so when it is not interactive the idle loop is good enough for handling the data flow (see also below)
well, not exactly, here Also currently with latest swdcon version the utility timer is used when the console is active = set as interactive console, then it solves the data flow nicely including ctrl+c. However when swdcon is not active it still should work as any other |
Ok so I have removed the slower idle polling code and cleaned it up a bit. Maybe I could include separate readme.md with some openocd commands to use it?
and BTW regarding the buffer allocation, was thinking about this comment, and what we could maybe do (in next PR?) is to check for some swdcon init request flag here https://github.com/espruino/Espruino/blob/master/targets/nrf5x/main.c#L39 in the while loop and use |
see top of SEGGER_RTT_custom.c for more info
Ahh, ok - sorry, I didn't spot that!
I'm pretty sure this is fixed now - and in a similar way: d217500?diff=split&w=1
Good point! I guess the only issue is it wouldn't be able to be enabled after Thanks for this - I'm still a bit busy but I'll try and try this out properly next week, but yes, some file with the relevant OpenOCD commands to make this work would be really cool. |
Hopefully no? I hoped the main loop https://github.com/espruino/Espruino/blob/master/targets/nrf5x/main.c#L39 is exited every now and then basically as often as the idle loop? so check for some flag there and grabbing piece of stack before running
No problem, there is no hurry. I am now looking into similar swdhost library that would do the other side of the RTT. The readme is already there so check it out if you plan to try with openocd. And if you use J-Link and Segger tools then it would be interesting if you could check the Segger RTT viewer how it handles this. As for openocd I even found and fixed a bug with copy paste so check the readme. Fortunately they have github workflow that builds the Windows binary so it is up there too and works for me pretty well now with pasting larger pieces of code. So if espruino commandline tool can connect to telnet port it could even work too already? My goal is to manage apps in bangle 2 with dead bluetooth. Can the https://github.com/espruino/EspruinoTools host the App Loader so it would work with openocd TCP port on localhost already? |
oh, it works, I am in :-) So using telnet command is not needed for gettting the console :-)
|
Tested the WebIDE a bit and with the patched openocd it looks stable however uploading random 27KB code is very very slow, many minutes. However viewing it back is much faster and the file is fine. Speed may be related to buffer size and possibly the polling interval on both sides. And SWD clock too I guess, by default openocd uses 1MHz clock, nrf52 could go up to 8Mhz. EDIT: increased SWD clock to 8MHz as I have cmsis-dap dongle with high speed 480Mbit USB (~$5 WCH-Link E dongle from aliexpress with https://github.com/prosper00/CH32V305-DAPLink-HS ) and there is basically no difference when viewing the file from storage in WebIDE. I can backup whole flash in openocd in 2 seconds so the USB/SWD is not the bottleneck.
|
It is very puzzling. Uploading 27KB code file takes 6 minutes from WebIDE over "espruino --ide 8080 --port tcp://127.0.0.1:2222" . Downloading/viewing the file takes 17 seconds. while(SEGGER_RTT_Read(0, &c, 1) > 0){
jshPushIOCharEvent(EV_SWDCON, c); it updates RTT pointer by 1 byte so when buffer is full the polling on other side can only push one or zero characters so I changed it to char buff[BUFFER_SIZE_DOWN];
int len,limit=128;
while((len = SEGGER_RTT_Read(0, buff, BUFFER_SIZE_DOWN)) > 0 && limit >= 0){
jshPushIOCharEvents(EV_SWDCON, buff, len);
limit -= len; so it could do bigger chunks at once and update buffer pointer only once per chunk and again no change, still 6 minutes. Then I increased the down buffer from 16 to 128 bytes and it got even slower - about 7 minutes. So for now I don't know. It can be openocd telnet server issue (or maybe the espruino command redirection issue?) so for now I'll wait for our own SWD/RTT host server and see how that changes the upload speed. If you use segger tools and it could make the console available over TCP port too then it would be to interesting to see if it is slow too with same espruino command. BTW when running espruino command as And another question - the |
OK, it looks like some inefficiency of the --ide 8080 and also the nrf52 storage is slow to write too. When uploading from normal https://www.espruino.com/ide/ WebIDE to webserial COM3 port the upload takes about 2:14 minutes, also the So not sure why with the IDE hosted at http://localhost:8080/ the upload takes almost 3 times more than direct espruino command and why normal WebIDE with COM port the upload has no slowdown compared to espruino command with COM3 port. And also why the download/view is about the same time (~13 seconds) in both http://localhost:8080/ WebIDE and command line. Now as a last thing I also tested http://localhost:8080/ WebIDE hosted by It is all on Windows 11 and "Espruino Command-line Tool 0.1.47". EDIT: tried also latest one 0.1.56 and it is same. And BTW just noticed by mistake that espruino command does work even with unpatched openocd which is dropping chars so maybe there is some delay after each character implemented on upload? That would explain such slow speed. |
That was it! created espruino/EspruinoTools#178 With slowWrites turned off in https://github.com/espruino/EspruinoTools/blob/master/core/serial_node_socket.js the upload takes just few seconds. However the uploaded data is broken. Possibly because it quits prematurely because I add "-e true" to avoid reset() being called at the beginning. Then I noticed XON/XOFF needs some effort to be enabled so I tried to enable it for SWDCON by moving it in enum past the EV_SERIAL_DEVICE_STATE_START and enable it in What fixed broken files however (they ended with all FFs) is waiting for upload to finish by adding --sleep to upload and/or fixing it by espruino/EspruinoTools#179 - the storage write delays are just about right for no |
I don't think there is an option at the moment, no...
I thought there was a function like we have for the RX buffer, but one could be added easily?
This is very frustrating - I've recently hit this with serial on STM32 as well. I'll discuss more on espruino/EspruinoTools#178 as there is reasoning behind the throttling - it's not like I added it just to make things slow for everyone |
Just FYI there is https://github.com/rgrr/yapicoprobe which is fork of picoprobe CMSIS-DAP debugger for Raspberry Pico and one new feature is automatic RTT to UART - it seems to work quite well for this - first I connect Bangle 2 to Pico then plug Pico into PC and it finds SWDCON automatically and works. When I upload with espruino command without throttling I see some errors that I don't see with openocd TCP sever, and XON/XOFF does not work with this too, but still it is interesting. WebIDE seems to works with it just fine including uploads. It did not work that well as ordinary cmsis-dap debugger with openocd though, so I had to switch to something else to upload espruino firmware. Maybe there is too many features some possibly conflicting with each other (RTT vs SWD). |
That's really cool, thanks! So I guess if someone stuck a Pi Pico on a PCB with USB-A and and a USB-C/micro USB connector we'd have a pretty much ready-to-go programming solution for the Bangle. The throttling issue would I guess be solved by the upload-in-chunks solution we were talking about in the other issue. I'll attempt to have a look at that at some point. Another option I think is I believe that WebUSB is able to connect to CMSIS-DAP (it used to require devices with a special descriptor but I believe it has now been opened up). There's even a webpage that does it at: https://armmbed.github.io/dapjs/examples/rtt/web.html If that actually works I'd be happy to add that as another connection method to the Web IDE? |
Interesting, for some reason it does not accept any input for me in recent Chrome on Windows. But it connects to my CMSIS-DAP and finds RTT automatically somehow becasue when in another browser tab I connect from WEB IDE over UART and write |
Ok, wow, that's very cool then! If this goes in the IDE it suddenly makes it something very accessible - no OpenOCD install or anything needed, just a <$10 dongle. |
Was testing it a bit and it doesn't work with just any cheap CMSIS-DAP I have around - the WebUSB dialog simply does not list them. Actually it even doesn't show it with https://github.com/raspberrypi/debugprobe flashed to raspberry Pico even if it is otherwise pretty good CMSIS-DAP v2 adapter. I was thinking it needs some extra USB descriptor magic targeted for WebUSB https://wicg.github.io/webusb/#webusb-platform-capability-descriptor but actually the ones I have and work for me do not have it either and still work. I checked Pico and those two working ones with https://www.uwe-sieber.de/usbtreeview_e.html#download and they look pretty identical regarding descriptors except VID/PID and minor variation in CMSIS-DAP name. However those that work are both USB high speed 480Mbit devices while Pico is full speed 12Mbit. The ones that work for me is the CH32V305 based one WCH Link E compatible board I mentioned previously and the second one is Sipeed M0S Dock board with BL616 chip with this firmware https://github.com/cherry-embedded/CherryDAP so both actually run the CherryDAP implementation. Both are ~US$5 boards from aliexpress but need to be flashed with custom firmware. M0S Dock https://www.aliexpress.com/item/1005005142466936.html WCH Link E https://www.aliexpress.com/item/1005005180653105.html or the blue WCH-LinkE Mini https://www.aliexpress.com/item/1005005901472089.html Anyway, I did slight modifications to the example ARMmbed/dapjs@gh-pages...fanoush:dapjs:gh-pages and now it works quite well at https://fanoush.github.io/dapjs/examples/rtt/web.html even with 52840 chip and feels relatively fast. However I am not sure how stable the WebUSB and that DAP/RTT is, it mostly works but it also happened to me that I got some errors from the js code and then could not reconnect until I unplugged the device so we'll see more after some more testing. |
Oh, it was easy after all. There was a filter for USB VID, added one for picoprobe fanoush/dapjs@93cd128 and it works! EDIT: however I don't see a v1 one that work over HID even with its VID added |
another good news is that it is probably specific to the M0S Dock board vs Bangle 2 - the LED on it stays glowing even if I disconnect it from USB, the Bangle somehow backfeeds power to it over SWD pins so maybe it messes up GPIOs too and breaks communication sooner or later (like weak pulldown on SWDIO/CLK or something), with other boards the dapjs was stable so far |
Thanks! Hmm, it's a shame it struggles on some devices, but good that you can get a few extra added with the VID. It'd be great if you could do a PR to dapjs with some of those changes. I know thegecko who I think has done most of the work on this and he's a really nice guy - I think he'd be pretty happy to merge in contributions. So just so I know where we stand on this PR at the moment: So it's working well, but if you have it in the BOARD.py it is built in and enabled by default? Although while it uses a small amount of RAM it's not waking up to poll unless you explicitly move the console over to it or send a keypress? (so basically it should be safe to merge on Bangle.js 2)? |
I am happy the Pico/RP2040 works fine - it is cheap and easy one to get for everyone. There are also lot of even cheaper RP2040 boards on aliexpress, some even smaller than Pico e.g. the Waveshare RP2040 Zero is about $2-$3. And even the full speed USB seems to be fast enough with picobrobe firmware for SWD. The v1 over HID was slow but the v2 is fast enough and even with 8MHz SWD clock the full speed USB seems fast enough to not be a bottleneck.
Yes, I think so. It takes 32+128 bytes of RAM for buffers + something for the RTT block (another 32 maybe?). Should be harmless and doing nothing unless someone writes data to RTT over SWD and yes if you do
I think these are just examples you can/should customize so not strictly needed to have upstream Yes I tried one pull request here ARMmbed/dapjs#116 just to allow empty lines in the example as it was very confusing for me it does nothing on empty lines, we'll see. So if you have RP2040 Pico you can just quickly try the pico UF2 from https://github.com/raspberrypi/debugprobe/releases/tag/debugprobe-v2.0.1 with https://fanoush.github.io/dapjs/examples/rtt/web.html and test with bangle 2 |
Some random ideas I had about this so far dapjs - the API is interesting, not sure you would want to integrate it directly into IDE/EspruinoTools or not (the commandline nodejs can talk also to older HID based CMSIS-DAP) but anyway this same same API could be used for the swdhost part of Espruino device so it is compatible and same RTT js can run in all of them hardware - the easiest is to have something with usb-A socket for bangle 2 cable - people may cut generic USB cable and attach wires to some RP2040 device with picoprobe (or any random CMSIS-DAP V2 debugger dongle), however when using picoprobe FW as is it uses GPIOs 2,3 for SWD, which are on the side so not sure if it is worth designing some simple PCB that would just have USB-A and traces/pinheader matching some existing board. There are some small cheap ones like the Waveshare RP2040 Zero https://www.waveshare.com/rp2040-zero.htm which is on aliexpress for 2-3 USD including VAT and shipping. So just custom USB-A PCB with pinheader matching this board could be quite small. Or it is not worth it as there are USB-A breakout boards or cables of variout shapes - but the result will be a bit more fragile. SWDCON - the Seger RTT header has some space after name and also some bits in buffer flags that can be used for signaling that RTT is connected or that the host device will trigger interrupt after each transfer - then the console could be activated by such flags and the polling timer too could be stopped if host set such flags. it would be easy to do this custom extension from dapjs code and still would be compatible with openocd that does not support it. Also with the rest of dapjs - the Processor API you can access memory and CPU so you can possibly reboot device, update firmware, backup firmware (or even SPI flash) for recovery/troubleshooting. And also mirror display in runtime, see variables usage/fragmentation etc - for better debugging. Or also trigger interrupt with fake events like screen touch/button click so you can test your app from computer like Bangle emulator currently does. |
This uses SEGGER RTT so that it can be used with existing tools like openocd. It is opensource, got it from https://github.com/adfernandes/segger-rtt but now I see there is older version also at https://github.com/SEGGERMicro/RTT
I did some modification as per comments in SEGGER_RTT_custom.c (maybe I can add patch file too, just to see the changes?)
No need to merge right now. Things to possibly discuss:
Name of console - I considered "SWD" "RTT" "SWDRTT" "Debug", it is "SWDCON" for now but maybe it is too long? SWD is shorter but maybe slightly confusing (at least for me but maybe not for others).
Dynamic allocation - RTT code is changed so that the buffers even for default channel 0 can be allocated later (now in
jswrap_swdcon_init
) so maybe locked flat buffer variable allocated on demand could work too, then buffers could be larger. Currently input buffer is 32 bytes and looks like openocd drops extra stuff when the buffer is full which breaks clipboard paste. However with our own RTT host we could wait while writing, so buffer can be even smaller. Not sure how XON/XOFF could be made to work over this - does it need any extra code? As it is done now the host->device is read only in idle loop so that's why the buffer gets full too. And maybe when device is busy executing javascript the console would never get any input so you can't even ctrl+c out of busy code . Can I hook library into busy interpreter loop like the current"type" : "idle",
method?Also there is no interrupt for this in SEGGER RTT but we could trigger some interrupt over SWD. At least for waking the device from sleep when initially switching the console I tested that triggering interrupt for TIMER1 (used for sleep timer in nrf52) wakes the device from sleep and is otherwise harmless. Or maybe we could even allocate SWI interrupt.