This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

how does retarget.c actually work?

I'm trying to get a handle on the various ways I can implement console output on this system.

I have in the past used the retarget.c/.h code from this forum that uses simple_uart.c.

There is also a file called "console.c" that seems to attempt to implement a similar thing.

And finally, there is app_uart.c, which is a bit more fully-featured uart, mainly in that it allocates FIFOs and has a callback on received data.

Questions:

  1. The retarget.c/.h approach worked for me before, but I don't understand why, and that bothers me. With it linked into my app, I am able to call printf() to push messages to the serial port. It seems to do this by implementing fputc(), and maybe it's obvious to other people, but I'm surprised that printf() would be fully supported by the implementation of fputc. Can someone explain this?

  2. Any reason why I can't rewire retarget.c using app_uart?

  3. How much memory is the overhead of printf versus lower-level hackery to push a string to the uart? I'd like to know how much I'm wasting. I assume console.c is there to be more efficient, but how much more efficient is it? Rough idea would be sufficient here.

  • Let me add that I was able to include retarget.c with the 4.4.1 SDK in the default stack/heap size of 2k/2k. Now I'm trying to get it to work under 5.1.0 and it won't even link if I don't set heap down to 0 and the stack to 768 bytes. Not sure if the app will run with only 768 bytes of stack, but more importantly, I have a bigger problem in that including printf shouldn't' be such a hit to my memory usage. I'm really trying to understand why printf is pulling in so much crap and what I should expect.

    UPDATE: my big hitters from my .map file are:

    from gnu: .data 0x428 .../libc.a(lib_a-impure.o) .data 0x408 .../libc.a(lib_a-mallocr.o)

    from the SDK (surprisingly large memory usage) .bss 0x238 _build/ble_bondmngr.o .bss 0x188 _build/ble_error_log.o .bss 0x288 _build/pstorage.o

  • Implementing a simple fputc is the easiest way to get printf to work with a custom interface. This is a result of the way that printf is implemented, and is hence outside of our control. I'm a little unsure what exactly you feel you need to know about this, but you may have use in taking a look at this.

    You most definitely could use app_uart for retargeting, but the only change would be to make fputc call app_uart_put() instead of simple_uart_putc(). It doesn't really make much of a difference.

    As for RAM usage of printf(), this will depend heavily on toolchain used. I can't find any numbers for Keil, but for GCC you can refer to this question. You can also consider setting aside a big heap, fill it with a known value and afterwards check how much of it was used to know how much heap your exact printf-use requires.

  • The reason I wanted to know was so I could control what is and is not getting included in my build. Knowing that all printf statements end up calling fputc() makes it all make sense.

    I'm finding more and more with embedded programming you can't not know everything about your code. The .map file is not just a bunch of noise; it tells you when you made an innocent mistake that hauled in libraries you might not need.

    I ended up implementing my own printf (more limited than the standard one) in my own console.c and saved another 2k of RAM by doing so. file attached.

    console.c

  • Since I got hung up with this and originally found this post helpful otherwise, I thought I would add that if you're building with the pure-gcc setup you actually need to implement _write() instead of fputc(). See this: github.com/.../.

Related