SAMD21 bootloader issue

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone,

 

I designed a bootloader which retrieves an hex file online and writes the content in the flash memory. This works great, no problem there.

 

Once this is done, I use the following code to jump to the downloaded code:

 

void new_boot(void)
{
	uint32_t app_check_address;
	uint32_t *app_check_address_ptr;
	
	if  (!(  WDT  ->CTRL.reg   & WDT_CTRL_ALWAYSON))
		WDT  ->CTRL.reg   &=  ~WDT_CTRL_ENABLE;
	
	app_check_address = APP_START_ADDRESS;
	app_check_address_ptr = (uint32_t *)app_check_address;

	if  (*app_check_address_ptr ==  0xFFFFFFFF)
		return;

	SystemInit();
	__disable_irq();

	/* Pointer to the Application Section */
	void (*application_code_entry)(void);
	/* Rebase the Stack Pointer */
	
	__set_MSP(*(  uint32_t *) APP_START_ADDRESS);
		
	/* Rebase the vector table base address TODO: use RAM */
	SCB->VTOR = (( uint32_t)APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk);	
	/* Load the Reset Handler address of the application */
	application_code_entry = (void (*)(void))(  unsigned *)(*(unsigned *)  (APP_START_ADDRESS + 4));
	/* Jump to user Reset Handler in the application */
	application_code_entry();	
}

 

But it stalls.

 

If I run new_boot() right at the start of my main() function, it works perfectly and the previously downloaded code runs.

Specifically, new_boot() only fails if I run it after a SERCOM initialization (usart, spi...).

 

I suspect I have to disable something before moving but I just cannot figure what.

 

Any clue ?

 

Thanks,

 

Fred

This topic has a solution.
Last Edited: Tue. Jul 2, 2019 - 01:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Consider including a reset before the new app starts. Thread here about this:

https://community.atmel.com/forum/samd21-bootloader-pingpong

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Lars, so what would be the best way to reset before jumping to the new location?

I tried "NVIC_SystemReset();" but it actually relaunches the main function so that's not what I need.

Last Edited: Tue. Jun 25, 2019 - 07:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It relaunches the main function of the bootloader? It's expected to start in the bootloader surly? Not only in this case but also for a cold start, i.e., the bootloader commonly has to detect that there is a flashed app and then immediately start it (without touching any peripherals). And then there is a need to signal to the bootloader that it should not start the app but start boot loading (using, e.g., a button or magic value in RAM).

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is probably something I am not understanding about the whole bootloader concept. Here's the architecture of my program so far:

 

- the bootloader connects over the internet (using USART), check for a new version

- if no new version is available, the bootloader jumps to the main address 0x0020000 (but right now it fails)

- if a new version exists, the rom is downloaded and processed onto an external flash memory (i2c)

- the rom is checked for errors and, if validated, it is then flashed at the main address

- the bootloader jumps to the main address (and fails...).

 

So, as my "bootloader" requires 2 peripherals, it seems it cannot make the jump and reset is not an option as it will only run the same process again and again...

 

Any idea?

 

Thanks a lot

 

 

 

Last Edited: Tue. Jul 2, 2019 - 08:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think a magic value in RAM can work, i.e., exclude in the linker script 4 bytes for a "variable". The bootloader writes a special value there when it's known that the app can start, then it performs a reset. The first thing done in the bootloader is to check for the magic value and if it is there it just starts the app. 

You find similar (not identical) use of RAM for controlling the bootloader in  https://github.com/arduino/ArduinoCore-samd/tree/master/bootloaders/zero (there it is about the double reset button press which forces that board to stay in the bootloader).

/Lars

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Could it work to use a specific address in the flash memory (out of the bootloader & main program memory) as a "magic value"?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, it could give you a boot loop if there is something wrong with the app and that specific address in flash is not updated ok. A good thing about using RAM is you recover with a cold start.

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good point...

Thanks for you input I'll look into it and post the results.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Lars it works fine now.

 

A few elements, should anyone require some more explanation:

 

- first to define a variable which will survive upon software reset, you need to exclude 4 bytes of ram by modifying the following line in the .ld file of your project (samd21j18a_flash.ld in my case)

  ram      (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000-0x0004

if I understand this correctly, it will prevent the mcu to write on the last 4 bytes of the ram.

 

- this variable can then be read/written using:

#define BOOT_MAGIC_ADDRESS           (0x20007FFCul)
#define BOOT_MAGIC_VALUE              (*((volatile uint32_t *) BOOT_MAGIC_ADDRESS))

 

- now the actual bootloader should look like that:

void main(void)
{
    if(BOOT_MAGC_VALUE == SOME_VALUE)
        jumpToApp();
    else
        checkForUpdate();    
    
}

no peripherals definition of any sort before the BOOT_MAGIC_VALUE check.

 

- jumpToApp() jumps to the main application address:

void jumpToApp(void)
{
	uint32_t app_check_address;
	uint32_t *app_check_address_ptr;
	
	if  (!(  WDT  ->CTRL.reg   & WDT_CTRL_ALWAYSON))
	WDT  ->CTRL.reg   &=  ~WDT_CTRL_ENABLE;
	
	app_check_address = APP_START_ADDRESS;
	app_check_address_ptr = (uint32_t *)app_check_address;

	if  (*app_check_address_ptr ==  0xFFFFFFFF)
		return;
	SystemInit();
	__disable_irq();

	/* Pointer to the Application Section */
	void (*application_code_entry)(void);
	/* Rebase the Stack Pointer */
	
	__set_MSP(*(  uint32_t *) APP_START_ADDRESS);
	
	/* Rebase the vector table base address TODO: use RAM */
	SCB->VTOR = (( uint32_t)APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk);
	/* Load the Reset Handler address of the application */
	application_code_entry = (void (*)(void))(  unsigned *)(*(unsigned *)  (APP_START_ADDRESS + 4));
	/* Jump to user Reset Handler in the application */
	application_code_entry();
}

- checkForUpdate() checks/updates:

void checkForupdate()
{
    update();//any routine which updates the flash memory at "APP_START_ADDRESS" address 
    BOOT_MAGIC_VALUE = SOME_VALUE;//memorize the fact that we already checked for update
    NVIC_SystemReset();//software reset
}

 

 

Thus the update process will only take place when powering on the mcu.