7 ms·
From zero to main(): How to write a bootloader from scratch
- numlock86 6y agoA good start. It's fun until you realize Cortex M0 doesn't have a VTOR. Then again, remapping the vector table by hand isn't much work.
- fra 6y ago> It's fun until you realize Cortex M0 doesn't have a VTOR. One of the many annoying limitations of the M0. Thankfully ARM seems to agree, and they've added the VTOR(s) to the M23 (the ARMv8M successor to the M0).
- dmitrygr 6y agoC-M0+ has VTOR as an option and many implementations have it.
- numlock86 6y agoYes, there are probably some. I've worked with quite some M0 chips from Renesas, STM and Freescale and none of them had a VTOR. I heard (some?) M0s from NXP have it, though. For portability I always include manual remapping anyway.
- mmoskal 6y agoThe tutorial is about SAMD21 which is M0+ with VTOR. Simple bootloaders often don't need interrupts so on M0 one can get away with placing the bootloader at the end of flash and only rewriting the reset vector to lead to bootloader. Regular interrupts are then handled without any delays.
- kbumsik 6y agoA good real-world example is the Arduino bootloader. [1] Once you read the OP and learn basic idea, it is pretty simple. [1] https://github.com/arduino/ArduinoCore-samd/blob/master/bootloaders/zero/main.c https://github.com/arduino/ArduinoCore-samd/blob/master/boot...
- cozzyd 6y agoWell, this is the ArduinoCore on SAMD, which (unsurprisingly, since it's same chip) looks a lot like this tutorial.
- fra 6y agoA great open source bootloader is MCUBoot[1], if you're looking for something a little more substantial. [1] https://github.com/JuulLabs-OSS/mcuboot https://github.com/JuulLabs-OSS/mcuboot
- giu 6y agoNice little trick I found in your link: A poor man's sleep routine :) for (uint32_t i=0; i<2500; i++) /* 10ms */ /* force compiler to not optimize this... */ __asm__ __volatile__("");
- friendzis 6y agoPoor man's sleeps (various NOP loops) are just that. In all cases you should try and utilize on-chip sleep instruments, timers most probably. On a Cortex-sized MCU that is most probably as simple as installing appropriate interrupt handler and launching the timer by writing appropriate data to some register(s).
- yitchelle 6y agoI have to support this advice. Poor man's sleep is not really sleep as it is still executing NOPs. The on-chip sleep mechanism puts various parts of the silicon into an off-state. The target of sleep is to reduce current consumption when the hardware is idle.
- kbumsik 6y agoBut the example here is to simply delay, not to sleep for power consumption.
- 6y ago
- minipci1321 6y agoConveniently omits any mention of hardware initialization and related fallouts in HVM. Find it easy? Try to push all components -- interconnect, DDR and flash controllers, accelerators, etc -- to max performance and get the phone ready to hear from the factory about how the boards fail testing. Not all of the SoC blocks may be reconfigured later after the boot completed, some have to be done right in the very early stages of it.
- 2rsf 6y agoI wrote my last bootloader more than a decade ago, debugging problems is fun !
- dgellow 6y agoHow do you debug a bootloader?
- mras0 6y agoDepends on the target, but sometimes you can use your regular debugger. Otherwise good old printf debugging/blinking with LEDs/toggling output pins/staring at the code while crying depending on the circumstances.
- devthrowawy 6y agoOften a pentagram is required.
- cozzyd 6y agogdb along with a debug server from OpenOCD or JLink or whatever. Assuming the MCU has the proper debugging support. This works well on the Cortex-M0 at least (the annoying thing is you often have to switch between the bootloader .elf and the application .elf).
- jlokier 6y agoOn physical hardware, it helps a lot to have at least one simple GPIO which you can control by writing to a memory location or I/O port, or even better a UART output. Then you can toggle the GPIO output to see if some code was reached and/or a condition was met. If you have subroutines (or macros if you don't have a stack, or use a register to hold the return address), you can toggle a GPIO repeatedly to output characters, doing a UART in software if you don't have one already on the chip. It's a lot like "printf" debugging. You repeatedly compile and run the boot code, testing how it went using this basic output. To run the boot code might need a tool that lets you load it into memory beforehand. Some boards will only have flash EEPROM to boot from. There you run the boot code by repeatedly flashing your code into it. Until your CPU is running useful software, the flashing tool runs outside the chip you're coding for. Typically it doesn't take a large number of efforts to get something useful running. But to boot up fully, there might be tricky or subtle issues around initialising clocks, PLLs, even voltages, enabling a cache, switching from cache-as-RAM to using external RAM, initialising DRAM, bus signal training, negotiating protocols with other devices, that sort of thing. Some CPUs boot into a special state where some features don't work normally yet, a state that only exists during boot. Some are executing direct from flash, slowly, and need RAM initialised before they can start running from it. That kind of stuff is very similar to writing device drivers: There may be "mystical incantations" with magic numbers and particular instruction sequences, that you just have to find out from somewhere because they aren't obvious, or even because trial-and-error doesn't guarantee you reliable behaviour. For example some PLLs require a set of magic values to be loaded into registers. You could find the values experimentally, but you wouldn't have the same behaviour guarantees in all environmental conditions that you get from using values from the manufacturer. In practice, most boards come with a "BSP" (board support package) already from the manufacturer, so the tricky essentials are done already. Also, many of them have a boot loader which can "chain" to a second bootloader, which you can write or change as you wish without having to get the chip initialisation right, and sometimes you can load the second bootloader into RAM with an external tool, or over a UART, so no flashing required. There is also simulation. Many CPUs have simulators, so you can debug the boot loader step by step, stop it and inspect the program state, much like any other program. However, some simulators only simulate a device after the tricky stuff mentioned above is done and the CPU+memory is running "normally". The early boot state might not be simulated accurately or at all.
- peterkelly 6y agoNote to the author: On the latest Firefox for mac, the code samples for some reason show up as black on grey text and are almost impossible to read. I've noticed that if I remove 'Source Code Pro' from the font-family in the inspector for the code blocks, it looks fine.
- fra 6y agoThanks for letting me know! Let me dig into this.
- steveklabnik 6y agoThis whole website is really great; it has been a fantastic resource for me. https://interrupt.memfault.com/blog/cortex-m-fault-debug https://interrupt.memfault.com/blog/cortex-m-fault-debug in particular was really great.
- cozzyd 6y agoCompletely agree. Way better than than the crappy documentation you often get from vendors like Microchip.
- Cerium 6y agowith Microchip you get more than crappy docs. You get plenty of bugs in the hardware. After spending weeks finding one you report it and they say "Yeah, we know.". I still use them. Once you patch and work around all the bugs you can get somewhere nice. Some examples on pic24fj256gb108: The onboard BOR circuit cannot be trusted. One of the GPIO is listed in the data sheet as being a real GPIO, but the driver is missing because it was used for a USB feature.
- the_spacebyte 6y agoNot adding much to the discussion, just want to say how great this website is and its articles! They have been a fun read and fantastic resource!