The bootloader is a very convenient tool for working with microcontrollers (hereinafter referred to as MK). This is a small program that allows MK "self-programming" (self-programming). Usually, when power is applied to the MK, the control first receives the bootloader, which will check the predefined conditions (a certain state on the MK leg, a flag in the EEPROM, a suitable firmware file on the SD card, etc.). If conditions are not met, then control is transferred to the main program. If the conditions are met, the bootloader switches to programming mode, receiving new firmware data over a predefined interface. This allows you to update the firmware MK without resorting to a soldering iron, programmer or in-circuit programming.
The usual algorithm for using the loader for MK, just taken out of the package:
- using the programmer / debugger, the boot loader is flashed
- MK is mounted in charge
- Using the bootloader on a predefined interface, the main firmware is loaded
This is quite acceptable for prototypes of products or for small-scale production. What to do if the production of large-scale? Or is the assembly carried out by automata (or maybe by people with the functionality of automata) - did it, solder it? Then it is reasonable to remove the 3rd point from the algorithm - to merge both the main program and the loader in one firmware.
In my practice, I came across a rather creepy method of obtaining such a firmware — a HEX file with a bootloader code was simply added to the main firmware HEX file. Of course. Such an approach has the right to be - whatever one may say, but the final “firmware named after Dr. Frankenstein” worked as it should. But the feeling that there should be more correct methods for solving this problem did not leave me.
')
When I looked for solutions on the Internet, I was unpleasantly surprised that there was no simple and clear description of the solution. Actually, this is what prompted me to write a publication describing my solution to this situation. Perhaps my vision of solving this problem differs from the most correct one, but it is much more logical than stitching HEX files.
Before turning to the topic of the publication itself, I want to give a list of simplifications and tools that were used:
- MPLAB IDE v8.85 (yes, a very outdated IDE)
- Microchip C30 Toolsuite v3.12
- object files in COFF format (i.e. the default for this toolchain)
- the location of the loader and the main program are static
- the bootloader is located at 0x0400
- the main program is located at 0x2000
- memory at addresses from 0x0200 to 0x0400 - unused
And most importantly, there will be no specific bootloader source code in this publication.
Layout loader in the main project
Just compile ...
Let's start with the simplest case. Good Grandfather Frost sent you a ready loader for the New Year. And he had already worked hard and compiled and assembled it for you. So, in your hands (on a flash drive / online / on a hard disk) there is a file - UltraBoot3000.blob. Then the algorithm is very simple - just add it to your project.
With regards to MPLAB IDE, it must be added to the Object Files category. Unfortunately, by default, only files with the extension “o” can be added to this category. I will also note that files with the extension “o” are also obtained during the compilation of your program. In order not to accidentally confuse and forget about the bootloader file, I recommend keeping it with another extension, for example, blob - binary linked object. In order for the IDE to put the blob file in the Object Files category, this category needs to adjust the filter settings. Click the right mouse button on this category and select the item "Filter ...". In the window that appears in the field, through a semicolon, we add the filter template we need. In our case, the field should contain the following description of the filters:
*.o;*.blob
After configuring the filters, you can add a boot file to the project.
Run the compy process ... NO! STOP!
To correctly assemble the firmware with our bootloader, you need the correct linker script. Of course, if Santa Claus was so kind that he sent you this script, then we simply add it to the project (MPLAB IDE supports files with the “gld” extension), start the project build process and get the correct firmware file with the embedded code loader.
But what to do if Grandpa forgot about this script or maybe you are the one who made this bootloader and you need to embed it in your / someone else's project? Read on ...
Preparing the linker script
Actually you should not write a script from scratch. It is enough to alter the script that is installed with the compiler. This script is located in the $ {ToolChainPath} \ support \ dsPIC33F \ gld \ folder. Note: hereinafter, $ {ToolChainPath} is the path where the C30 compiler was installed, by default it is “c: \ Program Files \ Microchip \ MPLAB C30 \”. Copy the default script for our device from there, for example, for the dsPIC33FJ128GP802 MC, this will be the file “p33FJ128GP802.gld”.
The first thing to do is to enter two characters describing the beginning of the bootloader area and the main program. For example:
_Booter = 0x000400;
_mainFW = 0x002000;
Further, in the MEMORY {...} structure, indicate in the program field the starting position (origin) and length (length) corresponding to the beginning of the bootloader and the size of the flash memory minus the start of the bootloader. Like that:
program (xr) : ORIGIN = 0x400,LENGTH = (0x15800 - 0x400)
The next step is to adjust the reset vector. In the structure of SECTIONS {...} we find the description "Reset Instruction". It is necessary that it looks like this:
.reset :
{
SHORT(ABSOLUTE(_Booter));
SHORT(0x04);
SHORT((ABSOLUTE(_Booter) >> 16) & 0x7F);
SHORT(0);
} >reset
It remains only to add a description of the zone loader. The zone is described in the SECTIONS {...} structure. This description must be inserted before the description of the “.text” zone. The description is as follows:
.boot _Booter :
{
*(.booter);
. = _mainFW - _Booter;
} >program = 0xFFFF
So, the script is ready.
Build Loader
Make a loader from the program
The first thing I would like to note: the loader should not be a standalone program. Of course, in the process of debugging the bootloader, it can be implemented as an independent program. But as soon as you plan to integrate it into another program, you need to specially prepare it.
So, what the program loses, turning into a bootloader:
- Configuration bits description
- Interrupt vectors
- Reset vector
In addition, it is impossible to correctly embed loader constants stored in flash-memory in the code of the main program. Therefore, they will also have to be abandoned. Note: in fact, there is a way, but it is so nontrivial that for mass use it is easier to refuse constants in the loader.
Source code refinement
Finalization is simple. Remove all macros describing configuration bits. Eliminate the use of global constants.
Project Setup
It is also necessary to check and, if necessary, adjust the project settings. All changes - in the “MPLAB LINK30” tab, category “General”. Install check boxes: don't pack data template; don't create hanldes; don't create default ISR; remove unused sections.
Completion of linker script
As well as for the main program with the loader, the script will be different from the default script. So, we take the default script and make the following changes.
The structure of MEMORY {...} is reduced to two positions: data and program. Moreover, the beginning and length of the program correspond to the beginning and length of the loader area:
{
data (a!xr) : ORIGIN = 0x800, LENGTH = 0x4000
program (xr) : ORIGIN = 0x400, LENGTH = 0x1C00
}
We completely remove the description of “Reset Instruction” in the SECTIONS {...} structure. In the same structure, delete the description “Configuration Words”. Completely remove the SECTIONS {...} structure, which describes interrupt vectors (label “Section Map for Interrupt Vector Tables”).
In the SECTIONS {...} structure, we modify the description of the “.text” zone, replacing the name of the zone with “.booter” and bringing it to the following form:
.booter 0x400 :
{
*(.init);
*(.user_init);
*(.handle);
*(.libc) *(.libm) *(.libdsp); /* keep together in this order */
*(.lib*);
*(.dinit);
*(.text);
} >program
Naturally, the resulting script must be added to the project.
Postprocessing the output file
After the previous steps, you can start the compilation process. In the output of the build process (for MPLAB IDE this will be in the Output window, Build tab) you can see the result of the build. For example:
Program Memory [Origin = 0x400, Length = 0x1c00]
section address length (PC units) length (bytes) (dec)
------- ------- ----------------- --------------------
.booter 0x400 0x7d0 0xbb8 (3000)
Total program memory used (bytes): 0xbb8 (3000) 27%
Data Memory [Origin = 0x800, Length = 0x4000]
section address alignment gaps total length (dec)
------- ------- -------------- -------------------
.nbss 0x800 0 0xa2c (2604)
bootdata 0x47c0 0 0x40 (64)
Total data memory used (bytes): 0xa6c (2668) 16%
If there is more than one section in program memory, then most likely you didn’t fully follow the steps described above. If there is exactly one section called “.booter”, then everything is done correctly.
You should also pay attention to the number of sections in data memory.
Now we need to postprocess the output file. Post processing is done with a file with the extension "cof". Open the command line in the folder with this file. Suppose the file is named ultraboot.cof, then execute the command:
"${ToolChainPath}\bin\pic30-strip.exe" -s --remove-section=.nbss --remove-section=bootdata -o ultraboot.blob ultraboot.cof
Do not forget to replace the $ {ToolChainPath} with a real path. The number of options “--remove-section = ...” should correspond to the number of sections in the data memory (from the output of the linker's output).
Next, you need to conduct a final check of the resulting binary file with the loader. Team:
"${ToolChainPath}\bin\pic30-objdump.exe" -ht ultraboot.blob
The output will be something like:
ultraboot.blob: file format coff-pic30
Sections:
Idx Name Size VMA LMA File off Algn
0 .booter 000007d0 00000400 00000400 00000058 2**1
CONTENTS, ALLOC, LOAD, CODE
SYMBOL TABLE:
no symbols
If you see that there are exactly one sections - with the name “.booter” and there are no symbols in the symbol table, then we can assume that everything has been done correctly.
And at the end of the link to the linker sample files: