在 i.MXRTyyyy 启动系列第二篇文章 Boot 配置(BOOT Pin, eFUSE) 里痞子衡提到了 eFUSE,部分 Boot 配置都存储在 eFUSE memory 里,但是对 eFUSE 的介绍仅仅浅尝辄止,没有深入,今天痞子衡就为大家再进一步介绍 eFUSE。
eFUSE 是 i.MXRTyyyy 里一块特殊的存储区域,用于存放全部芯片配置信息,其中有一部分配置信息和 Boot 相关。这块特殊存储区域并不在 ARM 的 4G system address 空间里,需要用特殊的方式去访问(读 / 写),如何访问 eFUSE 是本篇文章的重点。
一、eFUSE 基本原理
1.1 eFUSE 属性(OTP, Lock)
eFUSE 本质上就是 i.MXRTyyyy 内嵌的一块 OTP(One Time Programmable) memory,仅可被烧写一次,但可以被多次读取。eFUSE memory 的烧写是按 bit 进行的(RT10xx 都是按 bit 进行,RT1170 大部分是按 Word 进行的),初始状态下所有 eFUSE bit 均为 0,通过特殊的烧写时序可以将 bit 从 0 改成 1,一旦某 bit 被烧写成 1 后便再也无法被修改(可理解为硬件熔丝烧断了无法恢复)。
i.MXRT1050 的 eFUSE memory 总地址空间有 1.75KB(地址范围为 0x000 - 0x6FF),但可读写操作的空间只有 192bytes(位于 0x400 - 0x6FF 区域),分为 6 个 BANK,每个 BANK 含 8 个 word(1word = 4bytes)。下图中 0x00 - 0x2F 是 eFUSE 的 bank word 索引地址(也叫 index 地址),其与 eFUSE 空间地址对应关系是:
fuse_address = fuse_index * 0x10 + 0x400
上述可读写的 eFUSE memory 空间除了 OTP 特性外,还有 Lock 控制特性,Lock 控制是 OTP memory 的标配,Lock 控制有三层:第一层是 WP,即写保护,被保护的 eFUSE 区域只可读,不可写;第二层是 OP,即覆盖保护,被保护的 eFUSE 区域只能被写一次(用于保护 eFUSE Word 里那些不需要被烧写成 1 的 eFUSE bit);第三层是 RP(WP+OP+RP),即访问保护,被保护的 eFUSE 区域及其对应的 shadow register 均不能被读写。
Lock 控制在 eFUSE 的 BANK0_word0,如下是 RT1050 上具体 Lock bit 定义:
关于可读写 eFUSE 空间所有 bit 定义详见 Reference Manual 里的 Table 5-9. Fusemap Descriptions。
1.2 OCOTP 控制器与 Shadow Register
i.MXRTyyyy 内部有一个硬件 IP 模块叫 OCOTP_CTRL,即 OCOTP 控制器,对 eFUSE memory 的读写控制操作其实都是通过这个 OCOTP 控制器实现的,下图是 OCOTP_CTRL 模块图:
OCOTP_CTRL 模块寄存器一共分两类:一类是 IP 控制寄存器,用于实现对 OTP memory 的读写操作时序控制;一类是 Shadow register,用于上电时自动从 eFUSE memory 获取数据并缓存,这样我们可以直接访问 Shadow register 而不用访问 eFUSE memory 也能获取 eFUSE 内容(注意:当芯片运行中烧写 eFUSE,Shadow register 的值并不会立刻更新,需要执行 IP 控制器的 reload 命令或者将芯片 reset 才能同步)。
RT1050 上 IP 控制寄存器偏移地址范围是 0x000 - 0x3FF(下图仅截取部分):
Shadow register 寄存器偏移地址范围是 0x400 - 0x6FF(下图仅截取部分),看到 0x400 - 0x6FF 的地址范围,有没有感觉很熟悉?是的,这跟上一节讲的可读写操作 eFUSE 空间偏移地址范围是一致的。
二、使用 blhost 烧写 eFUSE
eFUSE memory 的烧写是通过 OCOTP_CTRL 模块来实现的,我们当然可以在 Application 中集成 OCOTP_CTRL 的驱动程序,然后在 Application 调用 OCOTP_CTRL 的驱动程序完成 eFUSE 的烧写,但这种方式并不是痞子衡要介绍的重点,痞子衡要介绍的是通过 Flashloader 配套的 blhost.exe 上位机工具实现 eFUSE 的烧写。
痞子衡在上一篇文章里介绍过如何引导启动 Flashloader 并且使用 blhost 与 Flashloader 通信,此处假设你已经使用 blhost 与 Flashloader 建立了通信。让我们再来回顾一下 blhost 的命令 help,可以得知 efuse-program-once 这个命令就是我们想要的命令。
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolsblhostwin> .blhost.exe -?
usage: C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolsblhostwinblhost.exe
[-p|--port <name>[,<speed>]]
[-u|--usb [[[<vid>,]<pid>]]]
-- command <args...>
Command:
efuse-program-once <addr> <data>
Program one word of OCOTP Field
<addr> is ADDR of OTP word, not the shadowed memory address.
<data> is hex digits without prefix '0x'
efuse-read-once <addr>
Read one word of OCOTP Field
<addr> is ADDR of OTP word, not the shadowed memory address.
让我们试一下 efuse-program-once 这个命令,开始试之前要解决 2 个问题:
addr 参数到底是什么地址?帮助里说是 OTP word address,其实这个地址就是 1.1 节里介绍的 fuse_index,index 范围为 0x00 - 0x2F,对应 48 个可读写操作的 eFUSE Word。
data 参数到底是什么格式?帮助里说是 hex digits without prefix '0x',但是似乎没有指明长度,我们知道每一个 index 对应的是 4byte,那就应该是 8 位 16 进制数据(实测下来必须要填 8 位,如果是非 8 位会返回 Error: invalid command or arguments)。
弄清了问题,那我们做一个小测试:要求将 eFUSE 里的 SRK_REVOKE word 的最低 byte 烧写成 0x5A,然后再将最高 byte 烧写成 0xFE,分两步进行。
翻看 OTP Memory Footprint 表,找到 SRK_REVOKE 的 index 地址是 0x2F(对应 Shadow register 地址是 0x401F46F0),命令搞起来:
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolsblhostwin> .blhost.exe -u -- efuse-program-once 0x2F 0000005A
Inject command 'efuse-program-once'
Successful generic response to command 'efuse-program-once'
Response status = 0 (0x0) Success.
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolsblhostwin> .blhost.exe -u -- efuse-program-once 0x2F FE000000
Inject command 'efuse-program-once'
Successful generic response to command 'efuse-program-once'
Response status = 0 (0x0) Success.
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolsblhostwin> .blhost.exe -u -- efuse-read-once 0x2F
Inject command 'efuse-read-once'
Response status = 0 (0x0) Success.
Response word 1 = 4 (0x4)
Response word 2 = -33554342 (0xfe00005a)
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolsblhostwin> .blhost.exe -u -- read-memory 0x401F46F0 4
Inject command 'read-memory'
Successful response to command 'read-memory'
5a 00 00 fe
(1/1)100% Completed!
Successful generic response to command 'read-memory'
Response status = 0 (0x0) Success.
Response word 1 = 4 (0x4)
Read 4 of 4 bytes.
看起来命令执行正常,但你是不是会有几个疑问:
为何执行第二条命令将 0xFE000000 烧写进 eFUSE 时没有报错?显然第一条命令已经将 0x0000005A 烧写进 eFUSE,而 0xFE000000 的最低 byte 是 0x00,看起来它跟已经烧写进去的 0x5A 是冲突的,而前面介绍过 eFUSE bit 只能从 0 烧写为 1,其实这不是问题,OCOTP controller 会自动过滤将 eFUSE bit 从 1 烧写为 0 的操作。
为何 eFUSE 被烧写后,并没有 reset 操作,用 read-memory 去获取 Shadow register 可以立即看到数据同步更新了?其实 blhost 里的 efuse-program-once 命令不仅包含 program 命令,也自动集成了 reload 命令。
虽然只有 blhost 可以实现 eFUSE 烧写功能,但要获取 eFUSE 状态并不是只有 blhost 可以做到,sdphost 也可以做到,因为 sdphost 提供了读写 Shadow register 的命令。
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolssdphostwin> .sdphost.exe -u 0x1fc9,0x0130 -- read-register 0x401F46F0
5a 00 00 fe
Status (HAB mode) = 1450735702 (0x56787856) HAB disabled.
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolssdphostwin> .sdphost.exe -u 0x1fc9,0x0130 -- write-register 0x401F46F0 32 0x00000000
Status (HAB mode) = 1450735702 (0x56787856) HAB disabled.
Reponse Status = 311069202 (0x128a8a12) Write complete.
PS C:Flashloader_i.MXRT1050_GAFlashloader_RT1050_1.1Toolssdphostwin> .sdphost.exe -u 0x1fc9,0x0130 -- read-register 0x401F46F0
00 00 00 00
Status (HAB mode) = 1450735702 (0x56787856) HAB disabled.