戰(zhàn):從單CS到雙CS的完整排查指南)
上個(gè)月我把一塊STM32MP2x板卡上的LPDDR4內(nèi)存從1GB升級(jí)到2GB結(jié)果U-Boot啟動(dòng)日志里始終只識(shí)別出一半容量。排查到最后才發(fā)現(xiàn)問(wèn)題不是顆粒壞、不是焊接虛而是chipselect的配置和實(shí)際硬件連接之間差了一個(gè)信號(hào)。這個(gè)坑在ST的參考手冊(cè)里往往一句話帶過(guò)但在真實(shí)項(xiàng)目里特別容易讓人卡頓好幾天。所以這篇想好好聊聊STM32MP2x平臺(tái)LPDDR4的chipselect它到底管什么怎樣把硬件設(shè)計(jì)、啟動(dòng)代碼、設(shè)備樹(shù)三端對(duì)齊以及我實(shí)測(cè)中遇到的幾種異常表現(xiàn)和定位思路。無(wú)論你是正在畫(huà)新板子還是在老平臺(tái)上做內(nèi)存擴(kuò)容這篇都能幫你少走彎路。1. chipselect在LPDDR4里管什么先把rank、通道、片選這三個(gè)概念理清1.1 為什么不是內(nèi)存顆粒選通而是“rank選通”很多朋友第一次接觸chipselect時(shí)容易把它理解成“選哪一顆DDR顆?!薄_@在SRAM或者并口NOR時(shí)代基本沒(méi)錯(cuò)但到了LPDDR4完全不適用。LPDDR4顆粒內(nèi)部做了通道Channel拆分每個(gè)16bit或32bit器件內(nèi)部有兩套獨(dú)立通道每個(gè)通道可以對(duì)應(yīng)一個(gè)或兩個(gè)rank。這里的rank可以理解成一組共享數(shù)據(jù)線、地址線、命令線的存儲(chǔ)陣列??刂破飨朐L問(wèn)哪一組rank就靠CS信號(hào)來(lái)選通拉低CS0就訪問(wèn)rank0拉低CS1就訪問(wèn)rank1兩者在同一個(gè)控制器通道下互斥運(yùn)行。也就是說(shuō)chipselect不是“選顆?!倍恰斑x顆粒內(nèi)部的存儲(chǔ)資源組”。這個(gè)區(qū)別很關(guān)鍵因?yàn)樗鼪Q定了你在計(jì)算內(nèi)存容量、分配地址空間、設(shè)置時(shí)序參數(shù)時(shí)都不能只按“一排顆?!眮?lái)思考。1.2 LPDDR4的CS信號(hào)和傳統(tǒng)DDR3/4有什么不同傳統(tǒng)DDR3/4內(nèi)存條上也會(huì)看到CS_n引腳但LPDDR4為了降低功耗、縮小封裝對(duì)CS信號(hào)的定義和電氣特性做了調(diào)整。LPDDR4的rank數(shù)量是封裝級(jí)預(yù)定義的同一個(gè)封裝里的兩個(gè)rank會(huì)共用大部分引腳只有CS/CKE等少數(shù)控制信號(hào)分開(kāi)走。而且LPDDR4沒(méi)有DDR4那種大規(guī)模內(nèi)存條形態(tài)絕大多數(shù)是板上貼裝所以片選信號(hào)的布線長(zhǎng)度、參考平面、端接方式都會(huì)直接影響系統(tǒng)穩(wěn)定性。這里有一個(gè)常見(jiàn)誤區(qū)有人覺(jué)得CS只是一個(gè)“高低電平”只要硬件上拉個(gè)電阻、軟件里填個(gè)寄存器就行。實(shí)測(cè)中恰恰相反CS信號(hào)和命令/地址線一樣必須滿足setup、hold、flight time約束。尤其在雙rank場(chǎng)景下控制器需要在CS0和CS1之間快速切換如果兩個(gè)CS的走線長(zhǎng)度差異過(guò)大或缺少合理的端接輕則初始化失敗重則高頻運(yùn)行時(shí)隨機(jī)死機(jī)。1.3 STM32MP2x平臺(tái)上為什么特別容易被chipselect卡住STM32MP2x系列作為ST新一代MPU把DDR控制器、LPDDR4 PHY、低功耗管理緊密集成在一個(gè)SoC里好處是硬件設(shè)計(jì)比外掛控制器簡(jiǎn)潔得多壞處是很多DDR配置被“隱藏”在初始化代碼和二進(jìn)制參數(shù)里。一旦你依賴ST提供的DDR tuning工具生成了參數(shù)默認(rèn)往往是單rank、單chipselect的配置。因?yàn)檫@是大多數(shù)評(píng)估板、小容量產(chǎn)品的標(biāo)準(zhǔn)形態(tài)SDK里不會(huì)主動(dòng)告訴你“你的板子其實(shí)可以開(kāi)第二個(gè)CS”。我見(jiàn)過(guò)不止一個(gè)硬件工程師畫(huà)板時(shí)明明把兩顆LPDDR4顆粒的CS1信號(hào)都連到SoC對(duì)應(yīng)引腳了但SDK里用的是單rank配置結(jié)果內(nèi)存容量死活只有一半。反過(guò)來(lái)也有直接把雙rank顆粒當(dāng)單rank用容量浪費(fèi)一半的。這種問(wèn)題往往不是“改一個(gè)寄存器”就能解決而是要從控制器初始化、內(nèi)存映射、設(shè)備樹(shù)內(nèi)存節(jié)點(diǎn)三層一起改所以特別值得系統(tǒng)性梳理一遍。2. 地址映射與容量規(guī)劃CS0/CS1在STM32MP2x上怎么分布、怎么算2.1 CPU看到的“多片選”其實(shí)是一段連續(xù)地址在STM32MP2x的DDR控制器里CS0和CS1通常會(huì)被映射到CPU地址空間的連續(xù)區(qū)域。也就是說(shuō)你不需要像訪問(wèn)SPI片選那樣去切換CSCPU始終用同一個(gè)基地址訪問(wèn)內(nèi)存控制器根據(jù)地址范圍自動(dòng)選通對(duì)應(yīng)的rank。舉個(gè)例子如果DDR控制器把CS0映射到0x60000000起始的1GB區(qū)域CS1可能緊跟著映射到0x40000000起始的另一段區(qū)域。具體基地址不同型號(hào)有差異但核心邏輯是一致的CS數(shù)量決定了你能覆蓋的物理地址窗口也決定了Linux內(nèi)核中memory節(jié)點(diǎn)的大小。配置時(shí)需要特別注意DDR控制器初始化參數(shù)里定義了CS映射和行列地址寬度設(shè)備樹(shù)reg屬性里定義的內(nèi)存大小必須和實(shí)際可用區(qū)域一致。如果初始化代碼只開(kāi)了CS0設(shè)備樹(shù)卻聲明了雙CS的內(nèi)存大小內(nèi)核啟動(dòng)時(shí)會(huì)因?yàn)樵L問(wèn)到不存在的物理地址而異常反過(guò)來(lái)初始化代碼開(kāi)了雙CS設(shè)備樹(shù)只聲明一半系統(tǒng)就會(huì)白白浪費(fèi)另一半內(nèi)存這種現(xiàn)象在裸機(jī)下還不容易發(fā)現(xiàn)進(jìn)入Linux后才通過(guò)free -m看到端倪。2.2 容量計(jì)算公式顆粒密度、通道數(shù)、rank數(shù)一個(gè)都不能少要判斷一個(gè)板子到底應(yīng)該開(kāi)幾個(gè)CS可以先做簡(jiǎn)單的容量拆解。LPDDR4顆粒容量 單個(gè)通道容量 × 通道數(shù)量 × rank數(shù)量。例如一顆常見(jiàn)的4GB32GbitLPDDR4顆粒內(nèi)部可能是2個(gè)通道、每個(gè)通道8Gbit、每通道2個(gè)rank構(gòu)成8Gbit×2×2。當(dāng)控制器只支持1個(gè)CS時(shí)這一顆顆粒最多能用一半容量支持2個(gè)CS且都使能時(shí)完整容量才被釋放。如果板上用的是兩顆4GB LPDDR4顆粒并聯(lián)組成64bit數(shù)據(jù)總線但每顆顆粒內(nèi)部都有雙rank那理論上總?cè)萘渴?GB×28GB。但如果SDK只配置了單CS即使PCB上兩根CS都連了實(shí)際只訪問(wèn)到每顆顆粒的一個(gè)rank總?cè)萘孔兂?GB。這就是我前面提到的“容量少一半”最常見(jiàn)原因。在設(shè)計(jì)階段建議先畫(huà)一張簡(jiǎn)單的表格把每個(gè)CS對(duì)應(yīng)的顆粒、rank、通道、容量列清楚。比如CS編號(hào)顆粒數(shù)量通道數(shù)rank配置設(shè)計(jì)容量開(kāi)啟后可用容量CS022單rank/雙rank2GB或4GB由rank配置決定CS122雙rank總?cè)萘繙pCS0部分由rank配置決定這種表格在后期排查時(shí)非常有用能直接定位是容量計(jì)算錯(cuò)還是代碼配置錯(cuò)。2.3 什么時(shí)候建議開(kāi)雙CS什么時(shí)候不建議開(kāi)雙CS最直接的收益是容量翻倍但代價(jià)也相當(dāng)明顯布線面積增大兩個(gè)CS信號(hào)需要等長(zhǎng)控制尤其是雙rank顆粒時(shí)CS0和CS1的走線長(zhǎng)度差不能太大。PHY初始化和訓(xùn)練復(fù)雜度上升需要額外調(diào)Rank-to-Rank切換時(shí)間否則兩個(gè)rank之間切換時(shí)可能出現(xiàn)數(shù)據(jù)沖突。低功耗功耗策略更復(fù)雜。LPDDR4本身主打低功耗但雙rank同時(shí)刷新、電源管理都要重新設(shè)計(jì)。我的個(gè)人建議是如果產(chǎn)品容量需求在2GB以內(nèi)優(yōu)先選用單CS的單rank方案讓初始化邏輯最簡(jiǎn)化只有明確需要4GB及以上再考慮雙CS。選雙CS不是不行但要把它當(dāng)成一個(gè)獨(dú)立的硬件設(shè)計(jì)任務(wù)而不是在已有單CS原理圖上“補(bǔ)一根線”。這一點(diǎn)在STM32MP2x這類(lèi)集成了復(fù)雜DDR PHY的平臺(tái)上尤其重要因?yàn)镻HY的校準(zhǔn)參數(shù)和CS數(shù)量是綁定的。3. 落到工程里從CubeMX/DDR工具生成參數(shù)到設(shè)備樹(shù)啟動(dòng)代碼的完整鏈路3.1 用DDR配置工具把芯片型號(hào)和CS數(shù)選對(duì)ST的STM32MP2x系列通常可以通過(guò)STM32CubeMX或配套DDR tuning工具生成DDR初始化參數(shù)。在配置時(shí)有幾個(gè)選項(xiàng)直接關(guān)系到chipselect-內(nèi)存類(lèi)型選擇LPDDR4不要選成LPDDR4X或DDR4。顆粒廠商/型號(hào)選擇或者手動(dòng)填寫(xiě)密度、bank數(shù)、row地址位寬、column地址位寬。Rank/CS數(shù)量通常有single rank和dual rank可選。這一步非常關(guān)鍵因?yàn)閠uning工具會(huì)根據(jù)你選的rank數(shù)量自動(dòng)生成不同的PHY訓(xùn)練序列和控制器寄存器配置。如果你用單rank配置去驅(qū)動(dòng)雙rank顆粒工具生成的時(shí)序表里根本沒(méi)有第二個(gè)CS相關(guān)的延遲參數(shù)跑起來(lái)自然會(huì)出錯(cuò)。有的工程師習(xí)慣直接拷貝參考設(shè)計(jì)SDK里的DDR初始化代碼再改一行內(nèi)存大小。這種操作在DDR3時(shí)代可能能蒙混過(guò)關(guān)但LPDDR4雙rank場(chǎng)景下千萬(wàn)別這么干。至少要讓工具重新生成一遍配置再把生成的寄存器表替換進(jìn)啟動(dòng)代碼里。3.2 啟動(dòng)代碼里DDR初始化參數(shù)如何生效STM32MP2x平臺(tái)的內(nèi)存初始化通常放在BootROM之后的第一階段由TF-A或U-Boot SPL完成。DDR控制器寄存器配置、PHY訓(xùn)練配置都是一段結(jié)構(gòu)體數(shù)據(jù)在啟動(dòng)早期被填入寄存器。如果你改了DDR配置參數(shù)卻沒(méi)有重新編譯并燒寫(xiě)對(duì)應(yīng)的啟動(dòng)二進(jìn)制那么無(wú)論設(shè)備樹(shù)怎么寫(xiě)都是白搭。我建議在排查chipselect問(wèn)題時(shí)先確認(rèn)燒錄的TF-A/U-Boot SPL版本是否與你使用的DDR配置一致。最簡(jiǎn)單的方法是看啟動(dòng)串口日志U-Boot一般會(huì)打印檢測(cè)到的DDR容量如果打印值和設(shè)計(jì)容量不符就先回到這一步檢查。需要留意的是在SPL階段DDR控制器會(huì)做一次完整的初始化包括進(jìn)入training、檢測(cè)顆粒密度、配置CS。只有這個(gè)階段正確識(shí)別出雙CS后續(xù)地址映射才有意義。很多“內(nèi)存容量只有一半”的案例問(wèn)題其實(shí)都出在這個(gè)早期階段而不是設(shè)備樹(shù)。3.3 設(shè)備樹(shù)里memory節(jié)點(diǎn)和DDR控制器配置當(dāng)啟動(dòng)代碼正確初始化雙CS后Linux內(nèi)核側(cè)還需要設(shè)備樹(shù)里聲明好物理內(nèi)存大小。在設(shè)備樹(shù)中典型的內(nèi)存節(jié)點(diǎn)會(huì)寫(xiě)成類(lèi)似這樣memory60000000 { device_type memory; reg 0x0 0x60000000 0x0 0x80000000; };這里的reg第二段就是內(nèi)存大小。如果你初始化了雙CS、容量是2GB0x80000000但設(shè)備樹(shù)里只寫(xiě)了0x40000000那內(nèi)核只能看到1GB。有時(shí)還需要在DDR控制器節(jié)點(diǎn)里顯式配置CS數(shù)量和rank映射具體屬性名以SoC的綁定文檔為準(zhǔn)。我一般會(huì)對(duì)照ST官方設(shè)備樹(shù)或評(píng)估板dts來(lái)改而不是憑記憶寫(xiě)屬性。改完之后用標(biāo)準(zhǔn)啟動(dòng)流程驗(yàn)證U-Boot打印DDR容量?jī)?nèi)核/proc/meminfo顯示總內(nèi)存兩者一致說(shuō)明鏈路通了。3.4 一個(gè)容易被忽略的點(diǎn)地址空間重疊在雙CS配置中如果兩個(gè)CS的地址窗口是獨(dú)立的要注意設(shè)備樹(shù)reg屬性不能超出SoC DDR控制器的實(shí)際地址空間寬度。比如控制器只支持從某個(gè)地址到某個(gè)地址范圍內(nèi)映射你硬聲明更大的內(nèi)存范圍會(huì)導(dǎo)致CPU訪問(wèn)到無(wú)效地址。我的經(jīng)驗(yàn)是先跑通ST官方單CS方案確認(rèn)最小系統(tǒng)穩(wěn)定再逐步修改DDR配置、擴(kuò)大內(nèi)存節(jié)點(diǎn)。每改一步都重新觀察U-Boot打印和Linux啟動(dòng)日志不要一次改幾個(gè)變量。這樣才能把問(wèn)題縮小到“chipselect配置”這一個(gè)維度。4. chipselect沒(méi)對(duì)齊時(shí)的三種表現(xiàn)從啟動(dòng)日志到數(shù)據(jù)錯(cuò)亂的完整排查4.1 表現(xiàn)一U-Boot和內(nèi)核都只顯示一半容量這是最典型、最好定位的現(xiàn)象。通常發(fā)生在硬件焊了完整雙rank顆粒但軟件只按單CS初始化的情況。此時(shí)U-Boot打印的DDR大小約等于設(shè)計(jì)容量的一半內(nèi)核啟動(dòng)后也顯示同樣大小。排查步驟可以這樣走確認(rèn)DDR配置工具的rank數(shù)量設(shè)置如果工具里選了single rank改成dual rank后重新生成參數(shù)。確認(rèn)啟動(dòng)代碼確實(shí)使用了新生成的參數(shù)很多項(xiàng)目從別的分支合并代碼時(shí)明明改了工具輸出卻沒(méi)有把新參數(shù)編進(jìn)最終鏡像。用示波器測(cè)CS1引腳在DDR初始化階段CS1必須出現(xiàn)正常的片選脈沖。如果一直保持高電平說(shuō)明控制器根本沒(méi)有拉低CS1。確認(rèn)CS1引腳沒(méi)有被復(fù)用成其他功能SoC引腳做過(guò)多功能復(fù)用啟動(dòng)時(shí)會(huì)根據(jù)boot pin或OTP設(shè)置把引腳切到非DDR功能這種情況會(huì)導(dǎo)致CS1信號(hào)完全失效。4.2 表現(xiàn)二容量顯示正常但系統(tǒng)隨機(jī)死機(jī)或數(shù)據(jù)被踩這種比“少一半”更隱蔽。容量正確說(shuō)明基本訓(xùn)練和地址映射完成了但雙rank之間的切換時(shí)序、刷新管理或電源噪聲可能有問(wèn)題。一個(gè)典型的現(xiàn)場(chǎng)是進(jìn)入Linux桌面系統(tǒng)后跑了一會(huì)兒大型程序突然segfault或者dmesg里出現(xiàn)大量的ECC error如果開(kāi)啟了ECC。這時(shí)候要優(yōu)先懷疑Rank-to-Rank切換參數(shù)。LPDDR4控制器在從rank0切換到rank1時(shí)必須在命令總線上插入適當(dāng)?shù)目臻e周期避免兩次片選命令重疊。這個(gè)參數(shù)通常以時(shí)鐘周期為單位在DDR控制器或PHY配置里可以調(diào)整。如果設(shè)得太小高速運(yùn)行下就會(huì)偶發(fā)數(shù)據(jù)沖突在長(zhǎng)時(shí)間壓力測(cè)試中才暴露出來(lái)。我碰到過(guò)一次雙rank配置下跑memtester小范圍測(cè)試能過(guò)但換成dd if/dev/urandom of/dev/mem這類(lèi)大范圍讀寫(xiě)幾分鐘后就卡死。后來(lái)把Rank-to-Rank切換時(shí)間從固定值調(diào)大一個(gè)時(shí)鐘周期問(wèn)題消失。4.3 表現(xiàn)三只接一個(gè)CS時(shí)正常接兩個(gè)CS后初始化不通過(guò)這種多見(jiàn)于硬件設(shè)計(jì)階段。板子上明明連了CS1但DDR初始化訓(xùn)練直接報(bào)錯(cuò)連U-Boot都進(jìn)不去。排查時(shí)優(yōu)先看PCBCS1走線是否明顯長(zhǎng)于CS0有沒(méi)有按等長(zhǎng)要求處理。CS1信號(hào)有沒(méi)有就近放置端接電阻端接電阻的參考電壓是否正確。有沒(méi)有把CS1誤連到SoC的測(cè)試引腳或接地導(dǎo)致無(wú)法產(chǎn)生有效片選電平。如果硬件沒(méi)問(wèn)題再檢查供電。雙rank顆粒對(duì)供電電流的需求比單rank高不少如果LPDDR4電源軌紋波偏大或壓降明顯雙CS初始化時(shí)容易訓(xùn)練失敗。這種情況下即使你把參數(shù)改回單rank系統(tǒng)能正常啟動(dòng)但容量不夠用仍然要回頭解決電源。4.4 一套我常用的排查鏈路總結(jié)我習(xí)慣把chipselect排查拆成“硬件連通性—控制器配置—地址映射—系統(tǒng)驗(yàn)證”四層硬件連通性用萬(wàn)用表量CS0/CS1到SoC引腳的連接確認(rèn)沒(méi)有虛焊、錯(cuò)位??刂破髋渲脝?dòng)代碼里rank數(shù)量、CS映射、時(shí)序參數(shù)是否與顆粒一致。地址映射U-Boot打印的DDR容量以及設(shè)備樹(shù)reg大小是否匹配。系統(tǒng)驗(yàn)證進(jìn)入Linux后做內(nèi)存壓力測(cè)試覆蓋地址起點(diǎn)、終點(diǎn)和雙CS邊界。按這個(gè)順序排查基本能覆蓋90%以上的chipselect問(wèn)題。不要一開(kāi)始就去翻寄存器先把硬件和配置兩個(gè)大頭排除掉。5. 設(shè)計(jì)參考LPDDR4顆粒單CS和雙CS的取舍以及我最后想說(shuō)的話5.1 單CS方案其實(shí)是被低估的穩(wěn)定選擇STM32MP2x這類(lèi)工業(yè)級(jí)MPU產(chǎn)品很多時(shí)候內(nèi)存2GB以內(nèi)已經(jīng)夠用。LPDDR4單CS方案意味著信號(hào)數(shù)量更少布線更容易DDR信號(hào)完整性風(fēng)險(xiǎn)更低。PHY訓(xùn)練只需要處理一組rank初始化時(shí)間和穩(wěn)定性都更好。電源設(shè)計(jì)可以按單rank負(fù)載設(shè)計(jì)余量更好控制。如果你的系統(tǒng)跑Linux但不跑重型GUI、不做大數(shù)據(jù)緩存2GB以內(nèi)完全夠用。一個(gè)穩(wěn)定的單CS LPDDR4設(shè)計(jì)比一個(gè)勉強(qiáng)跑通的雙CS設(shè)計(jì)要省心得多。5.2 雙CS方案的硬性條件不要只改配置要?jiǎng)诱麄€(gè)設(shè)計(jì)如果確定需要4GB及以上容量雙CS不可避免。請(qǐng)先自查以下幾點(diǎn)SoC的DDR控制器確實(shí)支持雙CS并確認(rèn)引腳分配沒(méi)有被復(fù)用。參考設(shè)計(jì)或原理圖中有完整的CS0/CS1等長(zhǎng)約束至少保證PCB Layout按ST的guideline做。DDR初始化參數(shù)由工具雙rank模式生成不是手動(dòng)拼出來(lái)的。內(nèi)存壓力測(cè)試時(shí)專(zhuān)門(mén)寫(xiě)一段跨越CS0和CS1地址邊界的讀寫(xiě)代碼檢查切換是否穩(wěn)定。我最早一次調(diào)試雙CS就是只改了工具配置、沒(méi)改PCB等長(zhǎng)約束結(jié)果高速運(yùn)行時(shí)數(shù)據(jù)錯(cuò)亂。后來(lái)仔細(xì)對(duì)比ST評(píng)估板的Layout發(fā)現(xiàn)CS1走線必須包地而且兩側(cè)要有足夠的間距我畫(huà)的板子把CS1和旁邊的命令線貼得太近串?dāng)_嚴(yán)重。重新改版后才穩(wěn)定下來(lái)。5.3 給正在做STM32MP2x項(xiàng)目的你一個(gè)建議如果你在設(shè)計(jì)階段就已經(jīng)決定了LPDDR4顆粒型號(hào)建議盡早把顆粒的數(shù)據(jù)手冊(cè)、ST參考手冊(cè)、DDR tuning工具的備選型號(hào)三份資料放在一起核對(duì)尤其是“容量/rank/通道”這三個(gè)字段。很多人只關(guān)注總?cè)萘繀s忽略了rank配置等板子貼回來(lái)才發(fā)現(xiàn)CS1根本沒(méi)法用。另外強(qiáng)烈建議在設(shè)計(jì)板上預(yù)留兩個(gè)測(cè)試點(diǎn)CS0和CS1。不要圖省事不引出來(lái)后面做DDR調(diào)試時(shí)這兩個(gè)測(cè)試點(diǎn)能幫你快速判斷片選信號(hào)是否正常。我后來(lái)在好幾個(gè)項(xiàng)目里都這么做每次遇到內(nèi)存容量或系統(tǒng)重啟問(wèn)題先量CS信號(hào)幾分鐘就能排除一大半可能性。調(diào)試chipselect這件事最后拼的往往不是寄存器知識(shí)而是硬件、啟動(dòng)代碼、設(shè)備樹(shù)三者之間的對(duì)齊程度。每次遇到這類(lèi)問(wèn)題先確認(rèn)“板子實(shí)際是怎么連的”再確認(rèn)“軟件認(rèn)為板子是怎么連的”把這倆對(duì)齊大部分坑都能繞開(kāi)。