0

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. Commented Sep 30, 2020 at 21:24
  • 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. Commented Sep 30, 2020 at 22:36

3 Answers 3

2

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();
}
Sign up to request clarification or add additional context in comments.

4 Comments

The Vector table address is changed into the file where it is declared: on STM implementation the address is calculated as: CODE_STARTING_ADDRESS | OFFSET and I changed offset value to 0x8000 (It must be a multiply of 0x200) (only in the user application file, that because the code Is stored into the second Sector while the bootloader Is stored into Sector 0 so it is not changed). The only missing thing are the upload of the value of SCB. Thank you! I'll try again and in a more clear way
rather CODE_STARTING_ADDRESS & OFFSET
I was facing similar issues: I had a much simple bootloader, where I just set the new VTOR and MSP, and basically jumped to the application address. My bootloader worked for some binaries, but not for others. It seems that the "Systick" part was what I missed out. I really would like to understand why this Systick 'deinitialziation' is so crucial.
Using appResetHandler after __set_MSP(newSP); looks unreliable, as it depends on the compiler if it's fetched from a register or from the stack. This can't be done in C, as it is always possible, that the compiler uses the current SP. source developer.arm.com
0

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.

Comments

0

You need to relocate the application's vector table

There are two programs here, user application and user bootloader.

  1. User bootloader: you want to run this at 0x08000000 location. And so you don't have to change the linker flash.ld file. Also since you are running at the uC memory's starting address you also don't have to shift the interrupt vector table. (By default it will be placed at 0x08000000)

  2. User application: Here this program is being kept in the 0x08008000 location. So you also have to change the default location of interrupt vector table(from 0x08000000 to 0x08008000).

To change the interrupt vector table address you can use SCB->VTOR register.

Or if you are using stm32cube ide. In there you'll find a system_stm32f4xx.c file.
Uncomment #define USER_VECT_TAB_ADDRESS
Then put offset as #define VECTOR_TABLE_OFFSET 0x8000

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.