After getting the whole robot assembled, I quickly realized that changing the firmware on each of the 12 servos was going to be a big annoyance. Doubly so, because the lateral servo programming ports were unreachable with this chassis design without disassembly. Thus, I bumped up a deferred piece of work to implement a bootloader that would allow for reflashing the primary application over the RS485 communication bus.
Pre-bootloader state
The moteus controller currently uses an STM32F446 controller, which has 512kb of flash memory. The memory map pre-bootloader looked like:
Sector | Size | Purpose |
0 | 16kb | ISR |
1 | 16kb | PersistentConfig |
2 | 16kb | unused |
3 | 16kb | unused |
4 | 64kb | application |
5 | 128kb | application |
6 | 128kb | application |
7 | 128kb | application |
The ISR section currently just contains the ISR table. It really isn’t even used beyond the reset vector, as the application moves the ISR table into RAM during initialization. The PersistentConfig section stores application settings and can be rewritten by the application. The application binary itself is stored in sectors 4-7.
Bootloader design
Rather than have a standalone bootloader, I decided to just implement something that assumed the controller was already configured properly and would be invoked directly from the application code. This has some advantages and disadvantages. The ups are that it means the bootloader can be much smaller. I don’t have to duplicate the setup code, or manage how to share settings like what the current baud rate or multiplex ID are. The downsides are that if the application code is bricked, then there will be no way to flash new firmware aside from the SWD port. Given that using the SWD port on rare occasions isn’t that big of a deal, I figured this was a fair trade.
As far as placement goes, I figured that sector 3 was a fair place to stick the bootloader for now. In the future, you could imagine saving a sector by putting it in sector 1, and actually hard-coding the ISR table to just include a reset vector. Currently, some of the ISR vectors are defined in flash, and others by the application in RAM. By forcing the application to define all of them in RAM, it means that there is no need to ever update the ISR table as part of updating the application. For now however, it was easiest to leave the status quo alone and continue to consider the ISR table as part of the application.
Since the bootloader can assume the part is already configured correctly, it is implemented as just a regular C linkage function with a linker script that includes none of the normal pre-main or main initialization logic. The application linker script forces that symbol to be defined at the start of sector 3, so that it can be just called as a regular function, albeit one that never returns.
The result
The resulting source is in a single translation unit (bootloader.cc / bootloader.h), although it does use some of the common libraries to help interoperate with the RS485 based protocol that moteus uses. I haven’t really tried to optimize the size at all, but it clocks in at 3900 bytes, which is well within the 16kb for sector 3.
moteus_tool has been enhanced to drive the flashing operation and doing all 12 servos currently takes around 5 minutes with no cable plugging or disassembly required. I could speed that up in a number of ways, but it is good enough for now.