
1. 問題現象DFU能燒錄eMMC啟動卻成了“鬼打墻”如果你正在調STM32MP257F-EV1大概率也遇到過這種讓人血壓飆升的場景板子昨天還用USB DFU好好地燒錄、啟動、跑Linux今天想把系統固化到eMMC按正常流程燒完、撥碼切到eMMC啟動、上電——結果串口開始刷屏板子無限重啟日志里反復出現一行刺眼的報錯。我這邊固化的啟動日志就卡死在下面這個狀態[TF-M] IAC exception 128 [TF-M] System reset due to exception然后就開始循環復位、起、再復位、再起。更抓狂的是這時候你把USB線插到板子的DFU/OTG口主機端完全枚舉不到設備fastboot devices一直空著STM32CubeProgrammer也連不上。表面看好像“板子變磚了”但扒開日志看問題并沒有那么玄乎。先說清楚這次故障的現場環境和操作路徑方便你對號入座硬件平臺STM32MP257F-EV1 官方評估板目標鏡像基于 STM32CubeMP2 SDK 構建的 TF-M FIP含 OP-TEE U-Boot以及 Linux 內核鏡像燒錄方式STM32CubeProgrammer 通過 USB DFU 將鏡像寫入 eMMC復現路徑DFU 模式下一切正常切到 eMMC 啟動后復現最終表現無限重啟 IAC exception 128 fastboot 不進如果你也卡在“DFU好用、eMMC起不來”這個分界點上這篇文章會把異常的含義、啟動鏈路的差異、排查順序和修復手段都拆開講。我自己已經從這套流程里救回來三塊MP2板子了按下面的思路走基本不用懷疑硬件。2. IAC exception 128 到底在說什么先搞懂異常機制再動手排查之前先把報錯本身啃明白。IAC exception的全稱是Instruction Access Constraint它的異常類型編碼EC是0x80十進制就是128。這是Armv8-M架構Cortex-M33下定義的一類故障異常核心含義是CPU在取指令階段訪問了一個當前執行狀態不允許訪問的內存地址。對這里的關鍵詞是“取指令階段”。它和數據訪問異常不同IAC管的是“CPU從某個地址去拉下一條要執行的指令”這個動作本身的合法性。在STM32MP257這種雙A35加一個M33的異構架構里TF-M跑在Cortex-M33上負責安全啟動、DDR初始化、外設隔離和跳轉控制。啟動早期M33會完成內存映射和安全屬性配置然后把非安全側的鏡像U-Boot、OP-TEE等交給A35去跑。這一跳轉過程中如果發生下面任何一種情況都會捅出IAC exceptionCPU處于非安全狀態Non-secure卻嘗試從一個被標記為安全Secure的地址取指令取指地址落在沒有被映射的地址區間比如DDR控制器還沒初始化好取指地址所在的內存區域被配置為不可執行XNExecute Never跳轉目標地址本身和實際鏈接地址不一致跑到了一段“什么都不是”的數據區。用大白話講你手里拿著導航但導航里填的終點是錯的或者去終點的路被交警封了CPU一腳油門踩下去直接撞墻。IAC Exception 128就是這堵墻撞出來的巨響。很多人在這一步就開始懷疑“eMMC壞了”“DDR虛焊”其實大可不必。DFU模式下能用、切eMMC才崩說明硬件大概率沒事問題幾乎都出在軟件鏈上TF-M與FIP版本不匹配、鏡像沒有放到eMMC正確的啟動位置、或者啟動源選擇與燒錄內容對不上。還有一點要特別說明異常在TF-M早期就發生了所以你在串口看到的是TF-M打印的異常信息而不是U-Boot的日志。如果TF-M日志只打印到一半就復位也能進一步佐證問題出在M33安全側而不是A35側的Linux內核。這為后面的排查順序定了方向。3. DFU到eMMC啟動的切換鏈路為什么DFU正常eMMC就崩要理解“為什么DFU好用、eMMC就崩”得先把STM32MP257的啟動鏈路和兩種啟動方式的差異拉通。3.1 STM32MP257F-EV1的完整啟動鏈MP2系列和MP1在啟動流程上有個顯著區別MP1用的是TF-A BL2而MP2改用TF-MTrusted Firmware-M作為BL2跑在Cortex-M33上。完整鏈路如下ROM (BootROM) - TF-M (BL2, 運行在Cortex-M33) - FIP (包含 TF-A BL31 / OP-TEE / U-Boot) - Linux Kernel關鍵點在前兩級BootROM從啟動介質中加載TF-M鏡像TF-M負責DDR初始化、FIP解析、鏡像校驗然后把非安全側的U-Boot和OP-TEE加載進DDR最后釋放A35執行。也就是說整個啟動鏈里“第一個吃螃蟹的人”是TF-M它一旦加載失敗或者跳轉出錯后面全白搭。3.2 DFU模式與eMMC啟動的本質差異DFU模式下STM32CubeProgrammer通過USB把鏡像直接下載到DDR里然后從RAM啟動。你燒錄的內容、地址、Flash布局都是CubeProgrammer在PC端算好并控制的TF-M在RAM里被加載后DDR等硬件已經被初始化完畢一切看起來“歲月靜好”。但eMMC啟動時情況完全不同BootROM會先讀取eMMC的boot分區或user area把TF-M鏡像加載到內部SRAMTF-M運行后要自己完成DDR初始化再去eMMC讀取FIP包FIP包在eMMC上的物理偏移必須和TF-M編譯時約定的偏移完全一致燒錄到eMMC的TF-M版本、FIP版本必須匹配同一個SDK版本。任何一個環節錯位都可能造成TF-M取出錯誤的鏡像數據跳到非預期地址觸發IAC exception 128。所以不要因為DFU模式下能跑就默認eMMC燒錄沒問題——這兩種方式對布局、版本一致性的敏感程度完全不同。3.3 切換時最容易被忽略的三個卡點我在實際排查中總結出三個高頻卡點基本覆蓋了這類故障的九成原因TF-M沒有更新或者被覆蓋。很多團隊的U-Boot和Linux是頻繁更新的但TF-M一份固件用很久。如果FIP升級了、TF-M還是老的兩邊對DDR安全區域劃分、FIP偏移的理解不一致啟動時直接跑到安全屬性沖突的地址。燒錄位置和啟動方式不匹配。STM32MP257的eMMC可以從boot partition 1、boot partition 2或者user area啟動燒錄時要根據flashlayout.tsv的分區定義把TF-M和FIP寫到正確的位置。比如TF-M要燒到eMMC boot1分區FIP也在boot1的后續偏移如果誤燒到user areaBootROM找不到TF-M或者TF-M找不到FIP同樣表現為死循環。BOOT引腳/OTP配置和實際燒錄介質沖突。EV1板上有BOOT撥碼開關如果撥到了eMMC啟動但eMMC里根本沒有有效鏡像BootROM會反復嘗試加載失敗表現就是不斷復位。這里可以做一個快速驗證把BOOT撥回DFU啟動模式如果板子能正常進入DFU基本可以判斷硬件沒有損壞問題集中在eMMC內容或啟動配置上。4. 系統化排查鏈路從日志定位到根因一步步來面多這類“無限重啟”故障最忌諱的就是一遍遍重燒、反復試然后越試越亂。我總結了一套從日志到配置的排查順序按這個順序走通常兩輪之內就能定位。4.1 第一步確認異常發生的具體階段先把串口日志完整抓下來我建議用邏輯分析儀或者串口工具記錄完整的上電日志不要只看最后一段。關鍵要確認日志里有沒有BootROM的打印MP2的BootROM一般會有啟動源信息TF-M有沒有打印出來打印到了哪一行就停止是TF-M自己報的IAC還是U-Boot階段報的異常。如果日志里TF-M打印完IAC exception 128就復位說明異常發生在TF-M跳轉前后。如果TF-M根本沒打印可能是TF-M鏡像本身沒加載出來或者BootROM根本沒從eMMC讀到數據——那就是啟動源和鏡像位置的問題。我之前遇到的情況是TF-M打印正常說明BootROM成功從eMMC讀到了TF-MDDR也初始化了但在跳轉U-Boot時崩了。這就把方向直接指向TF-M和FIP的匹配性。4.2 第二步核對TF-M與FIP的版本一致性這是我在多塊板子上踩出來的經驗MP2平臺這類跳轉失敗第一嫌疑永遠是TF-M版本和FIP版本不匹配。為什么因為U-Boot的鏈接地址、DDR安全區域的劃分、FIP的加載地址都在TF-M的配置里。FIP中的U-Boot被加載到哪個DDR地址、以什么安全屬性運行是由TF-M決定的。如果FIP是SDK v4.1構建的TF-M卻是v4.0的兩個版本對地址映射和安全屬性的定義有差異跳轉時CPU很可能嘗試從一個被標記為Secure的地址以Non-secure狀態取指IAC就來了。驗證方法很簡單# 在SDK目錄下查看當前構建的版本 git log --oneline -1 # 查看TF-M和FIP的編譯時間 ls -l build/tfm/bin/tfm_bl2.bin build/fip/fip.bin # 用STM32CubeProgrammer讀取eMMC中實際燒錄的TF-M內容 STM32_Programmer_CLI -c portUSB1 -r 0x00000000 0x20000 tfm_dump.bin然后把讀取出來的tfm_dump.bin和SDK里編譯出來的tfm_bl2.bin做對比用sha256sum算一下哈希不一致就說明eMMC里的TF-M不是你想燒的那一版。4.3 第三步核實eMMC分區布局與燒錄內容STM32MP2的燒錄依賴flashlayout.tsv文件它定義了每個鏡像燒到eMMC的哪個分區、哪個偏移。我見過太多人直接把舊工程的tsv拿過來用結果新工程的FIP偏移變了都不知道。正確的檢查方式有兩個層面第一個層面工程編譯時生成的flashlayout.tsv要和實際燒錄時使用的完全一致。打開文件重點看TF-M和FIP兩行的偏移# 重點關注兩個關鍵行 tfm_bl2.bin boot1 0x00000000 fip.bin boot1 0x00004000如果TF-M燒到了boot1起始偏移但FIP被定義到boot2而你的啟動配置恰好選了boot1TF-M在boot1后續偏移讀FIP就會讀到空數據或者舊數據跳轉自然失敗。第二個層面通過STM32CubeProgrammer的-r命令把eMMC boot1分區完整讀出來和編譯產物逐個對比偏移處的內容# 讀取eMMC boot1前512KB STM32_Programmer_CLI -c portUSB1 -r 0x00000000 0x80000 emmc_boot1_dump.bin對照tsv里的偏移看看對應位置的鏡像哈希是否正確。這一步能把“燒錄位置不對”這個根因直接坐實或排除。4.4 第四步檢查BOOT引腳與OTP fuse的啟動源配置如果TF-M、FIP內容都正確還是無限重啟就要懷疑啟動源本身是否和燒錄介質一致。STM32MP257F-EV1上BOOT撥碼開關可以直接指定啟動介質。DFU燒錄完成后如果撥碼還在DFU位置強行寫好了eMMC但沒切過去自然不會從eMMC啟動反過來如果撥碼在eMMC位置但OTP里fuse也已經燒成eMMC啟動那么即使DFU撥碼想臨時覆蓋也可能被OTP的優先級壓住。快速驗證方法把BOOT撥碼全部撥到DFU模式看板子能不能穩定進入DFU。如果能說明板子的基本啟動鏈路是通的。接著檢查OTP區域STM32_Programmer_CLI -c portUSB1 -otp read在輸出的OTP表里找BOOT相關字段確認沒有寫入強制eMMC啟動的fuse。如果OTP里確實有內容并且指向eMMC而你又確實想把系統留在eMMC那問題就回到eMMC內部布局如果OTP指向其他介質或者為空BOOT引腳就是唯一的決策者撥碼切到DFU后重燒即可。4.5 第五步檢查U-Boot與fastboot的聯動配置最后再看fastboot為什么起不來。很多人以為fastboot是“USB枚舉層面”的問題但其實在這類故障里它只是結果系統在TF-M階段就崩了根本沒走到U-BootUSB gadget自然永遠不會枚舉主機端fastboot waiting for device會一直等下去。如果前面的啟動鏈都正常了fastboot還進不去才需要檢查U-Boot配置CONFIG_CMD_FASTBOOT有沒有打開CONFIG_USB_FUNCTION_FASTBOOT有沒有打開U-Boot設備樹里USB OTG節點EV1上是DWC3有沒有正確配置為peripheral模式是否設置了fastboot的啟動命令進入方式比如按鍵觸發或環境變量控制。這里有個容易忽略的點Windows下fastboot識別設備需要正確的USB驅動Linux下需要udev規則。你可以先把USB線插到板子確認系統有沒有新增USB設備。如果板子U-Boot正常起來了USB枚舉是能看到VID/PID的Linux下用lsusb就能看到一行類似STMicroelectronics的設備。看不到再往U-Boot配置里查。5. 修復與驗證強制DFU重刷讓板子回到正軌定位到根因之后修復本身并不復雜但每一步都要驗證避免“燒完還是老樣子”。5.1 先讓板子進入強制DFU模式不管eMMC里是什么狀態只要BootROM還活著就能用BOOT引腳強制進入DFU模式。具體操作斷開板子電源USB線也拔掉把BOOT撥碼開關撥到DFU/USB啟動方式EV1板卡上一般有絲印標注按用戶手冊確認用USB線連接板子的DFU口通常是標著USB1或DFU的Type-C口到電腦上電。如果一切正常PC端會枚舉出一個USB設備Linux下lsusb能看到STMicroelectronics相關的設備Windows下設備管理器會出現一個STM32 Bootloader設備。此時用STM32CubeProgrammer連接STM32_Programmer_CLI -c portUSB1能連接成功說明BootROM和USB鏈路都沒有問題可以放心刷寫。5.2 校驗eMMC狀態并重新燒錄進入DFU后先做一次eMMC的狀態檢查特別是boot分區的寫保護位# 檢查eMMC相關信息 STM32_Programmer_CLI -c portUSB1 -mmc info如果eMMC的boot分區被打上了寫保護直接燒錄會失敗或者“假成功”。確認寫保護狀態后需要先解除保護再燒錄。STM32CubeProgrammer通常提供了命令行選項也可以直接在圖形界面里操作。如果有報錯提示寫保護就要先執行解除寫保護的操作。接下來重新燒錄完整的Flash布局# 使用與當前SDK版本匹配的flashlayout文件燒錄 STM32_Programmer_CLI -c portUSB1 -w flashlayout.tsv燒錄過程中留意每一條Download記錄的偏移和長度確認TF-M寫入了boot1的起始地址FIP寫入了正確的偏移。燒錄完成后建議立刻用-r命令回讀校驗一遍不要直接切啟動否則萬一燒錯了還得重新進DFU。5.3 切換啟動源并驗證啟動鏈燒錄校驗完成后斷電把BOOT撥碼切到eMMC啟動重新上電。此時串口應該能看到完整的啟動日志TF-M: boot stage 2 TF-M: FIP loaded TF-A: BL31 OP-TEE: ... U-Boot: ... Linux: ...如果還是復現IAC exception 128先別急著重復燒寫。回到第4章的排查鏈路重點檢查TF-M/FIP版本和boo1分區布局。我遇到過一次燒了三遍都沒解決的問題最后發現是工程里的tfm_bl2.bin和FIP不是同一次make的產物重新統一構建后一次就過了。5.4 恢復fastboot的驗證方法啟動鏈恢復正常后U-Boot起來fastboot自然就能用了。驗證方法在U-Boot命令行下執行fastboot usb 0主機端執行fastboot devices能看到設備時執行fastboot getvar version確認通信正常。如果你在Linux主機上遇到fastboot waiting for device但USB設備能看到大概率是udev規則沒配上。在/etc/udev/rules.d/下加一條規則把ST的USB VID加入白名單然后udevadm control --reload即可。Windows下則要確認設備管理器里是否識別成ADB Interface或Android Bootloader Interface沒有的話需要手動指定驅動。6. 把“無限重啟”轉化為可復用的排查方法論這類故障解決完我突然意識到一個問題IAC exception 128這類報錯對老手來說就是“版本不匹配”的代名詞但對剛接觸MP2平臺的人來說光看報錯根本無從下手。如果你把這個故障場景當成一個案例從里面抽出一套可復用的排查方法大概可以濃縮成下面這張表排查維度檢查內容快速定位方法異常階段TF-M日志打印到哪里串口完整日志鏡像版本TF-M與FIP是否同一次構建sha256sum對比eMMC回讀內容燒錄布局flashlayout.tsv是否匹配實際燒錄回讀eMMC分區并逐偏移檢查啟動源BOOT引腳/OTP fuse與實際介質一致強制DFU驗證 otp readU-Boot配置fastboot相關configmenuconfig檢查 USB枚舉狀態這張表不僅適用于STM32MP257F-EV1也適用于任何帶TF-M/TrustZone安全啟動鏈的板級調試。核心思路是一個“從內到外、從軟件到硬件”的排查順序先確認異常發生在哪一層再檢查鏡像和布局最后才去動硬件。我個人的幾條實操體會最后分享幾個我踩過坑之后的習慣不一定寫在哪份文檔里但對板級調試很管用。第一盡量用同一個SDK版本統一構建所有鏡像。TF-M、FIP、U-Boot、Linux全部在同一棵代碼樹下編譯禁止混用不同版本SDK的產物。MP2平臺的啟動鏈是連鎖反應一個版本錯位就可能導致跳轉失敗而這些問題排查起來非常耗時。第二每次燒錄前先看一次eMMC狀態。特別是boot分區寫保護位。STM32CubeProgrammer連接后花幾秒鐘看一下mmc info確認boot1、boot2都能正常訪問再燒能省掉很多“燒錄成功但起不來”的無效調試。第三回讀校驗是燒錄后必做的一步。燒完不要急著切啟動先花30秒把關鍵分區回讀出來和編譯產物做哈希對比。這個習慣讓我少走了很多彎路因為燒錄過程中出現“靜默失敗”的概率比想象高。DFU模式下偶爾會出現數據超時導致寫入不完整但工具不一定報錯回讀校驗是唯一可靠的兜底手段。第四BOOT引腳狀態和OTP fuse一致性檢查放在最后一步做。很多工程師一遇到啟動失敗就瘋狂撥BOOT開關其實是反的。先把內容燒對、版本配好再檢查啟動源選擇。順序反了容易陷入“撥碼燒錄、燒錄撥碼”的死循環越調越亂。這組問題解決下來我對TF-M在MP2平臺上的定位有了更深的體會——它不只是安全組件更是整個啟動鏈的交通指揮中心。理解了這個角色再回頭看IAC exception 128就不覺得它有多神秘了。無非是“指揮中心給CPU指了一條不該走的路”。順著這個思路復盤你也能在十分鐘內把這個故障從“鬼打墻”變成“一眼看穿”。