OpenBLT 1.20.0 release notes

The OpenBLT 1.20.0 release was made today, after another half year of development work. 6 tickets were processed, which resulted in 16 commits. Feel free to download the new version of the OpenBLT bootloader and give it a try. This release is on track with the standard release cycle.

Roadmap of the OpenBLT 1.20.0 release that shows progress is at 100% and the release ahead of schedule.

“A shiny new feature and overall maintenance and improvements” best summarizes the new OpenBLT release. This article provides detailed information of what you can expect from the newly released OpenBLT version 1.20.0.

Firmware info check feature

Imagine the following situation: While starting a firmware update, you accidentally selected the wrong firmware file. Perhaps one meant for a completely different product, yet based on a similar microcontroller. If this one gets programmed, it could have disastrous consequences. For example, if GPIOD-pin1 on product A is connected to an LED and on product B connected to a hydrogen purge valve. Instead of toggling an LED, hydrogen is purged. A bit of an extreme example, yet not impossible. With the help of the firmware info check feature, you can prevent the wrong firmware from being programmed on your product. The OpenBLT 1.20.0 release includes this new firmware info check feature.

Firmware info table

The firmware info check feature builds on the assumption that you add a small info table at a fixed memory location to your firmware. The contents of this info table are completely up to you. The same goes for its memory location, as long as it is always at the same location:

Illustration that shows where the firmware info table could go in the memory map.

A code example of how to declare an info table:

struct firmwareInfoTable
{
  unsigned long tableId;         /**< fixed value for identification. */
  unsigned long productId;       /**< product identification.         */
  unsigned long firmwareVersion; /**< firmware version.               */
};
 
const struct firmwareInfoTable firmwareInfoTable __attribute__ ((section (".infoTable"))) =
{
  .tableId = 0x9A4B8107UL,
  .productId = 1234UL,
  .firmwareVersion = 10429UL
};

Then use the infoTable attribute inside your project’s linker script to control where the linker places the info table inside your firmware. You could place the info table all the way at the end of flash or right after the vector table. It’s up to you, as long as it is always located at this location, also in future versions of your firmware.

Enable and configure the firmware info check feature

With the info table added to your firmware, you continue by enabling and configuring this feature in the bootloader’s blt_conf.h configuration header-file:

  • BOOT_INFO_TABLE_ENABLE ⇒ Set to 1 to enable the firmware info check feature.
  • BOOT_INFO_TABLE_LEN ⇒ Set to the length in bytes of the info table you added to your firmware.
  • BOOT_INFO_TABLE_ADDR ⇒ Set to the base address where the info table is located in your firmware.

Example:

Screenshot of the bootloader's blt_conf.h configuration header file. It shows how to configure the firmware info check feature that is part of the new OpenBLT 1.20.0 release.

With the firmware info check feature enabled and configured, the following now happens during a firmware update:

  1. The firmware update PC tool (MicroBoot or BootCommander) requests the info table base address and its length from the OpenBLT bootloader.
  2. The firmware update PC tool extracts the firmware into table from the firmware file selected for the firmware update, and downloads it to a RAM buffer inside the bootloader.
  3. The bootloader calls hook-function InfoTableCheckHook() while passing the pointers of:
    • The RAM buffer where the info table of the to-be-programmed firmware was downloaded to.
    • The address of the firmware info table of the currently programmed firmware.
  4. The return value of hook-function InfoTableCheckHook() determines if the bootloader allows the firmware update to proceed.

Implement the info table check hook-function

All that is left to do for you is implement the InfoTableCheckHook() hook-function, which compares the two info table contents and decides if the firmware update is allowed to proceed. Example:

/*******************************************************************************//**
** \brief     Callback that gets called at the start of the firmware update, before
**            performing erase and program operations on non-volatile flash. It 
**            enables you to implement info table comparison logic to determine if
**            the firmware update is allowed to proceed. Could for example be used
**            to make sure a firmware update only goes through if the selected
**            firmware file contains firmware for the correct product type.
** \param     newInfoTable Address of the opaque pointer to the info table that was
**            extracted from the firmware file that was selected for the firmware
**            update.
** \param     oldInfoTable Address of the opaque pointer to the info table of the
**            currently programmed firmware.
** \return    BLT_TRUE if the info table check passed and the firmware update is
**            allowed to proceed. BLT_FALSE if the firmware update is not allowed
**            to proceed.
**
***********************************************************************************/
blt_bool InfoTableCheckHook(blt_addr newInfoTable,  blt_addr currentInfoTable)
{
  blt_bool result = BLT_FALSE;
  /* Important: This structure must have the same layout as the actual info table
   * in the firmware itself.
   */
  struct firmwareInfoTable
  {
    blt_int32u tableId;          /**< fixed value for identification. */
    blt_int32u productId;        /**< product identification.         */
    blt_int32u firmwareVersion;  /**< firmware version.               */
  };
 
  /* Cast addresses of opaque pointers to info table pointers. */
  struct firmwareInfoTable const * newInfoTablePtr;
  struct firmwareInfoTable const * currentInfoTablePtr;
  newInfoTablePtr     = (void const *)newInfoTable;
  currentInfoTablePtr = (void const *)currentInfoTable;
 
  /* Sanity check on the configured length of the info table at compile time. */
  ASSERT_CT(BOOT_INFO_TABLE_LEN == sizeof(struct firmwareInfoTable));
 
  /* Do table IDs match? The table ID identifies the tables as firmware info
   * tables.
   */
  if (currentInfoTablePtr->tableId == newInfoTablePtr->tableId)
  {
    /* Only allow the firmware update to proceed if it's firmware for the same
     * product type.
     */
    if (currentInfoTablePtr->productId == newInfoTablePtr->productId)
    {
      /* Allow the firmware update to proceed. */
      result = BLT_TRUE;
    }
  }
 
  /* Give the result back to the caller. */
  return result;
}

Getting started with the firmware info check

As you can see in the above description and the presented examples, it does not take much to configure and use the new firmware info check feature. I personally think everyone who uses the OpenBLT bootloader benefits from this and should consider using this feature.

To help you get started with using the firmware info check feature yourself, you can find further details on the firmware info check Wiki page. Additionally, the Olimexino-STM32F3 demo programs have this feature enabled and configured and can be used as a reference. This board was selected because it is configured for firmware updates from both a PC (using MicroBoot or BootCommander) and from an SD-card, to show that this feature works in both use cases. Also note that there are demos for this board for different toolchain (STM32CudeIDE, IAR, Keil). That might come in handy when it is time to configure the linker script for placing the firmware info table as a specific memory location.

Having an info table present in your firmware also opens the path for implementing extra functionality. Think along the lines of firmware signing. A simple example would be to run a post-build tool to calculate an MD5 hash of the firmware data and then patch it into an element of the info table. In the OpenBLT bootloader, you can then implement functionality inside hook-function CpuUserProgramStartHook() to recalculate and verify this MD5 hash to decide if it’s actually okay for the bootloader to start your firmware.

STM32H7 multicore support

A couple of month ago I got my hands on a Riverdi 70STM32H7 board for a customer’s bootloader project. The board features a 7″ TFT display and, more importantly, an STM32H757XI microcontroller. This is one of those STM32H7 devices that feature a dual-core CPU consisting of a Cortex-M7 (CPU1) and a Cortex-M4 (CPU2). It was the first time working with such a dual core STM32H7 microcontroller and I was curious to find out how that works together with the OpenBLT bootloader. It turns out, it just works. No actual changes were needed to the OpenBLT bootloader itself. The following description presents the best way to run the OpenBLT bootloader on such dual-core STM32H7 microcontrollers.

Each CPU has its firmware in its own flash region. By default, the first flash part is assigned to CPU1 and the second part to CPU2. The bootloader itself always runs on CPU1 after power-on and reset events. The bootloader is able to reprogram the entire flash memory, including the part used by CPU2. For this to work properly, CPU2 should not access flash memory during firmware update operations. For this reason, the User Configuration Option Byte must be configured to disable the CM4 from booting.

Note that this is a different setting than the factory default. The easiest way to change this setting is by using the free STM32CubeProgrammer software in combination with an ST-Link debugger interface. This is what the BCM4 configuration bit should look like:

STM32CubeProgrammer screenshot that highlights how to disable the STM32H7 dual core's CM4 from booting.

If you plan on using just CPU1 in your own user program, you can stop here. If you plan on using both CPU1 and CPU2, you need to now manually start the CPU2 core. The is best done in your CPU1 firmware at the start of function main() by calling HAL_RCCEx_EnableBootCore(RCC_BOOT_C2):

Code screenshot that shows how to manually start the STM32H7 dual core's CM4 core.

When performing a firmware update on such a multicore device, you can either update the firmware on one core at a time or update them all at the same time (recommended). Note that you can use the free srec_cat tool to combine multiple firmware files into one large one, e.g.:

  • srec_cat firmware_cpu1.srec -Motorola firmware_cpu2.srec -Motorola -o firmware_all.srec -Motorola

MicroTBX-Modbus updated

Ever since OpenBLT version 1.17.0, the bootloader supports firmware updates via Modbus-RTU. When developing the Modbus-RTU support, I wanted a Modbus stack that I could integrate into a demo user program. I needed this to demonstrate how you can automatically reactivate the bootloader from your running firmware, at the start of a firmware update.

A few Modbus stacks exists, but I couldn’t find one that was open source, actively maintained, and one that enables you to add support for custom function codes. One thing lead to another and I ended up developing such a Modbus stack myself. It’s part of the MicroTBX product line that I am working on, parallel to the OpenBLT bootloader. The stack is called MicroTBX-Modbus and was in public beta for about two years.

During the public beta I received good feedback from users, allowing me to further improve the Modbus stack. It’s now at a point where a handful of customers already purchased the MicroTBX-Modbus commercial license. It uses a similar dual-license model as the OpenBLT bootloader. Now that I know for a fact that several users already deemed it stable enough for production purposes, it’s about time to take it out of public beta and make the official version 1.0 release. I implemented a whole bunch of unit tests to further verify that everything is up to snuff. Towards the end of June, I made the official version 1.0 stable release of MicroTBX-Modbus.

To get started with firmware updates using Modbus-RTU as the communication media, you can look at the Nucleo-F466RE demo programs. These come preconfigured for firmware update via Modbus-RTU, for both the on-board USB Virtual COM port (RS232) and using an RS485 transceiver with the help of the Waveshare RS485 CAN shield. The demo user program comes with MicroTBX-Modbus integrated.

For a step-by-step tutorial on integrating the MicroTBX-Modbus stack into your own firmware, refer to the STM32 Modbus RTU server tutorial. You can find additional details in the online user manual.

The road ahead

After a busy first part of the year, I am quit happy with the state of the OpenBLT bootloader project and specifically this version 1.20.0 release. Planning and work for the next release already started. I try to listen as much as I can to user feedback to make the release planning. Based on this, the one big dot on my radar screen is support for firmware updates via CAN FD. I hope to get around to this for the next release. If not, it will be another release in the near future.

I realized that most of my microcontroller boards that feature a CAN FD peripheral, don’t actually come with a CAN FD transceiver. To make a good start, I recently designed a simple shield for STM32 Nucleo boards with a CAN FD transceiver:

3D model of the CAN FD shield. A shield with Arduino pinouts with a CAN FD transceiver, specifically for STM32 Nucleo boards.

Prototypes arrived not too long ago and they work fine. Feel free to grab the CAN FD shield design and use if for your own purposes.

Closing thoughts

I would like to finish these release notes by thanking everyone for showing an interest in the OpenBLT bootloader project. Especially the ones who purchased the OpenBLT commercial license and the ones who keep renewing their support and maintenance contract. It is thanks to you that the OpenBLT project thrives and that is possible for Feaser to continue sponsoring the development and maintenance efforts.

This entry was posted in OpenBLT and tagged , , . Bookmark the permalink.