0
votes

I've got a question. I'm developing an IAP (In-Application Programming) tool for my STM32F446RE board and I'm stuck. I've developed all the necessary utilities in order to let to the microcontroller to receive a binary (.bin) compiled file from a GUI, write it on a specific flash memory sector and execute it. My problem comes when, from the uploaded code, I want to jump again to the bootloader that is stored on the flash memory sector 0, I see that the code does not jump to the bootloader but, instead, it continues the execution of the user application code. I've debugged the code and I seen that all the addresses (the msp and the reset handler) of the bootloader code are correctly set and they are different compared to the ones of the uploaded code.

The flow that i want to achieve is the following:

1 --> Execute the bootloader code stored on sector 0 (starting at address 0x0800 0000, when an interrupt from the User button is received) and write the newly received code into the sector 2 (starting at address 0x0800 8000)

2 --> set the msp address (@0x0800 8000) and the reset handler address (0x0800 8004)

3 --> jump to the reset handler address of the new code (@0x0800 8004)

4 --> execute the new uploaded code.

5 --> during the user code execution, if an interrupt is received (from user push button) then set the bootloader msp address, the reset handler and jump to the bootloader

6 --> repeat again from step one.

This is the code used to jump from the bootloader to the user application:

    IAP_loadProgram(&data);

//pointer to the user application reset handler address
void (*user_resetHandler)(void);


//set the user application MSP address (user application starts on the flash SECTOR2
uint32_t msp_addr = *(volatile uint32_t *)APPLICATION_ADDRESS;

__set_MSP(msp_addr);

//Set now the addres of the reset handler
uint32_t resetAddr = *(volatile uint32_t *)(APPLICATION_ADDRESS + 4);

user_resetHandler = (void *)resetAddr;

//When there, the bootloader sector will be leaved and the user code execution starts
user_resetHandler();

Finally, the code used to jump from the user application code to the bootloader is:

  if(toBootloader){
      toBootloader = 0;

      //pointer to the user application reset handler address
      void (*bootLoader_resetHandler)(void);

      //set the user application MSP address (user application starts on the flash SECTOR2
      uint32_t msp_addr = *(volatile uint32_t *)BOOTLOADER_ADDRESS;

     __set_MSP(msp_addr);

      //Set now the address of the reset handler
      uint32_t bootLoaderResetAddr = *(volatile uint32_t *)(BOOTLOADER_ADDRESS + 4);

      bootLoader_resetHandler = (void *)bootLoaderResetAddr;

      //When there, the user code sector will be leaved and the bootloader code execution starts
      bootLoader_resetHandler();
  }

Where APPLICATION_ADDRESS is 0x0800 8000 and BOOTLOADER_ADDRESS is 0x0800 0000. The content of the first two addresses of the bootloader code is: 0x08000000: 20020000
0x08000004: 080044DD

meanwhile the content of the first two addresses of the application code is: 0x08008000: 20020000
0x08008004: 0800A1F1

Last modify that i've done is on the user application linker (.ld) file, where i set the flash start to the address 0x0800 8000 (instead of the address 0x0800 0000).

All the interrupts are correctly working and, after that the code has been uploaded, if I do a hardware reset, the result is the same, the code execution starts from the user application code, not from the bootloader. Any tips?

2
You do not change VTOR - it means that your vector table is not being changed. It is very unlikely application to have exactly the same interrupts handlers as bootloader. Your logic is weird and description of the problem unclear.0___________
To make the jump (either way) you should load the SP and PC from the vector table rather then using a function call which will push a return address onto the stack. There is much else wrong with your method.Clifford

2 Answers

0
votes

Your problem description is unclear but the procedure of invoking the app is far not sufficient. You need to make sure that the environment for the application is same as after the uC reset. You need to change the vector table address as well.

I wrote tens of bootloaders but I do not understrand your problem

Here you have an example how it should be done (app call from bootloader)

void startAPP(void)
{
    static uint32_t *pAppPosition;
    static voidFunc *appResetHandler;
    static uint32_t newSP;

    pAppPosition = (uint32_t *)(bankStartAddress[0] + (uint32_t)&_BOOTFlashSize);
    appResetHandler = (voidFunc *)pAppPosition[1];
    newSP = pAppPosition[0];

    SPI_DeInit();
    FLASH_DeInit();
    I2C_DeInit();
    IRQ_DeInit();
    GPIO_DeInit();

    __disable_irq();
    __set_MSP(newSP);
    __enable_irq();
    SCB -> ICSR = 0x00000000;   // reset value;
    SCB -> SCR = 0;
    SCB -> CCR = 0x00000200;    // reset value
    SCB -> SHP[0] = 0;
    SCB -> SHCSR = 0;
    SCB -> CFSR = (SCB_CFSR_DIVBYZERO_Msk | SCB_CFSR_UNALIGNED_Msk | SCB_CFSR_UNDEFINSTR_Msk | SCB_CFSR_NOCP_Msk | SCB_CFSR_INVPC_Msk | SCB_CFSR_INVSTATE_Msk);
    SCB -> HFSR = (SCB_HFSR_DEBUGEVT_Msk | SCB_HFSR_FORCED_Msk | SCB_HFSR_VECTTBL_Msk);
    SCB -> VTOR = bankStartAddress[0] + (uint32_t)&_BOOTFlashSize;  // new vector table pos. I od not clear 8 LSB because APP start position is aligned to FLASH Sectors which are at least 2k aligned

    // SysTick
    SysTick -> CTRL = 0;
    SysTick -> LOAD = 0;
    SysTick -> VAL  = 0;

    appResetHandler();

    __builtin_unreachable();
}
0
votes

The simplest and safest method of running the bootloader from the application is simply to issue a soft reset using the CMSIS NVIC_SystemReset() function.

if( toBootloader )
{
    NVIC_SystemReset() ;
}

Jumping to the bootloader by a direct call is unnecessary and ill-advised. While it can be done, just as you can jump from the bootloader to the application, you need to at least disable interrupts/exceptions and switch the vector table from that of the application to that of the bootloader. Neither your application code nor bootloader code appear to be doing that. See ARM: How to Write a Bootloader for example.

Issuing a reset has the advantage of setting the processor and all on-chip peripherals and I/O into their known reset state so you do not need to worry about de-initialising the NVIC, or any peripherals that might generate an interrupt while you are switching vector tables.

If you need to communicate information to the bootloader from the application the state of the on-chip SRAM will survive the reset process, so you can reserve space that the run-time start-up will not initialise to pass parameters to the bootloader if you need to.