pondělí 27. srpna 2012

Bootloader theory

Hello,
I have just realised that I didn't write any update last week, but I wouldn't have had much to write anyway as there was little progress.
I have been busy working on the boot-loader. It consisted mostly of trying different things and failing. At least I have become more familiar with writing linker scripts, assembly and using binutils.

The current bootloader

The current firmware is (of course) not without boot-loader. My task now is to extend the existing boot-loader and firmware to allow for programming over the RS485 bus. I may have alluded to how the current boot-loader operates in the previous posts, but I think it will be useful if I describe it here again in a coherent fashion, so that everybody will be able to at least somewhat understand what I am talking about and the changes I made.
Firstly I have to make yet another detour and explain a bit about how the linker works and why do we need it.

Linker 101

Your typical program consists of several things: Firstly, the code itself - machine instructions for the processor. Parts of the program containing these are labelled as .text in the object files. Then there are initialised and uninitialised variables, which are labelled as .data and .bss respectively. Constants are labelled as .rodata.
What linker does (described in very primitive terms) is to place these different parts of a program where you tell it to in the memory and set the symbolic links between these different parts according to this placement so that it all works together. The thing is, that we want our program to be placed in FLASH because it's non-volatile but we need our variables to be in RAM during runtime so that it will be possible to change their values. One more thing that has to be done when working with Cortex-M3 is to place a valid interrupt vector table at the address 0 (bottom of FLASH).
Our linker script therefore does the following:

  • Places the interrupt vector table (specially labelled as .ivec) at the FLASH bottom.
  • Places .text together with .rodata into FLASH. We don't really care where, but if not specified otherwise the linker will just put it right after .ivec.
  • Now comes the interesting part. The section .data containing the initialised variables has to be loaded into FLASH so that it doesn't get lost after reset but for runtime it has to be in RAM. To do this the linker script places is into FLASH but links it (resolves the symbolic references to this section) as if it was in RAM. To make this work, our boot-loader has to copy the data from FLASH to RAM before the main program runs.
  • As for the uninitialised variables .bss, it's enough to link them for RAM. There is nothing to be placed in FLASH.

What the current boot-loader does

The assembly language file Crt0.S contains the boot-loader code and definition of the interrupt vector table.
The interrupt vector table basically only has to do one thing - set the reset interrupt entry to point to the start of the boot-loader code, so that it will be automatically called after the reset. For our purposes the boot-loader and the reset interrupt handler are therefore the same thing. The rest of the entries in the table are pointed to a dummy handler which contains only an infinite loop to prevent the micro from going crazy in case any of the other interrupts are asserted.
Then there is the code of the boot-loader itself. It does the three following things:

  1. copies the initialised variables from FLASH to RAM. 
  2. Zeroes out the .bss section (this is strictly speaking not necessary, but everybody else does it so I guess it's a good practice) 
  3. Calls the main function.

The 3 boot-loader drafts

Now that we have explained how the current boot process works I will describe what me and Mr. Svoboda have came up with when discussing our approach to writing the new boot-loader. Basically, we came up with three drafts of how it could operate. The first one being the simplest to implement but the least useful and flexible and the last one being the most complex one:

  1. The whole program will be loaded in FLASH but right after reset it will copy itself into RAM and run from there allowing it to rewrite itself in FLASH. After reset the new version of the program will run. This will be probably useful for experiments and testing only as there is no way that the whole program with all of its intended functions will fit just in RAM.
  2. The program will be divided into two parts: lib and src. Lib will contain the boot-loader code and other code necessary for its function. Rest of the firmware will be placed in src. The idea is that lib will never be modified but it will be able to overwrite src. This would mean that lib would have to be 100% (I dare say maybe even 110%) finalised and debugged before we implement this solution.
  3. The program will be placed in FLASH, as usual, but after receiving the command to enter boot-loader mode it will copy the necessary parts of itself into RAM and start executing from there, allowing it to overwrite itself in its entirety in FLASH and run the new version of itself after reset. 

Again, this is getting longer than expected so I will stop here for now and write about what I actually implemented and how tomorrow.

Žádné komentáře:

Okomentovat