cancel
Showing results for 
Search instead for 
Did you mean: 

Procedures for developing an STM32 bootloader + app

Hello there,

I am trying to develop an STM32L4 proof of concept bootloader application. I am basing on this source: https://github.com/akospasztor/stm32-bootloader and Atollic's "Working with bootloaders on Cortex-M devices".

I am trying to do a simple thing: My bootloader should start and jump to a fixed address in flash, under which the regular application lies. The procedure I have done is as follows:

I have programmed the bootloader app to the flash memory (using ST-LINK), ld file contains following flash description:

FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 220K

The total flash memory is 512K.

Then I flashed the application code to the memory (with ST-LINK). Its ld file flash parameter looks as follows:

FLASH (rx)      : ORIGIN = 0x8037000, LENGTH = 292K

220 * 1024 = 0x37000, so my starting address for the app is 0x08037000.

220K + 292K = 512K, so the memory sizes look fine.

My jump function looks as follows:

typedef void (*pFunction)(void);
 
/**
 * @brief	Jumps the program counter to the memory location set by \p address.
 * 			Before the jump is performed vector tables are set.
 */
void boot_jump2Address(const uint32_t address)
{
	uint32_t appStack;
	pFunction appEntry;
 
	// get the application stack pointer (1st entry in the app vector table)
	appStack = (uint32_t)*((__IO uint32_t*)address);
 
	// Get the app entry point (2nd entry in the app vector table
	appEntry = (pFunction)*(__IO uint32_t*)(address + 4);
 
	boot_basicClockConfig();
	HAL_RCC_DeInit();
	HAL_DeInit();
 
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL  = 0;
 
	// Reconfigure vector table offset to match the app location
#if (SET_VECTOR_TABLE)
	SCB->VTOR = address;
#endif
 
	__set_MSP(appStack); // Set app stack pointer
	appEntry(); // Start the app
 
	while (1); // never reached
}

I then start to debug the application (not bootloader code). The bootloader starts and it jump to the app code, then my debugger catches the PC and I can debug my application. The problem is that the application crashes. When debugging it, one can see that the line at which it crashes is in the HAL_RCC_OscConfig function generated by CubeMx:

assert_param(IS_RCC_PLLR_VALUE(RCC_OscInitStruct->PLL.PLLR));

This assert is not passed, because the PLLR is visible as 1, instead of 2. 2 is the value set in the code. For some reason its changing itself. But only if I run directly to the breakpoint set at this line- if I go trhough the code line by line until this point, I can see in the debugger view that its 2 correctly. That doesnt matter, because the code crashes later on on similar stages.

My question is- Is there any additional setting I need to modify for my base application in order to place it under different flash address than 0x08000000? I have only changed the linker script (FLASH parameter). But is there anything else?

I would really appreciate all help, as I am out of ideas on how to fix this.

1 ACCEPTED SOLUTION

Accepted Solutions

>>Other general gotchs are a) don't disable interrupts on the processor, and b) don't call from interrupt context, this includes HAL call backs.

This would be using __disable_irq() instead of actually addressing the issue of turning off interrupts at the source. The App likely uses a different memory map, and the linker has given uwTicks/usTickFreq entirely different addresses.

The "easy" way is to reset the processor and branch directly into the app via an expedited path. The other is to clean up the mess so you don't have any interrupts firing into handlers which have had none of their variables/state defined.

The test here would be to create a vector table where all entries point to HardFault_Handler, point SCB->VTOR to this for a couple of seconds, and see if the thing outputs fault data. The key here being to have HardFault_Handler to output diagnostic information, and not just a while(1) loop. If you can't determine the source of the interrupt use bisection of the table until it is apparent.

The b) point above relates to unlooping all context pushes on the stack, where LR points to user space code and doesn't contain magic values swapping what register map the processor is using, and preemption level the processor is running at.

Look for issues with SysTick, TIM and Watch Dogs.

You could certainly have an initialization/command-line structure you hand between loader/app, this is especially true where you've created contractual obligations between them. ie clocks and PLL's up, frequencies, Debug USART, etc.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

15 REPLIES 15
Sebastien1
Associate II

Hello Lukasz,

Try to cancel every initialization you do (or HAL library do) in boot firmware.

I see you do that but the HAL library can forget some parameters in the DeInit function.

Compare all registers before Init and after DeInit.

Also, verify that every interrupts enable are disable because I don't see that in your code.

Another try is to modify your application software to accept a already clock initialized.

Best regards.

You need to manage SCB->VTOR in the SystemInit() code of the application. I usually fix the #define nonsense ST uses and let the linker fix up the address like it was designed to do.

extern void *__Vectors; // Keil forks use this symbol, check your startup.s

void SystemInit (void)

{   

 /* FPU settings ------------------------------------------------------------*/

 #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)

   SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */

 #endif

 /* Configure the Vector Table ----------------------------------------------*/

   SCB->VTOR = (uint32_t)&__Vectors;

...

}

You can also decide not to repeat the SystemClock stuff if you've brought this up properly in the loader.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Hello guys, thank you for answer,

From my point of view there are couple places where the address could be changed.

  1. In linker script (I did that already),
  2. In system Init, one can find this:
  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif

After one follows The FLASH_BASE define:

#define FLASH_BASE            (0x08000000UL) /*!< FLASH(up to 512 KB) base address */

Should this define be altered as well? Because I tried to change this define to 0x08037000UL and it help solving my problem. I could try omitting another system clock config in the main program but would this cause the crash?

I'm saying the code you're quoting is dangerous and easily overlooked.

You shouldn't need to change things in multiple places, it should be sufficient to change the linker script, and everything else works off that address.

Yes, they expect you to change FLASH_BASE and/or VECT_TAB_OFFSET, I think this could be discarded and a single source file could be used in both loader and app context, reducing the chance of error.

At the end of the day for interrupts to function in your application, SCB->VTOR == 0x08037000

Other general gotchs are a) don't disable interrupts on the processor, and b) don't call from interrupt context, this includes HAL call backs.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

I agree with you on the modification places in 100%. The problem is that certain design patterns are forced by the startup code generated in cubemx. Over the years I have learned to "hack" it, I gues this is doable as well.

This is however not the reason for the application to not work. I call the jumo from an freertos task, which is not an interrupt rouitine. The only thing I can think of from you post that I have missed is disabling the interrupt, I will check that.

So to summarize, The FLASH_BASE define should be altered as well? It is used in various places in the code provided by ST.

FreeRTOS might however being running in a user state and not a system state. Is it faulting?

FLASH_BASE is only used for some asserts as far as I can see.

The ST model would have you modify VECT_TAB_OFFSET to 0x37000

SCB->VTOR is far simpler in it's implementation, it should ignore the low order 9-bits based on the size of the vector table, ie the NVIC/CMx feeds the low order bits as it initiates a read for the new PC

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Before jump, in the bootloader code there is no faults. Do you suggest that the scheduler should be stopped and the jump should occur from the main code?
Tge offset idea makes sense, I will do it like that, thanks.
So to summarize the VTOR setting, should I not modify it in the vootloader code before jump if I set the offset in the application code?

The app code typically sets VTOR pretty early, and is going to overwrite anything the loader does.

The loader shouldn't have any of it's routines running, think TIM and/or SysTick.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
So in other words:
- it doesnt matter raither the loader or app (in system init) set vector table?
- all interrupts should be turned off before jump?