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

SWD console via RTT #2537

Merged
merged 21 commits into from
Dec 9, 2024
Merged

SWD console via RTT #2537

merged 21 commits into from
Dec 9, 2024

Conversation

fanoush
Copy link
Contributor

@fanoush fanoush commented Aug 9, 2024

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 default allocation of channel 0 with name "Terminal"
  • customizable "SEGGER RTT" header in memory (to possibly coexist with full version), we could change it to "SWDCON RTT" if we divert too much and normal tools won't work so well, also openocd can search for any such header
  • removed some Terminal static declarations + related Terminal methods
  • INIT() macro used in most calls is now empty, needs SEGGER_RTT_Init() to work well but less code duplication
  • no extra _Conf.h header included

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.

@gfwilliams
Copy link
Member

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 reset() or load() currently those all get torn down, so maybe it's better to just stick it at the end of the stack or something?

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 jshGetCharToTransmit so you need to be using the output buffer (which is probably helpful anyway) rather than grabbing the data before it even goes in the buffer as you're doing now.

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 if ((ch=jshGetCharToTransmit())) swdconSendChar(ch); in that code for sending. It'll be tidier.

Perhaps we could say that we check in _idle and when RTT is enabled (do we know when there's an active connection?) we add a function to poll with jstExecuteFn from jstimer.c. Nice thing about that is it should work on STM32/others as well. We could potentially have some kind of sleep system where it polls at 100ms and then when there's activity it goes to 1ms for a second or something.

Power consumption isn't such a big deal as IIRC SWD itself draws a certain amount of power anyway?

@fanoush
Copy link
Contributor Author

fanoush commented Aug 16, 2024

sorry fo late answer, I'm on vacation, will be back on Sunday.
Buffers are not allocated dynamically now, but can be as segger's code was changed to not to have them built in, currently I have them as static arrays and initialize segger code with that. Could point it to stack bottom but that could be dangerous when stack becomes full.

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.

@fanoush
Copy link
Contributor Author

fanoush commented Aug 18, 2024

So probably just having a bit of code that does the polling and then use if ((ch=jshGetCharToTransmit())) swdconSendChar(ch); in that code for sending. It'll be tidier.

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

jshBusyIdle();
similar to USB polling. Can't put it into 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 while(true); loop and it does not work. So basically the idle loop is bad for polling/pushing RTT buffers in both directions. Is there some inner interpreter loop where I could put this that runs also in busy loops? Or some regular interrupt (systick?) Or maybe something in jstimer.c - the thing that does soft PWM?

Perhaps we could say that we check in _idle and when RTT is enabled (do we know when there's an active connection?) we add a function to poll with jstExecuteFn from jstimer.c. Nice thing about that is it should work on STM32/others as well.

This suggestion produces something like code scheduled via setInterval() ? That would probably not run when inside while/for loops too?

@fanoush
Copy link
Contributor Author

fanoush commented Aug 19, 2024

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
even the ctrl+c works nicely with that and breaks busy loops.

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 swdconBusyIdle code there with some loop counter to cleanup and switch away if buffers are full and RTT host is not reading - however I avoided sending/receiving anything when the utility timer is running just to be safe.

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?
Can I change the interval of existing timer? Currently I remove it and reschedule with different interval when switching between fast and slow mode.

@fanoush
Copy link
Contributor Author

fanoush commented Aug 20, 2024

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

@gfwilliams
Copy link
Member

Sorry for the delay,

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?

Yes, it's interrupt based so it could interrupt pretty much anything below it.

How heavy the utility timer is - can I keep it running at 50ms always

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.

Can I change the interval of existing timer?

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 while(1);? The util timer branch looks really good and I think that's the way to go, there's just a loopCount++; which I think is some leftover debug code?

@fanoush
Copy link
Contributor Author

fanoush commented Aug 27, 2024

yes, I'd just keep it at 50ms and save having the extra complexity.

OK I will remove the slower idle polling interval and commit to this PR branch

Am I right in thinking that without the util timer you can't ctrl-c out of while(1);?

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)

there's just a loopCount++; which I think is some leftover debug code?

well, not exactly, here
fanoush/Espruino@master...fanoush:Espruino:f-swdcon#diff-6610218919cdccb69d587e426cfacb1c6efd33af130e2468ddbb52f2c2689ffeR247
the loop count is passed into swdconBusyIdle here
fanoush/Espruino@master...fanoush:Espruino:f-swdcon#diff-37989d298ac6321b955d2c767d9cb21149bc44f1da271ff927d2ddce3f292cc0R230
and when it is too long busy waiting it clears the buffer, sets overflow and tries to switch away active console.
What I tried is to prevent freeze when output buffer is full and nobody is reading, it is similar to USB issue we had when you have USB plugged in to computer but nobody opened COM port and is reading the data (actually now I am not sure how/or if this case was really fixed?)

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 Serial device via read/write - then the idle loop handles the data flow (and ctr+c handling is not needed) and then the swdconBusyIdle call is needed in busy loop of jshTransmit in similar way the jshBusyIdle is polling the USB
https://github.com/fanoush/Espruino/blob/0f471f5b3d3d6026ceae7f68e417c3ec54b2de1e/targets/nrf5x/jshardware.c#L1053

@fanoush
Copy link
Contributor Author

fanoush commented Aug 28, 2024

Ok so I have removed the slower idle polling code and cleaned it up a bit.
Currently it is enabled for BANGLEJS2 for build check and so that you can possibly try it. Before merge it should be disabled again.

Maybe I could include separate readme.md with some openocd commands to use it?

so maybe it's better to just stick it at the end of the stack or something?

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 alloca to get the buffers - then it would be taken from stack - but still - either the normal stack is too large so could be made smaller or the size is fine and then making it smaller could cause running out of it. OTOH at least the stack checking code could catch it so maybe no harm done

@gfwilliams
Copy link
Member

Ahh, ok - sorry, I didn't spot that!

similar to USB issue we had ... I am not sure how/or if this case was really fixed?

I'm pretty sure this is fixed now - and in a similar way: d217500?diff=split&w=1

regarding the buffer allocation

Good point! I guess the only issue is it wouldn't be able to be enabled after reset() - only after a fresh reboot?

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.

@fanoush
Copy link
Contributor Author

fanoush commented Aug 29, 2024

Good point! I guess the only issue is it wouldn't be able to be enabled after reset() - only after a fresh reboot?

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 jsiLoop again would work? And current code in idle loop in jswrap_swdcon.c would check if pointer to buffer is set and handle the rest. reset() would not affect that I guess and keep the buffer allocated. And if initial content including the banner should be there then maybe the EV_LIMBO trick would work for SWDCON too? Disadvantage is that the main loop code is separate for every platform so at least nrf5x and STM32 would need to have this code now, could be some macro in jswrap_swdcon.h.

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.

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?

@fanoush
Copy link
Contributor Author

fanoush commented Aug 29, 2024

So if espruino commandline tool can connect to telnet port it could even work too already?

oh, it works, I am in :-) So using telnet command is not needed for gettting the console :-)

espruino --ide 8080 --port tcp://127.0.0.1:2222
Espruino Command-line Tool 0.1.47
-----------------------------------

Connecting to 'tcp://127.0.0.1:2222'
Connected
Web IDE is now available on http://localhost:8080

>
>process.env
={
  VERSION: "2v24.99",
  GIT_COMMIT: "be6669ce6",
  BOARD: "E104BT5032A",
  RAM: 65536, FLASH: 524288, STORAGE: 81920,
  SERIAL: "cd728763-9f160e6b",
  CONSOLE: "SWDCON",
  MODULES: "Flash,Storage,heatshrink",
  EXPTR: 386484 }
>Web IDE Connection accepted.

>< << {"VERSION":"2v24.99","GIT_COMMIT":"be6669ce6","BOARD":"E104BT5032A","RAM":65536,"FLASH":524288,"STORAGE":81920,"SERIAL":"cd728763-9f160e6b","CONSOLE":"SWDCON","MODULES":"Flash,Storage,heatshrink","EXPTR":386484} >> >
>
>

@fanoush
Copy link
Contributor Author

fanoush commented Aug 29, 2024

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.

> flash read_bank 0 flash.bin
nRF52832-QFAA(build code: E0) 512kB Flash, 64kB RAM
wrote 524288 bytes to file flash.bin from flash bank 0 at offset 0x00000000 in 2.019202s (253.566 KiB/s)
>

@fanoush
Copy link
Contributor Author

fanoush commented Aug 30, 2024

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.
Changing polling interval for timer on nrf52 side from 50 to 30ms cuts download from 17 to 12 seconds but has no effect on upload, still 6 minutes. Then I had idea that the swdconRecv method is not ideal

  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 espruino --ide 8080 --port tcp://127.0.0.1:2222 and then using WebIDE I still see the console duplicated on espruino command terminal and when viewing file from storage all the base64 code is going there too, is there option to just run IDE but disconnect the terminal console so only ctrl+c would work but no I/O is duplicated there?

And another question - the jshPushIOCharEvent returns void - is there a way to check if event buffer is (almost) full? I added the limit=128 to the "faster" code above to do at most "128" characters in one timer poll loop because otherwise if the data comes faster it would call jshPushIOCharEvent basically forever, and especially from timer interrupt it would not be a good idea(?). So is there a way to check how many characters/events should be pushed? I guess not for now as there is jshHasEventSpaceForChars which returns true. Looked into wrong place - stm32 bootloader, so jshHasEventSpaceForChars is there but the other way - something like jshIOCharEventsAvailable is not there.

@fanoush
Copy link
Contributor Author

fanoush commented Aug 30, 2024

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 espruino --port COM3 -b 115200 --storage code.js:code.js -e "print() takes about the same time. And what is good is that espruino --port tcp://127.0.0.1:2222 --storage code.js:code.js -e "print()" takes about the same time over SWDCON too - 2:14, so the SWDCON console is fast enough to not to be a bottleneck for nrf52 storage writing. However for downloading it is slower than 115200 baudrate serial - that file takes about 3-4 seconds to download over serial and ~13 seconds over SWDCON (with 30ms polling timer) even with no IDE - just espruino --no-ble --port tcp://127.0.0.1:2222 --download code.js

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 espruino --port COM3 -b 115200 --ide and indeed the slowdown is there too - 6 minutes too for uploading the file even with serial port.

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.

@fanoush
Copy link
Contributor Author

fanoush commented Sep 1, 2024

so maybe there is some delay after each character implemented on upload

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 jshInitDevices like it is done for USB and Bluetooth but it did not help, on storage uploads I still don't see XON/OFF being used when using -v espruino --no-ble -p tcp://127.0.0.1:2222 -v --storage _code.js:code.js not sure why, I hoped I should see log from this code
https://github.com/espruino/EspruinoTools/blob/master/core/serial.js#L208-L217
but I don't see them with SWDCON while I see them printed with USB connection to 52840 dongle.

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 --sleep needed, console recovers so writing is done just before upload quits.

@gfwilliams
Copy link
Member

is there option to just run IDE but disconnect the terminal console so only ctrl+c would work but no I/O is duplicated there?

I don't think there is an option at the moment, no...

is there a way to check if event buffer is (almost) full?

I thought there was a function like we have for the RX buffer, but one could be added easily?

XON/XOFF/throttling

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

@fanoush
Copy link
Contributor Author

fanoush commented Sep 5, 2024

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).

@gfwilliams
Copy link
Member

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?

@fanoush
Copy link
Contributor Author

fanoush commented Sep 5, 2024

https://armmbed.github.io/dapjs/examples/rtt/web.html

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 SWDCON.print("Hello") I see it in that RTT web example.

@fanoush
Copy link
Contributor Author

fanoush commented Sep 5, 2024

oh, it does! I just need to type anything and then press enter. just pressing enter does not work
image

@gfwilliams
Copy link
Member

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.

@fanoush
Copy link
Contributor Author

fanoush commented Sep 5, 2024

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
Both are relatively easy to flash - you hold button on board, plug into USB and use tool provided by manufacturer.
But let's hope the Pico debugprobe firmware can be somehow made to work with this too as that is much easier to get.

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.

@fanoush
Copy link
Contributor Author

fanoush commented Sep 5, 2024

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
...
But let's hope the Pico debugprobe firmware can be somehow made to work with this too as that is much easier to get.

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

@fanoush
Copy link
Contributor Author

fanoush commented Sep 6, 2024

it also happened to me that I got some errors from the js code and then could not reconnect until I unplugged the device

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

@gfwilliams
Copy link
Member

gfwilliams commented Sep 6, 2024

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)?

@fanoush
Copy link
Contributor Author

fanoush commented Sep 6, 2024

Thanks! Hmm, it's a shame it struggles on some devices, but good that you can get a few extra added with the VID.

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.

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?

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 SWDCON.setConsole().

It'd be great if you could do a PR to dapjs with some of those changes.

I think these are just examples you can/should customize so not strictly needed to have upstream
see ARMmbed/dapjs@gh-pages...fanoush:dapjs:gh-pages - these all just go to examples/rtt (web.html,rtt.js)

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

@fanoush
Copy link
Contributor Author

fanoush commented Sep 19, 2024

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.
https://www.aliexpress.com/item/1005007333583184.html 4 pin PCB USB-A < $1
https://www.aliexpress.com/item/1005004600629134.html - motherboard 9 pin to USB-A
https://www.aliexpress.com/item/1005003403049267.html - cable
I never designed PCB even this small so not sure how easy and costly it is nowadays to have a batch of such PCBs matching the RP2040 Zero manufactured and delivered from China - maybe not much?

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.

fanoush and others added 2 commits September 30, 2024 13:00
Move EV_SWDCON location so it doesn't break the Bangle.js emulators which have hard-coded IDs
@gfwilliams gfwilliams merged commit 6020229 into espruino:master Dec 9, 2024
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants