Electronics > Microcontrollers
Low-footprint printf replacement for embedded dev
<< < (10/11) > >>
i509VCB:
Maybe a little unrelated, but if your only use for printf is going to be debugging info and it the result is not shown to the user (i.e. serial console) then something like porting defmt (https://defmt.ferrous-systems.com/introduction) to act like printf from C could be very interesting. The gist of defmt is that at compile time the string format is converted to an ID. The "printf" in this case will just write the message ID and any arguments via whatever output you use (RTT for SWD debug, UART, etc). The host PC for debugging since it has the binary running on the target can interpret the ID and arguments and do the actual expensive printf formatting.
brucehoult:

--- Quote from: i509VCB on December 16, 2024, 03:50:48 am ---Maybe a little unrelated, but if your only use for printf is going to be debugging info and it the result is not shown to the user (i.e. serial console) then something like porting defmt (https://defmt.ferrous-systems.com/introduction) to act like printf from C could be very interesting. The gist of defmt is that at compile time the string format is converted to an ID. The "printf" in this case will just write the message ID and any arguments via whatever output you use (RTT for SWD debug, UART, etc). The host PC for debugging since it has the binary running on the target can interpret the ID and arguments and do the actual expensive printf formatting.

--- End quote ---

I already suggested doing this kind of thing in https://www.eevblog.com/forum/microcontrollers/low-footprint-printf-replacement-for-embedded-dev/msg5742613/#msg5742613

Has the benefit of not having to change the C source code. Just some tricks with the .o files, or maybe compiling via .s.

I've done it a few times in the past.
Nominal Animal:
One important point to consider is the difference between snprintf() and printf()/fprintf().

In embedded environments in C, I typically need the former.  In C++, the latter is usually an interface on top of a communications stream, implemented on top of a common base written=write(buffer,length) functionality.

The main difference is the buffer handling and partial writes.

For snprintf() and similar interfaces, the caller owns and is responsible for the buffer.  If the buffer has insufficient space, the caller can easily observe it from the result value.

For printf()-type interfaces, the underlying communications stream owns the buffer, and the call cannot return before all of the string has been buffered.

Neither is well suited for asynchronous interfaces where the call begins the buffering of the resulting string, with a separate interface provides information on the progress of the buffering/sending/storage.  Current C interfaces are particularly poorly suited for such asynchronous interfaces (and coroutines, which would make such async interfaces much easier to implement, by generating the result characters on an as-needed basis; noting, however, that the final reversal of the output string would not be feasible then).

(Such async printing interfaces are much more common in systems programming and "templated" responses from service daemons; that is where I encountered them first a couple of decades ago.  As IoT use increases, I suspect the usefulness of such interfaces will increase even in the microcontroller/small-embedded domains.)
SiliconWizard:

--- Quote from: Nominal Animal on December 16, 2024, 11:06:02 am ---Neither is well suited for asynchronous interfaces where the call begins the buffering of the resulting string, with a separate interface provides information on the progress of the buffering/sending/storage.  Current C interfaces are particularly poorly suited for such asynchronous interfaces (and coroutines, which would make such async interfaces much easier to implement, by generating the result characters on an as-needed basis; noting, however, that the final reversal of the output string would not be feasible then).

--- End quote ---

Yes, what you mean is that the printf functions are all "blocking" - they can only run to completion, even if said completion is a truncated string (in the case of the s*prinff functions).
For "async" behavior, one needs to retain the state. That would require passing a "state" variable (typically a structure). It does make things more complex to implement obviously.

For my replacement, I'm thinking of adding something like this. To make things simpler and more efficient, my thought was to handle the number conversions strictly synchronously and the overall string formatting asynchronous. Meaning if there are numbers to format, numbers would need to be each entirely converted, which would just require a relatively small minimum buffer (numbers will typically require only a few bytes as a string, maybe a couple tens of bytes for the very longest ones). If you see what I mean.
Nominal Animal:

--- Quote from: brucehoult on December 12, 2024, 02:03:37 pm ---Nothing prevents you using the C++ compiler instead of the C compiler.
--- End quote ---
Actually, there is one thing: older AVR (and other Harvard architecture MCUs) and named address spaces, on plain hardware, outside Arduino core libraries.

For very specific but oddball reasons, g++ does not support named address spaces nearly as well as gcc does.  Clang has no issues in C or in C++, though, but unless I'm mistaken, clang's/llvm's AVR support is not as complete as gcc's.

The benefit is being able to differentiate between character data in RAM and character data in Flash at the type level; including _Generic() macros.  This is not really possible in Arduino (using g++) right now, at least the last time I checked.


--- Quote from: SiliconWizard on December 16, 2024, 09:49:35 pm ---For my replacement, I'm thinking of adding something like this. To make things simpler and more efficient, my thought was to handle the number conversions strictly synchronously and the overall string formatting asynchronous. Meaning if there are numbers to format, numbers would need to be each entirely converted, which would just require a relatively small minimum buffer (numbers will typically require only a few bytes as a string, maybe a couple tens of bytes for the very longest ones). If you see what I mean.
--- End quote ---
Yes, I do believe I do.  For standard printf-type formatting strings, it means converting the format string to the stateful structure(s) synchronously, plus supplying RAM for the temporary buffer needed for longest dynamically generated field.  It is "wasteful" in the sense that it would be possible to generate these structures at compile time, for lower run-time cost.

In fact, I have done something similar, except without the printf-like part: users supply an array of fields, temporary one-field conversion storage, plus the pointers to the format specifications or formatters for each field (these being in Flash, either read-only or code).  This worked well for both embedded and systems programming under a fully featured OS, so I'd say your main hurdle will be the string-to-structure conversion, and using compact and efficient structures.  (I found that separating the varying state kept in RAM, and the formatting specs kept in Flash, was quite important.)
Navigation
Message Index
Next page
Previous page
There was an error while thanking
Thanking...

Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod