August 29, 2023

Raspberry Pi Pico - the SDK - second stage boot

By building the blink example from scratch with VERBOSE=xxx, I was able to get the following. This is my reformatting of the build log and could have errors due to my aggressive editing.
cd /u1/Projects/rp2040/pico-examples/build

make  -f pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/build.make pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/depend

/usr/bin/cmake -E cmake_depends "Unix Makefiles" /u1/Projects/rp2040/pico-examples /u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2 /u1/Projects/rp2040/pico-examples/build /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2 /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/DependInfo.cmake "--color="

Dependee "/u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/DependInfo.cmake" is newer than depender "/u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/depend.internal".
Dependee "/u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/depend.internal".

make  -f pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/build.make pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/build
cd /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2

/usr/bin/arm-none-eabi-gcc -DPICO_BOARD=\"pico\" -DPICO_BUILD=1 -DPICO_NO_HARDWARE=0 -DPICO_ON_DEVICE=1 -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/asminclude -I/u1/Projects/rp2040/pico-sdk/src/rp2040/hardware_regs/include -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/hardware_base/include -I/u1/Projects/rp2040/pico-sdk/src/common/pico_base/include -I/u1/Projects/rp2040/pico-examples/build/generated/pico_base -I/u1/Projects/rp2040/pico-sdk/src/boards/include -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/pico_platform/include -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/include -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -o CMakeFiles/bs2_default.dir/compile_time_choice.S.obj   -c /u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/compile_time_choice.S

/usr/bin/cmake -E cmake_link_script CMakeFiles/bs2_default.dir/link.txt --verbose=xxx
/usr/bin/arm-none-eabi-gcc -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -Wl,--build-id=none --specs=nosys.specs -nostartfiles -Wl,--script=/u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/boot_stage2.ld -Wl,-Map=bs2_default.elf.map CMakeFiles/bs2_default.dir/compile_time_choice.S.obj -o bs2_default.elf 

/usr/bin/arm-none-eabi-objdump -h /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.elf > bs2_default.dis
/usr/bin/arm-none-eabi-objdump -d /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.elf >> bs2_default.dis

make  -f pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default_padded_checksummed_asm.dir/build.make pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default_padded_checksummed_asm.dir/depend
cd /u1/Projects/rp2040/pico-examples/build

/usr/bin/cmake -E cmake_depends "Unix Makefiles" /u1/Projects/rp2040/pico-examples /u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2 /u1/Projects/rp2040/pico-examples/build /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2 /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default_padded_checksummed_asm.dir/DependInfo.cmake "--color="

make  -f pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default_padded_checksummed_asm.dir/build.make pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default_padded_checksummed_asm.dir/build
cd /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2

/usr/bin/arm-none-eabi-objcopy -Obinary /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.elf /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.bin

/usr/bin/python3.11 /u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/pad_checksum -s 0xffffffff /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.bin /u1/Projects/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default_padded_checksummed.S
The key line is this:
/usr/bin/arm-none-eabi-gcc -DPICO_BOARD=\"pico\" -DPICO_BUILD=1 -DPICO_NO_HARDWARE=0 -DPICO_ON_DEVICE=1 -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/asminclude -I/u1/Projects/rp2040/pico-sdk/src/rp2040/hardware_regs/include -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/hardware_base/include -I/u1/Projects/rp2040/pico-sdk/src/common/pico_base/include -I/u1/Projects/rp2040/pico-examples/build/generated/pico_base -I/u1/Projects/rp2040/pico-sdk/src/boards/include -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/pico_platform/include -I/u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/include -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -o CMakeFiles/bs2_default.dir/compile_time_choice.S.obj   -c /u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2/compile_time_choice.S
And the key source file is:
cd /u1/Projects/rp2040/pico-sdk/src/rp2_common/boot_stage2
compile_time_choice.S
This simply includes some other file, given by PICO_BOOT_STAGE2_ASM
And this is defined in include/boot_stage2/config.h as follows:
#define PICO_BOOT_STAGE2_ASM __PICO_XSTRING(__PICO_CONCAT1(_BOOT_STAGE2,.S))
I was mistaken by this on my first pass thinking it would pull in boot2_generic_03h.S -- this is not the case. It pulls in boot2_at25sf128a.S !!

The code includes "pico/asm_helper.S" which has:

.macro pico_default_asm_setup
.syntax unified
.cpu cortex-m0plus
.thumb
.endm
It also includes boot2_helpers/exit_from_boot2.S, which is as follows:
#include "hardware/regs/m0plus.h"

// If entered from the bootrom, lr (which we earlier pushed) will be 0,
// and we vector through the table at the start of the main flash image.
// Any regular function call will have a nonzero value for lr.
check_return:
    pop {r0}
    cmp r0, #0
    beq vector_into_flash
    bx r0
vector_into_flash:
    ldr r0, =(XIP_BASE + 0x100)
    ldr r1, =(PPB_BASE + M0PLUS_VTOR_OFFSET)
    str r0, [r1]
    ldmia r0, {r0, r1}
    msr msp, r0
    bx r1
The actual stage2 boot code is:
regular_func _stage2_boot
    push {lr}

    ldr r3, =XIP_SSI_BASE                // Use as base address where possible

    // Disable SSI to allow further config
    movs r1, #0
    str r1, [r3, #SSI_SSIENR_OFFSET]

    // Set baud rate
    movs r1, #PICO_FLASH_SPI_CLKDIV
    str r1, [r3, #SSI_BAUDR_OFFSET]

    ldr r1, =(CTRLR0_XIP)
    str r1, [r3, #SSI_CTRLR0_OFFSET]

    ldr r1, =(SPI_CTRLR0_XIP)
    ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET)
    str r1, [r0]

    // NDF=0 (single 32b read)
    movs r1, #0x0
    str r1, [r3, #SSI_CTRLR1_OFFSET]

    // Re-enable SSI
    movs r1, #1
    str r1, [r3, #SSI_SSIENR_OFFSET]

// We are now in XIP mode. Any bus accesses to the XIP address window will be
// translated by the SSI into 03h read commands to the external flash (if cache is missed),
// and the data will be returned to the bus.

// Pull in standard exit routine
#include "boot2_helpers/exit_from_boot2.S"
Note that nothing here is copying anything into SRAM. The game is to set up the XIP (execute in place) scheme to work with whatever external flash chip is present.

The norm is to execute from flash via XIP. If there is some desire to move code into SRAM so it will run faster, that is entirely upon the user to set up.


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org