1# Hello World 2 3Building embedded applications is tricky in part because of the huge 4number of configuration settings necessary to get something that 5works. This example shows how to use the installed version of 6picolibc, and uses 'make' instead of 'meson' to try and make the 7operations as clear as possible. Here's our fine program: 8 9 #include <stdio.h> 10 11 int 12 main(void) 13 { 14 printf("hello, world\n"); 15 return 0; 16 } 17 18## Selecting picolibc headers and C library 19 20Picolibc provides a GCC '.specs' file _(generated from 21picolibc.specs.in)_ which sets the search path for 22header files and picolibc libraries. 23 24 gcc -specs=picolibc.specs 25 26## Semihosting 27 28Our example program wants to display a string to stdout; because there 29aren't drivers for the serial ports emulated by qemu provided, the 30example uses Picolibc's semihosting support (`--oslib=semihost`) to 31direct stdout to the QEMU console: 32 33 gcc -specs=picolibc.specs --oslib=semihost 34 35## Target processor 36 37For ARM, QEMU emulates a "mps2-an385" board which has a Cortex-M3 38processor: 39 40 arm-none-eabi-gcc -specs=picolibc.specs --oslib=semihost -mcpu=cortex-m3 41 4264-bit ARM (aarch64) processors are pretty much the same, so the 43default target code will run fine on a cortex-a57 processor as 44supported by QEMU: 45 46 aarch64-linux-gnu-gcc -specs=picolibc.specs --oslib-semihost 47 48For RISC-V, QEMU lets you specify which CPU core you want, so we'll 49use something that looks like a SiFive E31 chip. That's a 32-bit 50processor with the 'imac' options (integer, multiply, atomics, 51compressed) and uses the 'ilp32' ABI (32-bit integer, long and 52pointer) 53 54 riscv64-unknown-elf-gcc -specs=picolibc.specs 55 --oslib-semihost -march=rv32imac -mabi=ilp32 56 57## Target Memory Layout 58 59The application needs to be linked at addresses which correspond to 60where the target memories are addressed. The default linker script 61provided with picolibc, `picolibc.ld`, assumes that the target device 62will have two kinds of memory, one for code and read-only data and 63another kind for read-write data. However, the linker script has no 64idea where those memories are placed in the address space. The example 65specifies those by setting a few values before including 66`picolibc.ld`. 67 68The mps2-an385 has at least 16kB of flash starting at 0. Picolibc 69places a small interrupt vector there which points at the first 70instruction of _start. The mps2-an385 also has 64kB of RAM starting 71at 0x20000000, so arm.ld looks like this: 72 73 __flash = 0x00000000; 74 __flash_size = 0x00004000; 75 __ram = 0x20000000; 76 __ram_size = 0x00010000; 77 __stack_size = 1k; 78 79 INCLUDE picolibc.ld 80 81The aarch64 virt model lets you define whatever memory spaces you 82like,so we'll just stick things at 0x40000000 (aarch64.ld): 83 84 __flash = 0x40000000; 85 __flash_size = 0x00400000; 86 __ram = 0x40400000; 87 __ram_size = 0x00200000; 88 __stack_size = 8k; 89 90 INCLUDE picolibc.ld 91 92For the RISC-V 'spike' model, you can have as much memory as you like, 93but execution starts at 0x80000000 so the first instruction in the 94application needs to land there. Picolibc on RISC-V puts _start at the 95first location in read-only memory, so we set things up like this 96(this is riscv.ld): 97 98 __flash = 0x80000000; 99 __flash_size = 0x00080000; 100 __ram = 0x80080000; 101 __ram_size = 0x40000; 102 __stack_size = 1k; 103 104 INCLUDE picolibc.ld 105 106The `-T` flag is used to specify the linker script in the compile 107line: 108 109 arm-none-eabi-gcc -specs=picolibc.specs --oslib=semihost 110 -mcpu=cortex-m3 -Tarm.ld 111 112 aarch64-linux-gnu-gcc -specs=picolibc.specs --oslib=semihost 113 -Taarch64.ld 114 115 116## Final Commands 117 118The rest of the command line tells GCC what file to compile 119(hello-world.c) and where to put the output (hello-world-riscv.elf and 120hello-world-arm.elf): 121 122 riscv64-unknown-elf-gcc --specs=picolibc.specs --oslib=semihost 123 -march=rv32imac -mabi=ilp32 -Thello-world-riscv.ld -o 124 hello-world-riscv.elf hello-world.c 125 126 arm-none-eabi-gcc --specs=picolibc.specs --oslib=semihost 127 -mcpu=cortex-m3 -Thello-world-arm.ld -o hello-world-arm.elf 128 hello-world.c 129 130## Running Under QEMU 131 132To run the hello-world example under qemu, we need to construct a 133virtual machine suitable for this. That means enabling semihosting 134(`-semihosting-config enable=on`), disabling the monitor interface 135(-monitor none), the emulated UART (-serial none) and the graphical 136interface (`-nographic). 137 138For arm, we're using the mps2-an385 139 140 qemu-system-arm -semihosting-config enable=on -monitor none 141 -serial none -nographic 142 -machine mps2-an385,accel=tcg 143 -kernel hello-world-arm.elf 144 145On aarch64, we use the 'virt' machine, which lets us plug in any 146processor we want. In this case, we'll use the cortex-a57: 147 148 qemu-system-aarch64 -semihosting-config enable=on -monitor none 149 -serial none -nographic 150 -machine virt -cpu cortex-a57 151 -kernel hello-world-aarch64.elf 152 153Risc-V is similar to aarch64 in providing a virtual host into which 154you can install any virtual processor you want, in our case, an rv32: 155 156 qemu-system-riscv32 -semihosting-config enable=on -monitor none 157 -serial none -nographic 158 -machine virt,accel=tcg -cpu rv32 -bios none 159 -kernel hello-world-riscv.elf 160 161