行:如何用OCM加載并運(yùn)行裸機(jī)程序)
做ZYNQ開發(fā)這幾年大部分項(xiàng)目都被DDR“綁架”了。Vivado里加DDR、跑內(nèi)存測試、要等DDR初始化完成、調(diào)試時(shí)還要看DDR的時(shí)序……這些流程大家都習(xí)以為常。但你真的每一次都需要DDR嗎我之前接過一個(gè)低成本、小體積的項(xiàng)目只需要跑一個(gè)簡單的狀態(tài)機(jī)、控制幾個(gè)外設(shè)、處理幾百字節(jié)的協(xié)議數(shù)據(jù)整板空間緊張到連DDR顆粒的位置都擠不出來。調(diào)研后發(fā)現(xiàn)ZYNQ內(nèi)部其實(shí)有一塊被很多人忽略的OCMOn-Chip Memory也就是280KB左右的片上SRAM。正常情況下它只給FSBL和BootROM用但只要思路對它完全可以承載你的裸機(jī)程序讓芯片在完全沒有DDR的情況下正常啟動(dòng)和運(yùn)行。這篇東西就是想把“不帶DDR的ZYNQ怎么用OCM加載程序并運(yùn)行”這件事講透。我不會(huì)只給你貼一個(gè)鏈接腳本片段而是會(huì)把啟動(dòng)流程、FSBL改造、地址分配、Bootgen打包、JTAG調(diào)試、在線升級等整套玩法都拆開講順便把我在這個(gè)過程中踩過的坑和優(yōu)化思路一并寫出來。適合誰看如果你手頭是個(gè)功能不復(fù)雜的裸機(jī)項(xiàng)目、想在低成本板卡上省掉一串DDR走線、或者正在設(shè)計(jì)一個(gè)不從DDR啟動(dòng)的Bootloader這篇應(yīng)該能幫你省下不少折騰時(shí)間。1. 不帶DDR的ZYNQ能做什么先看懂OCM和啟動(dòng)鏈路1.1 為什么要砍掉DDR場景、收益與代價(jià)很多人一聽說ZYNQ不帶DDR第一反應(yīng)是“這不就是殘廢芯片嗎”。確實(shí)帶Linux的話DDR避不開PetaLinux的U-Boot內(nèi)核和根文件系統(tǒng)都指望那塊大內(nèi)存。但在裸機(jī)或者輕量RTOS場景里DDR并不是必需品。省掉DDR之后物料成本能直接下降一塊PCB上少一組上百根走線的DDR總線板面積更緊湊布線難度也低一大截??煽啃詫用嫔僖粋€(gè)高頻存儲(chǔ)器件也少了一類EMI和信號完整性問題。當(dāng)然代價(jià)也很直接可用的RAM總量從幾百M(fèi)B縮水到兩百多KB程序鏡像必須精簡跑不了大堆棧的中間件。所以你只有在程序邏輯清楚、數(shù)據(jù)量可控、外設(shè)驅(qū)動(dòng)數(shù)量有限的場景下才適合這么做。我的經(jīng)驗(yàn)是如果你需要的外設(shè)不超過UART、GPIO、SPI、I2C、CAN這類輕量接口狀態(tài)機(jī)和協(xié)議棧又能控制在幾十KB以內(nèi)那OCM完全接得住。1.2 關(guān)鍵硬件資源這塊片上存儲(chǔ)到底有多大本事ZYNQ-7000系列自帶256KB的OCM地址從0x00000000開始到0x0003FFFF結(jié)束。注意它并不是一整塊均質(zhì)SRAM而是分成若干區(qū)域前192KB0x00000000至0x0002FFFF可以由CPU和DMA訪問后64KB0x00030000至0x0003FFFF通常被保留給安全啟動(dòng)和高系統(tǒng)控制使用普通裸機(jī)程序盡量不要占用。實(shí)際開發(fā)中能自由使用的是前192KB不過FSBL自己也要占一部分所以真正留給應(yīng)用代碼和數(shù)據(jù)的地方通常只有100多KB。OCM的訪問速度和DDR不是一個(gè)量級的概念。DDR走的是內(nèi)存控制器有刷新、預(yù)充電、行列切換這些繁文縟節(jié)而OCM直接掛在CPU的高性能端口上訪問延遲低得多。我實(shí)測過同一段純計(jì)算代碼在OCM里跑比在DDR里跑明顯快因?yàn)樗耆珱]有緩存未命中和總線仲裁的問題。它還允許PL側(cè)的AXI主機(jī)訪問這意味著FPGA邏輯可以通過AXI接口讀寫OCM在某些設(shè)計(jì)里可以當(dāng)共享內(nèi)存用。雖然容量小但它并不是個(gè)“只能裝FSBL的配角”而是一塊真正可用的高速SRAM。提示OCM的各個(gè)子區(qū)域可能存在訪問權(quán)限限制后64KB尤其敏感。非必要時(shí)不要碰0x00030000以上區(qū)域否則可能觸發(fā)異常。1.3 啟動(dòng)流程回顧BootROM、FSBL和OCM之間的關(guān)系ZYNQ的上電啟動(dòng)流程是芯片內(nèi)部固化了一段BootROM它先從配置引腳決定的啟動(dòng)源QSPI Flash、SD卡、NAND、JTAG等讀取Boot Header把FSBL鏡像加載到OCM的開頭地址然后跳到OCM執(zhí)行FSBL。FSBL再根據(jù)配置文件把用戶程序加載到DDR默認(rèn)路徑或者加載到其他內(nèi)存區(qū)域最后跳過去運(yùn)行。在帶DDR的常規(guī)設(shè)計(jì)里FSBL的第一個(gè)大動(dòng)作就是初始化DDR控制器否則后面沒法把應(yīng)用程序從Flash搬到DDR里去。在無DDR設(shè)計(jì)里這步就成了問題根源FSBL可能會(huì)因?yàn)镈DR控制器沒接器件而卡死或者跑飛。所以要實(shí)現(xiàn)“無DDR運(yùn)行”核心工作就是兩件事第一讓FSBL不去初始化DDR模塊第二讓用戶程序的鏈接腳本把代碼段、數(shù)據(jù)段、堆棧都放進(jìn)OCM的地圖范圍內(nèi)。2. 總體思路與方案選型三種“不帶DDR”跑法2.1 方案A定制FSBL 應(yīng)用鏈接在OCM量產(chǎn)推薦這是我最推薦的方式也是本文實(shí)操部分要展開的完整流程?;舅悸肥窃赩ivado硬件工程里把DDR相關(guān)的接口和配置徹底去掉然后把FSBL里DDR初始化相關(guān)的調(diào)用剪掉再把裸機(jī)應(yīng)用鏈接到OCM地址段最后用Bootgen把FSBL和應(yīng)用打包成BOOT.BIN燒進(jìn)QSPI Flash或者放到SD卡里。這么做的好處是流程干凈貼近真實(shí)產(chǎn)品形態(tài)。BootROM上電后自動(dòng)加載FSBLFSBL瘦身后加載應(yīng)用應(yīng)用在OCM里無縫跑起來。整個(gè)過程完全不需要人為干預(yù)符合工業(yè)現(xiàn)場的啟動(dòng)要求。缺點(diǎn)是FSBL和應(yīng)用擠在同一個(gè)物理內(nèi)存里地址管理要細(xì)心應(yīng)用代碼如果超過百來KB就會(huì)比較緊張。整個(gè)方案選型先列在這里如果你只是做開發(fā)驗(yàn)證、不想動(dòng)FSBL可以直接跳到方案B。2.2 方案BJTAG直接把程序加載進(jìn)OCM開發(fā)調(diào)試推薦如果產(chǎn)品形態(tài)還沒定型你只是想知道“這個(gè)程序在OCM里能不能跑”那完全沒必要先折騰啟動(dòng)鏡像。用Vitis或者XSDK的調(diào)試功能把鏈接到OCM地址段的應(yīng)用ELF直接下載到芯片里運(yùn)行就行。開發(fā)工具會(huì)通過JTAG把程序?qū)戇M(jìn)OCM然后控制CPU跑起來。這個(gè)方案的優(yōu)點(diǎn)是非常快完全繞開BootROM、FSBL這些繁瑣環(huán)節(jié)改代碼、編譯、下載、跑起來一分鐘內(nèi)就能驗(yàn)證一輪。缺點(diǎn)是你需要在電腦上插著JTAG調(diào)試器沒法脫離電腦獨(dú)立運(yùn)行所以它只適合驗(yàn)證用不適合最終產(chǎn)品。我一般先在方案B下把代碼調(diào)通了再去折騰方案A的啟動(dòng)鏡像這樣能把兩個(gè)問題的調(diào)試難度分開。2.3 方案C極簡Bootloader常駐OCM實(shí)現(xiàn)在線升級再往下延伸一種場景不帶DDR的板子需要支持在線升級。此時(shí)的做法通常是設(shè)計(jì)一個(gè)極簡Bootloader把它放在OCM里接收上位機(jī)發(fā)來的新固件寫入外部Flash然后跳轉(zhuǎn)到Flash里的應(yīng)用代碼執(zhí)行。Bootloader本身不出現(xiàn)在最終應(yīng)用里只負(fù)責(zé)“搬運(yùn)”和“校驗(yàn)”。這種方案比前兩種都復(fù)雜因?yàn)樗纫幚鞦lash擦寫又要管理協(xié)議和地址映射。我建議如果你只是做簡單燒寫老老實(shí)實(shí)用方案A就行只有當(dāng)你有明確的遠(yuǎn)程升級需求時(shí)才考慮在OCM里塞一個(gè)Bootloader。后面我會(huì)單獨(dú)講一下設(shè)計(jì)時(shí)容易踩的坑。2.4 為什么不用U-Boot和Linux有人會(huì)問我能不能不初始化DDR但照樣跑個(gè)裁剪過的Linux實(shí)話實(shí)說幾乎不可能。Linux內(nèi)核本身就遠(yuǎn)超OCM容量而且它的內(nèi)存管理、頁表、DMA子系統(tǒng)都建立在“有一片大內(nèi)存”這個(gè)前提上。就算你想用PetaLinux去生成不含DDR的鏡像它在啟動(dòng)階段也會(huì)因?yàn)檎也坏娇捎脙?nèi)存而崩潰。所以不要抱這個(gè)念想無DDR場景下的操作系統(tǒng)選擇就是裸機(jī)或極小RTOS比如FreeRTOS而且要非常克制地配置任務(wù)棧。3. 實(shí)操從Vivado到BOOT.BIN一步步跑起來3.1 第一步創(chuàng)建一個(gè)不帶DDR的硬件工程先用Vivado搭建最小系統(tǒng)。新建工程選擇具體ZYNQ型號然后添加ZYNQ7 Processing System IP。關(guān)鍵點(diǎn)來了在ZYNQ配置界面里找到DDR Configuration把DDR控制器相關(guān)選項(xiàng)去掉或者選擇“無”具體版本界面略有差異但你要確認(rèn)生成的PS配置里不再包含DDR端口和引腳。如果你的板子物理上根本沒有DDR顆粒這一步其實(shí)和你平時(shí)“沒選DDR”是一樣的。然后使能你需要的串口、GPIO、SPI等等外設(shè)。這里我的建議是最小化原則能不用就不開因?yàn)槊總€(gè)外設(shè)的驅(qū)動(dòng)和緩沖區(qū)都會(huì)吃掉OCM的空間。配好之后把PS和外部端口連接好約束文件里管腳分配好綜合、實(shí)現(xiàn)、生成Bitstream。最后導(dǎo)出硬件到Vitis的XSA文件。導(dǎo)出時(shí)要注意勾選“包括Bitstream”因?yàn)楹竺鍲SBL工程可能需要PL配置。如果你不打算配置PL只想跑純PS程序那Bitstream可要可不要但導(dǎo)出XSA時(shí)盡量保持默認(rèn)完整導(dǎo)出省得后面缺東西。3.2 第二步定制FSBL把它變成“無DDR感知”的引導(dǎo)程序打開Vitis用XSA創(chuàng)建一個(gè)FSBL工程。大多數(shù)版本里你新建應(yīng)用工程時(shí)可以直接搜索FSBL模板。生成出來的標(biāo)準(zhǔn)FSBL會(huì)調(diào)用ps7_init()而ps7_init.c里會(huì)自動(dòng)包含DDR初始化函數(shù)ps7_ddr_init()。既然我們硬件工程里已經(jīng)去掉了DDR配置這個(gè)函數(shù)在一些版本里會(huì)變成一個(gè)空殼或者壓根不生成。但保險(xiǎn)起見我強(qiáng)烈建議你打開ps7_init.c檢查一下看看里面有沒有DDR寄存器配置。如果發(fā)現(xiàn)還有DDR初始化代碼有兩條路可以走一是直接編輯ps7_init.c把ps7_ddr_init函數(shù)體內(nèi)的寄存器寫入操作注釋掉二是修改fsbl_main.c在初始化流程里不調(diào)用包含DDR初始化的那個(gè)函數(shù)。我更推薦第一種因?yàn)樗钪庇^而且萬一以后你恢復(fù)DDR設(shè)計(jì)這段代碼也還在。另外還要檢查FSBL的鏈接腳本。FSBL本身也是跑在OCM里的它的鏈接腳本默認(rèn)就指向OCM低地址這部分一般不用動(dòng)。但你要留意FSBL的堆棧大小因?yàn)樵跓oDDR情況下FSBL沒法把棧臨時(shí)切換到DDR所有變量都在OCM里默認(rèn)配置一般夠用不用特意改。編譯FSBL編譯完成后查看一下生成的ELF映射表確認(rèn).text和.data段都在0x00000000往上的OCM范圍內(nèi)。如果你在map文件里看到任何DDR地址段說明你漏了什么回頭檢查。3.3 第三步創(chuàng)建裸機(jī)應(yīng)用并改寫鏈接腳本到OCM現(xiàn)在基于同一個(gè)XSA創(chuàng)建裸機(jī)應(yīng)用工程。驅(qū)動(dòng)、BSP配置都選最小化。重點(diǎn)是打開鏈接腳本lscript.ld。Vitis里可以用GUI的Linker Script編輯器也可以直接改ld文件。你需要把可用的內(nèi)存區(qū)域從DDR換成OCM。我的做法是把MEMORY描述里的PS7_DDR_0區(qū)域直接刪掉新增一個(gè)區(qū)域叫PS7_OCM_0起始地址0x00000000長度0x00040000然后代碼段、只讀數(shù)據(jù)段、數(shù)據(jù)段、堆棧段全部映射到PS7_OCM_0上。如果你后64KB不想碰那就把起始地址改成0x00000000長度設(shè)置成0x00030000只使用前192KB。注意Vitis默認(rèn)生成時(shí)可能還會(huì)引用DDR區(qū)域符號。如果你在GUI里改了內(nèi)存區(qū)域后還需要檢查各個(gè)section的布局。棧和堆的大小尤其要控制裸機(jī)程序默認(rèn)的棧大小有時(shí)是1MB這在無DDR下直接超了。把棧設(shè)成比如16KB或32KB就足夠跑大多數(shù)裸機(jī)邏輯了。堆如果你用不到malloc直接設(shè)成1KB都行。再強(qiáng)調(diào)一下鏈接腳本的堆棧段不能和代碼段、數(shù)據(jù)段重疊。Vitis生成的段地址一般會(huì)自己按順序排列但你要確保_start地址在最前面中斷向量表能落到0x00000000附近。3.4 第四步用Bootgen生成BOOT.BIN并燒寫FSBL工程和應(yīng)用工程都編譯通過之后打開Vitis的“Create Boot Image”工具。這一步需要添加一個(gè)引導(dǎo)鏡像分區(qū)第一部分選FSBL的ELF第二部分選你的應(yīng)用ELF。如果你的PL需要配置還要把bitstream加進(jìn)去但無DDR項(xiàng)目通常不需要PL可以不加。在Bootgen的配置里需要確認(rèn)“Boot Mode”為QSPI、SD等對應(yīng)你的啟動(dòng)介質(zhì)。BOOT.BIN的生成原理是FSBL的ELF和應(yīng)用的ELF都會(huì)被轉(zhuǎn)換成帶有地址信息的鏡像段Bootgen按順序排好加上Boot Header打包成一個(gè)文件。因?yàn)槲覀兊膽?yīng)用ELF已經(jīng)鏈接到OCM地址所以Bootgen打包時(shí)會(huì)把對應(yīng)段的加載地址標(biāo)記成0x00000000往后的OCM區(qū)域。FSBL運(yùn)行時(shí)會(huì)把應(yīng)用從Flash讀到OCM對應(yīng)地址然后跳過去。之后就是燒寫了。如果啟動(dòng)介質(zhì)是QSPI Flash可以用Vivado的Hardware Manager燒BOOT.BIN如果是SD卡直接把BOOT.BIN放到FAT32分區(qū)的根目錄即可。上電啟動(dòng)后看串口輸出和應(yīng)用行為確認(rèn)程序跑起來。我習(xí)慣先讓應(yīng)用點(diǎn)個(gè)LED或者周期性打印一段字符這樣驗(yàn)證最直觀。3.5 驗(yàn)證執(zhí)行流串口打印、GPIO輸出和調(diào)試器觀測無DDR啟動(dòng)的驗(yàn)證重點(diǎn)不是“程序有沒有跑”而是“它是不是從OCM里跑的”。最簡單的方法是在應(yīng)用代碼開頭打印一段帶地址信息的日志比如讀取當(dāng)前PC寄存器的值然后通過串口發(fā)出來。PC值落在0x00000000到0x0003FFFF之間就說明程序確實(shí)在OCM執(zhí)行。第二個(gè)驗(yàn)證方法是點(diǎn)燈。把GPIO配置成某個(gè)LED程序里循環(huán)翻轉(zhuǎn)電平用示波器或者肉眼觀察閃爍頻率。這個(gè)測試主要確認(rèn)FSBL成功完成了跳轉(zhuǎn)而且應(yīng)用的主循環(huán)沒有被異常打斷。第三個(gè)方法是接JTAG調(diào)試器直接在Vitis里連接運(yùn)行中的目標(biāo)查看寄存器。調(diào)試器能掛上說明CPU沒有死循環(huán)在異常向量里運(yùn)行狀態(tài)健康。我在實(shí)際項(xiàng)目中會(huì)把這三種方法都用上因?yàn)樗鼈兎謩e驗(yàn)證了鏈接地址、跳轉(zhuǎn)行為和運(yùn)行穩(wěn)定性。4. 常見問題與排查技巧實(shí)錄4.1 FSBL卡死或反復(fù)重啟連串口都沒有輸出這個(gè)問題十有八九是FSBL仍然嘗試初始化DDR控制器而系統(tǒng)里根本沒有DDR顆粒導(dǎo)致寄存器操作失敗或者總線事務(wù)掛起。排查思路先用JTAG連上芯片看看PC停在哪條指令上。如果停在任何涉及DDR地址的代碼段基本就是這個(gè)問題。解決方案就是回到3.2節(jié)把ps7_init里DDR初始化部分徹底關(guān)閉必要時(shí)直接用純文本編輯器打開ps7_init.c把包含DDR寄存器配置的數(shù)組或初始化函數(shù)跳過去。另一種可能是Boot Header配置的啟動(dòng)設(shè)備和你實(shí)際燒寫的介質(zhì)不一致。比如你燒到QSPI卻把啟動(dòng)模式引腳跳線設(shè)成了SD卡那BootROM根本找不到FSBL自然沒有任何輸出。檢查一下MIO啟動(dòng)模式引腳的電平對照ZYNQ手冊確認(rèn)和設(shè)備對應(yīng)。4.2 程序能運(yùn)行但一訪問外設(shè)就死機(jī)或數(shù)據(jù)錯(cuò)亂這可能涉及OCM地址安全屬性和Cache配置的問題。默認(rèn)情況下OCM是支持CPU的Cache操作的但如果你在FSBL里配置了MMU允許了某些地址段為Device類型那么對OCM的訪問就可能產(chǎn)生異常。裸機(jī)BSP通常有自己的MMU配置建議查閱BSP生成的內(nèi)存屬性表確認(rèn)OCM區(qū)域被標(biāo)記為可緩存或至少為普通內(nèi)存類型。另外如果你在應(yīng)用里用了DMA引擎且DMA緩沖區(qū)放在OCM里要特別注意一致性。OCM雖然是SRAM但DMA和CPU并發(fā)訪問同一緩沖區(qū)時(shí)Cache會(huì)造成數(shù)據(jù)不同步。解決方法是要么關(guān)閉D-Cache只開I-Cache要么在DMA傳輸前后執(zhí)行Cache清理和無效化操作。我建議無DDR環(huán)境下干脆只用I-Cache省心很多。4.3 鏈接報(bào)錯(cuò)bin文件超過OCM容量這是最直白的空間不足問題。當(dāng)你的程序代碼量、只讀數(shù)據(jù)、全局變量、堆??偤统^你設(shè)定的OCM保留區(qū)域時(shí)鏈接器會(huì)報(bào)錯(cuò)或者生成超限的bin文件。我的經(jīng)驗(yàn)是把代碼精簡作為第一優(yōu)先級不要試圖通過調(diào)整地址硬塞進(jìn)去。一個(gè)比較實(shí)用的辦法是觀察map文件里各段的大小。通常占大頭的是庫函數(shù)和標(biāo)準(zhǔn)庫初始化代碼比如printf的浮點(diǎn)格式化功能非常占空間。如果你只需要整數(shù)打印可以用自己實(shí)現(xiàn)的簡易輸出函數(shù)能省下幾十KB。另外用-Os編譯優(yōu)化選項(xiàng)也能有效控制代碼體積。Vitis里可以在應(yīng)用工程的編譯選項(xiàng)中開啟優(yōu)化大小。4.4 在線升級設(shè)計(jì)中Bootloader需要注意什么如果你的Bootloader和App都試圖塞進(jìn)OCM就會(huì)遇到一個(gè)問題Bootloader本身的空間會(huì)擠壓App的空間。實(shí)際操作中我做的是把Bootloader放在OCM的前64KB然后把App放在QSPI Flash里App運(yùn)行時(shí)也直接從Flash里執(zhí)行或者只在啟動(dòng)時(shí)被Bootloader搬運(yùn)到OCM剩余區(qū)域。運(yùn)行在Flash里雖然慢一點(diǎn)但勝在省內(nèi)存。不過要注意Flash的隨機(jī)訪問延遲和緩存命中率問題建議開啟I-Cache。升級過程中Bootloader接收新固件時(shí)要先把新固件寫入Flash的臨時(shí)分區(qū)全部寫完并校驗(yàn)CRC通過后再覆蓋運(yùn)行分區(qū)否則中途斷電會(huì)直接變磚。我校驗(yàn)用的是CRC32內(nèi)存占用小對OCM環(huán)境非常友好。4.5 OCM運(yùn)行的程序掉電后不保存別把運(yùn)行數(shù)據(jù)放錯(cuò)地方剛接觸OCM開發(fā)的人容易把“運(yùn)行程序在OCM”和“數(shù)據(jù)存在OCM”混為一談。OCM是SRAM掉電全丟所以它只能用來放運(yùn)行時(shí)代碼和數(shù)據(jù)不能當(dāng)持久化存儲(chǔ)使用。你要保存的參數(shù)、日志、校準(zhǔn)值應(yīng)該放在外部Nor Flash、EEPROM或者SD卡里。如果你希望上電后能快速讀回上次運(yùn)行的狀態(tài)可以在應(yīng)用啟動(dòng)后立即從Flash讀取配置到OCM內(nèi)存然后在運(yùn)行中頻繁訪問OCM副本。這樣做的好處是讀寫速度快壞處是每次修改配置后要及時(shí)回寫Flash別等掉電了再后悔。5. 一些擴(kuò)展與我的個(gè)人實(shí)際體驗(yàn)5.1 在量產(chǎn)項(xiàng)目里用OCM省掉DDR的心得這個(gè)項(xiàng)目最后交付的時(shí)候我的同事還半信半疑覺得“ZYNQ不裝DDR還跑得動(dòng)嗎”實(shí)踐證明跑得很穩(wěn)。整個(gè)系統(tǒng)的邏輯很單一傳感器數(shù)據(jù)通過SPI進(jìn)來經(jīng)過簡單算法處理結(jié)果通過UART發(fā)出去外加控制兩個(gè)繼電器。代碼量壓縮到大概80KB數(shù)據(jù)緩沖區(qū)控制在30KB以內(nèi)剩下空間用于堆棧余量充足。量產(chǎn)之后幾乎沒有出現(xiàn)內(nèi)存相關(guān)的問題因?yàn)镺CM是片上SRAM不會(huì)像DDR那樣有信號完整性和刷新故障。低溫環(huán)境下DDR偶爾會(huì)出初始化失敗而OCM完全沒這個(gè)顧慮。當(dāng)然我們也在選型時(shí)反復(fù)斟酌過容量問題寧可多花時(shí)間精簡代碼也不冒超額的風(fēng)險(xiǎn)。5.2 什么情況下堅(jiān)決不建議省略DDR如果你的應(yīng)用涉及Linux、視頻緩沖、大數(shù)據(jù)采集、復(fù)雜TCP/IP協(xié)議棧、機(jī)器學(xué)習(xí)推理這些那不要考慮OCM了老老實(shí)實(shí)上DDR。OCM不是萬能藥它只適配“小、快、穩(wěn)”的場景。拿我另一個(gè)項(xiàng)目例子來說想用ZYNQ做網(wǎng)口數(shù)據(jù)采集數(shù)據(jù)包動(dòng)不動(dòng)幾MB即便程序能塞進(jìn)OCM緩沖區(qū)也無處安放。判斷標(biāo)準(zhǔn)很簡單把需求列出來估算一下所有全局變量和動(dòng)態(tài)內(nèi)存之和。如果超過OCM可用空間的一半我建議趁早放棄別在嵌入式開發(fā)里賭運(yùn)氣。內(nèi)存這種東西余量一定要留足否則后期每加一個(gè)功能都是痛苦的挪地址過程。5.3 最后分享一個(gè)小技巧把OCM當(dāng)“黑匣子”用在不帶DDR的項(xiàng)目里OCM空間雖然主要給程序用但你可以刻意留出最后幾KB作為運(yùn)行日志緩沖區(qū)。程序里把關(guān)鍵狀態(tài)、錯(cuò)誤碼、變量快照寫進(jìn)去掉電前把緩沖區(qū)整體寫入外部Flash。下次啟動(dòng)時(shí)Bootloader或者應(yīng)用先檢查這個(gè)區(qū)域就能知道上一次異常發(fā)生在哪里、當(dāng)時(shí)的現(xiàn)場是什么樣。這個(gè)方法幫我解決過一次很棘手的偶發(fā)死機(jī)問題。因?yàn)镺CM訪問快、不影響主循環(huán)幾乎可以把日志當(dāng)成實(shí)時(shí)記錄。等問題定位完再把緩沖區(qū)縮小騰出空間給其他功能。如果你做的也是無DDR的小產(chǎn)品強(qiáng)烈建議在鏈路設(shè)計(jì)時(shí)就把這個(gè)黑匣子區(qū)域預(yù)留出來省得后面滿世界找bug。