Starting to properly bare metal Raspberry Pi 0 (W) - though as bare metal as you can get with a π0 is not very bare, because the VideoCore does quite a bit of stuff. Bare ARM anyway… copied bootcode.bin
and start.elf
to root of FAT32 µSD card, then built a program to switch on and off GPIO47 which is the power LED.
git clone -n --depth=1 --filter=tree:0 git@github.com:raspberrypi/firmware.git
cd firmware/
git sparse-checkout set --no-cone boot
git checkout
These are the general purpose firmware files needed to bring any π system to full awakeness - most of this is actually executing on the VideoCore (bootcode.bin
and start.elf
👀) and call kernel.img
on ARMv6 based π like the π0. Here call == loads program into memory and executes from _start
.
Define _start
which just sets the stack pointer to the start of RAM defined in the linker script:
.globl _start
_start:
mov sp,#0x8000
bl main
halt: b halt
Linker script:
MEMORY
{
RAM : ORIGIN = 0x8000, LENGTH = 128M
}
SECTIONS
{
.text : { *(.text*) } > RAM
.data : { *(.data) } > RAM
.bss : { *(.bss*) } > RAM
}
No .data
right now but may do in future, so define where it will sit.
Main program:
// registers from §6.1 of data sheet
volatile unsigned int *GPIO = (unsigned int *)0x20200000;
#define GPFSEL4 (0x10 / 4)
#define GPSET1 (0x20 / 4)
#define GPCLR1 (0x2C / 4)
// GPIO47 on bank #1
#define LED (47 - 32)
extern int nop(int);
int main(void) {
unsigned int gpio_reg;
gpio_reg = GPIO[GPFSEL4];
gpio_reg &= ~(7 << 21);
gpio_reg |= 1 << 21;
GPIO[GPFSEL4] = gpio_reg;
while (1) {
GPIO[GPSET1] = 1 << LED;
for (int j = 0; j < 0x100000; j++)
nop(j);
GPIO[GPCLR1] = 1 << LED;
for (int j = 0; j < 0x100000; j++)
nop(j);
}
return 0;
}
Includes a nop
definition in a different file to avoid optimiser doing its stuff:
int nop(int x) { return 0; }
ARMGNU ?= arm-none-eabi
AFLAGS = --warn --fatal-warnings
CFLAGS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding
# bespoke optimisation flags
RPI0 = -O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
all: kernel.img
clean:
rm -f *.o
rm -f *.bin
rm -f *.hex
rm -f *.elf
rm -f *.img
boot.o: boot.s
$(ARMGNU)-as $(AFLAGS) boot.s -o boot.o
main.o: main.c
$(ARMGNU)-gcc $(CFLAGS) $(RPI0) -c main.c -o main.o
nop.o: nop.c
$(ARMGNU)-gcc $(CFLAGS) $(RPI0) -c nop.c -o nop.o
main.elf: boot.ld boot.o main.o nop.o
$(ARMGNU)-ld boot.o main.o nop.o -T boot.ld -o main.elf
kernel.img: main.elf
$(ARMGNU)-objcopy main.elf -O binary kernel.img
It’ll do.