Difference between revisions of "Using embOS for Cortex-M with a bootloader"

From SEGGER Wiki
Jump to: navigation, search
m
(Demo project)
 
(6 intermediate revisions by 4 users not shown)
Line 2: Line 2:
 
Cortex-M devices.
 
Cortex-M devices.
   
  +
== Introduction ==
__TOC__
 
 
=== Introduction ===
 
   
 
A bootloader is a program which allows the stand-alone programming of application
 
A bootloader is a program which allows the stand-alone programming of application
Line 11: Line 9:
 
There are no restrictions for building a bootloader using embOS.
 
There are no restrictions for building a bootloader using embOS.
   
=== Jump from bootloader to application ===
+
== Jump from bootloader to application ==
   
 
Some steps have to be performed before the main application can be safely accessed
 
Some steps have to be performed before the main application can be safely accessed
Line 18: Line 16:
   
 
<source lang="C">
 
<source lang="C">
#define THUMB_BIT 1
+
#define THUMB_BIT (1u)
#define APP_START_ADDR 0x00100000
+
#define APP_START_ADDR (0x00100000)
#define APP_STACK_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x00))
+
#define APP_STACK_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x00))
#define APP_RESET_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x04))
+
#define APP_RESET_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x04))
  +
 
void (*AppPtr)(void);
 
void (*AppPtr)(void);
  +
  +
static OS_STACKPTR int StackHP[128], StackLP[128]; // Task stacks
  +
static OS_TASK TCBHP, TCBLP; // Task control blocks
  +
  +
static void DeInitializeAllInterrupts(void) {
  +
//
  +
// De-initialize all interrupts to avoid any undesired interrupts to occur after the application was started.
  +
//
  +
}
   
 
static void HPTask(void) {
 
static void HPTask(void) {
Line 29: Line 37:
 
// Start the application
 
// Start the application
 
//
 
//
  +
OS_INT_Disable();
DeInitializeAllInterrupts(); // De-initialize all interrupts to avoid any interrupt to occur before the jump to the application
 
  +
DeInitializeAllInterrupts();
 
AppPtr = (void (*)(void))(APP_RESET_PTR | THUMB_BIT);
 
AppPtr = (void (*)(void))(APP_RESET_PTR | THUMB_BIT);
 
SCB->VTOR = APP_START_ADDR;
 
SCB->VTOR = APP_START_ADDR;
__set_MSP(APP_STACK_PTR); // Set main stack pointer to application initial stack value
+
__set_MSP(APP_STACK_PTR); // Set main stack pointer to application initial stack value
__set_CONTROL(0); // Use MSP and Privileged in thread mode
+
__set_CONTROL(0); // Use MSP and Privileged in thread mode
AppPtr(); // Start the application, we will not return from this function
+
AppPtr(); // Start the application, we will not return from this function
 
}
 
}
 
}
 
}
Line 42: Line 51:
   
 
<source lang="C">
 
<source lang="C">
#define THUMB_BIT 1
+
#define THUMB_BIT (1u)
#define APP_START_ADDR 0x00100000
+
#define APP_START_ADDR (0x00100000)
#define APP_STACK_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x00))
+
#define APP_STACK_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x00))
#define APP_RESET_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x04))
+
#define APP_RESET_PTR (*(volatile OS_U32*)(APP_START_ADDR + 0x04))
  +
 
void (*AppPtr)(void);
 
void (*AppPtr)(void);
 
</source>
 
</source>
Line 54: Line 64:
 
vector address.
 
vector address.
 
Both programs, the bootloader and the application, have their own vector table, so it
 
Both programs, the bootloader and the application, have their own vector table, so it
is neccessary to change the stack pointer value and the reset vector manually before
+
is necessary to change the stack pointer value and the reset vector manually before
 
the application can be started.
 
the application can be started.
   
Line 63: Line 73:
 
// Start the application
 
// Start the application
 
//
 
//
DeInitializeAllInterrupts();
+
DeInitializeAllInterrupts(); // De-initialize all interrupts to avoid any interrupt to occur before the jump to the application
 
</source>
 
</source>
   
Line 75: Line 85:
   
 
A function pointer will be used to jump to the new reset vector address. To indicate
 
A function pointer will be used to jump to the new reset vector address. To indicate
that the accessed reset handler is thumb code, the LSB of the vector must be set to
+
that the accessed reset handler is thumb code, the LSB of the vector must be set to 1.
1.
 
 
The CPU stores the address of the vector table in the vector table offset register
 
The CPU stores the address of the vector table in the vector table offset register
 
(VTOR), it has to be changed accordingly from reset value to the start address of the
 
(VTOR), it has to be changed accordingly from reset value to the start address of the
 
application.
 
application.
   
<source lang="C">
+
<source lang="C">
 
__set_MSP(APP_STACK_PTR); // Set main stack pointer to application initial stack value
 
__set_MSP(APP_STACK_PTR); // Set main stack pointer to application initial stack value
 
__set_CONTROL(0); // Use MSP and Privileged in thread mode
 
__set_CONTROL(0); // Use MSP and Privileged in thread mode
Line 99: Line 108:
 
Calling the function finally starts the application.
 
Calling the function finally starts the application.
   
=== Demo project ===
+
=== Cortex-M7 with cache ===
  +
If the cache is enabled in the bootloader it must be invalidated, cleaned and disabled before the jump to the application.
  +
  +
== Demo project ==
   
A simple demo project for the emPower board, which demonstrates these steps, can
+
A simple demo project for the SEGGER emPower board, which demonstrates these steps, can be downloaded here:
be downloaded from [https://www.segger.com/downloads/application-notes/embOS_BootloaderDemo_emPower_ES segger.com].
+
* [[Media:embOS_BootloaderDemo_emPower_ES.zip | embOS_BootloaderDemo_emPower_ES.zip]]

Latest revision as of 14:35, 4 November 2021

This wiki entry describes the usage of embOS within bootloader applications for Cortex-M devices.

Introduction

A bootloader is a program which allows the stand-alone programming of application code to the device it is running on. The bootloader has to ensure that the CPU is in the same state as after reset if the application is started. There are no restrictions for building a bootloader using embOS.

Jump from bootloader to application

Some steps have to be performed before the main application can be safely accessed from a task. For the following example, we assume that the bootloader is located at address 0x00000000 and the application at 0x00100000.

#define THUMB_BIT         (1u)
#define APP_START_ADDR    (0x00100000)
#define APP_STACK_PTR     (*(volatile OS_U32*)(APP_START_ADDR + 0x00))
#define APP_RESET_PTR     (*(volatile OS_U32*)(APP_START_ADDR + 0x04))

void (*AppPtr)(void);

static OS_STACKPTR int StackHP[128], StackLP[128];  // Task stacks
static OS_TASK         TCBHP, TCBLP;                // Task control blocks

static void DeInitializeAllInterrupts(void) {
  //
  // De-initialize all interrupts to avoid any undesired interrupts to occur after the application was started.
  //
}

static void HPTask(void) {
  while (1) {
    //
    // Start the application
    //
    OS_INT_Disable();
    DeInitializeAllInterrupts();
    AppPtr    = (void (*)(void))(APP_RESET_PTR | THUMB_BIT);
    SCB->VTOR = APP_START_ADDR;
    __set_MSP(APP_STACK_PTR);  // Set main stack pointer to application initial stack value
    __set_CONTROL(0);          // Use MSP and Privileged in thread mode
    AppPtr();                  // Start the application, we will not return from this function
  }
}

The following description analyzes the task step by step:

#define THUMB_BIT         (1u)
#define APP_START_ADDR    (0x00100000)
#define APP_STACK_PTR     (*(volatile OS_U32*)(APP_START_ADDR + 0x00))
#define APP_RESET_PTR     (*(volatile OS_U32*)(APP_START_ADDR + 0x04))

void (*AppPtr)(void);

After reset two words, the start value of the stack pointer and the reset vector, will be fetched from the beginning of the vector table, which is initially located at address 0x00000000. The reset vector is used to start the program execution from the reset vector address. Both programs, the bootloader and the application, have their own vector table, so it is necessary to change the stack pointer value and the reset vector manually before the application can be started.

static void HPTask(void) {
  while (1) {
    //
    // Start the application
    //
    DeInitializeAllInterrupts();  // De-initialize all interrupts to avoid any interrupt to occur before the jump to the application

At first all interrupts must be de-initialized. No interrupt, which was initialized within the bootloader, must occur after we switched to the application vector table.

    AppPtr    = (void (*)(void))(APP_RESET_PTR | THUMB_BIT);
    SCB->VTOR = APP_START_ADDR;

A function pointer will be used to jump to the new reset vector address. To indicate that the accessed reset handler is thumb code, the LSB of the vector must be set to 1. The CPU stores the address of the vector table in the vector table offset register (VTOR), it has to be changed accordingly from reset value to the start address of the application.

    __set_MSP(APP_STACK_PTR);     // Set main stack pointer to application initial stack value
    __set_CONTROL(0);             // Use MSP and Privileged in thread mode

The main stack pointer must be set to the initial value used for the application. Because embOS tasks use the process stack pointer, the control register must be set to switch to the main stack pointer. Furthermore, the thread mode should be set to privileged.

    AppPtr();                     // Start the application, we will not return from this function
  }
}

Calling the function finally starts the application.

Cortex-M7 with cache

If the cache is enabled in the bootloader it must be invalidated, cleaned and disabled before the jump to the application.

Demo project

A simple demo project for the SEGGER emPower board, which demonstrates these steps, can be downloaded here: