Step 0 - update config.txt and overlay
config.txt:
enable_uart=1
dtoverlay=miniuart-bt # or disable-bt as you want
kernel=bare_metal.bin
Please remember to download miniuart-bt.dtbo or disable-bt.dtbo and place the files to a folder named "overlays" in your SD card.
Now, Let's start the lab.
The first step to control a CPU is to dump any message you want!
$ git clone https://github.com/champyen/rpiz_bare_metal.git
$ git checkout 4e769069
There are 4 major files in the example:
- bare_metal.c - the main example flow
- head.S - the glue code for entering the flow
- bare_metal.c - the main example flow
- printf.c - printf for RPi Zero's PL011 uart
- bare_metal.ld - linker script of the example
From the "Boot options in config.txt" of Raspberry Pi Document, we know that the default start address is 0x8000.
In bare_metal.ld, you can see the linker script:
OUTPUT_ARCH(arm)In Makefile, you can see the linking order is - head.o bare_metal.o printf.o.
SECTIONS {
. = 0x8000;
.text . : {
*(.text)
}
. = ALIGN(4);
.data . : {
*(.data)
}
. = ALIGN(4);
.bss . : {
*(.bss)
*(COMMON)
}
. = ALIGN(4);
.rodata . : {
*(.rodata)
*(.rodata.*)
}
}
Therefore, after bootcode.bin, it will jump to the first function in head.S.
In head.S:
.textBefore calling the demo function - bare_metal_start, head.S does only one thing - setup the address of 0x100000 as stack pointer.
_start:
ldr sp, =stack_top
bl bare_metal_start
stack_top: .word 0x100000
In bare_metal.c:
void printf(const char *fmt, ...);
void bare_metal_start(void)
{
printf("\n\n%s: Hello World! %s %s %d\n\n", __func__, __DATE__, __TIME__, __LINE__);
printf("enter busy loop\n");
while(1);
}
the bare_metal_start function calls the 'printf' implemented in printf.c to dump two messages.
In printf, the fundamental function is _putc.
#define PL011_BASE 0x20201000Please refer to Chapter 13 of the "Peripheral Specificaion" of BCM2835, the _putc just check the Transmit buffer status, and send out a char when Transmit buffer/register is empty.
#define PL011_DR (PL011_BASE + 0x00)
#define PL011_FR (PL011_BASE + 0x18)
#define _REG(x) *((unsigned int t *)(x))
void _putc(unsigned char c)
{
while( (_REG(PL011_FR) & 0x80) == 0);
_REG(PL011_DR) = c;
}