在 i.MXRTxxx 启动系列第二篇文章 Boot 配置(ISP Pin, OTP) 里痞子衡提到了 OTP,部分 Boot 配置都存储在 OTP memory 里,但是对 OTP 的介绍仅仅浅尝辄止,没有深入,今天痞子衡就为大家再进一步介绍 OTP。
OTP 是 i.MXRTxxx 里一块特殊的存储区域,用于存放全部芯片配置信息,其中有一部分配置信息和 Boot 相关。这块特殊存储区域并不在 ARM 的 4G system address 空间里,需要用特殊的方式去访问(读 / 写),如何访问 OTP 是本篇文章的重点。
一、OTP 基本原理
1.1 OTP 属性(OTP, Shadow Lock)
OTP 本质上就是 i.MXRTxxx 内嵌的一块 One Time Programmable memory,仅可被烧写一次,但可以被多次读取。OTP memory 的烧写大部分是按 Word 进行的(也有极少部分是按 Bit 进行的),初始状态下所有 OTP bit 均为 0,通过特殊的烧写时序可以将 bit 从 0 改成 1,一旦某 bit 被烧写成 1 后便再也无法被修改(可理解为硬件熔丝烧断了无法恢复)。
i.MXRT600 的 OTP memory 总地址空间有 2KB(word index 范围为 0x000 - 0x1FF),分为 64 个 BANK,每个 BANK 含 8 个 word(1word = 4bytes)。
OTP memory 空间除了 OTP 特性外,还有 Shadow Lock 控制特性,Shadow Lock 控制是 OTP memory 的标配,Lock 控制有二种:第一种是 WP,即写保护,用于保护 OTP 区域对应的 shadow register 不能被改写;第二种是 RP,即读保护,被保护的 OTP 区域对应的 shadow register 不能被读取。看到这里,你会发现 i.MXRTyyyy 的 efuse 里的 LOCK 控制是同时针对 efuse 本身和 shadow register 的;而 i.MXRTxxx 的 OTP 里的 LOCK 控制仅针对 shadow register,那么对 OTP 本身的保护在哪里呢?先别急,后面会给你答案。
Shadow Lock 控制在 OTP 的 BANK0_word4、BANK1_word8/9,如下是 RT600 具体 Lock bit 定义:
关于 OTP 空间所有 bit 定义详见 Reference Manual 里的 otpmap Descriptions。
1.2 OCOTP 控制器与 Shadow Register
i.MXRTxxx 内部有一个硬件 IP 模块叫 OCOTP_CTRL,即 OCOTP 控制器,对 OTP memory 的读写控制操作其实都是通过这个 OCOTP 控制器实现的,下图是 OCOTP_CTRL 模块图:
OCOTP_CTRL 模块寄存器一共分两类:一类是 IP 控制寄存器,用于实现对 OTP memory 的读写操作时序控制;一类是 Shadow register,用于上电时自动从 OTP memory 获取数据并缓存,这样我们可以直接访问 Shadow register 而不用访问 OTP memory 也能获取 OTP 内容(注意:当芯片运行中烧写 OTP,Shadow register 的值并不会立刻更新,需要执行 IP 控制器的 reload 命令或者将芯片 reset 才能同步)。
下图是 RT600 里的 OCOTP_CTRL 模块寄存器 map,其中 Shadow register 寄存器偏移地址范围是 0x000 - 0x7FF(注意并不是所有 OTP Word 都会被加载到 Shadow register 里,虽然 Shadow register 预留了全部的 OTP 位置。这点与 i.MXRTyyyy efuse 会全部加载到 Shadow register 不同,原因是 i.MXRTxxx 的 OTP 里会有很多 Peripheral 寄存器加载初值,如果这些 OTP 值目的是加载 Peripheral,那就没有必要再加载到 Shadow register 里,而 i.MXRTyyyy 的 efuse 值没有加载 Peripheral 寄存器的用途)。IP 控制寄存器偏移地址范围是 0x800 - 0x82C:
痞子衡写过关于 i.MXRTyyyy 的 eFUSE 烧写的文章 飞思卡尔 i.MX RTyyyy 系列 MCU 启动那些事(5)- 再聊 eFUSE 及其烧写方法 ,其实 i.MXRTxxx 的 OCOTP 控制器与 i.MXRTyyyy 里的 OCOTP 控制器非常相似,虽然两者在寄存器组织上有差异,但其共同点更多。不过提及差异,有一个地方痞子衡不得不提,那就是 CTRL 寄存器的 bit15,在 i.MXRTyyyy 上这个 bit 是保留的,但是 i.MXRTxxx 上这个 bit 为 WORDLOCK,顾名思义即提供对操作的 OTP word 区域进行保护(主要是写保护),下一节介绍的 efuse-program-once 命令第三个可选参数[nolock/lock]其实就是利用了这个 bit。
二、使用 blhost 烧写 OTP
OTP memory 的烧写是通过 OCOTP_CTRL 模块来实现的,我们当然可以在 Application 中集成 OCOTP_CTRL 的驱动程序,然后在 Application 调用 OCOTP_CTRL 的驱动程序完成 OTP 的烧写,但这种方式并不是痞子衡要介绍的重点,痞子衡要介绍的是通过 Serial ISP 模式配套的 blhost.exe 上位机工具实现 OTP 的烧写。
痞子衡在前面的文章里介绍过如何进入 Serial ISP 模式与 BootROM 通信,此处假设你已经使用 blhost 与 BootROM 建立了通信。让我们再来回顾一下 blhost 的命令 help,可以得知 efuse-program-once 这个命令就是我们想要的命令。
PS D:NXP-MCUBootUtilitytoolsblhost2_3win> .blhost.exe
usage: D:NXP-MCUBootUtilitytoolsblhost2_3winblhost.exe
[-p|--port <name>[,<speed>]]
[-u|--usb [[[<vid>,]<pid>]]]
-- command <args...>
Command:
efuse-program-once <addr> <data> [nolock/lock]
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 节里介绍的 word index,index 范围为 0x000 - 0x1FF,对应 512 个可读写操作的 OTP Word。
data 参数到底是什么格式?帮助里说是 hex digits without prefix '0x',但是似乎没有指明长度,我们知道每一个 index 对应的是 4byte,那就应该是 8 位 16 进制数据(实测下来必须要填 8 位,如果是非 8 位会返回 Error: invalid command or arguments)。
弄清了问题,那我们做一个小测试:要求将 OTP 里的 REVOKE_IMG_KEY word 的最低 byte 烧写成 0x5A。翻看 OTP Memory Footprint 表,找到 REVOKE_IMG_KEY 的 index 地址是 0x66(对应 Shadow register 地址是 0x40130198),命令搞起来:
PS D:NXP-MCUBootUtilitytoolsblhost2_3win> .blhost.exe -u -- efuse-program-once 0x66 0000005A
Inject command 'efuse-program-once'
Successful generic response to command 'efuse-program-once'
Response status = 0 (0x0) Success.
PS D:NXP-MCUBootUtilitytoolsblhost2_3win> .blhost.exe -u -- efuse-read-once 0x66
Inject command 'efuse-read-once'
Response status = 0 (0x0) Success.
Response word 1 = 4 (0x4)
Response word 2 = 90 (0x5a)
看起来命令执行正常,如果此时你用 J-Link 去读取对应 Shadow register 的值,你会发现刚才烧写的 OTP 数据并没有自动同步更新到 Shadow register 里。与 i.MXRTyyyy 系列下 Flashloader 里 efuse program 操作有所不同的是,i.MXRTxxx Serial ISP 模式下 blhost 里的 efuse-program-once 命令仅包含 program 命令,没有集成 reload 命令。因此想要刷新 Shadow register,必须复位芯片。