Feature: RTT for debug output (was:3rd COM port)

Yeah, I know … another COM port?

But I have reasons to see if this would be supportable:

Positive reasons

Currently, debug output has to be output to the default COM port … the same one with the complex UI overlays, etc. As a result, adding ANY debug output changes what’s on the terminal.

That’s not great, especially if trying to track down a UI bug.

If there was a third COM port dedicated to debug output, then debug output would have zero impact on the main terminal’s view / layout. Although I have to test this to verify, I also think it would have nearly zero overhead unless there’s a terminal on the other side reading the data (listening).

Known downside

It’s a third COM port; The two existing ports get non-deterministically assigned port numbers, so adding a third exacerbates that problem. The mitigating factor there is that, since the USB device now includes a serial number, it’s easy to manually assign COM port numbers, and they’ll “stick” to the device (even if changing USB ports)… so one-time discovery of which port is which.

To be determined…

  • What sort of performance when there’s nothing reading the port?
  • Can we short-circuit before the costly formatting, if there’s nothing connected to the debug port? (If yes, really low overhead!)

Input requested

  • Are there other drawbacks to doing this?
  • What is the airspeed of an unladen swallow?
  • Is the third COM port for debug output useful / worthwhile?
3 Likes

I prefer speaking of “channel” instead of COM-port, because it would be an extra CDC ACM channel over USB, and not some extra pins you run UART over.

Who is the intended audience for this extra channel?

Is it BP firmware developers or also end users?

If it is primarily for BP firmware developers, then may I suggest an alternative:
Segger RTT: RTT - SEGGER Wiki

RTT runs over a SWD connection, so you’d need to have a debug probe connected to the SWD pins of the BP to use it. But once you have that it is very convenient and I really like using it.

It does NOT need any hardware or tools from Segger (even if they don’t tell you that). Standard OpenOCD supports it out of the box and it works with the default RasPi Debug Probe (or a Pico reflashed with the debug probe firmware). Alternative probes, like Blackmagic or yapicoprobe, also support it.

From the software side you include a small library from Segger into the firmware. It is licensed freely (like BSD-2-clause). In this small lib you declare an amount of channels you want, how large the buffers are you want and their behavior. Then you have a simple SEGGER_RTT_printf you can call to print stuff to the RTT channel. On the PC side you have OpenOCD running and started RTT with the rtt setup command. It creates a TCP port you can connect to. When you then telnet to that port, you get directly connected to the RTT channel on the target = BusPirate.

In the background it works by having a buffer in the memory assigned for RTT. OpenOCD constantly polls this buffer in the background over SWD to transfer the data. It does not interrupt normal execution of the firmware. This is what I always envisioned how semihosting / SWO should have been implemented. They also don’t have the issue of semihosting where the firmware only works when a debugger is connected - RTT can lie dormant in the firmware with no issues and just requires a few 100 bytes extra.

I would prefer using RTT over an extra CDC ACM channel because the two channels we have now already confuse some new users. Adding a third into the mix won’t improve this situation.

2 Likes

I appreciate the feedback and insite (not affiliated, just providing feedback). I understand some pros and cons, but the one I’d like to address in this comment in particular is the mention that adding a com port for developers should be avoided, as having 2 com ports already confuses new users.

I don’t disagree that two com ports can be new and potentially confusing to those unfamiliar with the Bus Pirate or concept. I have had to help a few users out with this. However, explaining two vs explaining 3 com ports will take the same amount of effort, imo.

As long as all is working as it should, the labels will make it clear that one is for dev, while the other two are labeled for Pi and one for (Pi)rate. < Obviously not the actual labels, just examples.

1 Like

I’ve not needed it on my BP, but I’ve impelemented Semihosting atop of RTT this way and it’s indeed awesome. Additional channels are cheap and it’s dead easy to implement tiny little stubs for system calls so you can catch write() (which gives you printf, but also a channel number, such as stderr/stdout or attaching to Arduino (the firmware, not the chip) streams or you can put different warning levels on different channels or …) but you can also easly read and write files in this way. Need to profile something, just make a buffer of histograms, and copy that buffer to the Real Computer so you can just slurp it into sheets.new and make a graph out of them or whatever.

You can choose to block when the queues are full (maybe to wait for OpenOCD to atttach), wrap and keep writing or, or just take that as a hint that you’re device isn’t being debugged and just disable your debug writing. It’s basically using JTAG to monitor the tail pointers on the queue and then execute a memory read/write on your behalf. Definitely one of those “why didn’t we think of this 30 years ago” topics.

Trivia: it’s very much how the firmware interface to the intelligent serial board (Arnet, Digi, etc.) of the stone ages worked…

You are right, and I also like the term “channel”. The existing terminal and binary channels already can be configured to duplicate the output to one of the hardware UARTs, so there’s already some virtualization of the channel outputs.


Thoughts on RTT

I have also used RTT in other projects. Once it’s setup, it’s quite nice. There’s a similar tech I used over 20 years back in Windows called WPP. (WPP had some features RTT still does not expose, and vice-versa.)

The downside is that RTT is only usable over the SWD channel. It doesn’t help when trying to get a normal user to create useful logs. While RTT would allow setting some flags to get verbose logging / debug output … they are locked behind that SWD connnection, and thus require more steps and complexity; It’s not simple.

At the same time, a COM port doesn’t provide the structured output that RTT (or WPP, or ETW) would provide. Instead, it focuses on being approachable and simple, so a user can spend five minutes or less to enable and dump logs.

Personally, I had non-technical headaches trying to integrate RTT (I think into an Adafruit Arduino BSP), and it was a headache. Thus, I’m not motivated to try it again.

Regardless, the beatiful thing about a properly designed logging macro is that it can be designed to work with RTT, or work with a COM port, or work with a CDC port, or work with a file on storage, or any combination of those simultaneously, with at most small changes and a recompile (maybe without recompilation).


In summary, RTT is an awesome feature. I’m not going to try to get RTT integrated, and I support anyone who wants to do so. RTT / COM3 is not an either/or scenario … they can co-exist quite well, and both cover unique scenarios … and any decent logger macro can trivially switch from one to the other, or use both for output.

1 Like

Throw another USB port that’s covered by the case, similar to the spot where the SD card slot was removed on Rev10. 3rd channel for dev. Lol :laughing:
I know there’s little/ no room left, but it sounded good.

Yes, sure. That was why my first question was about the intended audience.

If one goal is to send some specially prepared .uf2 file to an end user that is experiencing some problems and getting more detailed logs from them, then an extra channel is the better solution because it doesn’t require extra hardware and being comfortable with OpenOCD commands and such.

My only request would then be that the extra COM-channel is not enabled by default, to not increase the confusion for new users we already have with the two.

I guess the easy way would be to have the third channel be only enabled via #define during compile time.

A more tricky, but more comfortable solution would be to have the third channel be runtime-enabled. AFAIK you’d have to include it in the usb descriptor, so it is not that easy to do. One idea would be that you send a special command and that causes the firmware to force a usb disconnect, wait 2 seconds, then do a reconnect and then present a new descriptor that includes the third channel. Or a flag you store somewhere in the flash that says if you want to start with the third channel enabled or not and that is read out boot. Writing to the NOR flash comes with it’s own complexity of course as you need a linker script and startup that allows the flash-writing functions to be be fully executed from ram.

I would like to throw my opinion in here based on the discussion I have read through. The USB ports (channels) are intended for the end user to interact with the device as he/she manipulates or interrogates other devices. This thread has been focused on debugging which is not a normal end user task. I think that the USB ports should be left to the normal end user roles I described.

The SWD pins are a more powerful and convenient method for bugging the firmware. In addition the RTT, which is great, you can also single step the cores/peripherals and pull its state. I like using the Cortex Debugger in VSCode. When combined with the register information, you have a point and click way to explore the mapped register states.

So, my vote would be for users that want to develop an extension to the existing firmware, to use the existing SWD interface which is already piped out, has good documentation, and is more flexible than the USB option with already existing software suites.

1 Like

I really like the sound of RTT. Has anyone moved it forward? I’m bookmarking this in my todo list.

This is now issue #113 in the GitHub repository. Volunteers to integrate is are welcome!

Please let me know if you want to volunteer to tackle this critical functionality, and I’ll assign the GitHub issue to you to work on.

Thank you!