Bootloader for dsPIC33
The usual algorithm for using the bootloader for MK, just taken out of the package:
- using the programmer / debugger, the bootloader is flashed
- MK mounted on board
- using the bootloader, the main firmware is loaded on a predefined interface
This is quite acceptable for prototypes of products or for small-scale production. And what if large-scale production? Or is the assembly carried out by machines (or maybe people with the functionality of machines) - flashed, soldered? Then it is reasonable to remove the third point from the algorithm - to combine the main program and the bootloader in one firmware.
In my practice, I came across a rather creepy method of obtaining such firmware - a HEX file with the bootloader code was simply added to the HEX file of the main firmware. Of course. this approach has the right to be - whatever one may say, but the final “firmware of the name of another Frankenstein” worked as it should. But the feeling that there should be more correct methods to solve this problem did not leave me.
When I searched for solutions on the Internet, I was unpleasantly surprised that there was no simple and clear description of the solution. Actually, this was what prompted me to write a publication describing my solution to this situation. Perhaps my vision of solving this problem is different from the most correct one, but it is much more logical than stitching HEX files.
Before moving on to the topic of publication, 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. by default for this toolchain-a)
- the location of the bootloader and the main program are static
- the loader is located at 0x400
- The main program is located at 0x2000
- memory at addresses from 0x0200 to 0x0400 - unused
And most importantly, there will be no specific loader sources in this publication.
Linking the bootloader to the main project
Just compile ...
Let's start with the simplest case. Good Grandfather Frost sent you a ready-made loader for the New Year. And he already worked hard for fame and compiled and compiled it for you. So, in your hands (on a USB flash drive / network / hard drive) there is a file - UltraBoot3000.blob. Then the algorithm is very simple - just add it to your project.
With regards to the 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 also note that files with the extension “o” are also obtained during the compilation of your program. In order not to accidentally mix up and forget about the bootloader file, I recommend keeping it with a different extension, for example, blob - binary linked object. In order for the IDE to put the blob file in the "Object Files" category, you need to adjust the filter settings in this category. Right-click on this category and select "Filter ...". In the window that appears, in the field through the semicolon, add the filter template we need. In our case, the field should contain the following description of filters: After configuring the filters, you can add the loader file to the project.
*.o;*.blob
We start the comp process ... NO! STOP!
To correctly assemble the firmware with our bootloader, you need the correct linker script. Of course, if Grandfather Frost was so kind as to send this script to you, then just add it to your project (MPLAB IDE supports files with the “gld” extension), start the project build process and get the correct firmware file with the built-in code bootloader.
But what if Grandfather 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 slightly redo the script that is installed with the compiler. This script lies in the folder "$ {ToolChainPath} \ support \ dsPIC33F \ gld \". Note: hereinafter, $ {ToolChainPath} is the path where the C30 compiler was installed, by default it is "c: \ Program Files \ Microchip \ MPLAB C30 \". We copy the default script for our device from there, for example, for the dsPIC33FJ128GP802 MK, this will be the file “p33FJ128GP802.gld”.
The first step is to enter two characters that describe the beginning of the bootloader area and the main program. For example, like this: Next, in the MEMORY structure {...} in the program field, specify the initial 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:
_Booter = 0x000400;
_mainFW = 0x002000;
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 of "Reset Instruction". It is necessary that it looks as follows: It remains only to add a description of the bootloader zone. 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: So, the script is ready.
.reset :
{
SHORT(ABSOLUTE(_Booter));
SHORT(0x04);
SHORT((ABSOLUTE(_Booter) >> 16) & 0x7F);
SHORT(0);
} >reset
.boot _Booter :
{
*(.booter);
. = _mainFW - _Booter;
} >program = 0xFFFF
Create bootloader
Make bootloader from program
The first thing I would like to note: the bootloader should not be an independent program. Of course, in the process of debugging the bootloader, it can be implemented as a standalone program. But as soon as you plan to integrate it into another program, it must be specially prepared.
So, what the program loses, turning into a bootloader:
- Description of configuration bits
- Interrupt vectors
- Reset Vector
In addition, it is impossible to correctly embed the bootloader constants stored in flash-memory into the code of the main program. Therefore, they will also have to be abandoned. Note: in fact, there is a method, but it is so nontrivial that for mass use it is easier to refuse constants in the bootloader.
Finalization of the source code
The refinement is simple. We remove all the macros that describe the configuration bits. We exclude the use of global constants.
Project setup
It is also necessary to check and, if necessary, adjust the project settings. All changes are in the tab “MPLAB LINK30”, category “General”. Set check boxes: don't pack data template; don't create hanldes; don't create default ISR; remove unused sections.
Linker script refinement
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 the length of the program correspond to the beginning and the length of the bootloader: We completely delete the “Reset Instruction” description in the SECTIONS {...} structure. In the same structure we delete the description of “Configuration Words”. Completely delete the SECTIONS {...} structure, which describes the interrupt vectors (label “Section Map for Interrupt Vector Tables”). In the SECTIONS {...} structure, we refine the description of the ".text" zone, replacing the zone name with ".booter" and bringing it to the following form: Naturally, the resulting script must be added to the project.
{
data (a!xr) : ORIGIN = 0x800, LENGTH = 0x4000
program (xr) : ORIGIN = 0x400, LENGTH = 0x1C00
}
.booter 0x400 :
{
*(.init);
*(.user_init);
*(.handle);
*(.libc) *(.libm) *(.libdsp); /* keep together in this order */
*(.lib*);
*(.dinit);
*(.text);
} >program
Postprocessing the output file
After the previous steps, you can start the compilation process. In the output of the build process (for the MPLAB IDE it will be in the Output window, Build tab) you can see the result of the assembly. For example, like this: If there is more than one section in program memory, then most likely you have not completely completed the steps described above. If there is exactly one section with the name ".booter" - then everything is done correctly. You also need to pay attention to the number of sections in the data memory. Now you need to post-process the output file. Post-processing is carried out with a file with the extension “cof”. Open the command line in the folder with this file. Let's say the file has the name ultraboot.cof, then execute the command:
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%
"${ToolChainPath}\bin\pic30-strip.exe" -s --remove-section=.nbss --remove-section=bootdata -o ultraboot.blob ultraboot.cof
Do not forget to replace $ {ToolChainPath} with the 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).
Next, you need to conduct a final check of the resulting binary file with the loader. Command:
"${ToolChainPath}\bin\pic30-objdump.exe" -ht ultraboot.blob
The conclusion will be approximately the following: 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 is done correctly. And at the end of the link to examples of files for the linker:
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
- for the main project with the loader - drive.google.com/file/d/0B063O4zepkwsNDlCMkJ1S1ZxaE0/view?usp=sharing
- for the bootloader - drive.google.com/file/d/0B063O4zepkwsZU9Nck42cVoyWDA/view?usp=sharing