测试DFU时候,会生成并下载Bootloader Settings文件,以设置应用程序有效性,本文介绍Bootloader Settings文件的技术细节。

1. 基本信息

Bootloader Setting Page是指Bootloader工程开辟的一段Flash空间,其中保存了固件镜像的信息和DFU进度信息,这些信息称为Bootloader Settings。为了表述方便,Bootloader Settings Page或Bootloader Settings都简称为Settings。

对于nRF52系列芯片,Settings的位置如下图

nRF52832芯片的Flash大小为512 kB(0x0008 0000),Settings位于最顶端(0x0007 F000 – 0x0008 0000),大小为1 Page(4 kB)。

知道了Settings的位置,即可通过nrfjprog --readcode flash.hex读出芯片Flash数据,然后定位到0x0007 F000处读取Settings内容。

在Settings的下方是MBR Param Storage,它的长度也是4 kB,地址为:0x0007 E000 – 0x0007 F000。这块区域用于存放Settings的备份。

infocenter上得知,Bootloader Settings信息包含以下内容:

  • current firmware – size, CRC-32
  • pending firmware – size, CRC-32
  • progress of the firmware update
  • progress of the firmware activation
  • current firmware versions (application and bootloader)
  • transport-specific data

正常执行DFU升级过程中,Bootloader接收固件镜像并会自动计算这些信息,然后写入Flash。

也可以利用固件镜像app.hex手动生成Settings文件(settings.hex),命令如下:

nrfutil settings generate --family NRF52 --no-backup --application app.hex --application-version 0x01 --bootloader-version 0x01 --bl-settings-version 0x02 settings.hex

–no-backup表示不使用备份区域,对于SDK 12.0 ~ SDK 15.0,应该使用该参数以实现兼容性,对于SDK 15.1以上版本,应该忽略该参数。如果不使用该参数,则会在MBR Param Storage区域内保存Settings Page的副本,生成的settings.hex中能够看到0x7E000 – 0x7F000 的内容与0x7F000 – 0x80000的内容相同。如果使用该参数,则不保存备份副本,settings.hex中也没有0x7E000 – 0x7F000的内容。

–bl-settings-version只有两个可选项:0x01和0x02。对于SDK 12.3 ~ SDK 15.2,应该使用0x01,对于SDK 15.3应该使用0x02。如果使用0x02,会在Settings信息中引入 boot_validataion_crc等信息,这些额外的数据导致两个版本不兼容。具体差异其实很简单,打开bl_dfu_sett.py文件,找到BLDFUSettingsStructV2 与 BLDFUSettingsStructV1的类定义,一看便知。

更多的内容请参考官方文档:https://github.com/nordicsemiconductor/pc-nrfutil。

使用以下命令查看Settings.hex的内容:

nrfutil settings display settings.hex

Settings.hex 的内容形式如下:

Bootloader DFU Settings:
* File:                     settings.hex
* Family:                   nRF52
* Start Address:            0x0007E000
* CRC:                      0x740EFA00
* Settings Version:         0x00000002 (2)
* App Version:              0x00000001 (1)
* Bootloader Version:       0x00000001 (1)
* Bank Layout:              0x00000000
* Current Bank:             0x00000000
* Application Size:         0x0000EC84 (60548 bytes)
* Application CRC:          0x9B8FC175
* Bank0 Bank Code:          0x00000001
* Softdevice Size:          0x00000000 (0 bytes)
* Boot Validation CRC:      0xD0E62C99
* SD Boot Validation Type:  0x00000000 (0)
* App Boot Validation Type: 0x00000001 (1)

用文本工具打开Settings.hex文件,其内容如下:

:020000040007F3
:10F0000000FA0E7402000000010000000100000080
:10F01000000000000000000084EC000075C18F9B20
:10F0200001000000000000000000000000000000DF
:10F0300000000000000000000000000000000000D0
:10F0400000000000000000000000000000000000C0
:10F0500000000000000000000000000000000000B0
:10F0600000000000000000000000000000000000A0
:10F070000000000000000000000000000000000090
:10F080000000000000000000000000000000000080
:10F090000000000000000000000000000000000070
:10F0A0000000000000000000000000000000000060
:10F0B0000000000000000000000000000000000050
:10F0C0000000000000000000000000000000000040
:10F0D0000000000000000000000000000000000030
:10F0E0000000000000000000000000000000000020
:10F0F0000000000000000000000000000000000010
:10F1000000000000000000000000000000000000FF
:10F1100000000000000000000000000000000000EF
:10F1200000000000000000000000000000000000DF
:10F1300000000000000000000000000000000000CF
:10F1400000000000000000000000000000000000BF
:10F1500000000000000000000000000000000000AF
:10F16000000000000000000000000000000000009F
:10F17000000000000000000000000000000000008F
:10F18000000000000000000000000000000000007F
:10F19000000000000000000000000000000000006F
:10F1A000000000000000000000000000000000005F
:10F1B000000000000000000000000000000000004F
:10F1C000000000000000000000000000000000003F
:10F1D000000000000000000000000000000000002F
:10F1E000000000000000000000000000000000001F
:10F1F000000000000000000000000000000000000F
:10F2000000000000000000000000000000000000FE
:10F2100000000000000000000000000000000000EE
:10F2200000000000000000000000000000000000DE
:10F2300000000000000000000000000000000000CE
:10F2400000000000000000000000000000000000BE
:10F25000000000000000000000000000992CE6D033
:10F26000000000000000000000000000000000009E
:10F27000000000000000000000000000000000008E
:10F28000000000000000000000000000000000007E
:10F29000000000000000000000000000000000006E
:10F2A000000175C18F9B00000000000000000000FD
:10F2B000000000000000000000000000000000004E
:10F2C000000000000000000000000000000000003E
:10F2D000000000000000000000000000000000002E
:10F2E000000000000000000000000000000000001E
:10F2F000000000000000000000000000000000000E
:10F3000000000000000000000000000000000000FD
:10F3100000000000000000000000000000000000ED
:03F32000000000EA
:00000001FF

Intel hex文件的格式解析请参考:https://en.wikipedia.org/wiki/Intel_HEX 。

第一行为地址偏移,偏移量为0x0007,所以第二行的实际地址为0x0007 F000,这正是Settings的Flash起始地址。

倒数第二行为数据末尾行,可知数据总量为0xF322 + 1 = 803,即Settings.hex 的有效数据总数是803。

有效数据的排列顺序根据BLDFUSettingsStructV2 的定义而来, BLDFUSettingsStructV2 的内容如下:

    def __init__(self, settings_address):
        self.bytes_count = 803 # Entire settings page
        self.crc                  = settings_address + 0x0
        self.sett_ver             = settings_address + 0x4
        self.app_ver              = settings_address + 0x8
        self.bl_ver               = settings_address + 0xC
        self.bank_layout          = settings_address + 0x10
        self.bank_current         = settings_address + 0x14
        self.bank0_img_sz         = settings_address + 0x18
        self.bank0_img_crc        = settings_address + 0x1C
        self.bank0_bank_code      = settings_address + 0x20
        self.sd_sz                = settings_address + 0x34
        self.init_cmd             = settings_address + 0x5C

        self.boot_validataion_crc = settings_address + 0x25C
        self.sd_validation_type   = settings_address + 0x260
        self.sd_validation_bytes  = settings_address + 0x261
        self.app_validation_type  = settings_address + 0x2A1
        self.app_validation_bytes = settings_address + 0x2A2

        self.last_addr            = settings_address + 0x322

首先看到bytes_count=803,这与上面的计算一致。

第一个字段为CRC,本文中CRC为0x740EFA00,换成Little Endian后为
00 FA 0E 74。紧挨着下一个字段的偏移地址为4,所以本字段大小为4字节。

第二个字段为Sett_ver,本文中该值为1,通过下个字段的偏移量得知Sett_ver长度也是4。

以此类推,可以分析大多数字段。

找到sd_sz,该字段与上一字段之间差距为20字节,并不是bank0_bank_code长度是20字节,而是这之间还填充了两个字段:bank1 {img_sz, img_crc, bank_code}和write_offset,二者共16字节,加上bank0_bank_code 所占4字节,共20字节。

bank1与write_offset定义在Bootloader工程中的nrf_dfu_bytes.h -> nrf_dfu_settings_t结构体中。

打开nrf_dfu_settings_t的定义:

typedef struct
{
    uint32_t            crc;
    uint32_t            settings_version;
    uint32_t            app_version;
    uint32_t            bootloader_version;
    uint32_t            bank_layout;
    uint32_t            bank_current;
    nrf_dfu_bank_t      bank_0;
    nrf_dfu_bank_t      bank_1;
    uint32_t            write_offset;
    uint32_t            sd_size;
    dfu_progress_t      progress;
    uint32_t            enter_buttonless_dfu;
    uint8_t             init_command[INIT_COMMAND_MAX_SIZE];
    uint32_t            boot_validation_crc;
    boot_validation_t   boot_validation_softdevice;
    boot_validation_t   boot_validation_app;
    boot_validation_t   boot_validation_bootloader;
    nrf_dfu_peer_data_t peer_data;
    nrf_dfu_adv_name_t  adv_name;
} nrf_dfu_settings_t;

它与上面的 BLDFUSettingsStructV2 基本对应。

观察init_command,它是一个数组,数组长度为512!init_command 的内容请参考上一篇文章“弄懂Init Packet”。

再观察BLDFUSettingsStructV2的init_cmd与boot_validataion_crc的偏移量,二者之差恰好等于512。

后面四个字段与Validation有关,通常不专门设置。值得注意的是,默认情况下,app_validation_type=1,app_validation_bytes=bank0_img_crc,所以在hex文件中看到了一个75C18F9B。

至此,我们完整的分析了Settings.hex 的内容细节。

Bootloader启动时候,会检查Settings中以下信息:

  • bank0_bank_code
  • bank0_img_crc

如果二者都正确,则执行跳转进入Application,否则驻留在Bootloader中执行DFU。

(完)

前面几篇都在介绍Secure DFU,而在SDK 12之前版本的DFU,没有签名,称为Legacy DFU。

Secure DFU是由Legacy DFU发展而来,前面介绍的大多数概念都适用于Legacy DFU,本文做一些补充介绍。

(1)类型

总结一下DFU的类型:

DFU类型 SDK版本 特点
Secure DFU SDK 12.x + 带签名校验,Flash占用大,工具支持完善
Legacy DFU SDK 11.x – 不带签名,Flash占用小,得找很老的工具
Open DFU SDK 15.x + 仅适用于USB接口,不适用于BLE

如果使用SDK 11.x,Legacy DFU是一个自然的选择。如果使用较新的SDK,只能选择Secure DFU。

(2)升级包

Legacy DFU生成升级包需要使用nrfutil v0.3版本,而不能使用新版本(新版本已经到达3.x)。

下载安装Master Control Panel(PC版)可以在其安装目录下找到nrfutil v0.3,下载地址为:链接

nrfutil v0.3的命令行参数与新版本不一样,生成升级包的命令为:

nrfutil dfu genpkg --application app.hex dfu_app.zip

有了升级包就可以利用新版的nrfutil或nRF connect APP进行DFU。

(3)Bootloader Settings

Legacy DFU的Bootloader Settings结构比较简单,仅有一个Flag指示应用程序的有效性。

nrfutil v0.3不能生成bl_settings.hex,新版本的nrfutil生成的bl_settings.hex与Legacy DFU不兼容。

Bootloader Settings中的Flag地址是确定的,因此可以利用nrfjprog向该Flash地址写1,手动修改Flag,命令为:

nrfjprog --family NRF51 --memwr 0x3FC00 --val 1

其中0x3FC00是Bootloader Settings的储存地址,而Flag位于该地址的首字节。

同样,如果通过DFU方式升级固件,Bootloader会自动生成和维护Bootloader Settings。

(4)对比

与Secure DFU相比,Legacy DFU有一些特别的地方:

  • Bootloader占用Flash为12 kB,Secure DFU占24 kB
  • Bootloader中MTU Size只支持23,无法利用BLE 4.2的特性
  • SDK提供了ble_app_hrs_with_dfu作为DFU示例,没有一个专门的DFU示例
  • DFU服务不需要检测Bootloader,可以脱离Bootloader运行,当然跳转时会出错
  • 待续……

 

(完)

Nordic默认采用Dual-Bank模式执行DFU,即将新固件的数据保存在Flash的另一区域,而不是直接覆盖当前固件程序,只有新固件接收完毕校验通过,再复制到当前固件空间。这样能保证更新过程被意外中断,不会影响到原来的程序。

(1)Flash布局

一个典型的BLE应用程序其Flash布局图如下:

部件 含义或用途
Bootloader Settings 保存固件CRC、Size等信息
MBR Paramaters 保存MBR参数
Bootloader 执行DFU
Application 应用程序
Softdevice 协议栈
MBR(Master Boot Record) 引导Bootloader

逻辑上,将Application空间分为两部分:Bank 0和Bank 1。应用程序总是放在Bank 0,紧挨着Softdevice。Bank 1通常是空余空间。

做DFU时候,接收的新固件可以存放在Bank 0,也可以放在Bank 1:

  • 如果放在Bank 0,即覆盖当前程序,称为Signle Bank DFU。
  • 如果放在Bank 1,不影响当前程序,称为Dual Bank DFU。

Nordic SDK默认采用Dual Bank,绝大多数场景都喜欢Dual Bank,除了——Flash空间不够。

Bootloader在接收数据前会检查Bank 1的大小,如果空间足够,就执行Dual Bank,否则当前应用程序,腾出空间执行Single Bank。SDK自动完成这个判断动作。

(2)部件大小

MBR、Softdevice、Bootloader和BL Settings的大小都是确定的。对于nRF52832(512kB Flash)和Softdevice 6.0.x而言,各个部件的大小如下:

部件 地址范围 空间
BL Settings 0x0007 F000 – 0x0008 0000 4 kB
MBR Params 0x0007 E000 – 0x0007 F000 4 kB
Bootloader 0x0007 8000 – 0x0007 E000 24 kB
Appl. + Free area 0x0002 0000 – 0x0007 8000 352 kB
Softdevice 0x0000 1000 – 0x0002 6000 148 kB
MBR 0x0000 0000 – 0x0000 1000 4 kB

不同芯片的Flash大小不同,对于nRF52840芯片(1024 kB),Appl. + Free area = 352 + 512 = 864 kB,而对于nRF52810(192 kB),Appl. + Free area = 352 – 320 = 32 kB。

利用这些数值,可以粗略的判断出新的固件将要执行Dual Bank还是Single Bank。

(3)升级对象

一个完整的程序中,有三个固件:Softdevice、Bootloader和Application。

SDK支持多种组合的升级模式,不过由于Nordic的协议栈(Softdevice)已经非常成熟,Bootloader职责功能相对稳定,大多数情况下都是只升级Application。

下面借图说明升级过程。

Application(Dual Bank)

新固件先存放到Bank 1,然后再擦除Bank 0,并复制到Bank 0。

Application (Single Bank)

先将Bank 0擦除,然后直接将新固件写到Bank 0。

Softdevice + Bootloader

通常Application都依赖于Softdevice,升级Softdevice意味着也要升级Application,所以要分两个步骤:

  1. 先擦除Bank 0,将Softdevice + Bootloader当做一个固件写到Bank 0,再复制到各自区域。
  2. 再用新的Bootloader将Application升级到Bank 0。

具体流程请参考这个链接

 

(完)