-
Notifications
You must be signed in to change notification settings - Fork 550
Load Application Code To CPU
For SoCs with CPU(s), LiteX offers various approaches/methods to load and jump to application's code from the BIOS.
Running the application code from a embedded ROM is the fastest way to execute code and could be interesting when the code is small enough or on large devices where many block-rams are available at almost no cost, or simply when the execution speed is critical.
An boot ROM with contents from bootrom.bin
file, located at 0x20000000
can be added to the SoC with:
self.add_rom("bootrom", 0x20000000, 2**10, contents=get_mem_data("bootrom.bin", endianness="little")) # "little" for the default RISCV architectures selected by litex
Defining ROM_BOOT_ADDRESS
in the SoC will make it jump to the defined value at startup:
self.add_constant("ROM_BOOT_ADDRESS", 0x20000000)
Loading application code from serial is very convenient for development since provides an easy way to (re)load application code, boot to it and do quick iterations. LiteX supports very various serial interfaces that can be configured for the board with:
--uart_name=serial
: Regular UART at 115200Bauds, ~10-15KB/s upload.
--uart_name=serial --uart_baudrate=1e6
: Boosted UART at 1MBauds, ~80-90KB/s upload.
--uart_name=serial --uart_baudrate=3e6
: Overboosted UART at 3MBauds :), ~250KB/s upload.
--uart_name=usb_fifo
: UART through FT245 FIFO, ~300KB/s upload.
--uart_name=usb_acm
: UART through USB ACM Valenty USB core, ~160KB/s upload.
--uart_name=jtag_uart
UART through JTAG core, TBD upload.
--uart_name=etc... or create yours!
Serial boot is generally slow compared to Ethernet or SDCard boot but is the easiest to setup and usable on all boards so is still very interesting for easy use or when no alternative are available.
LiteX's litex_term tool is used to upload the application code and is directly installed with LiteX, available with the litex_term
command.
Serial boot has priority over the other boot methods and the BIOS will always try to boot from it. If litex_term
is running on the Host, it upload the binary(ies) at startup:
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
[LITEX-TERM] Received firmware download request from the device.
[LITEX-TERM] Uploading buildroot/Image to 0x40000000 (4545524 bytes)...
[LITEX-TERM] Upload complete (162.0KB/s).
[LITEX-TERM] Uploading buildroot/rootfs.cpio to 0x40800000 (8029184 bytes)...
[LITEX-TERM] Upload complete (163.3KB/s).
[LITEX-TERM] Uploading buildroot/rv32.dtb to 0x41000000 (1995 bytes)...
[LITEX-TERM] Upload complete (191.2KB/s).
[LITEX-TERM] Uploading emulator/emulator.bin to 0x41100000 (9600 bytes)...
[LITEX-TERM] Upload complete (161.1KB/s).
[LITEX-TERM] Booting the device.
[LITEX-TERM] Done.
Executing booted program at 0x41100000
--============= Liftoff! ===============--
...
Otherwise it will try the next available boot method, here for example the falling back to SDCard:
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
Timeout
Booting from SDCard in SPI-Mode...
...
- Uploading a single binary:
To load boot.bin
to default location (RAM at 0x4000000
), start litex_term
with:
litex_term /dev/ttyUSBX --kernel=boot.bin
- Uploading multiple binaries with a JSON description file:
When multiple binaries need to be loaded at different locations, a JSON description file with file/address tuples can be used, as for example the boot.json
file used for Linux-on-LiteX-VexRiscv:
{
"Image": "0x40000000",
"rv32.dtb": "0x40ef0000",
"rootfs.cpio": "0x41000000",
"opensbi.bin": "0x40f00000"
}
The last file/address tuple is used for the CPU jump (so here it will jump 0x41100000
).
It is also possible to optionally specify the r1/r2/r3
and addr
boot arguments:
{
"Image": "0x40000000",
"rv32.dtb": "0x40ef0000",
"rootfs.cpio": "0x41000000",
"opensbi.bin": "0x40f00000",
"bootargs": {
"r1": "0x00000000",
"r2": "0x00000000",
"r3": "0x00000000",
"addr": "0x41100000",
}
}
In this case, instead of defaulting to 0
, r1/r2/r3
will use the specified value and addr
will be used for the CPU jump.
To use a JSON file, start litex_term
with:
litex_term /dev/ttyUSBX --images=boot.json
On SoCs with Ethernet capability, loading application code with it can be very convenient for development since flexible and fast (on a Linux SoC, loading binaries only takes a few seconds).
Ethernet boot requires a TFTP server on the Host. LiteX uses 192.168.1.50
as default Host IP address, this default IP address can be changed by defining the REMOTEIPX
constants in the SoC:
self.add_constant("REMOTEIP1", 192)
self.add_constant("REMOTEIP2", 168)
self.add_constant("REMOTEIP3", 1)
self.add_constant("REMOTEIP4", X)
- Uploading multiple binaries with a JSON description file:
The BIOS will first try to load a boot.json
file over TFTP that will provide the list of files that need to be loaded and their location in the SoC, as for example the boot.json
file used in Linux-on-LiteX-VexRiscv
{
"Image": "0x40000000",
"rv32.dtb": "0x40ef0000",
"rootfs.cpio": "0x41000000",
"opensbi.bin": "0x40f00000"
}
- Uploading a single binary:
When boot.json
is available on the TFTP (or when boot.json is invalid), the BIOS will fallback to boot.bin
. So when only one binary is used, it could be simply named boot.bin
and put on the TFTP, the BIOS will loaded it and will jump to default location (RAM at 0x4000000
).
Example of Linux ethernet boot on Arty A7 board:
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
Timeout
Booting from network...
Local IP : 192.168.1.50
Remote IP: 192.168.1.100
Booting from boot.json...
Copying Image to 0x40000000... (4545524 bytes)
Copying rootfs.cpio to 0x40800000... (8029184 bytes)
Copying rv32.dtb to 0x41000000... (2167 bytes)
Copying emulator.bin to 0x41100000... (9600 bytes)
Executing booted program at 0x41100000
--============= Liftoff! ===============--
Most of the FPGA based systems are storing their bitstreams in SPIFlash. This SPIFlash is generally a lot larger than the FPGA bitstream size and thus allows storing software applications in the unused part.
To store an application in SPIFlash, the application first need to be prepared to add Length/CRC information to it:
python3 -m litex.soc.software.crcfbigen app.bin -o app.fbi --fbi --little
The .fbi
file can then be flashed to a specific location of the SPIFlash.
Let's say SPIFlash is mapped at address 0x30000000
of the SoC and application has been flashed at address 0x40000
of the SPIFlash, defining FLASH_BOOT_ADDRESS
in the SoC will make it jump to the defined value at startup:
self.add_constant("FLASH_BOOT_ADDRESS", 0x30040000)
Loading application code from SDCard/SATA is very interesting for standalone embedded systems and allow very large binaries.
The BIOS supports loading files from FAT16/FAT32 partitions and boot scheme is similar to Ethernet boot: File format is identical, BIOS will first look for a valid boot.json
file, will eventually fallback to boot.bin
if boot.json
is not available or not valid, and will fails if neither of these are found/valid.
Example of Linux SDCard boot on the OrangeCrab:
Have a question or want to get in touch? Our IRC channel is #litex at irc.libera.chat.
- Welcome to LiteX
- LiteX's internals
- How to
- Create a minimal SoC-TODO
- Add a new Board-TODO
- Add a new Core-WIP
- Add a new CPU-WIP
- Reuse-a-(System)Verilog,-VHDL,-Amaranth,-Spinal-HDL,-Chisel-core
- Use LiteX on the Acorn CLE 215+
- Load application code the CPU(s)
- Use Host Bridges to control/debug a SoC
- Use LiteScope to debug a SoC
- JTAG/GDB Debugging with VexRiscv CPU
- JTAG/GDB Debugging with VexRiscv-SMP, NaxRiscv and VexiiRiscv CPUs
- Document a SoC
- How to (Advanced)