RW61x Dual Boot with Shadow registers#

Shadow registers can control OTP fuses during development or testing. It enables the creation of “copies” of OTP fuses in the form of shadow registers. After each power-on reset, these shadow registers are cleared and the default OTP fuses are loaded. It’s important to note that there are two requirements: the device must be in a lifecycle that supports shadow registers and only certain registers are included in shadow registers.

In this examples, one of the possible use cases of shadow registers will be presented. We will configure shadow registers for performing dual boot. Dual boot represents the possibility to write two images and always boot the image that has the higher image version. Dual boot is normally not possible on RW61x devices without configuring the registers (OTP fuses).

The example is divided to 2 part to proper demonstrate do impact of shadow registers values on dual boot process:

  1. In first part there is create 2 boot images (blinking demo with software version 0 and hello world demo with software version 1). Both images are written to flash memory and system is rebooted. In this case must boot the application at standard position in flash (blinking led demo), because the MCU is not configured to use dual boot and software version are not taken in count.

  2. As a prove of working shadow registers functionality, the simply configuration is loaded where is enabled dual boot functionality to MCU and device is rebooted (DO NOT USE POWER on RESET). In this scenario, the shadow register values are applied and the device check both places of bootable images, checks the software versions and load the image with higher version (hello world application).

# Initialization cell

from spsdk.utils.jupyter_utils import YamlDiffWidget

WORKSPACE = "workspace/"  # change this to path to your workspace
INPUTS = "inputs/"
SR_CONFIG = INPUTS + "sr_config_dualboot.yaml"
# choose debug interface
INTERFACE = "pyocd"
FAMILY = "rw612"

# This env variable sets colored logger output to STDOUT
%env JUPYTER_SPSDK=1
# Set a magic for command execution and echo
%alias execute echo %l && %l
%alias_magic ! execute
env: JUPYTER_SPSDK=1
Created `%!` as an alias for `%execute`.

2. Prepare Shadow registers configuration file#

Generation of shadow registers template is done with the shadowregs tool. First, we need to get the configuration template that will be used as a starting point.

Let’s begin by creating a template configuration file using the shadowregs  get-template command. To simplify this example, we have already prepared a configuration template, which can be found in the sr_config_dualboot.yaml file. Below, we’ll compare the differences between the template and our customized example to highlight the additions we’ve made.

In our case, we modified these items in the shadow register configuration template:

  1. Choose Flex-SPI flash such as the primary boot source.

  2. Disable the DICE computation.

  3. Set the image offset.

  4. Move the life cycle state to Develop2. You can see details below in the diff view.

# Get difference of template and user YAML configuration
YamlDiffWidget("inputs/sr_config_dualboot.diffc").html
shadowregs -f rw612 get-template -o workspace/sr_template_rw61x.yaml --force 
The Shadow registers template for rw612 has been saved into workspace/sr_template_rw61x.yaml YAML file

Configuration Differences

3. Device preparation#

Take a FRDM-RW612 development board without burned any fuses and connect MCU-LINK USB port.

Here is the picture of device:

frdm-rw612

Switch device into ISP mode by nxpdebugmbox application and find the UART port.

%! nxpdebugmbox -f $FAMILY -i $INTERFACE cmd ispmode -m 1
# list available connected devices
%! nxpdevscan
nxpdebugmbox -f rw612 -i pyocd cmd ispmode -m 1 
  #   Interface   Id           Description             
-------------------------------------------------------
  0   PyOCD       1069211762   Segger J-Link MCU-Link  
Entering into ISP mode succeeded
nxpdevscan 
-------- Connected NXP USB Devices --------

-------- Connected NXP UART Devices --------

Port: COM119
Type: mboot device

-------- Connected NXP SIO Devices --------

-------- Connected NXP UUU Devices --------

The detected UART port from previous step write to following step as UART_CONNECTION. And run the script. nxpmemcfg blhost-script generates a blhost script for the used chip memory on development board and this script called by blhost application configure a external flexSPI NOR memory in MCU.

# choose com port for rw612
UART_CONNECTION = "COM119"
# generate memory configuration
%! nxpmemcfg blhost-script -f $FAMILY -p flexspi_nor -m W25QxxxJV -i quad_spi --output workspace/script.txt --force
# configure memory
%! blhost -p $UART_CONNECTION batch workspace/script.txt
# Check the configured memory
%! blhost -p $UART_CONNECTION list-memory
nxpmemcfg blhost-script -f rw612 -p flexspi_nor -m W25QxxxJV -i quad_spi --output workspace/script.txt --force 
Loaded option words: Opt0: 0xC0000007
Exported blhost script.
blhost -p COM119 batch workspace/script.txt 
Response status = 0 (0x0) Success.
Response status = 0 (0x0) Success.
blhost -p COM119 list-memory 
Internal Flash:
Internal RAM:
    Region 0: 0x10000000 - 0x1012FFFF; Total Size: 1.2 MiB
    Region 1: 0x00000000 - 0x0012FFFF; Total Size: 1.2 MiB
    Region 2: 0x20000000 - 0x2012FFFF; Total Size: 1.2 MiB
    Region 3: 0x30000000 - 0x3012FFFF; Total Size: 1.2 MiB
External Memories:
FLEX-SPI-NOR:
  Start Address = 0x08000000  Total Size = 64.0 MiB  Page Size = 256  Sector Size = 4096  Block Size = 65536 
SPI-MEM:
  Not Configured

Following step prepare the application into external flash:

  • Export bootable images for both application used in dual boot example(check App 1: Led Blinky,App 2: Hello World).

  • Erase flash on used blocks

  • Write the exported applications on correct place (offset 256KB as we configure in Shadow registers)

# build bootable images with their versions
%! nxpimage bootable-image merge -c inputs/bootimg_flexspi_nor_v0.yaml -o workspace/bootable_image_v0.bin
%! nxpimage bootable-image merge -c inputs/bootimg_flexspi_nor_v1.yaml -o workspace/bootable_image_v1.bin

# erase the flash on dual boot areas
%! blhost -p $UART_CONNECTION -t 50000 flash-erase-region 0x0800_0000 0x10_000
%! blhost -p $UART_CONNECTION -t 50000 flash-erase-region 0x0804_0000 0x10_000

# write the dual boot images
%! blhost -p $UART_CONNECTION write-memory 0x0800_0000 workspace/bootable_image_v0.bin
%! blhost -p $UART_CONNECTION write-memory 0x0804_0000 workspace/bootable_image_v1.bin
nxpimage bootable-image merge -c inputs/bootimg_flexspi_nor_v0.yaml -o workspace/bootable_image_v0.bin 
Success. (Bootable Image: workspace\bootable_image_v0.bin created) 
nxpimage bootable-image merge -c inputs/bootimg_flexspi_nor_v1.yaml -o workspace/bootable_image_v1.bin 
Success. (Bootable Image: workspace\bootable_image_v1.bin created) 
blhost -p COM119 -t 50000 flash-erase-region 0x0800_0000 0x10_000 
Response status = 0 (0x0) Success.
blhost -p COM119 -t 50000 flash-erase-region 0x0804_0000 0x10_000 
Response status = 0 (0x0) Success.
blhost -p COM119 write-memory 0x0800_0000 workspace/bootable_image_v0.bin 
Writing memory
Response status = 0 (0x0) Success.
Response word 1 = 22337 (0x5741)
blhost -p COM119 write-memory 0x0804_0000 workspace/bootable_image_v1.bin 
Writing memory
Response status = 0 (0x0) Success.
Response word 1 = 26140 (0x661c)

4. Reboot the device without dual boot and run application at standard position#

After loaded the bootable images into the external flash, we will reboot the device without any modification of device configuration (no shadow register load, no enabling dual boot). After reboot, the board should start blink with led, because the blinking application is placed on standard booting position and no dual boot is taken in count.

# reset the device and run image
%! nxpdebugmbox -i $INTERFACE -f $FAMILY tool reset -h
nxpdebugmbox -i pyocd -f rw612 tool reset -h 
  #   Interface   Id           Description             
-------------------------------------------------------
  0   PyOCD       1069211762   Segger J-Link MCU-Link  
Reset MCU by Debug Mailbox succeeded.

After that step, the onboard led should be blinking

5. Load shadow register configuration into device and enable dual boot#

In this step we load the prepared configuration of shadow registers where the dual boot is enabled, and reboot the device.

After reboot, the board should stop blinking (application with software version 0) and start sending hello world string to serial line(application with software version 1).

# load modified shadowregs
%! shadowregs -i $INTERFACE -f $FAMILY loadconfig -c $SR_CONFIG --no-verify
# reset the device and run image
%! shadowregs -i $INTERFACE -f $FAMILY reset
shadowregs -i pyocd -f rw612 loadconfig -c inputs/sr_config_dualboot.yaml --no-verify 
  #   Interface   Id           Description             
-------------------------------------------------------
  0   PyOCD       1069211762   Segger J-Link MCU-Link  
WARNING:spsdk.debuggers.utils:The test address is not specified. The standard address that doesn't fit all devices is used: 0x2000_0000 (3161ms since start, utils.py:138)
The Shadow registers has been loaded by configuration in inputs/sr_config_dualboot.yaml YAML file
shadowregs -i pyocd -f rw612 reset 
  #   Interface   Id           Description             
-------------------------------------------------------
  0   PyOCD       1069211762   Segger J-Link MCU-Link  
The target has been reset.

After that step, the onboard led should be switched off and the ``hello world` message should be periodically printed on uart. (115200 baudrate)