A ROM Bootloader on the NXP FRDM-KL03Z
Here's everything you wanted to know about successfully using a ROM Bootloader on your boards, focusing on the NXP FRDM-KL03Z.
Join the DZone community and get the full member experience.
Join For FreeA bootloader on a microcontroller is a very useful thing. It allows me to update the firmware in the field if necessary. There are many ways to use and make a bootloader (see Serial Bootloader for the Freedom Board with Processor Expert). But such a bootloader needs some space in FLASH, plus it needs to be programmed first on a blank device, so a JTAG programmer is needed. That’s why vendors have started including a ROM bootloader into their devices: the microcontroller comes out of the factory with a bootloader in FLASH. So instead writing my bootloader, I can use the one in the ROM.
And as with everything, there are pros and cons of that approach.
Several of the newer NXP Kinetis microcontrollers include a ROM bootloader. I have selected the NXP FRDM-KL03Z board for my experiments because it is inexpensive (less than $20) and a tiny microcontroller useful for smaller applications. And that KL03Z is present on the Ultimate Hacking Keyboard which sparked my interest. In this article I’ll share my experience and journey to use the ROM bootloader on the KL03Z (ARM Cortex-M0+, 32 KByte of FLASH. 2 KB SRAM in a 24QFN Package):
The source code used in this article can be found on GitHub (see Links section at the end).
Bootloader? Bootloader(s)!
The NXP Kinetis bootloader (KBOOT software) is available in version v2 (at the time of this article) on www.nxp.com/kboot. NXP provides three different variants:
- Flashloader: That bootloader is a ‘one-time-bootloader’ and resides in FLASH, either factory or user programmed. The bootloader area will be overwritten by the application, so the full FLASH memory is available to the application. But it is only possible to update the application one time, unless the application itself includes a bootloader. The Flashloader is interesting for programming devices without the need for a JTAG programmer. The KBOOT package comes with several example applications.
- Flash-Resident Bootloader: This bootloader is a ‘traditional’ bootloader that resides in FLASH memory. The source code is provided and I can fully configure it, but I initially needa JTAG programmer to program the device. The KBOOT package includes several examples for different boards.
- ROM Bootloader: This bootloader is present on all Kinetis devices with a boot ROM, including the KL03Z. Basically, it is a special version of the Flash-Resident Bootloader programmed into the ROM of the device. But no source files or any other information apart of the information in the reference manual is provided.
There are different ways how to communicate with the bootloader (I²C, SPI, UART, USB HID, USB MSD, CAN). The bootloader uses a special communication protocol, and the KBOOT software includes GUI and command line utilities to communicate with the device.
ROM Bootloader
The ROM Bootloader resides in the ROM starting at 0x1c00200 and uses some part of the RAM while the bootloader is running:
It would be useful to have the sources and memory map of the ROM bootloader available. That way, it would be possible to re-use some of the bootloader functions from the application to save FLASH space.
The bootloader could be called by the application with the following piece of code:
static void RunRomBootloader(void) {
uint32_t runBootloaderAddress;
void (*runBootloader)(void *arg);
/* Read the function address from the ROM API tree. */
runBootloaderAddress = **(uint32_t **)(0x1c00001c);
runBootloader = (void (*)(void * arg))runBootloaderAddress;
/* Start the bootloader. */
runBootloader(NULL);
}
With the above code, the application can enter the bootloader any time.
Booting
During POR (Power on Reset) or a normal reset, it can be configured if the microcontroller enters the ROM bootloader or directly boots from the normal FLASH.
This is controlled by several things:
- FORCEROM: Two bits used to always enter the ROM bootloader. If the bits are not zero, it always enters the ROM bootloader.
- BOOTPIN_OPT: Part of the FOPT register. If this bit is zero, it enables checking an optional bootloader enable pin (NMI in the case of the KL03Z): if that pin is pulled low, it enters the ROM bootloader.
-
BOOTCFG0 pin: This is the pin enabled by the BOOTPIN_OPT setting (NMI pin for the KL03Z).
- BOOTSRC_SEL: Part of the FOPT register. If set to 10 or 11, then it enters the ROM bootloader:
Below is the diagram of the boot process:
Entering Bootloader
So the ROM bootloader is entered if:
- FORCEROM is set non-zero
- Or: A bootloader pin is configured with BOOTPIN_OPT and that pin is asserted (NMI)
- Or: BOOTSRC_SEL is set to 10 or 11
The NMI pin is on pin 19 of the KL03Z32VFK4:
On the FRDM-KL03Z the NMI pin is routed to the SW3 push button:
The button is present on the FRDM-KL03Z board here:
Bootloader Pin Muxing
As outlined above, the NMI pin can be used to enter the bootloader. On which pins the bootloader can communicate is documented in the reference manual. The KL03Z can communicate over UART, SPI and I²C:
The UART (PTB1, PTB2) is routed to the OpenSDA USB CDC of the K20, and the the I2C (PTB3, PTB4) are available on the J2 header:
Bootloader Configuration Area (BCA)
The bootloader is configured by a structure located at 0x3C0 in FLASH (after the vector table starting at address 0x0:
BCA and Linker File
To configure the bootloader and the BCA, I use the following implementation:
Note that the bootloader will select the first ‘working’ communication channel! I had an issue that on the I2C bus another device was sending data during bootloader boot-up, causing the bootloader listening to the I2C bus instead to the UART. I recommend only to enable the needed communication channels in the BCA settings.
/* bootloader_config.c
* KBOOT ROM Bootloader configuration for FRDM-KL03Z Board
*/
#include <stdint.h>
#define ENABLE_BCA (1)
/*!< 1: define Bootloader Configuration Area mit magic number to use it from ROM bootloader; 0: use default setting (no BCA) */
typedef struct BootloaderConfiguration
{
uint32_t tag; //!< [00:03] Magic number to verify bootloader configuration is
//! valid. Must be set to 'kcfg'.
uint32_t crcStartAddress; //!< [04:07] Start address for application image CRC
//! check. If the bits are all set then Kinetis
//! bootloader by default will not perform any CRC
//! check.
uint32_t crcByteCount; //!< [08:0b] Byte count for application image CRC
//! check. If the bits are all set then Kinetis
//! bootloader by default will not prform any CRC check.
uint32_t crcExpectedValue; //!< [0c:0f] Expected CRC value for application CRC
//! check. If the bits are all set then Kinetis
//! bootloader by default will not perform any CRC
//! check.
uint8_t enabledPeripherals; //!< [10:10] Bitfield of peripherals to enable.
//! bit 0 - LPUART, bit 1 - I2C, bit 2 - SPI,
//! bit 3 - CAN, bit 4 - USB
//! Kinetis bootloader will enable the peripheral if
//! corresponding bit is set to 1.
uint8_t i2cSlaveAddress; //!< [11:11] If not 0xFF, used as the 7-bit I2C slave
//! address. If 0xFF, defaults to 0x10
//! for I2C slave address.
uint16_t peripheralDetectionTimeoutMs; //!< [12:13] Timeout in milliseconds
//! for active peripheral detection. If
//! 0xFFFF, defaults to 5 seconds.
uint16_t usbVid; //!< [14:15] Sets the USB Vendor ID reported by the device
//! during enumeration. If 0xFFFF, it defaults to 0x15A2.
uint16_t usbPid; //!< [16:17] Sets the USB Product ID reported by the device
//! during enumeration.
uint32_t usbStringsPointer; //!< [18:1b] Sets the USB Strings reported by the
//! device during enumeration.
uint8_t clockFlags; //!< [1c:1c] The flags in the clockFlags configuration
//! field are enabled if the corresponding bit is cleared (0).
//! bit 0 - HighSpeed Enable high speed mode (i.e., 48 MHz).
uint8_t clockDivider; //!< [1d:1d] Inverted value of the divider to use for
//! core and bus clocks when in high speed mode.
} bootloader_config_t;
/* bits for enabledPeripherals */
#define ENABLE_PERIPHERAL_UART (1<<0)
#define ENABLE_PERIPHERAL_I2C (1<<1)
#define ENABLE_PERIPHERAL_SPI (1<<2)
#define ENABLE_PERIPHERAL_CAN (1<<3) /* not supported for KL03! */
#define ENABLE_PERIPHERAL_USB_HID (1<<4) /* not supported for KL03! */
#define ENABLE_PERIPHERAL_USB_MSC (1<<7) /* not supported for KL03! */
/* Bootloader configuration area, needs to be at address 0x3C0! */
__attribute__((section(".BootloaderConfig"))) const bootloader_config_t BootloaderConfig =
{
#if ENABLE_BCA
.tag = 0x6766636B, //!< Magic Number
#else
.tag = 0xFFFFFFFF, //!< No Magic Number
#endif
.crcStartAddress = 0xFFFFFFFF, //!< Disable CRC check
.crcByteCount = 0xFFFFFFFF, //!< Disable CRC check
.crcExpectedValue = 0xFFFFFFFF, //!< Disable CRC check
.enabledPeripherals = ENABLE_PERIPHERAL_UART, //ENABLE_PERIPHERAL_UART|ENABLE_PERIPHERAL_I2C|ENABLE_PERIPHERAL_SPI|ENABLE_PERIPHERAL_CAN|ENABLE_PERIPHERAL_USB_HID|ENABLE_PERIPHERAL_USB_MSC, //!< Enabled Peripheral: UART I2C SPI CAN USB-HID
.i2cSlaveAddress = 0x10, //!< Use default I2C address(0x10)
.peripheralDetectionTimeoutMs = 5000, //!< Use user-defined timeout(ms)
.usbVid = 0xFFFF, //!< Use default Vendor ID(0x15A2)
.usbPid = 0xFFFF, //!< Use default Product ID(0x0073)
.usbStringsPointer = 0xFFFFFFFF, //!< Use default USB String
.clockFlags = 0xFF, //!< 0 bit cleared: Enable High speed mode. NOTE: Enabling high speed mode makes UART connection worse or requires pull-up on Rx line!
.clockDivider = 0xff, //!< Use clock divider(0)
};
/* 16 bytes at address 0x400 */
__attribute__((used, section(".FlashConfig"))) const uint32_t FOPTConfig[4] = {
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
// 0xFFFF3DFE // boot from FLASH
0xFFFFBDFE // boot from ROM, means this will kick in the bootloader by default
};
Be careful with the FlashConfig (FOPT) configuration! If not done right, you might brick your part! See “How (not) to Secure my Microcontroller“
Using the macro:
#define ENABLE_BCA (1)
/*!< 1: define Bootloader Configuration Area mit magic number to use it from ROM bootloader; 0: use default setting (no BCA) */
I can turn on/off the BCA. If turned off (set to 0), the BCA signature will not be used, and the bootloader ignores the BCA.
The BootloaderConfig variable needs to be allocated in FLASH at address 0x3C0. For this I have allocated a MEMORY area
m_bootloader_config (RX) : ORIGIN = 0x000003C0, LENGTH = 0x20 /* ROM Bootloader configuration */
with the corresponding placement in the linker file (see Defining Variables at Absolute Addresses with gcc):
/* placing my named section at given address: */
.myBootloaderConfigBlock :
{
KEEP(*(.BootloaderConfig)) /* keep my variable even if not referenced */
} > m_bootloader_config
Same for the Flash Configuration (FOPT): declared a memory area:
m_bootloader_config (RX) : ORIGIN = 0x000003C0, LENGTH = 0x20 /* ROM Bootloader configuration */
And placed it in the linker section like this:
.flash_config :
{
. = ALIGN(4);
KEEP(*(.FlashConfig)) /* Flash Configuration Field (FCF) */
. = ALIGN(4);
} > m_flash_config
Erasing BCA and FLASH
If that configuration area is not properly set up, then running the bootloader might fail. I highly recommend to erase the KL03Z flash starting playing with the FRDM-KL03Z board, as the preloaded demo application seems to confuse the bootloader. To erase the FLASH, I’m using the SEGGER OpenSDA firmware and the SEGGER J-Link J-Flash Lite program which is free of charge for non-production purposes:
Select the device:
Then erase the chip:
With this, the BCA is erased and set to default 0xFF memory pattern.
Connecting to Bootloader Over UART
To make an initial connection to the ROM bootloader after erased the FLASH, connect a USB cable to the OpenSDA USB port:
Determine the virtual COM port (e.g. using the Windows Device Manager):
Then do a Reset while the SW3/NMI pin pulled LOW:
- Press and hold the Reset button
- Press and hold the SW3 button
- Release the Reset button while still holding the SW3 button
- Release the SW3 button
There is no visual sign at all to show that the microcontroller is in bootloader mode now.
NOTE: There are several issues with the ROM bootloader, see the following errata:
Because of this, I only was able to connect with 19200 baud (and not always!). So make sure you are using 19200 baud!
To connect to the board use the KinetisFlashTool (GUI) or blhost (command line tool).
Both the used communication channel (I2C, UART, SPI) and the speed (baud) is selected at the first communication attempt. To change the communication speed or communication channel, the KL03Z has to be reset first!
The KinetisFlashTool (for Windows) is located in
<KBOOT Installation path>\NXP_Kinetis_Bootloader_2_0_0\bin\Tools\KinetisFlashTool\win
The blhost tool is located in
<KBOOT Installation path>\NXP_Kinetis_Bootloader_2_0_0\bin\Tools\blhost
Then press ‘Connect’, and hopefully it connects (otherwise retry):
With the blhost command line tool, use
blhost --port COM9,19200 -d -- get-property 1
Make sure that the COM port is not already used (close the KinetisFlashTool).
The -d option produces some debug output, and with get-property 1 I ask for the bootloader version information.
The first command after power-on might be lost (yet another entry in the errata): In that case, cancel the command with CTRL-C:
blhost --port COM9,19200 -d -- get-property 1
Warning: Operation canceled!
- The target device must be reset before sending any further commands.
^C
Then try it again, the second time it usually works:
blhost --port COM9,19200 -d -- get-property 1
[5a a6]
<5a>
Ping responded in 1 attempt(s)
<a7>
<00 00 01 50 00 00 29 ae>
Framing protocol version = 0x50010000, options = 0x0
Inject command 'get-property'
[5a a4 0c 00 4b 33 07 00 00 02 01 00 00 00 00 00 00 00]
<5a>
<a1>
<5a>
<a4>
<0c 00>
<07 7a>
<a7 00 00 02 00 00 00 00 00 00 01 4b>
Successful response to command 'get-property(current-version)'
- took 0.009 seconds
[5a a1]
Response status = 0 (0x0) Success.
Response word 1 = 1258356736 (0x4b010000)
Current Version = K1.0.0
Success! We are able to talk with the bootloader!
I was further able to improve above situation after considering the e8086 in the errata:
So I added a 10K pullup to the LPUART Rx pin:
Enabling Higher BAUD for UART Connection
With lots of trial-and-error, I managed to get a stable bootloader UART connection. Here is what I did:
- Added 10k Pull-Up resistor to LPUART Rx Pin
- Setting BCA clock flags to 0xFE: .clockFlags = 0xFE
- Setting BCA cock divider to 0xFF: .clockDivider = 0xff
- Reset and enter bootloader with above settings
That way I have been able to use for example 57600:
blhost --port COM9,57600 -d -- get-property 0x2
[5a a6]
<5a>
Ping responded in 1 attempt(s)
<a7>
<00 00 01 50 00 00 29 ae>
Framing protocol version = 0x50010000, options = 0x0
Inject command 'get-property'
[5a a4 0c 00 3e fb 07 00 00 02 02 00 00 00 00 00 00 00]
<5a>
<a1>
<5a>
<a4>
<0c 00>
<2d c6>
<a7 00 00 02 00 00 00 00 01 00 00 00>
Successful response to command 'get-property(available-peripherals)'
- took 0.013 seconds
[5a a1]
Response status = 0 (0x0) Success.
Response word 1 = 1 (0x1)
Available Peripherals = UART
Loading a Binary With the Bootloader
I have put on GitHub three blinky programs you can use to test the bootloader:
If using the Flash tool, browse for the image file and use the Update button:
The same with the blhost command line utility looks like this:
blhost --port COM9,57600 write-memory 0 "FRDM-KL03Z_LED_blue.bin"
If using S19/S-Record files, there is the ‘flash-image’ command:
blhost --port COM9,57600 flash-image "FRDM-KL03Z_LED_blue.srec"
However, I had bad luck with these, they returned an error: kStatus_FlashCommandFailure
blhost --port COM9,57600 write-memory 0 "c:\tmp\FRDM-KL03Z_LED_blue.bin"
Ping responded in 1 attempt(s)
Inject command 'write-memory'
Preparing to send 14784 (0x39c0) bytes to the target.
Successful generic response to command 'write-memory'
(1/1)24%Data phase write aborted by status 0x2712 kStatus_AbortDataPhase
Response status = 105 (0x69) kStatus_FlashCommandFailure
Wrote 3616 of 14784 bytes.
Only until by trial-and-error I have found out that if I do an flash-erase-all first it succeeds:
blhost --port COM9,57600 -d -- flash-erase-all
[5a a6]
<5a>
Ping responded in 1 attempt(s)
<a7>
<00 00 01 50 00 00 29 ae>
Framing protocol version = 0x50010000, options = 0x0
Inject command 'flash-erase-all'
[5a a4 08 00 0c 22 01 00 00 01 00 00 00 00]
<5a>
<a1>
<5a>
<a4>
<0c 00>
<2d 84>
<a0 00 08 02 00 00 00 00 01 00 00 00>
Successful generic response to command 'flash-erase-all'
- took 0.034 seconds
[5a a1]
Response status = 0 (0x0) Success.
Then do the programming:
blhost --port COM9,57600 write-memory 0 "c:\tmp\FRDM-KL03Z_LED.bin"
Ping responded in 1 attempt(s)
Inject command 'write-memory'
Preparing to send 17544 (0x4488) bytes to the target.
Successful generic response to command 'write-memory'
(1/1)100% Completed!
Successful generic response to command 'write-memory'
Response status = 0 (0x0) Success.
Wrote 17544 of 17544 bytes.
I have no explanation, as nothing in my flash was protected or secured. At least I have found a way to get it working
Summary
A ROM bootloader is a nice thing: I don’t need to reserve FLASH space for the bootloader. Instead the bootloader is burned into the ROM and I can communicate to it with I²C, UART and SPI on the NXP KL03Z. Be aware of the errata, and with the right steps and connection settings it should hopefully work.The KL03Z device errata affects several parts of the ROM bootloader, requiring some workarounds.
What’s next? I’m currently exploring and using BusPal: This part is really interesting, as it allows to use a microcontroller to interface with the bootloader of another microcontroller. That way, one device can update another. The NXP provided projects are for IAR and Keil, but I have managed to port BusPal to the FRDM-KL25Z using free and open GNU tools. So this could be the topic of a future article?
I hope this article is useful for you to get started with the bootloader on the FRDM-KL03Z or any other board. And don’t forget to always check the errata of your device.
Happy Bootloading!
Links
- Kinetis Bootloader and Tools download page: www.nxp.com/kboot
- Source code used in this article on GitHub: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL03Z/FRDM-KL03Z_LED
- NXP Freedom boards: http://www.nxp.com/freedom
- FRDM-KL03Z board: http://www.nxp.com/products/software-and-tools/hardware-development-tools/freedom-development-boards/freedom-development-platform-for-kinetis-kl03-mcus:FRDM-KL03Z
- FRDM-KL03Z errata: http://www.nxp.com/docs/en/errata/KL03Z_1N86K.pdf
- Ultimate Hacking Keyboard: https://ultimatehackingkeyboard.com/
- Serial Bootloader: Serial Bootloader for the Freedom Board with Processor Expert
- Terminal program for bootloaders: Swiss Army Knife of Terminal Program for Serial Bootloaders
Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments