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