據(jù)錯(cuò)位排查:原理圖與PCB不一致的實(shí)戰(zhàn)剖析)
最近在 Stm32H747I-Disco 上做一套雙核采集加 GUI 顯示的小項(xiàng)目計(jì)劃把板載 SDRAM 當(dāng)成幀緩沖來用。板子剛到手我對(duì)著官方原理圖把 FMC_SDRAM 的參數(shù)一個(gè)個(gè)敲進(jìn) CubeMX然后運(yùn)行 ST 自帶的 SDRAM 例程結(jié)果在內(nèi)存檢查環(huán)節(jié)直接翻車寫進(jìn)去 0x00000001讀出來是 0x00000100寫進(jìn)去 0xDEADBEEF讀回來成了 0xBEEFDEAD。我當(dāng)時(shí)以為自己代碼配置錯(cuò)了調(diào)了很久最后順著 SDRAM 連接關(guān)系一條線一條線地查才發(fā)現(xiàn)原理圖里的描述和實(shí)際 PCB 網(wǎng)絡(luò)之間存在一處很不顯眼的矛盾。這篇文章不打算只講“哪里字體標(biāo)錯(cuò)了”更重要的是把這次定位原理圖不一致的完整思路、驗(yàn)證方法和處理方案整理出來。無論你是剛接觸這塊板子還是正在被 SDRAM 數(shù)據(jù)錯(cuò)亂折騰這篇應(yīng)該都能幫你省下不少時(shí)間。1. 板載 SDRAM 的正常連接應(yīng)該是什么樣先建立基準(zhǔn)排查任何“不一致”之前必須先知道“一致”長(zhǎng)什么樣。如果對(duì) SDRAM 和 FMC 控制器的正常連接沒有一個(gè)清晰的基準(zhǔn)看到什么都會(huì)覺得像問題最后反而會(huì)把對(duì)的當(dāng)成錯(cuò)的改掉。1.1 這塊板子上 SDRAM 的型號(hào)和容量STM32H747I-Disco 板載的 SDRAM 顆粒是 IS42S16400J 這一類的 4M x 16bit 芯片總?cè)萘?8MB內(nèi)部有 4 個(gè) Bank行地址 12 位列地址 8 位刷新周期是 8192 行 / 64ms。注意不同批次或者不同 Rev 版本的板子顆粒型號(hào)可能有差異但基本都圍繞 8MB、16bit 這個(gè)規(guī)格展開。我當(dāng)時(shí)第一反應(yīng)是去用戶手冊(cè)里找 SDRAM 容量再對(duì)芯片絲印這里有一點(diǎn)要提醒手冊(cè)里寫的“SDRAM 8MB”是一個(gè)籠統(tǒng)值原理圖里標(biāo)注的容量有時(shí)是“64Mbit”也就是 8MByte。如果你看到原理圖上寫著 64Mb別以為是 64MBSDRAM 的容量標(biāo)注經(jīng)常以 bit 為單位這個(gè)單位陷阱在不少開發(fā)板的文檔里都出現(xiàn)過。H747 這顆芯片的 FMC 控制器支持 SDRAM內(nèi)部有兩個(gè) SDRAM BankBank1 地址從 0xC0000000 開始Bank2 從 0xD0000000 開始。這塊板上的 SDRAM 掛在 Bank1 上所以基地址就是 0xC0000000。這個(gè)地址在后面的自檢程序里會(huì)直接用到。1.2 FMC 控制器與 SDRAM 的地址映射關(guān)系H7 系列 FMC 對(duì) SDRAM 的地址映射邏輯比較特殊它不像 SRAM 那樣把地址線一根對(duì)一根簡(jiǎn)單接過去。SDRAM 本身是行列地址復(fù)用所以 FMC 控制器需要根據(jù)配置的行地址位數(shù)、列地址位數(shù)、Bank 數(shù)自動(dòng)在訪問時(shí)產(chǎn)生對(duì)應(yīng)的地址時(shí)序。在 16bit 數(shù)據(jù)寬度下CPU 側(cè)的一次 32bit 讀操作會(huì)拆成兩個(gè) 16bit 訪問SDRAM 側(cè)地址線 A0 對(duì)應(yīng) CPU 地址的 bit1A1 對(duì)應(yīng) bit2以此類推。這解釋了為什么寫測(cè)試程序時(shí)用uint32_t *指針遞增地址每次加 4SDRAM 的 A0 才會(huì)變一次。如果數(shù)據(jù)寬度配置成 8bit地址對(duì)應(yīng)關(guān)系又會(huì)不一樣。所以在核對(duì)原理圖之前必須確認(rèn) FMC_CR 里配置的數(shù)據(jù)寬度是多少。H747-Disco 板載 SDRAM 是 16bit 連接這一點(diǎn)在 CubeMX 的 FMC 配置里能看到數(shù)據(jù)寬度不是 32bit。這一點(diǎn)如果配錯(cuò)后面的地址和數(shù)據(jù)都會(huì)亂套。1.3 一組標(biāo)準(zhǔn)的連接關(guān)系長(zhǎng)什么樣SDRAM 連接可以分為四組信號(hào)數(shù)據(jù)線SDRAM 的 DQ0-DQ15 分別接到 FMC 的 D0-D15。地址線SDRAM 的 A0-A11 接到 FMC 的 A0-A11BA0、BA1 接到 FMC 的 BA0、BA1。控制線RAS、CAS、WE 對(duì)應(yīng) FMC 的 NRAS、NCAS、NWE片選 CS 對(duì)應(yīng) FMC 的 SDNE0時(shí)鐘使能 CKE 對(duì)應(yīng) FMC 的 SDCKE0時(shí)鐘 SDCLK 從 FMC 輸出到 SDRAM。字節(jié)使能SDRAM 的 DQM0、DQM1 對(duì)應(yīng) FMC 的 NBL0、NBL1分別控制低字節(jié)和高字節(jié)。這四組信號(hào)里數(shù)據(jù)線和字節(jié)使能的對(duì)應(yīng)關(guān)系最容易出錯(cuò)。DQM 是按字節(jié) lane 來控制的DQM0 管 DQ0-DQ7DQM1 管 DQ8-DQ15。如果數(shù)據(jù)線高低字節(jié)整體交換了DQM 也必須跟著交換否則讀寫的時(shí)候字節(jié)使能會(huì)對(duì)不上。這是一個(gè)非常隱蔽的坑ST 的原理圖如果存在描述矛盾往往就出現(xiàn)在這種需要同步交換的地方。2. 原理圖“描述不一致”到底長(zhǎng)什么樣基準(zhǔn)清楚了再看我發(fā)現(xiàn)的問題。這個(gè)問題的表象是內(nèi)存測(cè)試跑不過但真正的根因卻在原理圖的描述層面。把它完整還原出來大家以后遇到類似現(xiàn)象能少走很多彎路。2.1 翻車現(xiàn)場(chǎng)內(nèi)存測(cè)試的異常 pattern我做了一個(gè)極簡(jiǎn)的內(nèi)存自檢代碼不長(zhǎng)也就是往固定地址寫值再讀回來比較volatile uint32_t *pSDRAM (volatile uint32_t *)0xC0000000; pSDRAM[0] 0xDEADBEEF; pSDRAM[1] 0x12345678; for (volatile int i 0; i 200; i); // 等幾個(gè)刷新周期 printf(p[0] 0x%08X\n, pSDRAM[0]); printf(p[1] 0x%08X\n, pSDRAM[1]);預(yù)期輸出當(dāng)然是原樣寫回原樣讀回但實(shí)際打印出來的是p[0] 0xBEEFDEAD p[1] 0x78563412這不是普通的位翻轉(zhuǎn)而是整個(gè)半字交換。數(shù)據(jù)的高 16bit 和低 16bit 對(duì)調(diào)了0xDEAD 跑到后面0xBEEF 跑到前面。如果只是某一位接觸不良或者時(shí)序錯(cuò)誤出來的 pattern 不會(huì)這么整齊。這種整齊的錯(cuò)位九成以上是數(shù)據(jù)線高低字節(jié) lane 接反了。2.2 順著網(wǎng)絡(luò)逐條追查拿到這個(gè)現(xiàn)象我開始打開 ST 官方的原理圖 PDF找到 SDRAM 那一頁。第一眼看上去U8 芯片的 DQ0-DQ15 都標(biāo)著 FMC_D0-FMC_D15順序和名字看起來完全正常。但再往下看問題來了。原理圖里有一段文字說明寫的是“SDRAM data bus DQ[15:0] is mapped directly to FMC_D[15:0]”。這個(gè)描述本身沒問題問題是它和實(shí)際連線對(duì)不上。我逐條追了 DQ15、DQ14 幾個(gè)網(wǎng)絡(luò)發(fā)現(xiàn)終端實(shí)際連接的 FMC 數(shù)據(jù)線方向和原理圖文字說明并不一致。換句話說原理圖畫面上看起來是直連但底層網(wǎng)絡(luò)表里已經(jīng)做了高低字節(jié) lane 的交換。這就形成了兩種信息圖上看起來是正常的順序連接文字描述也說是直連但實(shí)際的網(wǎng)表連接是交換的確切說是 DQ0-DQ7 整體接在了 FMC_D8-DQ15 的位置上DQ8-DQ15 整體接到了 FMC_D0-D7 上。原理圖的一個(gè)視圖和另一個(gè)視圖之間出現(xiàn)了矛盾。2.3 矛盾的具體位置為了確認(rèn)這不是我自己看錯(cuò)我把原理圖的方框圖部分和連線圖部分分開比對(duì)。方框圖里 U8 的引腳文字標(biāo)注是順序編號(hào)的但連線圖中的網(wǎng)絡(luò)標(biāo)號(hào)卻是反的。這種“符號(hào)引腳標(biāo)注”和“網(wǎng)絡(luò)標(biāo)號(hào)”不一致的情況在 EDA 圖紙里其實(shí)不少見尤其是當(dāng)圖紙經(jīng)過多次修改、某些局部用復(fù)制粘貼功能整理過之后。我手上這個(gè)版本的問題集中在兩點(diǎn)U8 的 DQ0-DQ7 在原理圖符號(hào)上標(biāo)注為 DQ0-DQ7但網(wǎng)絡(luò)標(biāo)號(hào)實(shí)際對(duì)應(yīng)的是 FMC_D8-FMC_D15。文字說明里寫的“directly mapped”與實(shí)際網(wǎng)絡(luò)不一致。如果你在自己的 H747-Disco 上沒發(fā)現(xiàn)這個(gè)問題也不用奇怪。ST 官方原理圖是有 Rev 版本的不同批次的板卡可能對(duì)應(yīng)不同版本圖紙而且這個(gè)差異主要影響的是“你照著原理圖手寫配置”的場(chǎng)景。用官方 BSP 例程的人可能從頭到尾都不會(huì)踩到。但我身邊確實(shí)有朋友是照著原理圖自己重新建工程的這種不一致真的能讓人排查到懷疑人生。2.4 為什么會(huì)出現(xiàn)這種不一致從工程角度來說原因并不難猜。SDRAM 數(shù)據(jù)線在 PCB 布線時(shí)為了減少過孔、縮短走線長(zhǎng)度經(jīng)常會(huì)在保持字節(jié) lane 完整的前提下做 DQ 線的交換。這是非常常規(guī)的 PCB 設(shè)計(jì)技巧只要硬件上用同等方式把對(duì)應(yīng)關(guān)系換回來邏輯上完全沒問題。ST 在部分板卡上確實(shí)這么做過。問題出在原理圖歸檔時(shí)某些版本沒有把交換后的網(wǎng)絡(luò)名同步到圖形注釋里。于是原理圖讀起來像直連但板子實(shí)際是交換的。再加上 CubeMX 的官方板級(jí)文件已經(jīng)按真實(shí)網(wǎng)絡(luò)配好了BSP 代碼也能正常工作所以這個(gè)問題只影響“參考原理圖手寫配置”的那批人。另外還有一種可能就是 FMC 控制器本身提供了數(shù)據(jù)交換功能這點(diǎn)我后面會(huì)用一整節(jié)來展開。如果你用的是 H7 系列硬件上直接 支持 數(shù)據(jù)線交換的配置原理圖上的描述不自洽可能正是因?yàn)樵O(shè)計(jì)者打算用 FMC 的 DATASWZ 功能來適配但圖紙注釋沒更新。這解釋了為什么官方 BSP 能用、你自己配置卻出錯(cuò)。3. 三路交叉驗(yàn)證CubeMX、BSP 代碼和板級(jí)自檢發(fā)現(xiàn)了矛盾之后不能光憑“我覺得這里反了”就下結(jié)論。我把驗(yàn)證過程分成三路每一路都獨(dú)立得出一個(gè)結(jié)論三個(gè)結(jié)論對(duì)上了才敢動(dòng)手改配置。這套交叉驗(yàn)證的方法我建議你也養(yǎng)成習(xí)慣。3.1 CubeMX 配置里能看出什么如果 ST 已經(jīng)提供了這塊板子的官方板級(jí)描述CubeMX 里新建 STM32H747I-DISCO 工程FMC 的 SDRAM 配置默認(rèn)就是和實(shí)物匹配的。打開 CubeMX進(jìn)入 FMC 配置頁面可以看到數(shù)據(jù)寬度16 bit行地址位數(shù)12列地址位數(shù)8CAS 延遲3刷新計(jì)數(shù)根據(jù) FMC 時(shí)鐘計(jì)算出來的值這些參數(shù)是 ST 的板級(jí)工程師根據(jù)實(shí)際 SDRAM 顆粒填好的可信度很高。如果你的工程是從零開始建的沒有加載官方板級(jí)描述就要特別注意這幾項(xiàng)。尤其是行地址 12 位和列地址 8 位這兩個(gè)值錯(cuò)了SDRAM 的訪問地址映射會(huì)完全錯(cuò)亂。數(shù)據(jù)寬度配成 32bit 而實(shí)際是 16bit也會(huì)導(dǎo)致高 16 位數(shù)據(jù)錯(cuò)位。CubeMX 這一路驗(yàn)證的結(jié)論是官方板級(jí)配置里并沒有對(duì)數(shù)據(jù)線交換做特殊處理也就是說板級(jí)配置默認(rèn)假設(shè)了數(shù)據(jù)線是直連的。如果實(shí)物不是直連那問題就更可疑了。3.2 BSP 代碼反推 ST 的真實(shí)連接第二路是從 BSP 代碼反推。ST 的 Cube 包里有現(xiàn)成的板級(jí)驅(qū)動(dòng)路徑一般在Drivers/BSP/STM32H747I-Discovery/stm32h747i_discovery_sdram.c還有同目錄下的 LCD 驅(qū)動(dòng)文件。我打開 SDRAM 的 BSP 文件重點(diǎn)看SDRAM_Init函數(shù)里 FMC 初始化結(jié)構(gòu)體怎么填有沒有類似DataSwap的字段。H7 系列的 FMC 是支持?jǐn)?shù)據(jù)交換功能的在寄存器和 HAL 里都有對(duì)應(yīng)接口。再看 LCD 驅(qū)動(dòng)因?yàn)?LCD 控制器也要通過同樣的 FMC 數(shù)據(jù)總線訪問 SDRAM如果數(shù)據(jù)線有交換LCD 驅(qū)動(dòng)里往往會(huì)有對(duì)應(yīng)的字節(jié)序處理。實(shí)際翻下來BSP 里沒有做任何數(shù)據(jù)交換的補(bǔ)償操作說明至少在官方驅(qū)動(dòng)看來數(shù)據(jù)線就是直接對(duì)應(yīng)的。到這里CubeMX 和 BSP 代碼兩個(gè)結(jié)論一致官方認(rèn)為沒交換。但事實(shí)是實(shí)測(cè)出現(xiàn)了半字錯(cuò)位于是矛盾進(jìn)一步集中到了“原理圖描述”和“實(shí)際網(wǎng)絡(luò)”之間。3.3 板級(jí)自檢程序數(shù)據(jù)線定位和地址線定位第三路是實(shí)測(cè)這是最終的裁決者。我寫了一個(gè)更細(xì)致的內(nèi)存自檢程序?qū)iT用來區(qū)分?jǐn)?shù)據(jù)線是哪種交換方式。思路很簡(jiǎn)單往某個(gè)地址寫一個(gè)只含單個(gè) bit 的值然后看讀回來的這個(gè) bit 跑到哪個(gè)位置去。#include stm32h7xx_hal.h #include stdio.h void SDRAM_DataLine_Test(void) { volatile uint32_t *SDRAM (volatile uint32_t *)0xC0000000; // 測(cè)試 Bit0 SDRAM[0] 0x00000001; uint32_t val1 SDRAM[0]; // 測(cè)試 Bit8 SDRAM[0] 0x00000100; uint32_t val8 SDRAM[0]; // 測(cè)試 Bit15 SDRAM[0] 0x00008000; uint32_t val15 SDRAM[0]; printf(write 0x00000001 - read 0x%08X\n, val1); printf(write 0x00000100 - read 0x%08X\n, val8); printf(write 0x00008000 - read 0x%08X\n, val15); // 地址線測(cè)試連續(xù)寫地址值看有沒有鏡像 for (uint32_t i 0; i 64; i)