So I’ve got a working bootloader (based on Openware MIDI bootloader). I wasn’t happy about both approaches proposed by ST example apps, because:
-
XiP means that QSPI can’t be written when firmware is runnning (and I need to store patches/settings)
-
Bootrom would waste ~512k SDRAM to store a copy of data from flash, even though most of it could be moved to faster RAM/TCM regions. Plus it would require writing SDRAM init with registers, too much pain.
The solution I came up with is based on creating a firmware header placed on flash before firmware itself. It’s populated with addresses of regions that need to be relocated from QSPI flash to various memories by startup file based on values exported from linker script.
Something like this:
struct FirmwareHeader {
uint32_t magic;
uint32_t section_0_start; /* ITCM */
uint32_t section_0_end;
uint32_t section_0_address;
uint32_t section_1_start; /* DTCM */
uint32_t section_1_end;
uint32_t section_1_address;
uint32_t section_2_start; /* Code + static */
uint32_t section_2_end;
uint32_t section_2_address;
uint32_t section_3_start; /* Reserved */
uint32_t section_3_end;
uint32_t section_3_address;
uint32_t section_4_start; /* Reserved */
uint32_t section_4_end;
uint32_t section_4_address;
};
This is more or less the same approach that is used for defining .data section relocation, except that in this case the copy is performed by bootloader rather than firmware startup code. This is required because we need to relocate .code and .isr_vector section for firmware before it runs.
Result is that firmware uses up to 256k from RAM2 for code and constants, ITCM for ISR vector and various frequentlly used code, DTCM for data/bss/stack. Some LUTs are left on flash, so it should be written to only if no patch is currently running.