cancel
Showing results for 
Search instead for 
Did you mean: 

Switching to application from custom bootloader

pLanka
Associate II

Hi, 

Please help me sort this out.

I am trying to port a custom bootloader originally written for the SAMD21 Arm chip to the STM32U385VGT6. We are upgrading our current product with an STM32 Chip. I am having problems with switching from the bootloader to the application binary. My bootloader is loaded at address 0x08000000. The application binary is compiled to run from 0x08010000. I edited the linker script files to locate the binaries properly and tested opening the binary file with a hex editor. All seems situated correctly. Here is my function to jump to the application. APP_CODE_START is set to 0x08010000. I get "jumping to application" in my debug terminal. But the application code is not running. 

 

void jumpToApplication()
{    DEV_DEBUG_LOG("jumping to application");
     // Disable interrupts before changing MSP and VTOR
    __disable_irq();
    // Jump to the sketch
    __set_MSP(*((uint32_t *)APP_CODE_START));

    // Reset vector table address
    SCB->VTOR = ((uint32_t)(APP_CODE_START)&SCB_VTOR_TBLOFF_Msk);

    // Address of Reset_Handler is written by the linker at the beginning of the .text section.
    uint32_t resetHandlerAddress = *((uint32_t *)APP_CODE_START + 1);

    if (resetHandlerAddress < APP_CODE_START || resetHandlerAddress > (APP_CODE_START + APP_CODE_SIZE)) {
        //this will later cause watchdog reset
        while(1);
    }
 
    // Jump to application
    void (*app_reset_handler)(void) = (void (*)(void))resetHandlerAddress;
    app_reset_handler();
}
1 ACCEPTED SOLUTION

Accepted Solutions
pLanka
Associate II

<pre><code>

Thanks LCE and TDK,

I got the code working with the code snippet. Now the bootloader jumps to the application, and the application runs. 

// Function pointer to the application's Reset Handler

typedef void (*pFunction)(void);

#define APP_CODE_START 0x08010000U

 

void jumpToApplication()

{

// 1. De-initialize (optional: if HAL was used before)

// Not needed since we only use CMSIS here

 

// 2. Get the main stack pointer (MSP) value of the application

uint32_t appStack = *((__IO uint32_t*)APP_CODE_START);

 

// 3. Get the reset handler address of the application

uint32_t appEntry = *(__IO uint32_t*)(APP_CODE_START + 4U);

pFunction jumpToApp = (pFunction)appEntry;

// 4. Reconfigure vector table offset register (VTOR)

SCB->VTOR = APP_CODE_START;

// 5. Set the MSP to the application's stack pointer

__set_MSP(appStack);

// 6. Jump to the application

jumpToApp();

 

// If something goes wrong, loop here

while (1) {}

}

</code></pre>

 

View solution in original post

5 REPLIES 5
TDK
Super User

Do you ever re-enable interrupts?

You should disable interrupts individually so they do not immediately fire after the jump.

If you feel a post has answered your question, please click "Accept as Solution".
LCE
Principal II

What TDK said.

And:  __disable_irq(); does not actually disable any interrupts, it just prevents the MCU from entering any ISRs.

So better clear the Interrupt Enable and Pending registers. In H7 that's several NVIC->ICER[n] and NVIC->ICPR[n] .

 

pLanka
Associate II

<pre><code>

Thanks LCE and TDK,

I got the code working with the code snippet. Now the bootloader jumps to the application, and the application runs. 

// Function pointer to the application's Reset Handler

typedef void (*pFunction)(void);

#define APP_CODE_START 0x08010000U

 

void jumpToApplication()

{

// 1. De-initialize (optional: if HAL was used before)

// Not needed since we only use CMSIS here

 

// 2. Get the main stack pointer (MSP) value of the application

uint32_t appStack = *((__IO uint32_t*)APP_CODE_START);

 

// 3. Get the reset handler address of the application

uint32_t appEntry = *(__IO uint32_t*)(APP_CODE_START + 4U);

pFunction jumpToApp = (pFunction)appEntry;

// 4. Reconfigure vector table offset register (VTOR)

SCB->VTOR = APP_CODE_START;

// 5. Set the MSP to the application's stack pointer

__set_MSP(appStack);

// 6. Jump to the application

jumpToApp();

 

// If something goes wrong, loop here

while (1) {}

}

</code></pre>

 

pLanka
Associate II

Once again, I encountered a situation where I found the SySTick_Handler interrupt is not firing when I relocate my application to 0x08010000 in my STM32U385VGT6 chip. After debugging, I found that the timer is running and the interrupt is not firing. I have a delay function that uses the uwTick variable to count the delays. It is not incremented. Once the delay is called, the code gets stuck there. The same code runs when located at 0x08000000 by changing the linker script. I need help to get the SysTick interrupt fired after jumping from the bootloader to the application image. 

 

Here is my jump to the Application code. 

<pre><code>

// Application base address

#define APP_CODE_START 0x08010000U

void jumpToApplication()

 

{

// 2. Get the main stack pointer (MSP) value of the application

uint32_t appStack = *((__IO uint32_t*)APP_CODE_START);

 

// 3. Get the reset handler address of the application

uint32_t appEntry = *(__IO uint32_t*)(APP_CODE_START + 4U);

pFunction jumpToApp = (pFunction)appEntry;

 

// 4. Reconfigure vector table offset register (VTOR)

 

SCB->VTOR = APP_CODE_START;

 

// 5. Set the MSP to the application's stack pointer

__set_MSP(appStack);

 

// 6. Jump to the application

jumpToApp();

 

// If something goes wrong, loop here

while (1) {}

}

 

Here is my application code located at 0x08010000

 

#define LED_PIN 15

static void gpio_init(void)

{

// Enable GPIOA clock (bit in AHB2ENR1)

RCC->AHB2ENR1 |= RCC_AHB2ENR1_GPIOAEN;

 

// Set PA15 as general-purpose output (MODER bits 30:31 = 01)

GPIOA->MODER &= ~(3U << (LED_PIN * 2)); // clear

GPIOA->MODER |= (1U << (LED_PIN * 2)); // set output

 

// Push-pull (default), no pull-up/down

GPIOA->OTYPER &= ~(1U << LED_PIN);

GPIOA->PUPDR &= ~(3U << (LED_PIN * 2));

 

// Set medium speed

GPIOA->OSPEEDR |= (1U << (LED_PIN * 2));

}

 

//SySTick-based delay function.

void Delay(uint32_t delay_ms)

{

uint32_t tickstart = uwTick;

while ((uwTick - tickstart) < delay_ms)

{

// busy wait

}

}

 

 

int main(void)

{

 

/* USER CODE BEGIN 1 */

 

/* USER CODE END 1 */

 

/* MCU Configuration--------------------------------------------------------*/

 

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

 

/* USER CODE BEGIN Init */

 

/* USER CODE END Init */

 

/* Configure the system clock */

SystemClock_Config();

 

/* USER CODE BEGIN SysInit */

 

/* USER CODE END SysInit */

 

/* Initialize all configured peripherals */

/* USER CODE BEGIN 2 */

gpio_init();

/* USER CODE END 2 */

 

/* Infinite loop */

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */

// Toggle PA15

GPIOA->ODR ^= (1U << LED_PIN);

// delay(500000);

Delay(2000);

/* USER CODE BEGIN 3 */

}

/* USER CODE END 3 */

}

</pre></code>

 

TDK
Super User

Probably one of these two things:

  • Don't make the jump from within an interrupt.
  • Don't disable interrupts.
If you feel a post has answered your question, please click "Accept as Solution".