Many times when someone tells you that language X is “better” at something they really mean that it has better built-in libraries for that task. Java is a great example. The language isn’t all that different from C++ outside of garbage collection and multiple inheritance, but the standard libraries are super powerful, especially for networking. Even C relies on a library to provide a lot of functions people think of as part of the language — printf
, for example. That’s not really part of the C language, but just part of the standard library. When you are writing for a tiny processor, the choice of library is critical and [Keith Packard] offers you one choice: picolibc.
The library has its genesis from two other diminutive libraries: Newlib and the AVR version of libc. It provides support for ARC, ARM, i386, m68k, MIPS, MSP430, Nios II, PPC, RISC-V, Sparc64, x86_64, and the ESP8266/ESP32.
There is documentation for how to graft the library into your projects. That includes a few APIs that it expects from the operating environment. There are also documents on how the library uses thread local storage, locking, and other technical details.
Is it better than other choices? That’s not for us to say. You’ll have to build it on your exact platform and make your own comparisons. However, it is a viable candidate and since it is based on newlib, it should be fairly stable. You can debate if you should use printf
, or not. Or you can just lean into it. But you can also use other parts of the library without delving into printf
.
Even if you don’t need a tiny library, sometimes reading through library code for your chosen target can be illuminating. For example, how would you write an efficient strchr
function? Now, look how they did it. Portability is the devil here since you could probably do even better with some CPU-specific instructions like AVX2 or SSE.
Title graphic courtesy [Priscilla Du Preez]
If all you want is printf() etc. for the Arduino IDE (I don’t consider the Arduino IDE v2.x stable yet but I suspect this will work with it too):
* LibPrintf by Embedded Artistry
https://github.com/embeddedartistry/arduino-printf
https://www.arduino.cc/reference/en/libraries/libprintf/
This library provides a standalone implementation for the following functions: printf(), sprintf(), snprintf(), vprintf(), and vsnprintf().
Project Target:
This library aims to offer a complete printf() solution while maintaining low storage and RAM requirements. This is critical for MCUs with limited storage and RAM. This project is ideal for AVR based MCUs like the Arduino Uno and it’s siblings.
ESP8266 and ESP32:
The Arduino implementations for the ESP8266 and ESP32 already include a printf() implementation as part of the base library. You do not need this library for those platforms. (But in my experience installing this library anyway does no harm.)
Install, Update, or Remove:
Sketch > Include Library > Manage Libraries… > Search: LibPrintf
“the ESP8266 and ESP32 already include a printf() implementation” uses a printf() from newlib or newlib_nano, both of which are MUCH bigger than picolib’s version.
Technically, the ROM of these chips contains a (proprietary) printf implementation as well; you could use that ‘for free’.
I tried using printf() by itself in the Arduino IDE ESP32 and ESP8266 but it did not work. However it may be because I didn’t invoke the library first – without documentation I would not know how. How do you use the “built-in” printf()?
I can vouch for picolibc as very useful. Have a RiscV-based SOC here on a FPGA (home-cobbled-together from a VexRiscV and some peripherals) and plunking in Picolibc has been more-or-less trivial. It helps if you already know Newlib as it’s based on that.
Nice! But if I understand the scope correctly, picolib makes no attempt at being a HAL; you need to supply the UART interfacing, and probably whatever needs to be done to make malloc (more likely: sbrk) possible, right?
Out of curiosity: So, what’s “under” your picolib? Some handful of hand-written functions, or some small RTOS? I’m still very partial to ChibiOS, but don’t think that has been ported to RISC-V, nor would it contain any HAL for an existing SoC/SoC generator).
Also, curiosity: your SoC, hand-knitted or industrial knitting machine of the LiteX variety, or any other base to build SoCs based on VexRiscv?
A libc only has a small amount of things that need to interface with the hardware to have utility: things like memcmp(), strcpy(), sprintf() etc will work without. That being said, I needed to implement UART bindings and if I wanted I could hook up the filesystem layer to fatfs (didn’t bother here). Sbrk works out-of-the-box, the default implementation uses the amount of RAM that the CPU has as defined in the linker script.
My SoC is a bit of a weird beast, as it contains a SDRAM-interface that needs to act like a SRAM for a (slow) 6809 that must always run cycle-accurate, but has a shared memory interface with the RiscV CPU as well as the DMA interface to a SDMMC peripheral. The Vexriscv is simply built from the SpinalHDL source, the rest of the thing is hand-rolled Verilog (SDRAM controller, arbiter, …) connecting it to the UART and SDMMC peripherals.
re: libc/out-of-the-box functionality. If the linker script reserves you some memory, your libc can play memory allocator. Makes sense; thanks!
re: SoC: what in the retro hell are you building there? An arcade machine emulator / debugger / dumper? This is indeed quite wild, but the good kind of wild.
Seems the RP2040 should have been included.
RP2040 is ARM (Cortex M0+), so it probably is supported