cancel
Showing results for 
Search instead for 
Did you mean: 

Start/Reset app with custom bootloader

Wyrm
Associate II

Edit: fixed a typo -- pa

Hellow! When i debugging an application with a pre-installed bootloader in the Keil environment, I used the following script:

FUNC void DebugSetup (void) {
// </h>
SP = _RDWORD(0x08040800); // Setup Stack Pointer
PC = _RDWORD(0x08040804); // Setup Program Counter
_WDWORD(0xE000ED08, 0x08040800); // VTOR to Image Base
}

FUNC void OnResetExec(void) {
DebugSetup();
}

DebugSetup(); // Debugger Setup

in vscode plugin i add to launch.json

"preRunCommands": [
"set $sp = *(unsigned int *)0x08018000",
"set $pc = *(unsigned int *)0x08018004",
"set {unsigned int}0xE000ED08 = 0x08018000",
],


And it works fine until I do a reset, is there a way to fix this?
For example, something like "postRestartSessionCommands". 

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Super User

The new code is very different from the original posted code.

I suspect the issue is with setting pmain_app after you modify SP. Showing the disassembly would confirm.

If you follow the recommended code, it gets compiled correctly with GCC:

community.st.com/t5/stm32-mcus/how-to-jump-to-system-bootloader-from-application-code-on-stm32/ta-p/49424

 

> Correct the linker file GCC to shift the stack below end ram

There is nothing to correct here. That is where the stack should start.

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

View solution in original post

10 REPLIES 10
TDK
Super User

> it works fine until I do a reset

How are you doing a reset? NRST pin or some debugger option? Do things work if the chip is powered on without the debugger attached?

Is there a reason your addresses in launch.json and DebugSetup don't match?

If you feel a post has answered your question, please click "Accept as Solution".
Pavel A.
Super User

Yes, the Keil debugger (and some others) allow to script an action at the reset event.

For the ST debugger the easiest way to jump to specific address is to put a minimal "bootloader" at the default vectors address of your STM32, usually 0x08000000. Put there the same stack and entry point address as you want (copy two words from 0x08018000).

Wyrm
Associate II

The example shows different addresses, simply from different projects.
Using the debugger's reset button.
I'm having some kind of problem with jumping into applications.
I created 2 project in the Keil for STM32F765:
1 is the jump at startup, according to this code.

#define APP_START_ADDR    0x08020000
void Board_GoToApp(void)
{
  void (*pmain_app)(void);; 
    
  typedef void (*pFunction)(void); 
  pFunction Jump_To_Application; 
    
  //_this_BLRamApp_SetGo(cthis);
  //  __disable_irq();

  SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk ; 
  for (int i = 0; i < 8; i++) NVIC->ICPR[i] = 0xFFFFFFFF;
   
  SCB->VTOR = APP_START_ADDR; 
  
  __DSB();
  __ISB();
  
  __set_MSP(*(uint32_t*) APP_START_ADDR);
  //SCB->MPU = 0; 
  //__set_CONTROL(0);
  pmain_app = (void (*)(void)) * ((uint32_t*)APP_START_ADDR + 1); /*!< reset handler */
  pmain_app();
}

main(void)
{
  /* 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 */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_IWDG_Init();
  MX_USART3_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_MspDeInit(&huart3);
  HAL_DeInit();
  HAL_RCC_DeInit();
  
  Board_GoToApp();
  while(1){};
} 


2 is the main application, where the vector is shifted, and then a reset is immediately performed (for verification).

int main(void)
{

  /* USER CODE BEGIN 1 */
__set_PRIMASK(1); /* disable irq*/

SCB->VTOR = 0x08020000; // move irq vector to app start addr

__DSB(); // Data Synchronization BarrierqqqBarrier

__set_PRIMASK(0);/* enable irq*/
  /* USER CODE END 1 */

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

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  NVIC_SystemReset();
}

 

In Keil, everything works fine, and application loops: Loader -> Main Application -> Loader.

If I generate the same simple application in VScode, everything falls apart, and a hard failure occurs when jumping from the loader to the main application.
I suspect the difference is in the linker and the assembly code...

 

I Correct ld file.. It would be great if you could help me.

/* Entry Point */
ENTRY(Reset_Handler)

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 512K
FLASH (rx)      : ORIGIN = 0x08020000, LENGTH = 1024K
}

 

Pavel A.
Super User

Again - with the ST debugger, when the MCU resets during debug session, it just jumps to the reset handler defined by hardware (or via "option bytes") and goes on. Only when you start the session you can specify  the start address. To change this behavior you need to tweak something in the debugger config. It's easier just to make the reset vector at the default location with proper content.

Your address in the linker script 0x8020000 is not the default location (unless you changed the start address via the "option bytes"). The MCU does not know how to get there.

Wyrm
Associate II

After 8 hours of struggle, I finally got around to this error! To my shame, I'm not very knowledgeable about linker scripts. It turns out that Keil, unlike GCС, places the stack after the data, while GCС places it at the end. That's why my bootloaders written in Keil for various controller families crashed when calling __set_msp(), since they're designed for Keil -> Keil projects, not Keil -> vssode(gcc). 

There are two solutions: 1) edit the linker script 2) set the msp slightly higher than the end of memory.

Pavel A.
Super User

In your jump code (void Board_GoToApp(void)...) the local variables (pmain_app, Jump_To_Application) can sit on the stack. What happens to these variables when __set_MSP() moves the stack?

 

In fact, everything is fine with them (this code works reliably for me on many boards with other STM processors). The problem is this:
1) In the GCC linker script generated using CubeMX, the stack starts at 0x020080000 (end of ram) i see this address at 0x08020000 addres.

MEMORY /* GCC ld file*/
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 512K
FLASH (rx)      : ORIGIN = 0x08020000, LENGTH = 896K
}

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM);    /* end of RAM */


2) In the bootloader, i read the address of the main application, which contains the stack address, and i set it via __set_msp() and in the next step, when i need to jump, Keil call hardfault because the stack is already set and it increments by 4 (stack overflow).

So, there are two options:
1) Remove _set_msp completely because after jump i get into the reset_handler, and the first operation there is to load the stack address into MSP.

/*asm file generated by CubeMX*/
.section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  
  ldr   sp, =_estack      /* set stack pointer */


2) Correct the linker file GCC to shift the stack below end ram

Or are you talking about the fact that there might still be an error in the code?

 

Yes these options are valid, just consider that the alignment of the stack pointer on Cortex-M7 (maybe even on CM4) should be 8, not 4. 

Adding 4 to MSP likely should compensate for the call - but call does not push the return address on stack, it is passed in LR. So if Keil startup adds 4, it does a wrong thing, perhaps they've already fixed it in their current version. 

The jump code works likely due to optimization (allocates the locals in registers).

TDK
Super User

The new code is very different from the original posted code.

I suspect the issue is with setting pmain_app after you modify SP. Showing the disassembly would confirm.

If you follow the recommended code, it gets compiled correctly with GCC:

community.st.com/t5/stm32-mcus/how-to-jump-to-system-bootloader-from-application-code-on-stm32/ta-p/49424

 

> Correct the linker file GCC to shift the stack below end ram

There is nothing to correct here. That is where the stack should start.

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