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