Finally I managed to get Chibios booting from the Pico's own flash storage. It wasn't that trivial as expected or at least not for me.
*kitchen language warning: I am not an expert in this area therefore I could sometimes use incorrect terminology.
So here is what I found:
The RP2040's boot sequence is burned into its ROM memory and therefore it cannot be omitted nor changed. In the processor controlled boot sequence, the top (or bottom?) 256 bytes of the flash storage is loaded searching for a correct second stage bootloader by verifying the checksum of the loaded bytes. If the checksum is not ok - there is no correct second stage bootloader in place - then the RP2040 enters into USB device mode. If the checksum is ok this second stage bootloader is executed initializing some further stuff (for example the Execute In Place (XIP) hardware). After finished its work the second stage bootloader jumps with the program counter to the next address in the flash (base_address + 256 bytes) to execute the user code.
There are a few different second stage bootloader variants in Pico SDK for (I think) different flash storage types. The right fit for the board is chosen and compiled at build time. There is also a Python script which adds the padding to it. For more details refer to the RP2040 datasheet.
So here is what I did:
I cloned the ChibiOS'
os/common/startup/ARMCMx/compilers/GCC/ld/RP2040_RAM.ld and added the following two sections from the Pico SDK's linker script and of course changed the necessary regions to point to the flash1:
Code: Select all
/*
* RP2040 memory setup.
*/
MEMORY
{
flash0 (rx) : org = 0x00000000, len = 16k /* ROM */
flash1 (rx) : org = 0x10000000, len = 2048k /* XIP (length TBD) */
flash2 (rx) : org = 0x00000000, len = 0
flash3 (rx) : org = 0x00000000, len = 0
flash4 (rx) : org = 0x00000000, len = 0
flash5 (rx) : org = 0x00000000, len = 0
flash6 (rx) : org = 0x00000000, len = 0
flash7 (rx) : org = 0x00000000, len = 0
ram0 (wx) : org = 0x20000000, len = 256k /* SRAM0 striped */
ram1 (wx) : org = 0x00000000, len = 256k /* SRAM0 non striped */
ram2 (wx) : org = 0x00000000, len = 0
ram3 (wx) : org = 0x00000000, len = 0
ram4 (wx) : org = 0x20040000, len = 4k /* SRAM4 */
ram5 (wx) : org = 0x20041000, len = 4k /* SRAM5 */
ram6 (wx) : org = 0x00000000, len = 0
ram7 (wx) : org = 0x20041f00, len = 256 /* SRAM5 boot */
}
/* For each data/text section two region are defined, a virtual region
and a load region (_LMA suffix).*/
/* Flash region to be used for exception vectors.*/
REGION_ALIAS("VECTORS_FLASH", flash1);
REGION_ALIAS("VECTORS_FLASH_LMA", flash1);
/* Flash region to be used for constructors and destructors.*/
REGION_ALIAS("XTORS_FLASH", flash1);
REGION_ALIAS("XTORS_FLASH_LMA", flash1);
/* Flash region to be used for code text.*/
REGION_ALIAS("TEXT_FLASH", flash1);
REGION_ALIAS("TEXT_FLASH_LMA", flash1);
/* Flash region to be used for read only data.*/
REGION_ALIAS("RODATA_FLASH", flash1);
REGION_ALIAS("RODATA_FLASH_LMA", flash1);
/* Flash region to be used for various.*/
REGION_ALIAS("VARIOUS_FLASH", flash1);
REGION_ALIAS("VARIOUS_FLASH_LMA", flash1);
/* Flash region to be used for RAM(n) initialization data.*/
REGION_ALIAS("RAM_INIT_FLASH_LMA", flash1);
/* RAM region to be used for Main stack. This stack accommodates the processing
of all exceptions and interrupts.*/
REGION_ALIAS("MAIN_STACK_RAM", ram4);
/* RAM region to be used for the process stack. This is the stack used by
the main() function.*/
REGION_ALIAS("PROCESS_STACK_RAM", ram4);
/* RAM region to be used for Main stack. This stack accommodates the processing
of all exceptions and interrupts.*/
REGION_ALIAS("C1_MAIN_STACK_RAM", ram5);
/* RAM region to be used for the process stack. This is the stack used by
the main() function.*/
REGION_ALIAS("C1_PROCESS_STACK_RAM", ram5);
/* RAM region to be used for data segment.*/
REGION_ALIAS("DATA_RAM", ram0);
REGION_ALIAS("DATA_RAM_LMA", flash1);
/* RAM region to be used for BSS segment.*/
REGION_ALIAS("BSS_RAM", ram0);
/* RAM region to be used for the default heap.*/
REGION_ALIAS("HEAP_RAM", ram0);
SECTIONS
{
.flash_begin : {
__flash_binary_start = .;
} > flash1
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > flash1
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
}
/* Generic rules inclusion.*/
INCLUDE rules_stacks.ld
INCLUDE rules_stacks_c1.ld
INCLUDE rules_code.ld
INCLUDE rules_data.ld
INCLUDE rules_memory.ld
SECTIONS
{
.flash_end : {
__flash_binary_end = .;
} > flash1
}
The .boot2 section comes from boot_stage2. Here I took an already built boot_stage2 file and added to the end of linking command (similarly to how they the Pico team does). Finally I had to change the .vectors section in the
os/common/startup/ARMCMx/compilers/GCC/ld/rules_code.ld to not be aligned which causes not to be placed right after the .boot2 section and to not starting up ChibiOS ( probably there are alternatives such as changing the second stage boot-loader's code to load the vectors from the aligned address).
After all of that, ChibiOS is successfully boots from the flash.
Szilveszter