在 i.MXRTxxx 啟動系列第二篇文章 Boot 配置(ISP Pin, OTP) 里痞子衡提到了 OTP,部分 Boot 配置都存儲在 OTP memory 里,但是對 OTP 的介紹僅僅淺嘗輒止,沒有深入,今天痞子衡就為大家再進一步介紹 OTP。
OTP 是 i.MXRTxxx 里一塊特殊的存儲區(qū)域,用于存放全部芯片配置信息,其中有一部分配置信息和 Boot 相關(guān)。這塊特殊存儲區(qū)域并不在 ARM 的 4G system address 空間里,需要用特殊的方式去訪問(讀 / 寫),如何訪問 OTP 是本篇文章的重點。
一、OTP 基本原理
1.1 OTP 屬性(OTP, Shadow Lock)
OTP 本質(zhì)上就是 i.MXRTxxx 內(nèi)嵌的一塊 One Time Programmable memory,僅可被燒寫一次,但可以被多次讀取。OTP memory 的燒寫大部分是按 Word 進行的(也有極少部分是按 Bit 進行的),初始狀態(tài)下所有 OTP bit 均為 0,通過特殊的燒寫時序可以將 bit 從 0 改成 1,一旦某 bit 被燒寫成 1 后便再也無法被修改(可理解為硬件熔絲燒斷了無法恢復(fù))。
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 的標(biāo)配,Lock 控制有二種:第一種是 WP,即寫保護,用于保護 OTP 區(qū)域?qū)?yīng)的 shadow register 不能被改寫;第二種是 RP,即讀保護,被保護的 OTP 區(qū)域?qū)?yīng)的 shadow register 不能被讀取??吹竭@里,你會發(fā)現(xiàn) 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 定義:
關(guān)于 OTP 空間所有 bit 定義詳見 Reference Manual 里的 otpmap Descriptions。
1.2 OCOTP 控制器與 Shadow Register
i.MXRTxxx 內(nèi)部有一個硬件 IP 模塊叫 OCOTP_CTRL,即 OCOTP 控制器,對 OTP memory 的讀寫控制操作其實都是通過這個 OCOTP 控制器實現(xiàn)的,下圖是 OCOTP_CTRL 模塊圖:
OCOTP_CTRL 模塊寄存器一共分兩類:一類是 IP 控制寄存器,用于實現(xiàn)對 OTP memory 的讀寫操作時序控制;一類是 Shadow register,用于上電時自動從 OTP memory 獲取數(shù)據(jù)并緩存,這樣我們可以直接訪問 Shadow register 而不用訪問 OTP memory 也能獲取 OTP 內(nèi)容(注意:當(dāng)芯片運行中燒寫 OTP,Shadow register 的值并不會立刻更新,需要執(zhí)行 IP 控制器的 reload 命令或者將芯片 reset 才能同步)。
下圖是 RT600 里的 OCOTP_CTRL 模塊寄存器 map,其中 Shadow register 寄存器偏移地址范圍是 0x000 - 0x7FF(注意并不是所有 OTP Word 都會被加載到 Shadow register 里,雖然 Shadow register 預(yù)留了全部的 OTP 位置。這點與 i.MXRTyyyy efuse 會全部加載到 Shadow register 不同,原因是 i.MXRTxxx 的 OTP 里會有很多 Peripheral 寄存器加載初值,如果這些 OTP 值目的是加載 Peripheral,那就沒有必要再加載到 Shadow register 里,而 i.MXRTyyyy 的 efuse 值沒有加載 Peripheral 寄存器的用途)。IP 控制寄存器偏移地址范圍是 0x800 - 0x82C:
痞子衡寫過關(guān)于 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 區(qū)域進行保護(主要是寫保護),下一節(jié)介紹的 efuse-program-once 命令第三個可選參數(shù)[nolock/lock]其實就是利用了這個 bit。
二、使用 blhost 燒寫 OTP
OTP memory 的燒寫是通過 OCOTP_CTRL 模塊來實現(xiàn)的,我們當(dāng)然可以在 Application 中集成 OCOTP_CTRL 的驅(qū)動程序,然后在 Application 調(diào)用 OCOTP_CTRL 的驅(qū)動程序完成 OTP 的燒寫,但這種方式并不是痞子衡要介紹的重點,痞子衡要介紹的是通過 Serial ISP 模式配套的 blhost.exe 上位機工具實現(xiàn) OTP 的燒寫。
痞子衡在前面的文章里介紹過如何進入 Serial ISP 模式與 BootROM 通信,此處假設(shè)你已經(jīng)使用 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 參數(shù)到底是什么地址?幫助里說是 OTP word address,其實這個地址就是 1.1 節(jié)里介紹的 word index,index 范圍為 0x000 - 0x1FF,對應(yīng) 512 個可讀寫操作的 OTP Word。
data 參數(shù)到底是什么格式?幫助里說是 hex digits without prefix '0x',但是似乎沒有指明長度,我們知道每一個 index 對應(yīng)的是 4byte,那就應(yīng)該是 8 位 16 進制數(shù)據(jù)(實測下來必須要填 8 位,如果是非 8 位會返回 Error: invalid command or arguments)。
弄清了問題,那我們做一個小測試:要求將 OTP 里的 REVOKE_IMG_KEY word 的最低 byte 燒寫成 0x5A。翻看 OTP Memory Footprint 表,找到 REVOKE_IMG_KEY 的 index 地址是 0x66(對應(yīng) 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)
看起來命令執(zhí)行正常,如果此時你用 J-Link 去讀取對應(yīng) Shadow register 的值,你會發(fā)現(xiàn)剛才燒寫的 OTP 數(shù)據(jù)并沒有自動同步更新到 Shadow register 里。與 i.MXRTyyyy 系列下 Flashloader 里 efuse program 操作有所不同的是,i.MXRTxxx Serial ISP 模式下 blhost 里的 efuse-program-once 命令僅包含 program 命令,沒有集成 reload 命令。因此想要刷新 Shadow register,必須復(fù)位芯片。