Adding the Picolib C/C++ Standard Library to an existing GNU ARM Embedded Toolchain

It looks like my previous article “Which Embedded GCC Standard Library? newlib, newlib-nano, …” stirred up something: I saw and knew about the Picolib created and maintained by Keith Packard, but never had the time to try it out. With the university grading mostly over, I have put aside a few hours to try it out. And the result is very interesting:

Footprint of different embedded libraries

Outline

I’m more in the camp of ‘not using anything from the C/C++ Standard library for embedded’. Simply because a single printf() easily blows up the FLASH and RAM footprint of smaller systems, and because reentrancy is not guaranteed in my cases. As outlined in “Which Embedded GCC Standard Library? newlib, newlib-nano, …” the usual candidates for an embedded C/C++ Standard library are newlib and newlib-nano. But they are aged, newlib still huge and not up to the latest standard libraries. As the commenters pointed out: there is the Picolibc and Picolib library for both C and C++ which aims at a low footprint, suitable for embedded targets.

Approach

There is good documentation available on the Picolib websites, including pre-built libraries for various GNU ARM Embedded toolchain. I wanted to integrate and use it with the NXP MCUXpresso IDE and toolchain, and as this one is using a ‘managed linker script’ concept, the integration of the library is somewhat different.

In this article I describe how to install and use the Picolib with the NXP MCUXpresso IDE 11.7.0 and its toolchain. For this I need to identify the gcc version, then install the library as a drop-in into the existing library structure. Finally I need to tweak the linker files to use it the Picolib instead of newlib, newlib-nano or Redlib.

The resulting project for an ARM Cortex-M4F (K22FN512) can be found on GitHub (see links at the end of this article).

Choosing pre-built Picolib

It is possible to build the Picolib, but for simplicity I’m using a pre-built library from https://keithp.com/picolibc/dist/gnu-arm-embedded/

The IDE uses by default the toolchain located in this installation folder on Windows:

C:\NXP\MCUXpressoIDE_11.7.0_9198\ide\tools
GNU ARM Embedded Toolchain inside MCUXpresso IDE

The associated documentation and version information is in

C:\NXP\MCUXpressoIDE_11.7.0_9198\ide\tools\share\doc\gcc-arm-none-eabi

There is the release note file:

Release Notes

Which shows the version:

Release notes for
****************************************************
GNU Arm Embedded Toolchain 10.3-2021.10
****************************************************

With this I know which file of the pre-built libraries I can use: the picolibc-1.78-10.3-2010.10.zip:

pre-built libraries

Installing Picolib

The next step is to unzip the files over the existing installation. In my case the tools are in

C:\NXP\MCUXpressoIDE_11.7.0_9198\ide\tools

And there is where I have to extract the files:

Using Piclib with MCUXpresso

Writing GNU linker scripts can be a difficult task. Actually it is not so hard, once you get used to it.

The MCUXpresso IDE uses a concept called ‘manged linker script’ which has its benefits: you can change libraries easily and the IDE cares about it. But it gets complicated if you want to make your own changes. There is a way with using Freemarker scripts, but honestly that way is very complicated too. The IDE creates the linker scripts in the output folder:

First, build the project with newlib or newlib-nano library variant (do *not* use the RedLib, it will not work in the later steps):

Which variant (none, semihost, …) does not matter, as long it is not the proprietary Redlib. The reason is that the library selection sets as well some macros, and the Redlib ones won’t work with the Picolib. We are just generating the linker files once to have something we can work with.

After the files have been generated, turn off the managed linker script functionality:

turned off managed linker script

Then for C add

-specs=picolibc.specs

For C++ add

-specs=picolibcpp.specs

to the linker flags:

If you are interested, the linker specs files are located here:

C:\NXP\MCUXpressoIDE_11.7.0_9198\ide\tools\lib\gcc\arm-none-eabi\10.3.1

Next, we have to change the libraries used. For this edit the *library.ld.

Basically you need the following libraries:

 libgcc.a
 libm.a
 libc.a                 /* << picolib standard library */
 libsemihost.a          /* << picolib semihost library */
 /* libdummyhost.a */   /* << picolib 'none' stdio lirary */

Depending if you need semihosting or not, the libsemihost.a or libdummyhost.a is used.

Below the is how it could look like (I commented out the newlib-nano specific entries, so I can easily go back):

Picolib libraries

The Picolib uses an optimized malloc()/free() which requires the two symbols __heap_start and __heap_end defined in the linker script file for the heap memory. So add these two symbols to the linker file:

__heap_start = .;
__heap_end = .;

The last step is optional (but recommended), if you want to use the ‘Heap and Stack Usage’ view in the IDE. The brk variable in the library indicates the current heap allocation level and can be used as a heap ‘used’ indicator in that view. The symbols are used in the debugger to calculate the current heap usage.

Heap & Stack Usage

So update the project settings as below:

With this, the application shall link fine with the Picolib, and you shall see some code and data size reduction compared to what you were using before, depending on your usage of the standard library.

Optimizations

By default, the library comes with full support for float and double for printf() and scanf(). Using float and double are not recommended for many embedded applications, and having support for it in the library would be a waste of resources. That’s why libraries offer to have a ‘no-float’ option. For Picolib this is very easy: just map the two symbols in the linker command line settings:

--defsym=vfprintf=__i_vfprintf
--defsym=vfscanf=__i_vfscanf

So have them added to the linker settings:

no-float option for printf() and scanf()

The Picolib documentation provides other settings as well, but to me that ‘no-float’ option is the most useful one.

Benchmark

So with all the things set up, is it worth the effort? The check the footprint of different libraries (newlib, newlib-nano, Redlib and PicoLib), with the ‘printf-scanf-no-floating-point’ option if applicable. I created a simple test application using printf(), scanf(), malloc(), free(), strcpy() and memset(). No special optimization turned on, with a ‘default’ stack size of 0x1000 and heap size of 0x1000. You can find the test application on Github.

The get the code and data size information, the GNU size utility has been used, see text, data and bss: Code and Data Size Explained.

Below the output for the Picolib with ‘no-float’ optimization:

Memory region         Used Size  Region Size  %age Used
   PROGRAM_FLASH:        9652 B       512 KB      1.84%
      SRAM_UPPER:        8276 B        64 KB     12.63%
      SRAM_LOWER:          0 GB        64 KB      0.00%

For the FLASH memory size (code and constant data), the Picolib wins both for the ‘full’ and ‘no-float (nf)’ variants.

FLASH usage

Same for the RAM usage (keep in mind that the test application has 4K for heap and 4K for stack reserved), the Picolib needs the least amount of RAM:

RAM Usage

For the stack usage, a pattern is written to the stack and then checked how much of that 4 KByte has been used. Here again, the Picolib wins the competition:

Stack Memory Usage

Finally, the heap memory usage, for which the ‘Heap and Stack Usage’ view in Eclipse as been used:

Here again: the clear winner is Picolib. Honestly, I did not think upfront that the result would be that clear.

Summary

I have been a big fan using newlib-nano, and newlib-nano is used in most of my projects. But now after exploring the Picolib, I need to start using the Picolib: it is not only a modern library with a very permissive license (comparable to newlib and newlib-nano), its footprint (Code and Data) is smaller too. Of course this depends on what is used from the library, but I think now it is time to move on using the Picolib, at least for the C projects, as I have not used it yet for a C++ project. If you have some comments or thoughts about the Picolib, let us know.

After all, I hope I gave you some thoughts about library usage, and what it could mean choosing one.

Happy picoing:-)

Links

Advertisement

10 thoughts on “Adding the Picolib C/C++ Standard Library to an existing GNU ARM Embedded Toolchain

  1. Hi Erich There’s some great benefits here. For some projects on release builds its desirable to eliminate all calls to printf() – to be sure there’s no debug console output for performance and security reasons. Eliminating use of malloc() is also a goal for some safety & high reliability applications. Would you suppose this is possible?

    Like

    • Yes, I tried it out shortly after it has announced, but I was not able to make it work. Have not tried it afterwards.
      What I really like is the way how Espressif does it: simple and straight forward, and isolated environment, and no need to fuzz with ‘yet another package manager’ kind of thing.

      Like

  2. Thank you very much for this article about Picolibc. The original docs also cover TLS and Locking, but I didn’t succeed to implement it. Could you add another post showing how to implement this?

    Like

      • Thank you, but maybe I created a misunderstanding. “My” TLS points to Thread Level Storage, which is used inside Picolibc. I found some entries in the linker script examples provided with Picolibc, but I am not sure how to include this into CubeMX generated linker scripts. Could you review these examples?

        Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.