Creating a solution with bootloader and application
This article contains a guide that explains how a SEGGER Embedded Studio(ES) solution can be created to set up a bootloader and application project. In a typical bootloader setup on micro controllers the bootloader initializes the MCU and verifies that the application that has been flashed may be launched by the bootloader. This is also shown in the example project below with a simple CRC check.
The following import tutorial will be based on the following Hardware and Software combinations:
- SEGGER Embedded Studio 5.70a or later
- Target Evalboard: SEGGER Cortex-M Trace Reference Board
- J-Link V11 or later
- J-Link 19-Pin Cortex-M Adapter
- J-Link software V7.60f or later
- Example project
This tutorial will be based on the hardware and software combination above. So if you are using another hardware steps might vary, but the general approach stays the same.
- Start Embedded Studio.
- Install CPU support Package for your device family via Tools->Package Manager.
- Create new project via File->New Project->Create the project in a new solution. Henceforth we will refer to this project as bootloader.
- Select the "A C/C++ executable for..." from the corresponding package. Press Next, select your target device and keep pressing Next until your project is finished.
- Now create a second project in the same solution via File->New Project->Add the project to the current solution. Make sure it has a different name the the first project.
- Select the "A C/C++ executable for..." from the corresponding package. Press Next, select your target device and keep pressing Next until your project is finished. Henceforth we will refer to this project as application.
- You will be prompted with a couple of pop-ups notifying you, that some of the files already exist. Simply confirm the pop-ups.
- Now you have two projects in the same solution. Note: As the same project template was used for the creation of both projects some files are shared. Should you not want to use shared files in some instances make sure to duplicate and rename them for the project. This will for example be needed for the linker scripts.
- Now add your bootloader and application sources to the corresponding projects.
- Duplicate and rename the linker script (.icf file) and set each file in the corresponding project as linker script.
- Edit the memory regions and section/symbol placements in the linker script to fit your bootloader and application needs. For reference see the example project and the SEGGER Linker documentation.
- Now you should have a setup where you can debug bootloader and application independently.
- To make sure that the bootloader runs before your application project make sure to set Debug->Debugger->Start From Entry Point Symbol->No in the application project.
To learn how to use additional features that Embedded Studio offers which make the debug experience more enjoyable see the example project explanation below.
Example project explanation
The provided example project creates a Embedded Studio solution which contains two projects. One is the bootloader and the other the application project. The significant memory placement is as follows:
FLASH_START = 0x08000000 FLASH_TOTAL_SIZE = 0x00080000 BOOTLOADER_START = FLASH_START BOOTLOADER_SIZE = 0x00004000 APP_START = 0x08004000 APP_SIZE = 0x0007C000 BOOTHEADER = APP_START CRC_LOCATION = 0x0807FFFC
There are four source files and two linker scripts which are important in this example.
- main.c - Simple Systick interrupt sample
- bootheader.s - Defines an optional bootheader which is usually used to configure the bootloader or pass fixed information to the bootloader via the application.
- STM32F4xx_Flash_CCM_App.icf - Linker script for the application which places the bootheader and the SEGGER Linker calculated CRC. It also handles the generic placement of all application parts.
- main_boot.c - Prints the content of the bootheader from the application, calculates the CRC via the ST CRC peripheral. Compares the Linker and ST CRC, if successful jumps to the application.
- boot.s - Handles the jumping to the application.
- STM32F4xx_Flash_CCM_Boot.icf - Generic placement of the bootloader parts into the bootloader Flash section.
- Bootloader starts from Reset_Handler.
- Runs System Init.
- Read Bootheader from application image and print it.
- Calculate CRC over application Flash area.
- Compare calculated CRC with CRC provided by application, if they match boot application.
- Jump to Reset_Handler from application.
- Run System Init of application. Skip clock init, as this has been done by bootloader already.
- Set VTOR to vector table of application.
- Run main application which is a Systick timer sample which ticks every second and prints the tick count.
- Make the application project dependent on the bootloader project so the bootloader gets build and downloaded automatically each time the application debug session is started.
- In the bootloader project set the option Code->Linker->Additional Output Format to hex.
- In the application project set the option Code->Build->Project Dependencies to the bootloader project.
- In the application project set the option Debug->Loader->Additional Load File to the hex file of the bootloader project (you might need to have build the bootloader project once after setting it to generate the hex output to see the file)
- As mentioned in the tutorial make sure that setting Debug->Debugger->Start From Entry Point Symbol is set to No in the application project, so the bootloader is executed before the application.
- To make sure that the vector table of the application is used the VTOR register needs to be set. When using a SEGGER startup file it is sufficient to set the preprocessor define __VTOR_CONFIG.
- Typically a bootloader would handle the system clock init. To avoid doing the same init in the application project and you are using a SEGGER startup file simply set preprocessor define __NO_SYSTEM_INIT.
- The default I/O channel for e.g. printf messages in ES is SEGGER RTT and it is typically recommended to use that I/O channel for its fast speed, low size and simple usage. But if you require an I/O channel that can be used across both the bootloader and application project for debugging we recommend SWO. To enable SWO simply set project option Code->Library->Library I/O->SWO and Debug->Target Trace->Trace Clock Speed to the clock value of the CPU after the bootloader system core clock init. See the example project for reference. Keep in mind that SWO is only available on some Arm Cortex-M target devices and requires an additional pin, so it might not be available to your setup.
How to create a Solution with existing Aplication and Bootloader Project
- Start Embedded Studio with the Bootloader Solution.
- Right click the Solution and click Add Existing Project.
- Embedded Studio will copy the whole Solution of the application project.
- Drag and Drop the application project of the imported application solution into the bootloader solution.
- Remove the application solution.
- Set the following options to the bootloader project.
- Additional Output Format: hex.
- Libary I/O: SWO.
- Linker Script File: path to the customized linker script file.
- Start from Entry Point Symbol: Yes.
- Set the following options to the application project.
- Additional Load File: path to the output bootloader hex file.
- Libary I/O: SWO.
- Linker Script File: path to the customized linker script file.
- Project Dependencies: select the bootloader project.
- Project Directory: path to the source files of the application project.
- Start From Entry Point Symbol: No.
Make sure both projects had the same Trace Clock Speed set (Project Options -> Target Trace -> Trace Clock Speed.)
- If your CRC checks fail make sure that the CRC algorithm that you use in the bootloader is the same used in the SEGGER Linker and vice versa. Which algorithms are available can be found here: Integrity_checks_with_Embedded_Studio_and_SEGGER_Linker
- If your jump to application function does not work from the bootloader make sure that the values that you set for PC and SP are correct. On Arm Cortex-M for example these values are always the first two of a vector table. On other architectures they might be located somewhere else. Another reason this might fail could be that the application was not flashed yet.
Debug with Ozone
How to debug bootloader projects with Ozone is explained here: Debug_Bootloader_and_Application_in_same_Ozone_project