Picolibc Hello World Example
It's hard to get started building applications for embedded RISC-V and ARM systems. You need to at least:
Find and install the toolchain
Install a C library
Configure the compiler for the right processor
Configure the compiler to select the right headers and libraries
Figure out the memory map for the target device
Configure the linker to place objects in the right addresses
I've added a simple 'hello-world' example to picolibc that shows how to build something that runs under qemu so that people can test the toolchain and C library and see what values will be needed from their hardware design.
The Source Code
Getting text output from the application is a huge step in embedded
system development. This example uses the “semihosting” support
built-in to picolibc to simplify that process. It also explicitly
calls exit
so that qemu will stop when the demo has finished.
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
printf("hello, world\n");
exit(0);
}
The Command Line
The hello-world documentation
takes the user through the steps of building the compiler command line, first using the picolibc.specs
file to
specify header and library paths:
gcc --specs=picolibc.specs
Next adding the semihosting library with the --semihost option (this is an option defined in picolibc.specs which places -lsemihost after -lc):
gcc --specs=picolibc.specs --semihost
Now we specify the target processor (switching to the target compiler here as these options are target-specific):
riscv64-unknown-elf-gcc --specs=picolibc.specs --semihost -march=rv32imac -mabi=ilp32
or
arm-none-eabi-gcc --specs=picolibc.specs --semihost -mcpu=cortex-m3
The next step specifies the memory layout for our emulated hardware, either the 'spike' emulation for RISC-V:
riscv64-unknown-elf-gcc --specs=picolibc.specs --semihost -march=rv32imac -mabi=ilp32 -Thello-world-riscv.ld
with hello-world-riscv.ld containing:
__flash = 0x80000000;
__flash_size = 0x00080000;
__ram = 0x80080000;
__ram_size = 0x40000;
__stack_size = 1k;
INCLUDE picolibc.ld
or the mps2-an385 for ARM:
arm-none-eabi-gcc --specs=picolibc.specs --semihost -mcpu=cortex-m3 -Thello-world-arm.ld
with hello-world-arm.ld containing:
__flash = 0x00000000;
__flash_size = 0x00004000;
__ram = 0x20000000;
__ram_size = 0x00010000;
__stack_size = 1k;
INCLUDE picolibc.ld
Finally, we add the source file name and target elf output:
riscv64-unknown-elf-gcc --specs=picolibc.specs --semihost
-march=rv32imac -mabi=ilp32 -Thello-world-riscv.ld -o
hello-world-riscv.elf hello-world.c
arm-none-eabi-gcc --specs=picolibc.specs --semihost
-mcpu=cortex-m3 -Thello-world-arm.ld -o hello-world-arm.elf
hello-world.c
Summary
Picolibc tries to make things a bit simpler by offering built-in compiler and linker scripts along with default startup code to try and make building your first embedded application easier.
Picolibc Version 1.0 Released
I wrote a couple of years ago about the troubles I had finding a good libc for embedded systems, and for the last year or so I've been using something I called 'newlib-nano', which was newlib with the stdio from avrlibc bolted on. That library has worked pretty well, and required very little work to ship.
Now that I'm doing RISC-V stuff full-time, and am currently working to improve the development environment on deeply embedded devices, I decided to take another look at libc and see if a bit more work on newlib-nano would make it a good choice for wider usage.
One of the first changes was to switch away from the very confusing "newlib-nano" name. I picked "picolibc" as that seems reasonably distinct from other projects in the space and and doesn't use 'new' or 'nano' in the name.
Major Changes
Let's start off with the big things I've changed from newlib:
Replaced stdio. In place of the large and memory-intensive stdio stack found in newlib, picolibc's stdio is derived from avrlibc's code. The ATmel-specific assembly code has been replaced with C, and the printf code has seen significant rework to improve standards conformance. This work was originally done for newlib-nano, but it's a lot cleaner looking in picolibc.
Switched from 'struct _reent' to TLS variables for per-thread values. This greatly simplifies the library and reduces memory usage for all applications -- per-thread data from unused portions of the library will not get allocated for any thread. On RISC-V, this also generates smaller and faster code. This also eliminates an extra level of function call for many code paths.
Switched to the 'meson' build system. This makes building the library much faster and also improves the maintainability of the build system as it eliminates a maze of twisty autotools configure scripts.
Updated the math test suite to use glibc as a reference instead of some ancient Sun machine.
Manually verified the test results to see how the library is doing; getting automated testing working will take a lot more effort as many (many) tests still have invalid 'correct' values resulting in thousands of failure.
Remove unused code with non-BSD licenses. There's still a pile of unused code hanging around, but all non-BSD licensed bits have been removed to make the licensing situation clear. Picolibc is BSD licensed.
Picocrt
Starting your embedded application requires initializing RAM as appropriate and calling initializers/constructors before invoking main(). Picocrt is designed to do that part for you.
Building Simplified
Using newlib-nano meant specifying the include and library paths very carefully in your build environment, and then creating a full custom linker script. With Picolibc, things are much easier:
Compile with -specs=picolibc.specs. That and the specification of the target processor are enough to configure include and library paths. The Debian package installs this in the gcc directory so you don't need to provide a full path to the file.
Link with picolibc.ld (which is used by default with picolibc.specs). This will set up memory regions and include Picocrt to initialize memory before your application runs.
Debian Packages
I've uploaded Debian packages for this version; they'll get stuck in the new queue for a while, but should eventually make there way into the repository. I'll plan on removing newlib-nano at some point in the future as I don't plan on maintaining both.
More information
You can find the source code on both my own server and over on github:
You'll find some docs and other information linked off the README file
FOSDEM 2017 -- The Machine and ChaosKeys
Yay! I get to go to FOSDEM this year. I'll be speaking about Free Software for The Machine on Sunday afternoon at 14:00 in K.1.105 (La Fontaine).
I'll also be bringing along a number of ChaosKeys and will have them available for either $40 US or €40. You can either bring cash or pre-pay at the Altus Metrum shop.
Hope to see you there.
Finding a Libc for tiny embedded ARM systems
You'd think this problem would have been solved a long time ago. All I wanted was a C library to use in small embedded systems -- those with a few kB of flash and even fewer kB of RAM.
Small system requirements
A small embedded system has a different balance of needs:
Stack space is limited. Each thread needs a separate stack, and it's pretty hard to move them around. I'd like to be able to reliably run with less than 512 bytes of stack.
Dynamic memory allocation should be optional. I don't like using malloc on a small device because failure is likely and usually hard to recover from. Just make the linker tell me if the program is going to fit or not.
Stdio doesn't have to be awesomely fast. Most of our devices communicate over full-speed USB, which maxes out at about 1MB/sec. A stdio setup designed to write to the page cache at memory speeds is over-designed, and likely involves lots of buffering and fancy code.
Everything else should be fast. A small CPU may run at only 20-100MHz, so it's reasonable to ask for optimized code. They also have very fast RAM, so cycle counts through the library matter.
Available small C libraries
I've looked at:
μClibc. This targets embedded Linux systems, and also appears dead at this time.
musl libc. A more lively project; still, definitely targets systems with a real Linux kernel.
dietlibc. Hasn't seen any activity for the last three years, and it isn't really targeting tiny machines.
newlib. This seems like the 'normal' embedded C library, but it expects a fairly complete "kernel" API and the stdio bits use malloc.
avr-libc. This has lots of Atmel assembly language, but is otherwise ideal for tiny systems.
pdclib. This one focuses on small source size and portability.
Current AltOS C library
We've been using pdclib for a couple of years. It was easy to get running, but it really doesn't match what we need. In particular, it uses a lot of stack space in the stdio implementation as there's an additional layer of abstraction that isn't necessary. In addition, pdclib doesn't include a math library, so I've had to 'borrow' code from other places where necessary. I've wanted to switch for a while, but there didn't seem to be a great alternative.
What's wrong with newlib?
The "obvious" embedded C library is newlib. Designed for embedded systems with a nice way to avoid needing a 'real' kernel underneath, newlib has a lot going for it. Most of the functions have a good balance between speed and size, and many of them even offer two implementations depending on what trade-off you need. Plus, the build system 'just works' on multi-lib targets like the family of cortex-m parts.
The big problem with newlib is the stdio code. It absolutely requires dynamic memory allocation and the amount of code necessary for 'printf' is larger than the flash space on many of our devices. I was able to get a cortex-m3 application compiled in 41kB of code, and that used a smattering of string/memory functions and printf.
How about avr libc?
The Atmel world has it pretty good -- avr-libc is small and highly optimized for atmel's 8-bit avr processors. I've used this library with success in a number of projects, although nothing we've ever sold through Altus Metrum.
In particular, the stdio implementation is quite nice -- a 'FILE' is effectively a struct containing pointers to putc/getc functions. The library does no buffering at all. And it's tiny -- the printf code lacks a lot of the fancy new stuff, which saves a pile of space.
However, much of the places where performance is critical are written in assembly language, making it pretty darn hard to port to another processor.
Mixing code together for fun and profit!
Today, I decided to try an experiment to see what would happen if I used the avr-libc stdio bits within the newlib environment. There were only three functions written in assembly language, two of them were just stubs while the third was a simple ultoa function with a weird interface. With those coded up in C, I managed to get them wedged into newlib.
Figuring out the newlib build system was the only real challenge; it's pretty awful having generated files in the repository and a mix of autoconf 2.64 and 2.68 version dependencies.
The result is pretty usable though; my STM 32L discovery board demo application is only 14kB of flash while the original newlib stdio bits needed 42kB and that was still missing all of the 'syscalls', like read, write and sbrk.
Here's gitweb pointing at the top of the tiny-stdio tree:
And, of course you can check out the whole thing
git clone git://keithp.com/git/newlib
'master' remains a plain upstream tree, although I do have a fix on that branch. The new code is all on the tiny-stdio branch.
I'll post a note on the newlib mailing list once I've managed to subscribe and see if there is interest in making this option available in the upstream newlib releases. If so, I'll see what might make sense for the Debian libnewlib-arm-none-eabi packages.
ChaosKey v1.0 Released — USB Attached True Random Number Generator
ChaosKey, our random number generator that attaches via USB, is now available for sale from the altusmetrum store.
We talked about this device at Debconf 16 last month
Support for this device is included in Linux starting with version 4.1. Plug ChaosKey into your system and the driver will automatically add entropy into the kernel pool, providing a constant supply of true random numbers to help keep the system secure.
ChaosKey is free hardware running free software, built with free software on a free operating system.
Re-joining HP
Thursday was my first day back with HP. I've joined Steve Geary's group to work on Linux support for “the machine”
I had a great time at Intel and wish my old team all the best.
Appointed to the Debian Tech Committee
I'm pleased to announce that I've been appointed to serve on the Debain Technical Committee.
I'd like to thank the other committee members and the Debian project leader, Lucas Nussbaum, for giving me this opportunity to serve. I look forward to working within the committee to further Debian's goals as the universal operating system.