-
Notifications
You must be signed in to change notification settings - Fork 32
Booting
While not an absolutely necessity, a bootloader such as UBoot is nice to have, mostly so that we can load binaries to test over serial or Ethernet instead of having to remove the SD card, write files, and re-insert the card every time we update the code. It may also be possible to add one of these transfers as a target in our Makefile for added convenience.
FreeBSD developer Oleksandr Tymoshenko (blog) has been working on porting UBoot to the Pi. The current binary in use is here. The instructions to install it are as follows:
- Download Raspian
dd if=<raspian-image> of=<sd-card-dev> bs=2M
- Extract the UBoot
.tar.gz
into the boot partition - Connect serial device/monitor and boot the Pi
To load Raspian:
-
fatload mmc 0:1 0x00200000 kernel.img
(loads the Raspian kernel) -
bootz
(boots zImage binaries)
To [possibly] load Xinu:
fatload mmc 0:1 0x00000000 xinu.bin
go 0
Currently, nothing interesting happens when we try to boot Xinu. This may be an issue with how we're loading the binary into memory or something more subtle. The xinu.bin
file was also tried, but it would not boot with any boot mode.
Fellow GitHub-er David Welch has been developing on the Pi "bare metal"-style, his notes are available on his project page.
Brain Dump, beware of possible inaccuracies and sudden changes
The ARM effectively boots from 0x8000, kernel.img is a raw binary that's loaded here.
XINU currently (1/22/2013) wants to run starting at 0x10000. From there, it branches to the address at 0x10020 which is 0x0x10044, the Reset_Handler routine. (this is confirmed via arm-...-objdump -D
on xinu.elf) UBoot also booted by jumping to this area (Starting application at 0x00010044 ...
) so I think the Pi does, in fact, use this address as the reset vector.
Sadly, we can't just .org 0x8000
then b 0x10020
in start.S
because we're already at 0x10000 by the time we get to assembling it. Changing compile/platforms/arm-qemu/ld.script
allows us to set the initial address to 0x8000. The behavior of .org
is a bit odd in that it doesn't seem to set or advance the current code address, but rather it seems to increment it. Using this, we can insert a small bit of code to keep the start.S code where it normally lives at 0x10000 and still boot at 0x8000:
/* the GPU starts the CPU at 0x8000 */
b Reset_Handler
.org 0x8000
Currently the Pi does boot our Xinu code from the SD card, but it is not able to get far enough to send anything over the UART to tell us that it booted. We know it does at least start executing because the following ARM code will light up the "ACT" ("OK" on some cases) LED:
/* turn on an LED */
ldr r0, GPFSEL1 //allocate registers poorly
ldr r1, GPSET0
ldr r2, GPCLR0
ldr r3, MASK0
ldr r4, MASK1
ldr r5, MASK2
ldr r7, LOOPCT
ldr r6, [r0, #0] //GPFSEL1 &= ~(7<<18)
and r6, r6, r3
str r6, [r0, #0]
ldr r6, [r0, #0] //GPFSEL1 |= (1<<18)
orr r6, r6, r4
str r6, [r0, #0]
loop:
ldr r6, [r2, #0] //GPCLR0 |= (1<<16)
orr r6, r6, r5
str r6, [r2, #0]
ldr r6, [r1, #0] //GPSET0 |= (1<<16)
orr r6, r6, r5
str r6, [r1, #0]
subs r7, r7, #1 //loop until r7 is zero
bne loop
b done
.align 2
GPFSEL1:
.word 0x20200004
GPSET0:
.word 0x2020001C
GPCLR0:
.word 0x20200028
MASK0:
.word 0xFFE3FFFF //~(7<<18)
MASK1:
.word 0x00040000 //(1<<18)
MASK2:
.word 0x00010000 //(1<<16)
LOOPCT:
.word 0x00FFFFFF //big
done:
Important register base locations with (bus addresses) and datasheet page numbers:
Peripheral address space: 0x20000000 - 0x20FFFFFF (0x7E000000 - 0x7EFFFFFF) (pg 6)
DMA engines use bus addresses (pg 6)
GPIO base 0x20200000 (0x7E200000) see pg 90
Interrupt 0x2000B000 (0x7E00B000) see pg 112
0x2000B200 IRQ basic pending
0x2000B204 IRQ pending 1
0x2000B208 IRQ pending 2
0x2000B20C FIQ control
0x2000B210 Enable IRQs 1
0x2000B214 Enable IRQs 2
0x2000B218 Enable Basic IRQs
0x2000B21C Disable IRQs 1
0x2000B220 Disable IRQs 2
0x2000B224 Disable Basic IRQs
PL011 (16650 variant) UART 0x20201000 (0x7E201000) see pg 175
0x20201000 DR - Data Register
0x20201004 RSRECR
0x20201018 FR - Flag register
0x20201020 ILPR - not in use
0x20201024 IBRD - Integer Baud rate divisor
0x20201028 FBRD - Fractional Baud rate divisor
0x2020102C LCRH - Line Control register
0x20201030 CR - Control register
0x20201034 IFLS - Interupt FIFO Level Select Register
0x20201038 IMSC - Interupt Mask Set Clear Register
0x2020103C RIS - Raw Interupt Status Register
0x20201040 MIS - Masked Interupt Status Register
0x20201044 ICR - Interupt Clear Register
0x20201048 DMACR DMA - Control Register
0x20201080 ITCR - Test Control register
0x20201084 ITIP - Integration test input reg
0x20201088 ITOP - Integration test output reg
0x2020108C TDR - Test Data reg
SP804 variant Timer 0x2000B000 (0x7E00B000) see pg 196
0x2000B400 Load
0x2000B404 Value (Read Only)
0x2000B408 Control
0x2000B40C IRQ Clear/Ack (Write Only)
0x2000B410 RAW IRQ (Read Only)
0x2000B414 Masked IRQ (Read Only)
0x2000B418 Reload
0x2000B41C Pre-Divider (Not in real 804!)
0x2000B420 Free running counter (Not in real 804!)
Also take a look at the rather long errata page about the datasheet.
This is an example UART initialization routine for a PC clone:
write(UA4_FCR, 0);
write(UA4_FCR, UA5_FCR_FIFO_EN);
write(UA4_FCR, (UA5_FCR_FIFO_EN | UA5_FCR_RX_SOFT_RESET));
write(UA4_FCR, (UA5_FCR_FIFO_EN | UA5_FCR_RX_SOFT_RESET | UA5_FCR_TX_SOFT_RESET));
write(UA4_IER, 0);
write(UA4_LCR, UA4_LCR_BANK1);
write(UA4_LBGD_L, BAUD_LOW_BYTE( BAUD_9600));
write(UA4_LBGD_H, BAUD_HIGH_BYTE( BAUD_9600));
write(UA4_LCR, (UA4_LCR_BANK0 | UA4_LCR_BITS_8 | UA4_LCR_1_STOP_BIT | UA4_LCR_NO_PARITY));
write(UA4_MCR, (UA4_MCR_DTR | UA4_MCR_RTS | UA4_MCR_ISEN));