Simplified Boot Flow for the Cyclone V
by [lockbox]The Cyclone V Technical Reference Manual details the boot flow for the common “running linux to boot your FPGA” usecase like so:
But because everything after the BootROM is classified as “user software,” there is not much described in the manual.
What the manual describes as “user software” still encompasses an entire software stack built, maintained, and packaged by Altera, but can be overriden by the user. As far as behavior and code that users cannot overwrite go, we are limited to the BootROM.
The BootROM is described in short on page A-5
, and the state machine is fully diagrammed on
page A-27
.
- BootROM is 64kb in size
- Located in on-chip ROM at address range
0xFFFD_0000
to0xFFFD_FFFF
- The purpose is to determine the boot source, initialize the HPS after reset, and jump to the PreLoader
There is a little more nuance if the PreLoader needs to be loaded from flash into RAM. RTFM for more.
Executing User Software
The BootROM’s entire purpose is to call the PreLoader, and setup the initial environment necessary for the PreLoader to be able to load the BootLoader (u-boot) which in turn loads the Operating System (Linux).
Page A-31
details the entry state of the processor on calling the PreLoader:
- I-cache disabled
- Branch Predictor enabled
- D cache disabled
- MMU disabled
- FPU enabled
- NEON enabled
- Processor is in ARM
supervisor
mode
Register state:
r0
: pointer to shared memory block used to pass information to PreLoader (located in top 4kb of on-chip RAM)r1
: length of the shared memoryr2
: set to0x0
r3
: reserved
Other system state:
- BootROM is mapped to address
0x0
L4
watchdog timer active and toggled- Reset cause saved to
stat
register of the Reset Manager - Shared memory is setup as described in table
A-18
Before the PreLoader can actually be executed, the BootROM validates the PreLoader image after mapping
it into memory at address 0xFFFF_0000
-> 0xFFFF_F000
.
PreLoader Image Requirements
- Validation word matches magic
0x3130_5341
(“10SA”) - Header version matches
- Header program length (in 32-bit words) from offset 0 to the end of the code area
- Not including exception vectors or CRC
- Checksum of all bytes in the header from offset
0x40
to0x49
- Maximum size of 60 kb (on-chip RAM - 4kb reserved RAM)
PreLoader Actions
The manual very helpfully describes the PreLoader functions as “user-defined, however typical functions include …”
But also describes a detailed state machine + order of operations on page A-29
.
TLDR;
- Freeze all I/O banks
- Reset peripherals
- Setup clocks
- Unfreeze all I/O banks
- L3/L4 configuration
- Timer + UART initialization
- SDRAM initialization
- Boot next stage
PreLoader Memory State
Probably the most helpful thing in the manual so far is this image that shows the state of the memory regions.
In the paragraph before this in the manual, it discusses that until the L3
interconnect remap bit 0 is
set high, the exception vectors are still pointing to the exceptions handlers provided by the BootROM.
Setting this bit high remaps the on-chip RAM that the PreLoader is executing with into the 0 page to
successfully splat onto the BootROM handlers.
Comparing this to the ARM Cortex-A booting a bare-metal system guide, we can start to see some similarities and start to line things up at least as far as code-layout goes.
The PreLoader (after settting up the respective peripherals), then calls the BootLoader.
Bootloader execution (u-boot)
u-boot is going to do a few main things:
- Get loaded at
0xFFFF_0000
- Load u-boot scripts that are present
- Find the device tree it is supposed to use
- Execute the kernel with the device tree passed as a parameter
In the default provided-from-altera-open-source u-boot configuration, there are a few things to note in the `KConfig` for the target:
- booting with SPL u-boot (full blown u-boot), with
0xFFFF_0000
start address - default malloc len is size
0x4000_0000
(1GB) - u-boot expects to be at specific offsets into the SPI flash device memory
Looking at the default device-tree (also provided by Altera),
we can get the list of all the device drivers we need to bake into our linux kernel image (via the “compatible” attribute in each section),
the size of memory via the memory
section, and the default peripherals setups of everything else. Note that you can also describe SRAM
by using the sram
section (mmio-sram
is the correct driver aiui), and the sdram
/ mmc
blocks as well.
From there, any u-boot
scripts or environment will dictate how linux (and your fpga :p) is actually going to get loaded and executed.
Nowadays you’ll probably run into a `bootz` (docs).
Operating System Boot
If you get the default zlinux, then it will self unpack into memory, and then execute the `init` command as specified in your kernel command line environment.
And it’s done and booted! :D