This article explains what is meant by a "ROM Bootloader" (ROM BTL) of embedded devices and goes into detail on what needs to be considered when working with them, especially in regard to SEGGER's J-Link.
What is a ROM Bootloader
A ROM bootloader is some code programmed to read-only memory of an embedded device. That code is usually the very first thing that is executed after the chip is being powered - hence bootloader.
These ROM bootloaders have a variety of jobs, depending on what is needed for the actual MCU:
- Check different boot sources (internal flash, external QSPI flash, external NAND flash, ...), so where to start the user application from
- Make sure that the device is the state that is described as "reset state" in the technical reference manual of the device
- Make sure that all peripheral registers contain the so-called "reset values" described in the manual
- Evaluate the option bytes of the device and potentially open the debug interface, boot from a different source, setup flash partitioning, ... depending on the option bytes values
- Calibrate internal oscillators or peripherals of the chip
As the code is in read-only memory, it is programmed into the chip once during production by the manufacturer before being shipped to the customer. After that, it cannot be changed.
This procedure is present in most modern embedded devices as it provides silicon vendors with more flexibility when designing chips.
Instead of having to re-design and produce new silicon whenever the behavior of a device needs to be adjusted (i.e. a new chip revision is introduced), chip vendors can simply ship new devices with an updated ROM bootloader while the silicon stays the same.
Because of that, ROM bootloaders are different from one type of device to another and may even be different from one revision of a device to another revision of the same type of device.
When developing an application that should run on a device with a ROM BTL, it is important to know what the ROM BTL does in order to determine how and where to execute user code.
As mentioned above, what a ROM BTL does in detail is different from one device to another. It is recommended to look through documentation of the device in use and look for any explanation of the chip's boot sequence.
It is common for ROM BTL to verify the validity of a program image before starting to execute it.
Some bootloaders, for example, expect a so called bootheader, i.e. structured data at a predefined location in flash memory which provides the ROM BTL with some information like application entry point, size of application, application data checksum and so on.
Any data that needs to be programmed to the target device for the ROM BTL to be satisfied needs to be considered when (ideally before) designing the application image, as it may occupy memory at specific locations or similar. It is also important that this data is downloaded to the device in use, in addition to the actual "user application" (if not already present).
When debugging an application that is running on a device with a ROM BTL, it is important that the ROM BTL was executed before any user application code.
At the start of a debug session, it is common for IDEs to set the target's program counter (PC register) to the entry point of the user application (i.e. main()) after downloading and resetting the target.
This can cause issues on devices with a ROM BTL, because the ROM BTL may not have been executed correctly meaning that the chip was not fully initialized which can cause unexpected behavior when the user code is run.
What the IDE should do in cases like this is the following:
- Download the application image to the target
- Set a breakpoint at the first user application instruction (e.g. main())
- Reset the target device
- Let target device run to user application "by itself", ensuring that the ROM BTL is executed correctly
- Wait for the target to run into the breakpoint at the start of the application instruction
Some IDEs (like SEGGER's Embedded Studio) offer project options for enabling behavior like explained above:
SEGGER's Ozone requires project adjustments that are explained in a separate article: Ozone - Debugging on a target with ROM Bootloader
A common concept by many customers is: "Let's debug our application in RAM, until development is done and then move it to flash"
However, with a device implementing a ROM bootloader, this is no longer as easy as it sounds.
Imagine the following case:
- No valid application in any boot source
- ROM bootloader will stop booting at some point and remain in some kind of endless loop etc. in the boot ROM
- Debugger session is started and application is downloaded into RAM
- Debugger sets program counter and stack pointer to start of downloaded application
The developer will write the application in a way that it relies on what is called "reset state" or "reset values" described in the manual of the chip.
However, as the ROM bootloader did not find any valid application and stopped booting, it may also not have initialized or calibrated the peripheral clocks / registers / peripherals itself / ...
So the application now relies on a state the device simply is not in.
Possible effects range from none, over "Ethernet / USB / ... works unstable" up to random crashes of the chip.
These problems that are very time consuming to identify and usually they cannot be solved without having something "bootable"
in any of the boot sources the ROM bootloader checks, therefore making sure that the ROM bootloader boots through and fully initializes the chip.
So while debugging applications in RAM may work for simple applications that only toggle some PIOs / LEDs,
usually as soon as it gets more complex and peripherals like USB / ETH / ... are involved, things are more or less guaranteed to fail / work unstable.
Solution: When debugging in RAM is an absolute must have, then at least some valid boot header etc. should be programmed into flash once, to make sure that the ROM bootloader boots through and fully initializes the chip so that the values from the chip manual are matching the state the chip really is in.