November 1, 2023

Raspberry Pi Pico - build the bootrom

Why do this? You ought to ask this, since the bootrom is already built and present in an immutable form in every RP2040 chip. For me the reason is that I am hoping that this is an "easy" way to begin dealing with the SDK, and in particular TinyUSB.
Away we go ...
git clone https://github.com/raspberrypi/pico-bootrom
cd pico-bootrom
git submodule update --init
Submodule 'pico_sdk' (https://github.com/raspberrypi/pico-sdk.git) registered for path 'pico_sdk'
Cloning into '/u1/Projects/rp2040/pico-bootrom/pico_sdk'...
Submodule path 'pico_sdk': checked out '26653ea81e340cacee55025d110c3e014a252a87'
Note that this pulls in pico-sdk, but there is no mention of TinyUSB.
mkdir build
cd build
cmake ..
Pico SDK is located at /u1/Projects/rp2040/pico-bootrom/pico_sdk
Defaulting PICO_PLATFORM to rp2040 since not specified.
Defaulting PICO platform compiler to pico_arm_gcc since not specified.
-- Defaulting build type to 'Release' since not specified.
PICO compiler is pico_arm_gcc
PICO_GCC_TRIPLE defaulted to arm-none-eabi
-- The C compiler identification is GNU 13.2.0
-- The CXX compiler identification is GNU 13.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/arm-none-eabi-gcc
Defaulting PICO target board to pico since not specified.
Using board configuration from /u1/Projects/rp2040/pico-bootrom/pico_sdk/src/boards/include/boards/pico.h
-- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
-- Found Python3: /usr/bin/python3.11 (found version "3.11.6") found components: Interpreter
-- Configuring done (0.6s)
-- Generating done (0.0s)
-- Build files have been written to: /u1/Projects/rp2040/pico-bootrom/build
This has generated a 565 line Makefile in the build directory. Now we should just type "make". (We could type "make -j4" to run lots of things in parallel. I see little point in that, this will go fast enough by itself. What I do though is to set VERBOSE and capture the output in a file for study.
make VERBOSE=XXX >make.log
CMake Warning (dev) at CMakeLists.txt:1 (PROJECT):
  cmake_minimum_required() should be called prior to this top-level project()
  call.  Please see the cmake-commands(7) manual for usage documentation of
  both commands.
This warning is for project developers.  Use -Wno-dev to suppress it.

/usr/lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: bootrom.elf section `.text' will not fit in region `ROM'
/usr/lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: region `ROM' overflowed by 16384 bytes
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/bootrom.dir/build.make:349: bootrom.elf] Error 1
make[1]: *** [CMakeFiles/Makefile2:764: CMakeFiles/bootrom.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
So, we get some weird warning from the cmake system, but then a serious error. The error essentially tells us that the code we generated will not fit into the space allocated for the ROM. What could have happened? Clearly the Pico engineers once used this to build the rom code. Probably some aspect of the build system has changed since then. Maybe gcc 13.2.0 generates more bloated code?

Lets look at bootrom/bootrom.ld, which is the linker script. Here the key line to consider is this one:

    ROM(rx) : ORIGIN = 0x00000000, LENGTH = 16K
Let's just hack this and tell it that we have 64K to hold the rom code. This ought to let the build finish and then we can look things over and try to figure out why the current build doesn't fit into 16K.
make clean
make VERBOSE=XXX >make2.log
Indeed this works. We get no errors other than the weird cmake warning. The new final steps in the build log are:
/usr/bin/arm-none-eabi-objcopy -Obinary bootrom.elf bootrom.bin
/usr/bin/arm-none-eabi-objdump -h bootrom.elf >bootrom.dis
/usr/bin/arm-none-eabi-objdump -d bootrom.elf >>bootrom.dis
/usr/bin/arm-none-eabi-objcopy -Oihex bootrom.elf bootrom.hex
/usr/bin/python3.11 /u1/Projects/rp2040/pico-bootrom/bin2hex -32 bootrom.bin bootrom.h32

What went wrong?

The bootrom builds to go to address 0. It should then live in addresses 0x0000 to 0x3fff. In fact 16K is 16384, exactly the amount the error says it overflowed by. Did it in fact double in size?

I look at bootrom.dis, and it does overflow (but only by 56 bytes), we see:

00003ffc :
    3ffc:       0112 0110 0000 4000 2e8a 0003 0100 0201     .......@........
    400c:       0103                                        ..

0000400e <_picoboot_cmd_transfer_type>:
    400e:       0104                                        ..

00004010 :
    4010:       0f6c 5010 0ecc 5010                         l..P...P

00004018 <_msc_endpoints.3>:
    4018:       0ae0 5010 0b0c 5010                         ...P...P

00004020 :
    4020:       0209 0037 0102 8000 09fa 0004 0200 0608     ..7.............
    4030:       0050 0507 0281 0040 0700 0205 4002 0000     P.....@......@..
    4040:       0409 0001 ff02 0000 0700 0305 4002 0000     .............@..
    4050:       0507 0284 0040                               ....@..

00004057 :
    4057:       00              .byte   0x00
So, the error is sort of telling the truth, but the 16384 number is misleading. It is of no interest to me whatsoever to figure out why the code no longer fits and what can be done to use the current gcc to build it so it does, this is just a distraction. What I would like to do is to understand how the build process is set up, in particular in regard to TinyUSB.

The build process

My "make.log" is only 136 lines. Some of the lines are very long with millions of options being passed to the compiler. The following is a butchered outline of what the build actually includes.
There are only 17 actual source files, only 4 seem to deal with USB.

There are lots of curious aspects to the build process, many of which are omitted here.

/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/bootrom_rt0.S.obj   -c /u1/Projects/rp2040/pico-bootrom/bootrom/bootrom_rt0.S
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/bit_functions.S.obj   -c /u1/Projects/rp2040/pico-bootrom/bootrom/bit_functions.S
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/bootrom_main.c.obj -c /u1/Projects/rp2040/pico-bootrom/bootrom/bootrom_main.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/bootrom_misc.S.obj   -c /u1/Projects/rp2040/pico-bootrom/bootrom/bootrom_misc.S
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/program_flash_generic.c.obj -c /u1/Projects/rp2040/pico-bootrom/bootrom/program_flash_generic.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/usb_boot_device.c.obj -c /u1/Projects/rp2040/pico-bootrom/bootrom/usb_boot_device.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/virtual_disk.c.obj -c /u1/Projects/rp2040/pico-bootrom/bootrom/virtual_disk.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/async_task.c.obj -c /u1/Projects/rp2040/pico-bootrom/bootrom/async_task.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/mufplib.S.obj   -c /u1/Projects/rp2040/pico-bootrom/bootrom/mufplib.S
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/bootrom/mufplib-double.S.obj   -c /u1/Projects/rp2040/pico-bootrom/bootrom/mufplib-double.S

/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/usb_device_tiny/runtime.c.obj -c /u1/Projects/rp2040/pico-bootrom/usb_device_tiny/runtime.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/usb_device_tiny/usb_device.c.obj -c /u1/Projects/rp2040/pico-bootrom/usb_device_tiny/usb_device.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/usb_device_tiny/usb_msc.c.obj -c /u1/Projects/rp2040/pico-bootrom/usb_device_tiny/usb_msc.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/usb_device_tiny/usb_stream_helper.c.obj -c /u1/Projects/rp2040/pico-bootrom/usb_device_tiny/usb_stream_helper.c

/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/pico_sdk/src/rp2_common/pico_platform/platform.c.obj -c /u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/pico_platform/platform.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/pico_sdk/src/rp2_common/hardware_sync/sync.c.obj -c /u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/hardware_sync/sync.c
/usr/bin/arm-none-eabi-gcc -o CMakeFiles/bootrom.dir/pico_sdk/src/rp2_common/hardware_claim/claim.c.obj -c /u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/hardware_claim/claim.c

/usr/bin/cmake -E cmake_link_script CMakeFiles/bootrom.dir/link.txt --verbose=XXX

/usr/bin/arm-none-eabi-gcc $(LINK_OPT)
CMakeFiles/bootrom.dir/bootrom/bootrom_rt0.S.obj 
CMakeFiles/bootrom.dir/bootrom/bit_functions.S.obj 
CMakeFiles/bootrom.dir/bootrom/bootrom_main.c.obj 
CMakeFiles/bootrom.dir/bootrom/bootrom_misc.S.obj 
CMakeFiles/bootrom.dir/bootrom/program_flash_generic.c.obj 
CMakeFiles/bootrom.dir/bootrom/usb_boot_device.c.obj 
CMakeFiles/bootrom.dir/bootrom/virtual_disk.c.obj 
CMakeFiles/bootrom.dir/bootrom/async_task.c.obj 
CMakeFiles/bootrom.dir/bootrom/mufplib.S.obj 
"CMakeFiles/bootrom.dir/bootrom/mufplib-double.S.obj" 
CMakeFiles/bootrom.dir/usb_device_tiny/runtime.c.obj 
CMakeFiles/bootrom.dir/usb_device_tiny/usb_device.c.obj 
CMakeFiles/bootrom.dir/usb_device_tiny/usb_msc.c.obj 
CMakeFiles/bootrom.dir/usb_device_tiny/usb_stream_helper.c.obj 
CMakeFiles/bootrom.dir/pico_sdk/src/rp2_common/pico_platform/platform.c.obj 
CMakeFiles/bootrom.dir/pico_sdk/src/rp2_common/hardware_sync/sync.c.obj 
CMakeFiles/bootrom.dir/pico_sdk/src/rp2_common/hardware_claim/claim.c.obj 
-o bootrom.elf 

/usr/bin/arm-none-eabi-objcopy -Obinary bootrom.elf bootrom.bin
/usr/bin/arm-none-eabi-objdump -h bootrom.elf >bootrom.dis
/usr/bin/arm-none-eabi-objdump -d bootrom.elf >>bootrom.dis
/usr/bin/arm-none-eabi-objcopy -Oihex bootrom.elf bootrom.hex
/usr/bin/python3.11 /u1/Projects/rp2040/pico-bootrom/bin2hex -32 bootrom.bin bootrom.h32

--- compile options
CC_OPT =
-DBOOTROM_ONLY_SIZE_HACKS -DCOMPRESS_TEXT -DGENERAL_SIZE_HACKS -DNDEBUG -DPICO_BOARD=\"pico\" -DPICO_BUILD=1
-DPICO_NO_HARDWARE=0 -DPICO_ON_DEVICE=1 -DUSB_MAX_ENDPOINTS=5 -DUSE_16BIT_ROM_FUNCS -DUSE_BOOTROM_GPIO -DUSE_CLZ32 -DUSE_CTZ32
-DUSE_HW_DIV -DUSE_PICOBOOT -DUSE_POPCOUNT32 -DUSE_REVERSE32
-I/u1/Projects/rp2040/pico-bootrom/bootrom -I/u1/Projects/rp2040/pico-bootrom/usb_device_tiny -I/u1/Projects/rp2040/pico-bootrom/build
-I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/hardware_resets/include -I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2040/hardware_regs/include 
-I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/hardware_base/include -I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/common/pico_base/include 
-I/u1/Projects/rp2040/pico-bootrom/build/generated/pico_base -I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/boards/include 
-I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/pico_platform/include -I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2040/hardware_structs/include 
-I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/hardware_sync/include -I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/rp2_common/hardware_claim/include 
-I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/common/boot_uf2/include -I/u1/Projects/rp2040/pico-bootrom/pico_sdk/src/common/boot_picoboot/include 
-march=armv6-m -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -fno-jump-tables -g -Os -ffunction-sections -fdata-sections -flto -nostdlib -nodefaultlibs -Wall -Wextra
--param max-completely-peel-times=4 -MD -MT

--- link options

LINK_OPT = 
-march=armv6-m -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -Wl,--build-id=none -fno-jump-tables -g -Os
-ffunction-sections -fdata-sections -flto -nostdlib -nodefaultlibs -Wl,--build-id=none -Wl,--undefined=isr_irq5 -Wl,--gc-sections
--specs=nosys.specs -nostartfiles -Wall -Wextra --param max-completely-peel-times=4
-Wl,--script=/u1/Projects/rp2040/pico-bootrom/bootrom/bootrom.ld -Wl,-Map=bootrom.elf.map 


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org