Overview
All current Cortex-M processors get out of reset as follows:
- the initial stack pointer value is at address 0 of the vector table;
- the initial program counter value is at address 4 of the vector table.
The processor’s hardware will initialise the stack pointer register and the program counter register using these values.
Astrobe puts the two values at the beginning of the binary file produced by the linker. However, the RPx MCUs don’t not use these values for the stack pointer and the program counter right after reset – they first execute start-up code in the boot rom.
Stepping over the initial steps after reset, the boot rom code will eventually execute our program code uploaded as UF2 file, as created from Astrobe’s .bin
file, and use the aforementioned initial values. This is done differently on the RP2040 and the RP2350.
RP2040
On the RP2040, the boot code loads the first 256 bytes in flash memory from address 010000000H
into SRAM.1 This code’s responsibility is to configure and enable Execute In Place (XIP). It must be prepended to Astrobe program code.2
This prepended code also must, as its last instructions, load the actual program’s initial stack pointer value and program counter value into their respective registers. The easiest way is to load the program code in Astrobe’s .bin
file at flash address 010000100H
, so that address 010000100H
contains the initial stack pointer value, and address 010000104H
the initial program counter value. That’s where the prepended code finds them.
From there on, the initialisation sequence of the Oberon program starts to execute.
RP2350
On the RP2350, the boot code in the ROM takes care of configuring and enabling Execute In Place (XIP), so there is no need to prepend any initialisation code. However, the RP2350 has a plethora of different possible modes and memory layouts to load and execute code, including code signing and address translation. Therefore, the boot code needs to get the corresponding configuration data, which is provided via meta data blocks in the UF2 file, which must be added to Astrobe’s program code.
These meta data blocks can be prepended or inserted in different ways, as described in chapter 5 of the datasheet. Astrobe for RP2350 uses an insertion technique, while makeuf2
prepends the meta data.
Let’s look at the two techniques.
Astrobe for RP2350
Module LinkOptions
contains a what the datasheet calls Minimum ARM IMAGE_DEF meta data block (section 5.9.5.1) in its body as data (using SYSTEM.DATA
). That is, the linker will put the meta data block embedded into the binary, where the bootrom code will find and interpret it (the first meta data block must be in the first 4k of flash memory).
This IMAGE_DEF
item does not contain any information about the vector table location, and with it the initial stack pointer value and code entry address as explained above. Therefore, the vector table is assumed to be at address 010000000H
.
The bootrom code will find the two values at 010000000H
(stack pointer) and 010000004H
(entry address), load them into their corresponding registers, and from there on the initialisation sequence of the Oberon program starts to execute. The program runs in Secure, privileged thread mode.
makeuf2
Utility makeuf2
prepends the meta data block in the first 256 data bytes (a UF2 data block) at address 010000000
, and adds the program code in the subsequent UF2 blocks, starting from address 010000100H
, which is the address of the vector table with the initial values of the stack pointer and program counter.
To inform the bootrom code about this location, makeuf2
inserts the same IMAGE_DEF
item as Astrobe, plus an additional item that defines the location of the vector table to be at 010000100H
– unsurprisingly called a VECTOR_TABLE
item.
Now the bootrom code finds the initial stack pointer and program counter values at 010000100H
and 010000104H
, respectively, loads them into their corresponding registers, and from there on the initialisation sequence of the Oberon program starts to execute. The program runs in Secure, privileged thread mode.
Quick Preliminary Assessment
Both techniques work equally well, and are indistinguishable from a practical point of view.3
That is, for the very basic use case with the most simple flash memory layout as possible. As soon as we’ll venture into more complex scenarios, like A/B partitioning with roll-back option, which require different meta data blocks, possibly added to the image in different locations, the makeuf2
approach may prove to be better suited, but that remains to be seen. More advanced scenarios really require exploration and evaluation first with regard their practical value. So we’ll cross that bridge when we get there, I guess. :)
The release document for Astrobe for RP2350 requires to provide customised versions of LinkOptions
in this case, but this is not as straightforward as it sounds,4 and appending meta data blocks to a program is not possible.5 It was after testing the LinkOptions
technique with a customised version that I decided to rewrite abin2uf22
as makeuf2
, since I think I gain flexibility, and avoid the complications regarding reconfiguring the Astrobe library. But I’ll happily abandon makeuf2 rp2350
as soon as I don’t see any advantage (anymore) of this approach. You can always count on my laziness.
Note that the meta data in module LinkOptions
does not interfere with the meta data block prepended by makeuf2
– the boot code just ignores it after finding the prepended block.
-
Obviously, this code cannot run from flash, as XIP is not working yet. ↩︎
-
The utility
abin2uf2
does this, or Astrobe for RP2040 does it directly. ↩︎ -
Just don’t forget to use the appropriate config.ini file for each case – see section Astrobe Configuration at the bottom of this document. ↩︎
-
This requires removing
LinkOptions
from the Astrobe library, putting the customisedLinkOptions
into the program directory, and creating a project-specific config.ini file that lists the program directory as part of the library search path. MovingLinkOptions
could be scripted, and project-specific config files are OK in my book. So I am not fundamentally discounting that technique, and will revisit it. But as framework developer I find it difficult to ask the user to modify their Astrobe library, to, say, build an example program, not least in the light of version management. And if I need to run a script to modify the Astrobe library before building a program, then again a script to restore the Astrobe library thereafter, I can also revert to just usingmakeuf2
in the first place. ↩︎ -
Then there’s the infamous RP2350-E10 error with the current revision of the chip (A2), which requires to prepended a special UF2 block. ↩︎