同設(shè)計:從NPU部署到SoC啟動的實(shí)戰(zhàn)筆記)
AI芯片的軟硬件設(shè)計做到第四篇我終于想清楚一個道理真正難的不是把TOPS堆高而是讓數(shù)據(jù)在合適的時間出現(xiàn)在合適的位置。這篇筆記不是課程講義是我在RK3588、Jetson Orin Nano上做部署、調(diào)NPU、查SoC啟動問題時攢下來的實(shí)操記錄。適合正在做AI落地、想從應(yīng)用層往底層看一眼的工程師也適合準(zhǔn)備選型AI芯片的產(chǎn)品同學(xué)。1. 軟硬件協(xié)同設(shè)計為什么AI芯片不能只看算力1.1 算力不是全部帶寬和調(diào)度才是瓶頸“AI芯片”這個概念被討論最多的參數(shù)是TOPS可真正做部署的人都知道TOPS只是紙面峰值。給你一顆標(biāo)稱8TOPS的NPU如果內(nèi)存帶寬只有25GB/s運(yùn)行一個YOLO模型每層推理都要把特征圖和權(quán)重從DDR搬進(jìn)搬出數(shù)據(jù)搬運(yùn)時間往往超過計算時間。我在RK3588上實(shí)測過理論6TOPS的NPU跑一個輕量分割模型實(shí)際能用到的算力不到35%原因不是算子不行而是DDR帶寬反復(fù)被占用計算單元經(jīng)??盏葦?shù)據(jù)。所以軟硬件協(xié)同設(shè)計的第一步永遠(yuǎn)是算清“每秒鐘要搬多少數(shù)據(jù)”再決定把計算放在哪一級存儲。這里有個很直觀的類比算力就像廚房里的大師傅帶寬就像傳菜的小工。大師傅再快如果每炒一個菜都要親自去倉庫取食材整個廚房就廢了。AI芯片設(shè)計時要做的最重要取舍就是在片上堆多少SRAM、跑到多大頻率、連接多大的DDR位寬。SRAM越大越能留住中間結(jié)果但成本和面積都上去了DDR位寬越寬帶寬越高但封裝和布線也更難。軟硬件設(shè)計的好壞實(shí)際上就是在這幾個變量之間找平衡點(diǎn)。按我的經(jīng)驗?zāi)玫揭活wAI芯片先別急著跑模型。把芯片的DDR帶寬、SRAM總?cè)萘?、?nèi)存吞吐參數(shù)、片間互聯(lián)帶寬拉一個表再拿你的模型算一下每層特征圖尺寸和權(quán)重大小估算一下理論搬運(yùn)量。如果理論搬運(yùn)時間已經(jīng)接近甚至超過推理時間那就別指望能跑滿TOPS應(yīng)該優(yōu)先優(yōu)化網(wǎng)絡(luò)結(jié)構(gòu)或改用更激進(jìn)的量化方案。1.2 異構(gòu)架構(gòu)CPU、NPU、GPU、DSP各管一攤現(xiàn)代AI SoC普遍是異構(gòu)計算常見組合是CPU加NPU加上GPU、DSP、ISP、視頻編解碼單元。CPU主控調(diào)度NPU做矩陣運(yùn)算GPU做通用并行計算DSP處理音頻或基帶ISP負(fù)責(zé)圖像信號。設(shè)計或者選型的時候最難的不是看懂每個子模塊的算力而是決定“哪類計算放進(jìn)哪個引擎”。我的做法是先把業(yè)務(wù)拆成數(shù)據(jù)流圖。計算密集的算子比如卷積、矩陣乘、Transformer里的QKV投影盡量落到NPU分支邏輯復(fù)雜、訪存不規(guī)則的任務(wù)留在CPU視頻編解碼交給專用硬件。這樣分工的原因很實(shí)際NPU是“專用電路”它對規(guī)則的矩陣運(yùn)算執(zhí)行效率極高但遇到一個if-else或者動態(tài)shape就會大幅退化CPU相反什么都能跑但乘法效率低。如果把動態(tài)算子扔進(jìn)NPU經(jīng)常觸發(fā)回調(diào)CPU性能直接掉一個數(shù)量級。另一個容易忽略的是存儲層次。CPU訪問DDR的路徑和NPU訪問DDR的路徑往往不同共用同一片內(nèi)存帶寬。多核CPU跑業(yè)務(wù)、NPU跑推理、GPU跑渲染三者同時密集訪問DDR時會產(chǎn)生“搶帶寬”的問題。軟硬件設(shè)計里叫內(nèi)存調(diào)度策略實(shí)際工程中我會給不同引擎設(shè)置優(yōu)先級比如NPU推理設(shè)為高優(yōu)先級GPU渲染其次CPU后臺任務(wù)最低保證關(guān)鍵時延不抖動。2. 硬件視角從SoC啟動到芯片后端設(shè)計2.1 SoC啟動流程一顆AI芯片是怎么醒過來的很多人把AI芯片當(dāng)成一塊會算數(shù)的黑盒子裝上驅(qū)動就開始用但真正做軟硬件設(shè)計時首先要面對的是SoC啟動。AI芯片本質(zhì)上仍然是一顆完整的SoC上電后要經(jīng)歷BootROM、ATF、U-Boot、Kernel、Driver、Runtime這一條完整鏈路。BootROM是芯片出廠固化的代碼芯片上電后CPU從復(fù)位向量地址取第一條指令由BootROM完成最基礎(chǔ)的時鐘和DDR初始化然后去讀取啟動介質(zhì)里的引導(dǎo)鏡像。這里值得展開的是DDR初始化。BootROM通常只負(fù)責(zé)最小化初始化復(fù)雜的內(nèi)存時序參數(shù)要靠ATF或U-Boot的一部分來設(shè)置。如果你在調(diào)試一塊新板子最經(jīng)典的故障就是DDR參數(shù)不對導(dǎo)致串口完全無輸出或者輸出一堆不可讀字符。遇到這種情況第一反應(yīng)不要是懷疑CPU壞了而是確認(rèn)DDR顆粒型號、頻率、時序參數(shù)和原理圖是否匹配。很多問題就是DDR4和LPDDR4的配置寄存器寫錯或者不同走線長度下CL值沒留余量。AI芯片的啟動鏈路里還有一個容易被忽略的角色ATF。ARM Trusted Firmware負(fù)責(zé)安全啟動、異常等級切換、電源狀態(tài)管理。如果你的方案需要做簽名啟動ATF環(huán)節(jié)要提前參與設(shè)計否則后面想加簽名就得整個引導(dǎo)鏈推翻重來。我見過不少項目功能開發(fā)都做完了客戶突然要求啟動鏡像加簽名校驗結(jié)果BootROM和ATF已經(jīng)沒法改只能做二次校驗繞了一大圈。這個點(diǎn)盡量在芯片選型階段就確認(rèn)清楚。2.2 以RK3588為參考理解實(shí)際啟動鏈路RK3588是我平時用得比較多的一顆國產(chǎn)SoC4個Cortex-A76加4個Cortex-A55NPU算力6TOPS接口豐富。它的啟動過程可以分為幾個階段BootROM先從復(fù)位向量執(zhí)行根據(jù)boot引腳的電平狀態(tài)決定從eMMC、SD卡、SPI NOR還是USB下載模式啟動讀入idbloader包含DDR初始化代碼然后加載U-Boot SPLSPL負(fù)責(zé)建立早期內(nèi)存環(huán)境再跳到ATF和U-Boot proper最后引導(dǎo)Linux內(nèi)核。實(shí)際調(diào)試的時候我強(qiáng)烈建議先用SD卡啟動。因為SD卡方便替換不像eMMC焊在板子上一旦引導(dǎo)分區(qū)寫壞恢復(fù)手段少很可能要進(jìn)maskrom模式用燒錄工具重新擦寫。用SD卡可以不斷換idbloader和u-boot版本驗證SD卡插拔就能回退。這個習(xí)慣幫我省了很多事有幾次U-Boot改壞SD卡直接換回之前備份的鏡像十分鐘解決而同事修改板載eMMC后折騰了一下午。在RK3588上還有一個實(shí)用技巧U-Boot串口打印是排查啟動問題的最重要信息源。默認(rèn)情況下U-Boot會打印DDR初始化日志、設(shè)備樹加載信息、引導(dǎo)介質(zhì)信息。我會預(yù)先在串口工具里保存完整日志然后和正常啟動日志逐行對比。啟動卡在DDR訓(xùn)練、卡在USB驅(qū)動、卡在從mmc讀取內(nèi)核日志上的差異很明顯。掌握了這種方法再奇怪的啟動故障都能變成“找差異”的體力活。2.3 芯片后端設(shè)計為什么也影響軟硬件熱搜里經(jīng)常出現(xiàn)“芯片后端”這個詞很多做軟件的同學(xué)可能不太清楚它是什么意思。芯片后端是物理設(shè)計階段的工作簡單說就是把邏輯網(wǎng)表變成可以流片的版圖。這部分包括布局布線、時鐘樹綜合、時序收斂、電源網(wǎng)絡(luò)設(shè)計和IR-drop分析。AI芯片因為NPU面積大、大規(guī)模陣列翻轉(zhuǎn)時電流變化非常劇烈后端設(shè)計里的電源完整性非常關(guān)鍵。如果IR-drop問題沒處理好NPU高負(fù)荷工作時核心電壓會被拉低導(dǎo)致邏輯單元翻轉(zhuǎn)變慢最終表現(xiàn)為偶發(fā)性的計算錯誤。這類錯誤在軟件層很難查因為不是每次都錯而是頻率高、溫度高、電流大的時候錯。我在做板卡級壓測時就遇到過一次單模塊測試沒問題整機(jī)同時跑NPU和GPU時推理結(jié)果出現(xiàn)1%左右的錯誤率。后來用示波器量核心電源紋波發(fā)現(xiàn)NPU啟動瞬間電壓跌落超過80mV已經(jīng)低于芯片規(guī)格書要求。這屬于典型的軟硬件聯(lián)調(diào)問題靠改軟件是治不了的需要從電源設(shè)計或降頻入手。對于做應(yīng)用層開發(fā)的工程師了解后端設(shè)計不是要去畫版圖而是明白一個道理AI芯片的可用算力受供電和散熱約束。廠家標(biāo)稱的峰值TOPS往往需要在特定電壓和溫度窗口下才能達(dá)到實(shí)際產(chǎn)品設(shè)計必須留出降額。選型時不要只看算力墻上的TOPS還要看芯片的熱設(shè)計功耗TDP、電源紋波要求和散熱方案否則設(shè)計出來的產(chǎn)品輕則降頻重則計算錯誤。3. 軟件棧與工具鏈編譯器、算子優(yōu)化、運(yùn)行時3.1 AI編譯器到底在編譯什么AI芯片的軟件棧核心是AI編譯器。很多人第一次接觸RKNN、TensorRT的時候以為這只是普通的格式轉(zhuǎn)換工具實(shí)際上它承擔(dān)的是編譯器的職責(zé)。PyTorch或ONNX模型表達(dá)的是一個計算圖里面的節(jié)點(diǎn)是Conv、MatMul、Add、Reshape這些算子NPU本身不認(rèn)識這種通用計算圖它只認(rèn)識自己定義的專用指令。AI編譯器要做的就是把這個高層計算圖一步步映射成NPU能執(zhí)行的底層程序。這個過程大致分三步前端解析、圖優(yōu)化、代碼生成。前端把模型讀進(jìn)來并轉(zhuǎn)換成內(nèi)部IR這一步要注意模型里是否存在不支持的自定義算子圖優(yōu)化階段會做算子融合、常量折疊、死代碼消除目的是減少執(zhí)行開銷代碼生成階段則把優(yōu)化后的圖切分成適合NPU并行執(zhí)行的子圖規(guī)劃每塊數(shù)據(jù)的存放位置最終生成二進(jìn)制執(zhí)行程序。實(shí)際使用中經(jīng)常出現(xiàn)同一個模型用不同工具鏈轉(zhuǎn)換后性能差異巨大的現(xiàn)象。原因往往不在前端解析而在圖優(yōu)化階段。工具鏈能否識別一個多算子組合并融合直接影響最終執(zhí)行效率。以RKNN為例它對MobileNet這類結(jié)構(gòu)里的ConvBNReLU融合做得很到位但遇到比較新的Attention算子可能只支持單算子映射性能就下來了。因此做模型部署時我會特意看工具鏈release notes了解它最近優(yōu)化了哪些算子組合再決定網(wǎng)絡(luò)結(jié)構(gòu)調(diào)整方向。3.2 算子融合與內(nèi)存排布的實(shí)際意義算子融合是最重要也最立竿見影的優(yōu)化手段之一。拿ConvBNReLU來說三個算子如果分開執(zhí)行每一步都要從DDR讀出輸入、寫入輸出。BN和ReLU都屬于逐元素算子計算量不大但數(shù)據(jù)搬運(yùn)量很大分開執(zhí)行會讓DDR帶寬白白浪費(fèi)。融合成一個算子后卷積的輸出留在片上寄存器或SRAM中馬上完成BN和ReLU再寫回DDR數(shù)據(jù)搬運(yùn)次數(shù)直接減少三分之二。另一個關(guān)鍵點(diǎn)是數(shù)據(jù)的內(nèi)存排布layout。常見的圖像張量排布有NCHW和NHWC兩種不同的NPU對layout有不同偏好。規(guī)則是如果NPU的硬件通路設(shè)計成按行做向量處理NHWC能直接按通道連續(xù)訪問如果設(shè)計成按通道做矩陣運(yùn)算NCHW更自然。工具鏈通常會在前端做layout轉(zhuǎn)換但轉(zhuǎn)換本身也是算子會消耗時間。我在RK3588上就遇到過模型輸入已經(jīng)是NHWC工具鏈仍然轉(zhuǎn)換成NCHW又轉(zhuǎn)回去的笨拙情況最后靠手動指定輸入layout才解決。關(guān)于內(nèi)存對齊這里也有一個經(jīng)驗NPU的buffer地址通常要求64字節(jié)或者128字節(jié)對齊尺寸沒有對齊時會填充空白數(shù)據(jù)導(dǎo)致帶寬浪費(fèi)。一個小模型可能看不出差別但大模型的Embedding層如果對齊不當(dāng)可能浪費(fèi)幾十MB的存儲和搬運(yùn)。設(shè)計推理引擎時我會對每個tensor做內(nèi)存池對齊規(guī)劃而不是簡單malloc性能穩(wěn)定性會明顯提升。3.3 驅(qū)動與Runtime的協(xié)作關(guān)系在NPU執(zhí)行模型軟件層級通常是這樣的應(yīng)用調(diào)用Runtime APIRuntime解析執(zhí)行計劃把命令寫到與驅(qū)動的共享內(nèi)存隊列然后觸發(fā)一個軟中斷或硬件中斷通知驅(qū)動驅(qū)動把命令轉(zhuǎn)成NPU硬件可執(zhí)行格式提交到硬件命令隊列并維護(hù)狀態(tài)NPU執(zhí)行完畢后發(fā)送中斷驅(qū)動更新完成標(biāo)志Runtime再返回給應(yīng)用。這里面任何一個環(huán)節(jié)丟數(shù)據(jù)都會導(dǎo)致推理卡死或錯誤。我調(diào)試時遇到過最詭異的一個問題是同一份RKNN模型每次推理前幾十次都是好的后面隨機(jī)出現(xiàn)返回碼錯。排查了很久最后發(fā)現(xiàn)是命令隊列深度設(shè)置太小驅(qū)動在連續(xù)提交多個任務(wù)時來不及處理導(dǎo)致一個同步信號被覆蓋。這是個典型的軟件配置問題不在代碼邏輯上而在隊列深度、緩沖大小這類參數(shù)上。不同工具鏈版本對這類參數(shù)的默認(rèn)值不同升級工具鏈后最好重新驗證壓力場景。這里也建議大家多利用廠商提供的測試模式。比如RKNN有l(wèi)oopback和性能分析模式TensorRT有l(wèi)ayerwise信息和dump功能。不要等出了問題才開而是在項目初期就建立性能基線。開著profiling跑一遍模型把每層耗時、耗時占比、算力利用率記錄下來。這樣后續(xù)不管是工具鏈升級還是硬件批次更換都有一個參照物排查問題會快很多。4. 實(shí)際部署案例在Jetson Orin Nano和RK3588上的實(shí)踐4.1 平臺選型為什么在兩個平臺之間反復(fù)橫跳最近做邊緣AI項目我在Jetson Orin Nano 8GB和RK3588之間反復(fù)比較。Orin Nano標(biāo)稱最高40TOPSINT4稀疏支持TensorRT和齊全的CUDA生態(tài)但整體價格高、供貨周期長RK3588的NPU算力6TOPS紙面數(shù)據(jù)低很多但勝在價格、國產(chǎn)供應(yīng)鏈穩(wěn)定外設(shè)接口豐富無風(fēng)扇被動散熱也能跑。說實(shí)話如果只看TOPSOrin Nano碾壓RK3588但真實(shí)項目里還要看總成本、功耗、供貨和散熱需要實(shí)際部署測試才能拍板。我的測試方法是保持同一個模型和同一份校準(zhǔn)數(shù)據(jù)分別在兩端做INT8量化測三組數(shù)據(jù)單幀推理延遲、連續(xù)運(yùn)行半小時后的平均功耗、Max溫度下的性能衰減。測完之后很意外RK3588上某些輕量級模型的表現(xiàn)并沒有想象中差原因是它NPU與CPU的數(shù)據(jù)通路較短小模型搬運(yùn)開銷低而Orin Nano在較大模型上優(yōu)勢明顯畢竟CUDA核多、帶寬大。選型時不能只看一個benchmark要把你的核心業(yè)務(wù)模型、典型batch size和功耗預(yù)算一起列出來對比。兩款平臺的開發(fā)流程差異也很大。Orin上用的還是CUDA生態(tài)那一套基于TensorRT的engine序列化、量化校準(zhǔn)、多流推理資料豐富RK3588則依賴RKNN-Toolkit2從ONNX到rknn轉(zhuǎn)換再到板上rknn runtime推理。如果你團(tuán)隊熟悉CUDA選Orin上手快如果需求是成本敏感的邊緣盒子RK3588成熟度高社區(qū)資料多部署國產(chǎn)化要求也容易滿足。4.2 模型部署與量化注意事項量化是邊緣部署里最危險的一環(huán)。Orin Nano支持INT8和INT4RK3588的RKNN也支持INT8和INT16量化策略不外乎PTQ和QAT。PTQ即訓(xùn)練后量化需要一個校準(zhǔn)數(shù)據(jù)集來統(tǒng)計每層激活值的范圍。校準(zhǔn)集的選擇極其重要覆蓋真實(shí)場景的數(shù)據(jù)分布才能讓量化尺度合理。我踩過一個很典型的坑RK3588上用開源ImageNet校準(zhǔn)集做了一個檢測模型直接部署到產(chǎn)線上的缺陷檢測場景精度掉了2%換成現(xiàn)場采集的200張真實(shí)缺陷圖重新校準(zhǔn)就恢復(fù)到1%以內(nèi)。所以校準(zhǔn)集不是隨便找一批圖片就行必須貼近實(shí)際使用環(huán)境。還需要注意量化感知訓(xùn)練的入口。如果PTQ精度損失接受不了就要在訓(xùn)練階段插入偽量化節(jié)點(diǎn)做QAT。QAT能保留更多精度但需要重新訓(xùn)練模型會增加時間成本。模型結(jié)構(gòu)上某些算子對量化很敏感比如高精度的Softmax、LayerNorm部署時可以保留為浮點(diǎn)。RKNN支持混合精度量化我會把敏感算子保留INT16或FP16避免整網(wǎng)量化為INT8后精度崩掉。Orin上也可以對特定層關(guān)閉量化TensorRT支持每層精度覆蓋只是配置方法不太一樣。部署中還有一個實(shí)際細(xì)節(jié)要充分考慮輸入輸出的預(yù)處理放在CPU還是NPU。常見的圖像縮放、顏色轉(zhuǎn)換、歸一化如果在CPU做會額外增加延遲。RKNN和TensorRT都支持把部分預(yù)處理融合進(jìn)模型或硬件解碼通道。我一般會優(yōu)先把縮放和歸一化融入模型輸入層減少host和device間的數(shù)據(jù)拷貝。實(shí)測下來整體延遲能下降10%到20%這個收益在低功耗設(shè)備上非??捎^。4.3 性能分析與優(yōu)化順序如果NPU利用率上不去最忌諱一上來就懷疑芯片或者堆算力。我按這個順序排查第一看內(nèi)存帶寬是否成為瓶頸第二查計算圖里有沒有NPU不支持的算子掉到CPU第三檢查量化精度和per-channel配置第四確認(rèn)驅(qū)動和工具鏈版本匹配最后才考慮散熱降頻。前面幾步的排查工具都現(xiàn)成Orin上用Nsight Systems看kernel時間線和CPU/GPU/NPU負(fù)載RK3588上用RKNN的profiling接口打印每層耗時和帶寬占比。一個很常見的情況某些模型在轉(zhuǎn)rknn時部分算子工具鏈沒有NPU實(shí)現(xiàn)自動落到CPU執(zhí)行。CPU算力相比NPU差很多導(dǎo)致整體延遲被拉高。有一次我把一個YOLOv5s模型跑在RK3588上發(fā)現(xiàn)Detection Head里幾個動態(tài)shape算子全部落在CPUNPU利用率只有40%總延遲反而比直接上CPU慢。后來換成固定shape輸出、改造后處理部分才真正發(fā)揮了NPU性能。這個經(jīng)驗說明模型結(jié)構(gòu)要跟著芯片的算子支持列表走而不是把模型當(dāng)成不可修改的輸入。另外要監(jiān)控溫度。NPU長時間高負(fù)載運(yùn)行后芯片溫度上升DVFS會主動降頻性能曲線會出現(xiàn)明顯的“先高后低”。如果產(chǎn)品需求是長時間連續(xù)推理需要按峰值頻率的80%來評估穩(wěn)定算力。用壓力腳本跑至少30分鐘觀察推理耗時有沒有逐步上漂。上漂的話先改善散熱再考慮降低頻率或提高任務(wù)分批度。很多時候“芯片性能不夠”其實(shí)是散熱設(shè)計不夠。5. 多AI協(xié)作與AI Agent的芯片新需求5.1 AI Agent給芯片設(shè)計帶來的新壓力最近“AI Agent”和“多AI協(xié)作”這些詞很火本質(zhì)上代表工作負(fù)載從單模型推理變成多模型組合推理。一個智能體應(yīng)用里面可能同時要維護(hù)意圖識別模型、工具調(diào)用模型、對話生成模型、記憶檢索模型。單看每個模型都不大但并發(fā)調(diào)度會帶來全新的芯片設(shè)計需求上下文切換、中間結(jié)果緩存、任務(wù)優(yōu)先級管理都要重新考慮。過去的推理框架大多只針對單個模型優(yōu)化現(xiàn)在必須面對多個模型輪流占用NPU的問題。這種場景下NPU的任務(wù)切換開銷會被放大。每次切換不僅要保存和恢復(fù)上下文還要把前一個模型的中間特征從片上清出或者保留。如果芯片支持多個硬件上下文并發(fā)比如RK3588的NPU支持同時運(yùn)行多個上下文就能有效減少切換損耗。但多上下文又帶來資源分配問題每個上下文占用多少執(zhí)行單元、多少內(nèi)存帶寬需要在運(yùn)行時動態(tài)規(guī)劃。軟硬件協(xié)同設(shè)計在這里體現(xiàn)得很明顯硬件提供并發(fā)能力軟件要決定怎么分資源。從系統(tǒng)層面看AI Agent還有一個特點(diǎn)推理請求是發(fā)散式、碎片化的時延要求卻很高。這對芯片的模型加載速度提出要求。模型權(quán)重如果很大每次對話都要從DDR甚至Flash加載會產(chǎn)生明顯延遲?,F(xiàn)在有些方案會把高頻模型常駐SRAM低優(yōu)先級模型放DDR靠內(nèi)存分層來滿足時延。設(shè)計推理引擎時這個層級劃分要提前規(guī)劃。5.2 多AI協(xié)作場景下的硬件資源分配多模型并發(fā)時最稀缺的資源往往不是算力而是內(nèi)存帶寬。假設(shè)一個對話模型推理時平均占用25GB/s的DDR帶寬另一個檢測模型要占15GB/s兩個同時跑在40GB/s帶寬的平臺上必然互相爭搶。這時候無論算力多強(qiáng)都會出現(xiàn)排隊等待。工程上我會按優(yōu)先級分流把交互鏈路上最關(guān)鍵的任務(wù)設(shè)為高優(yōu)先級占用獨(dú)立計算核心和高帶寬通道把后臺批量任務(wù)調(diào)度到空閑時間片或者降低batch size和精度。還有一種思路是物理上把任務(wù)分擔(dān)到不同芯片。比如用一顆RK3588做對話主控另一顆做視覺檢測中間通過PCIe或以太網(wǎng)通信。多AI協(xié)作在硬件形態(tài)上往往就是多芯片協(xié)同。設(shè)計時要注意片間通信延遲不能把頻繁交換的數(shù)據(jù)放到網(wǎng)絡(luò)鏈路上否則整個系統(tǒng)時延就毀了。我的一般原則是高頻小數(shù)據(jù)走共享內(nèi)存或消息隊列大數(shù)據(jù)走DMA或?qū)S猛ǖ滥P蜋?quán)重盡量本地化降低跨芯片依賴。在軟件框架層面多模型并發(fā)還需要一個統(tǒng)一的資源管理器。它負(fù)責(zé)統(tǒng)計每個模型占用的算力、帶寬、內(nèi)存并根據(jù)實(shí)時負(fù)載做動態(tài)調(diào)度。開源方案里有些Runtime已經(jīng)提供類似功能但沒有統(tǒng)一標(biāo)準(zhǔn)。做實(shí)際項目時我傾向于在推理層之上加一個簡單任務(wù)隊列把每個模型封裝成worker帶獨(dú)立的優(yōu)先級和時間片。這樣做的好處是即使底層芯片不支持硬件多上下文也能在軟件上實(shí)現(xiàn)基本的并發(fā)保護(hù)。6. 芯片測試與軟硬件聯(lián)調(diào)6.1 芯片測試的層次從晶圓到板卡說到“芯片測試”很多軟件工程師第一反應(yīng)是跑個benchmark但在真正的芯片生命周期里測試是分層的。芯片出廠前要做晶圓測試CP測試檢查每顆die的基本功能和參數(shù)封裝后要做成品測試FT測試驗證引腳連通和功能到了應(yīng)用端就是板卡級和系統(tǒng)級測試。AI芯片因為集成了大量計算單元更需要系統(tǒng)級壓力測試用真實(shí)模型長時間跑看輸出正確性和性能穩(wěn)定性。我在板卡測試中有一個習(xí)慣先跑一遍廠商自帶的demo程序確認(rèn)鏈路是通的再用自己的模型。demo除了驗證功能更重要的是建立一個標(biāo)準(zhǔn)環(huán)境。測試時同步記錄核心電壓、DDR頻率、NPU頻率和溫度。如果壓力測試中推理結(jié)果偶發(fā)錯誤首先看溫度再看電壓和電流的波動。很多時候不是邏輯錯誤而是電源紋波或者時鐘抖動導(dǎo)致的偶發(fā)錯誤。這種問題恰好在軟硬件接口上需要電子工程師和軟件工程師一起定位。另外芯片測試還要關(guān)注多芯片一致性。同一塊板子有兩顆芯片或者多個批次芯片混合使用性能可能有細(xì)微差異。為了量產(chǎn)穩(wěn)定我會在產(chǎn)線端做一個性能閾值檢測跑一套固定模型記錄推理耗時誤差范圍超出閾值就判為異常。這個方法能在早期發(fā)現(xiàn)供電、散熱或時鐘配置不均的問題避免壞板流向客戶。6.2 快速定位軟硬件問題的方法軟硬件聯(lián)調(diào)最有效的辦法就是分層隔離。我通常把問題分成四層啟動層、驅(qū)動層、工具鏈層、應(yīng)用層。啟動層看BootROM、U-Boot、內(nèi)核日志排查DDR和引導(dǎo)介質(zhì)驅(qū)動層看設(shè)備樹和驅(qū)動加載日志確認(rèn)NPU時鐘、中斷、IOMMU都正常工具鏈層用廠商示例模型驗證編譯和運(yùn)行應(yīng)用層再看自己模型的算子支持和數(shù)據(jù)通路。每層都有對應(yīng)的checklist逐層排除后剩下的才是真正需要深入研究的問題。一個頻繁出現(xiàn)的現(xiàn)象是換了內(nèi)核版本后NPU設(shè)備節(jié)點(diǎn)還在但驅(qū)動加載失敗。這大概率是設(shè)備樹里NPU的電源或時鐘依賴變了或者驅(qū)動內(nèi)核版本API不兼容。要查看dmesg看具體報錯是資源獲取失敗還是probe spin。還有一次是IOMMU沒有開啟導(dǎo)致NPU訪問內(nèi)存權(quán)限不足推理時直接頁錯誤。這類問題排查時除了日志還要確認(rèn)芯片勘誤表里是否有已知問題很多廠商勘誤表會列出特定型號的軟件規(guī)避方法這個是普通文檔里沒有的。排查工具方面JTAG和邏輯分析儀是硬件工程師的武器軟件工程師也不要完全回避。遇到非常隱蔽的問題比如啟動時序異常、中斷丟失簡單的邏輯分析儀抓一下signal就能看出端倪。當(dāng)然條件不足時先用軟件手段盡量縮小范圍再請硬件同事幫忙抓信號。我在多個項目里的經(jīng)驗是軟硬件聯(lián)調(diào)中最差的策略是“亂猜”最好的策略是“記錄一切現(xiàn)場數(shù)據(jù)”包括時間戳、寄存器值、電壓值后期才能復(fù)現(xiàn)和對比。7. 常見問題與避坑指南7.1 啟動卡在U-Boot串口無輸出怎么辦先別慌按順序查量一下各路電源的電壓是否在規(guī)格范圍內(nèi)確認(rèn)時鐘信號有沒有起振再用示波器看DDR DQ上的信號活動是否正常。如果這些都沒問題換一個已知好的啟動介質(zhì)排除介質(zhì)本身被寫壞的可能。特別提醒eMMC的boot分區(qū)如果被寫壞現(xiàn)象是“芯片能識別但啟動不了”普通擦寫工具可能都看不到分區(qū)要進(jìn)maskrom模式或使用專用燒錄器恢復(fù)。這一步在試產(chǎn)階段最容易踩所以功能開發(fā)階段盡量用SD卡啟動。串口無輸出還有一個常見原因串口參數(shù)配置錯誤。RK3588默認(rèn)調(diào)試串口通常是UART2波特率1500000很多新手拿著115200的配置去看自然全是亂碼或空白。先確認(rèn)硬件原理圖上調(diào)試串口接的是哪個UART再對照官方默認(rèn)配置。不要一上來就懷疑芯片壞了。7.2 NPU利用率上不去先做profiling看每層實(shí)際耗時再看DDR帶寬占用。最常見原因有三個一是模型里有動態(tài)shape觸發(fā)NPU頻繁重新編譯或CPU回退二是layout轉(zhuǎn)換太多數(shù)據(jù)在NPU和CPU之間反復(fù)拷貝三是小算子過多NPU并行度沒跑滿。對應(yīng)的手段分別是固定shape、手動指定layout、用工具鏈做算子融合或者調(diào)整網(wǎng)絡(luò)結(jié)構(gòu)。別盲目換更大的芯片先把自己的模型和工具鏈“配合好”。另一個容易被忽視的點(diǎn)是并發(fā)場景下的利用率。單模型推理利用率可能有80%兩個模型同時推就會互相干擾總利用率反而下降。如果業(yè)務(wù)有并發(fā)需求需要去查NPU的上下文切換開銷以及驅(qū)動是否支持多流。硬件不支持的場景只能靠軟件串行化或者調(diào)整任務(wù)優(yōu)先級。7.3 驅(qū)動版本與內(nèi)核不匹配RKNN工具鏈和RK3588內(nèi)核驅(qū)動有版本對應(yīng)關(guān)系Jetson的L4T版本也和CUDA/TensorRT綁定。升級內(nèi)核或更換L4T鏡像后NPU驅(qū)動沒同步升級就會出現(xiàn)設(shè)備節(jié)點(diǎn)存在但無法加載或者推理失敗。解決方案很機(jī)械對照廠商發(fā)布說明確認(rèn)內(nèi)核、驅(qū)動、工具鏈三個版本是否在同一個兼容矩陣?yán)?。這一點(diǎn)建議寫進(jìn)項目CMake配置或CI腳本里防止團(tuán)隊成員各自升級后集體踩坑。設(shè)備樹層面也要注意。NPU的電源域和時鐘節(jié)點(diǎn)往往和設(shè)備樹綁定換了開發(fā)板或者改了dts一定要重新檢查這些節(jié)點(diǎn)。之前有同事把RK3588開發(fā)板的dts直接用到自研主板NPU電源樹配置不對導(dǎo)致驅(qū)動probe成功但推理時死機(jī)。最后查出來是少了兩個電源域依賴這種問題一旦出現(xiàn)排查成本非常高。最后再說一個非常實(shí)用的習(xí)慣每個項目建一個軟硬件兼容性矩陣文檔記錄芯片型號、內(nèi)核版本、驅(qū)動版本、工具鏈版本、demo結(jié)果和已知問題。這個矩陣會伴隨整個產(chǎn)品生命周期每次變更先查矩陣再動手。我在幾個項目里靠這個文檔避開了很多重復(fù)的坑也方便團(tuán)隊協(xié)作時快速對齊環(huán)境。自己做這個系列時最大的感觸是AI芯片的軟硬件設(shè)計沒有一個固定的“標(biāo)準(zhǔn)答案”更多的是一種在數(shù)據(jù)流、算力、帶寬、工具鏈、散熱之間不斷權(quán)衡的過程。每次把上層模型的性能問題追到底層寄存器或者驅(qū)動參數(shù)都會對整顆芯片的理解深一層。這篇第四篇主要記錄了我近期在啟動、部署、測試上的實(shí)操下一篇我打算專門聊聊NPU微架構(gòu)和算子映射的具體細(xì)節(jié)。如果大家正在做類似的事情希望這些記錄能幫你們少走一些彎路也歡迎在評論區(qū)聊聊你們在實(shí)際項目中遇到的怪問題。