Here is code I use. Some of it won't apply to you. In this product a customer can write "overlays" which execute at base+32k.
// Interrupts are still disabled at this point.
// Configure the Vector Table location to its new place at the start of the customer image.
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET_CUST; // set new VT location
// Fill user stack and load SP to top of *real* RAM per CPU type.
// Note the 437 stack is filled with 'S' while the 417 stack is filled with 's'
// Setting SP throws away the current stack frame so local variables cannot be used anymore!
// The 417 stack fills are shorter because b_memset needs to be able to return; at
// that position on the stack frame about 700 bytes room is needed.
extern char _top;
static char* stack_base; // base of stack (from linkfile, for 417)
stack_base = &_top;
extern char _Stack_Size; // size of stack (from linkfile)
static char* stack_size;
stack_size = &_Stack_Size;
#ifdef EXTRA_64K
if (B_g_dev_id==437)
{
B_memset((char*)stack_base+65536,'S',(uint32_t)stack_size);
asm volatile ("ldr sp, = _estack+65536 \n");
}
else
{
B_memset((char*)stack_base,'s',(uint32_t)stack_size-1024);
asm volatile ("ldr sp, = _estack \n");
}
#else
B_memset((char*)stack_base,'s',((uint32_t)stack_size)-1024);
asm volatile ("ldr sp, = _estack \n");
#endif
// Enter the normal main() (via main_stub) in the customer application program.
// Note that we can get here with the serial FLASH totally zeroed, if above all-zero code was used
// We do "jmp main()" even though the C code further below also works, in case main() is not
// in the symbol table.
extern char _customer_code_base; // This is base+32k
uint32_t jmpaddr = (uint32_t)&_customer_code_base; // Weird stuff to get a usable value
jmpaddr |= 1; // Bit 0 = 1 for thumb code
asm("bx %0"::"r" (jmpaddr)); // jmp (bx) etc
If you use a loader which runs out of RAM (so it can program CPU FLASH with no restrictions) end it with a reboot so you start with a freshly reset CPU
// Reboot the unit. This function is copied in a number of places.
// Probably a good idea to wait say 100ms for any SPI ops to finish.
static inline void reboot(void)
{
// Ensure all outstanding memory accesses including buffered write are completed before reset
__ASM volatile ("dsb 0xF":::"memory");
// Keep priority group unchanged
SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk );
__ASM volatile ("dsb 0xF":::"memory");
// wait until reset
for(;;)
{
__NOP();
}
}