絡(luò)調(diào)試:Ethernet PHY軟件復(fù)位失效根因與解決實踐)
搞嵌入式網(wǎng)絡(luò)開發(fā)的朋友應(yīng)該都撞過這個場景板子上電代碼初始化 PHY執(zhí)行了軟件復(fù)位但寄存器讀出來還是老樣子或者鏈路死活起不來。Ethernet PHY 的軟件復(fù)位不生效看起來是個很小的點但牽扯到 MDIO 時序、PHY 地址、strap 管腳、甚至驅(qū)動初始化順序問題藏得深的時候能折騰好幾天。我調(diào)過不少帶 PHY 的板卡從 RTL8211、KSZ9031 到 DP83848 都踩過坑這篇文章就專門聊聊 Software reset 失效這件事哪些原因會導(dǎo)致它失效、怎么一步步定位、以及驅(qū)動側(cè)怎么把軟復(fù)位寫得足夠穩(wěn)。1. 軟復(fù)位的第一課寄存器行為與正常時序1.1 軟件復(fù)位到底在復(fù)位什么以太網(wǎng) PHY 通過 MDIOManagement Data Input/Output接口與 MAC 或 CPU 通信這個接口專門用于讀寫 PHY 內(nèi)部寄存器。軟件復(fù)位一般指往 BMCRBasic Mode Control Register寄存器地址 0x00的 bit15 寫 1 來觸發(fā) PHY 內(nèi)部復(fù)位。這個復(fù)位位是自清零的PHY 復(fù)位完成后硬件會自動把 bit15 清 0所以“復(fù)位是否完成”可以通過輪詢 bit15 判斷。PHY 軟復(fù)位會做幾件事把大部分 MII 寄存器恢復(fù)成默認值、重新初始化 PHY 內(nèi)部狀態(tài)機、中斷當(dāng)前自協(xié)商并重新啟動。這也就意味著如果復(fù)位后你原本配置的自協(xié)商使能、速度、雙工等參數(shù)沒有重新寫一遍PHY 會回到默認配置鏈路可能和你的預(yù)期不一致。很多“軟復(fù)位不工作”的現(xiàn)象表面上是 PHY 沒有反應(yīng)其實是復(fù)位之后配置被清掉了但又被誤讀成“沒復(fù)位成功”。1.2 寫 1 不等于執(zhí)行讀 0 才算數(shù)不少人在代碼里寫了一個mdio_write(phy_addr, 0, 0x8000)就以為復(fù)位完成了。這個想法在多數(shù) PHY 上會出問題。因為復(fù)位需要時間不同 PHY 的復(fù)位時間差別很大快的幾十微秒慢的能到幾百毫秒甚至更久。如果你寫完立刻讀 0x00很有可能會讀到 0x8000這并不代表復(fù)位失敗而是復(fù)位還沒執(zhí)行完。正確的做法是寫 0x8000 后循環(huán)讀取 BMCR直到 bit15 變?yōu)?0同時設(shè)置一個超時值防止卡死。比如int phy_soft_reset(int phy_addr) { int ret; uint16_t val; int timeout 100; ret mdio_write(phy_addr, 0x00, 0x8000); if (ret 0) return ret; while (timeout--) { ret mdio_read(phy_addr, 0x00, val); if (ret 0) return ret; if ((val 0x8000) 0) return 0; usleep(1000); } return -1; }1.3 怎么證明“軟復(fù)位真的執(zhí)行了”調(diào)試時不要只盯著 BMCR 的 bit15還要從多個角度確認復(fù)位確實發(fā)生過。首先是讀 PHY ID 寄存器地址 0x02 和 0x03正常復(fù)位后 ID 應(yīng)該保持不變。其次是觀察鏈路狀態(tài)復(fù)位期間 PHY 的 link 會掉線自協(xié)商重新開始所以如果你用示波器或 ethtool 持續(xù)觀察會看到 link down 又 up 的過程。最后是核對配置寄存器比如 BMCR 的 bit15 清零后bit12自協(xié)商使能、bit13速度選擇等會恢復(fù)到默認值。如果復(fù)位前后這些值完全沒變化那基本可以確定軟復(fù)位沒有真正執(zhí)行。2. 根因分類為什么軟復(fù)位會失效2.1 MDIO 總線層面數(shù)據(jù)根本沒寫進去最常見的軟復(fù)位失效原因不是 PHY 本身的問題而是 MDIO 上的寫操作根本沒成功。MDIO 的通信時序有規(guī)范要求MDC 時鐘頻率在 IEEE 802.3 標準里最大是 2.5 MHz部分 PHY 支持更高的 25 MHz 模式但需要特定寄存器開啟而且不是所有 PHY 都保證在高時鐘下穩(wěn)定工作。排查時先看 MDC 頻率。有的系統(tǒng)為了趕速度把 MDC 配到了 10 MHz 甚至更高結(jié)果 PHY 對數(shù)據(jù)采樣出錯寄存器寫入不生效。我遇到過一塊板子讀 PHY ID 一直正常但寫 BMCR 復(fù)位位后讀回還是 0x0000最后用示波器抓波形才發(fā)現(xiàn)寫幀的數(shù)據(jù)段完全亂了把 MDC 降到 2.5 MHz 之后立刻恢復(fù)正常。MDIO 幀格式也值得了解。一個完整的 MDIO 寫幀包含 32 位 preamble、起始碼、操作碼01 表示寫、5 位 PHY 地址、5 位寄存器地址、2 位 turnaround 和 16 位數(shù)據(jù)。如果你用邏輯分析儀或者示波器抓幀可以按這個格式手動解碼確認 CPU 發(fā)出的 PHY 地址、寄存器地址和實際想要訪問的是否一致。上拉電阻也是常見問題點。MDIO 數(shù)據(jù)線是雙向的讀操作時由 PHY 驅(qū)動如果 MDIO 缺少上拉電阻讀數(shù)據(jù)時高電平可能不夠?qū)е伦x到全 0 或隨機值寫操作倒是正常。判斷方法很簡單只讀 PHY ID如果每次讀出來都不一樣優(yōu)先檢查上拉電阻和 IO 電平。2.2 你操作的不是你想要的 PHYPHY 地址由 PHYAD strap 管腳在上電復(fù)位時鎖存范圍是 0 到 31。MDIO 寫幀里的 PHY 地址必須和 PHY 實際鎖存的地址一致否則 PHY 根本不應(yīng)答。很多板卡的 PHY 地址在硬件設(shè)計階段已經(jīng)確定比如通過上下拉電阻把 PHYAD[4:0] 配成 0x01但如果你在代碼里寫死了 0x00軟復(fù)位自然無效。在多 PHY 場景下更容易踩坑。比如帶交換芯片的板卡CPU 通過 MDIO 總線接 switchswitch 內(nèi)部又管理多個 PHY這種時候 PHY 地址往往不是簡單的 0-4可能包含頁選擇或者內(nèi)部端口映射。最穩(wěn)妥的做法是上電后先遍歷地址 0-31讀取每個地址的 PHY ID和 datasheet 上的期望值比對確認哪個地址對應(yīng)哪顆 PHY再做復(fù)位操作。2.3 寄存器訪問路徑或間接訪問陷阱有些 PHY 芯片的寄存器訪問不是簡單的直接訪問。比如擴展寄存器需要先寫頁寄存器再訪問目標地址或者 PHY 掛在 MDIO mux 后面需要先切換通道。如果驅(qū)動把地址配置錯軟復(fù)位命令寫到了錯誤的寄存器PHY 當(dāng)然不會有反應(yīng)。還有一個隱蔽的坑有些 PHY 在低功耗或者隔離模式下MDIO 接口仍然能夠響應(yīng)但寄存器寫入可能被忽略或者只有特定命令才能喚醒。如果你把一個 PHY 配置成 power down 之后再去寫軟復(fù)位會發(fā)現(xiàn) bit15 寫進去了但 PHY 狀態(tài)不對鏈路也起不來。這種時候先檢查 BMCR 的 bit11power down和 bit10isolate是否被意外置位。2.4 被后面的代碼覆蓋最常見也最隱蔽這是我見過最多的“軟復(fù)位失效”原因現(xiàn)象非常具有迷惑性單獨執(zhí)行軟復(fù)位是好的但接入完整驅(qū)動流程后復(fù)位好像沒發(fā)生過。問題出在初始化順序上。比如驅(qū)動流程是寫軟復(fù)位、延時固定時間、配置自協(xié)商、等待自協(xié)商完成。如果延時時間不夠PHY 的復(fù)位還沒走完后面的自協(xié)商配置寫入就可能被復(fù)位過程覆蓋掉。最終效果就是 BMCR 里的自協(xié)商使能位不正確link 建立不起來看起來像是“復(fù)位把配置清了”實際上是復(fù)位還沒完成配置寫得過早。排查方法在復(fù)位完成后延時幾毫秒把 BMCR 寄存器讀出來再對比你后面代碼寫入的預(yù)期值。不一致就說明寫入順序有時序問題解決方法是把軟復(fù)位改成輪詢等待自清零完成再繼續(xù)配置。2.5 strap 管腳在復(fù)位釋放時鎖定嚴格來說軟件復(fù)位不會重新采樣 strap 管腳strap 只在硬件復(fù)位釋放或者上電過程中鎖存。但很多“軟復(fù)位不生效”的板子根因反而在硬件復(fù)位和 strap 上。如果硬件復(fù)位釋放瞬間 strap 管腳電平?jīng)]有穩(wěn)定PHY 會鎖存到錯誤的地址或者模式后續(xù)軟復(fù)位即使執(zhí)行成功PHY 的工作模式也是錯的。比如有的電路用 RC 延時給 strap 管腳做默認電平如果 RC 時間常數(shù)太大復(fù)位釋放時電平還沒穩(wěn)定PHY 就把不確定的電平鎖進去了。這種問題在低功耗設(shè)計、IO 電壓切換的板子上尤其常見。排查時需要示波器看復(fù)位釋放時刻 strap 管腳的電平是否符合設(shè)計預(yù)期。2.6 時鐘沒起來狀態(tài)機不轉(zhuǎn)PHY 需要一個參考時鐘才能維持內(nèi)部狀態(tài)機運行一般是外接晶振或由 MAC 提供 25 MHz 時鐘。如果時鐘沒起振或者頻率不對MDIO 接口可能還能讀 ID因為讀 ID 不需要完整的狀態(tài)機但軟復(fù)位后的自協(xié)商完全不會進行鏈路也就起不來。我調(diào)試時有個習(xí)慣碰到復(fù)位不生效先測 PHY 時鐘輸出或者晶振波形。用示波器探一下 XI 或 CLK_OUT 管腳確認頻率和幅度符合 datasheet 要求。很多時候軟復(fù)位本身沒問題問題在于 PHY 的數(shù)據(jù)通路根本沒工作時鐘就是第一嫌疑。下面是失效現(xiàn)象和優(yōu)先排查方向的速查表方便現(xiàn)場對照定位失效現(xiàn)象優(yōu)先排查項常見原因?qū)憦?fù)位后 BMCR 仍為 0x8000MDC 頻率、MDIO 波形總線時序不滿足 PHY 要求讀 PHY ID 全 0 或全 1PHY 地址、上拉電阻地址不匹配或總線無應(yīng)答軟復(fù)位單測正常加上配置代碼后失效初始化順序配置寫入早于復(fù)位完成復(fù)位后 link 始終起不來時鐘、strap、硬件復(fù)位PHY 數(shù)據(jù)通路未工作PHY 地址與實際硬件不符PHYAD strap 管腳strap 鎖存錯誤3. 現(xiàn)場排查把問題“逼”出來3.1 先讀 PHY ID 建立基線排查任何 PHY 問題前先把 PHY ID 讀出來。PHY ID 由寄存器 0x02OUI 高 16 位和 0x03OUI 低 6 位 型號 6 位 版本 4 位組成。每個 PHY 型號有固定 ID你可以對照 datasheet 確認讀取結(jié)果是否正確。如果讀到 0x0000 或者 0xffff說明 MDIO 總線上根本沒有 PHY 響應(yīng)或者地址不對。建議寫一個掃描腳本遍歷 PHY 地址 0-31把所有能讀到合法 ID 的地址打印出來。以 mdio-tools 為例# 遍歷地址0-31讀寄存器2和3 for i in $(seq 0 31); do id1$(mdio read phy 0 $i 2 2/dev/null | awk {print $NF}) id2$(mdio read phy 0 $i 3 2/dev/null | awk {print $NF}) if [ $id1 ! 0xffff ] [ $id1 ! 0x0000 ]; then echo addr$i id$id1$id2 fi done腳本的目的是找到所有存活的 PHY 地址。正常板卡上結(jié)果里應(yīng)該能看到 datasheet 對應(yīng)的 ID如果找不到先別糾結(jié)軟復(fù)位把 MDIO 總線基本問題解決再說。3.2 示波器抓 MDIO/MDC觀察幀波形軟件層面的寄存器讀寫最終要通過 IO 波形體現(xiàn)。用示波器同時測 MDC 和 MDIO觸發(fā)方式選 MDC 上升沿抓一次寫 BMCR 0x8000 的完整幀。觀察幾個關(guān)鍵點MDC 頻率是否在 PHY 允許范圍內(nèi)MDIO 數(shù)據(jù)是否在 MDC 下降沿后變化、上升沿前穩(wěn)定幀里的 PHY 地址和寄存器地址是否和代碼一致。如果發(fā)現(xiàn)數(shù)據(jù)線在 MDC 上升沿附近還在跳變說明時序余量不夠。這時候可以降低 MDC 頻率或者調(diào)整 MDIO 驅(qū)動能力優(yōu)先保證通信穩(wěn)定。還有一種情況MDIO 線上有電平競爭。比如多個 MDIO 主機同時操作同一根總線或者 PHY 的 MDIO 引腳被復(fù)用為其他功能讀寫數(shù)據(jù)就會錯亂。用示波器看波形時如果發(fā)現(xiàn) MDIO 高電平被拉低大概率是總線沖突。3.3 讀回 BMCR確認寫操作落地寫軟復(fù)位后立即讀 BMCR有兩種典型情況需要區(qū)分第一種讀到 0x8000說明復(fù)位還在進行中繼續(xù)輪詢等待自清零。第二種讀到 0x0000或者其他值但 bit15 已經(jīng)為 0說明 PHY 接受了復(fù)位并完成。如果此時你看到 bit12 自協(xié)商使能、bit13 速度選擇等配置寄存器都恢復(fù)默認了證明復(fù)位執(zhí)行過。如果寫完 0x8000 后無論怎么輪詢 bit15 都一直為 1而且 PHY ID 能正常讀到那問題基本在 PHY 內(nèi)部狀態(tài)。建議檢查 PHY 的電源、時鐘、復(fù)位管腳是否正常。3.4 檢查硬件復(fù)位和 strap 信號軟件復(fù)位不工作很多人忽略了硬件復(fù)位管腳。PHY 的 RST# 管腳如果在軟件復(fù)位過程中被外部拉低會導(dǎo)致 PHY 一直在硬件復(fù)位狀態(tài)任何 MDIO 寫操作都不會被真正執(zhí)行。檢查 RST# 電平是否穩(wěn)定在高電平如果有周期性拉低要追蹤是誰在操作這個管腳。strap 信號檢查要在復(fù)位釋放瞬間做。示波器觸發(fā)在 RST# 上升沿看 PHYAD、ANEG、MODE 等 strap 管腳的電平是否已經(jīng)穩(wěn)定。如果 strap 管腳在復(fù)位釋放后才慢慢爬升PHY 鎖存的就不是你想要的配置這對后續(xù)軟復(fù)位的“無效”判斷會產(chǎn)生誤導(dǎo)。3.5 最小化復(fù)現(xiàn)腳本把問題從完整驅(qū)動里剝離出來用最小化腳本復(fù)現(xiàn)。在嵌入式 Linux 下可以直接在 shell 里讀寫 MDIO 寄存器在裸機環(huán)境寫一個簡單的測試函數(shù)只做三件事讀 PHY ID、寫軟復(fù)位、輪詢 BMCR。下面是一個最小化的 C 示例#include stdio.h #include unistd.h int main(int argc, char *argv[]) { int addr argc 1 ? atoi(argv[1]) : 1; uint16_t id1, id2, bmcr; mdio_read(addr, 2, id1); mdio_read(addr, 3, id2); printf(PHY ID: 0x%04x%04x\n, id1, id2); mdio_write(addr, 0, 0x8000); for (int i 0; i 100; i) { mdio_read(addr, 0, bmcr); if (!(bmcr 0x8000)) { printf(reset done after %d ms\n, i); break; } usleep(1000); } printf(BMCR after reset: 0x%04x\n, bmcr); return 0; }如果這個最小化腳本能正常復(fù)位說明硬件和 MDIO 總線沒問題問題在完整驅(qū)動的代碼邏輯。如果腳本都復(fù)現(xiàn)不了軟復(fù)位失效那就回到硬件和總線排查。4. 三個真實案例復(fù)盤4.1 案例 AMDC 頻率太高導(dǎo)致寫操作丟失現(xiàn)象板卡上調(diào)試讀取 PHY ID 正常但寫軟復(fù)位寄存器后再讀 BMCR 一直是 0x0000bit15 始終沒有置位PHY 也沒有任何 link 變化。當(dāng)時第一反應(yīng)是 PHY 壞了換了芯片還是一樣。排查用示波器抓 MDC 波形發(fā)現(xiàn) MDC 頻率被一個共用驅(qū)動的代碼配置成了 12.5 MHz。再抓 MDIO 寫幀發(fā)現(xiàn)數(shù)據(jù)段在 MDC 上升沿采樣時不穩(wěn)定PHY 把數(shù)據(jù)采樣錯了相當(dāng)于寫進去的數(shù)據(jù)不是 0x8000而是隨機值。解決把 MDC 頻率降到 2.5 MHz重新測試軟復(fù)位正常生效。后面我養(yǎng)成了一個習(xí)慣只要能調(diào)低 MDC 頻率就先用低頻率跑通功能再去考慮性能優(yōu)化。4.2 案例 B多 PHY 板卡地址認錯對象現(xiàn)象一塊帶交換芯片的板卡交換芯片內(nèi)部有 5 個 PHY。代碼里固定往 PHY 地址 0 執(zhí)行軟復(fù)位結(jié)果交換機某個端口的 link 總是異常另一個端口倒是正常。排查用掃描腳本遍歷 0-31發(fā)現(xiàn) PHY 地址分布和代碼預(yù)期完全不同。交換芯片把內(nèi)部 PHY 映射到了以 0x04 起始的地址區(qū)間地址 0 上根本沒有 PHY 響應(yīng)。也就是說軟復(fù)位命令一直被發(fā)到了總線上一個不存在的 PHY那“不工作”就是必然的。解決修改驅(qū)動中的 PHY 地址表按掃描到的實際地址配置軟復(fù)位和后續(xù)鏈路配置都正常。4.3 案例 C復(fù)位成功但配置被覆蓋現(xiàn)象最小化腳本測試軟復(fù)位是好的但跑完整驅(qū)動后PHY 的自協(xié)商配置總是不對link 起不來。當(dāng)時排查了很久一度以為 PHY 有問題。排查在軟復(fù)位函數(shù)返回后加延時讀取 BMCR發(fā)現(xiàn)自協(xié)商使能位為 0而代碼里明明在后面寫了配置。再細看代碼發(fā)現(xiàn)驅(qū)動里軟復(fù)位后有一個usleep(1000)的固定延時但實際 PHY 復(fù)位需要 5 ms 左右。后面的自協(xié)商配置寫入太早被復(fù)位過程清掉了。解決把固定延時改成輪詢 bit15 自清零清零后再寫自協(xié)商配置問題消失。同時加了超時保護防止 PHY 異常時卡死初始化。案例復(fù)盤匯總案例現(xiàn)象定位手段修復(fù)方式MDC 頻率過高寫復(fù)位后 BMCR 不置位示波器抓 MDC/MDIO 波形MDC 降到 2.5 MHzPHY 地址錯誤復(fù)位命令發(fā)到不存在的 PHY掃描 0-31 地址讀 PHY ID改用實際 PHY 地址配置寫入過早復(fù)位單測正常驅(qū)動整體異常復(fù)位后讀回 BMCR 比對輪詢自清零后再配置5. 驅(qū)動側(cè)穩(wěn)妥實現(xiàn)一份可以直接落地的軟復(fù)位代碼5.1 封裝 MDIO 讀寫原語不同平臺的 MDIO 讀寫實現(xiàn)差別很大但接口可以統(tǒng)一。建議封裝兩個函數(shù)返回 0 表示成功負值表示錯誤。代碼里不直接操作硬件寄存器方便后續(xù)適配或用 mock 測試int mdio_read(uint8_t phy_addr, uint8_t reg_addr, uint16_t *val); int mdio_write(uint8_t phy_addr, uint8_t reg_addr, uint16_t val);底層實現(xiàn)里注意 MDC 頻率必須控制在 PHY 支持范圍內(nèi)MDIO 讀寫幀要按照 IEEE 802.3 格式構(gòu)造幀間隔最好留幾個 MDC 時鐘周期避免連續(xù)操作時 PHY 來不及處理。5.2 軟復(fù)位實現(xiàn)的四個關(guān)鍵點一個可靠的phy_soft_reset函數(shù)要有四個關(guān)鍵點寫 0x8000 到 BMCR、輪詢 bit15 自清零、超時保護、復(fù)位完成后延遲讓內(nèi)部狀態(tài)機穩(wěn)定。參考實現(xiàn)#define PHY_REG_BMCR 0x00 #define BMCR_RESET 0x8000 #define BMCR_ANENABLE 0x1000 #define BMCR_SPEED100 0x2000 #define BMCR_DUPLEX_FULL 0x0100 int phy_soft_reset(uint8_t phy_addr) { uint16_t val; int ret; int timeout 200; ret mdio_write(phy_addr, PHY_REG_BMCR, BMCR_RESET); if (ret 0) return ret; while (timeout--) { ret mdio_read(phy_addr, PHY_REG_BMCR, val); if (ret 0) return ret; if ((val BMCR_RESET) 0) break; usleep(1000); } if (timeout 0) return -1; /* 給PHY內(nèi)部狀態(tài)機一點時間 */ usleep(1000); return 0; }注意這里的usleep(1000)是給狀態(tài)機啟動用的不同 PHY 可能有差異如果后面緊接著要配置自協(xié)商建議再加一個 10 ms 級別的延時或者輪詢 PHY 狀態(tài)寄存器確認自協(xié)商已經(jīng)啟動。5.3 復(fù)位 自協(xié)商 鏈路檢測的編排軟復(fù)位之后不能直接等 link需要重新配置自協(xié)商參數(shù)。推薦的初始化順序是讀取 PHY ID確認訪問對象正確。執(zhí)行軟復(fù)位等待自清零。寫 ANAR寄存器 0x04配置本端支持的速率和工作模式。寫 BMCR使能自協(xié)商并重啟自協(xié)商bit12 1bit9 1。輪詢基本狀態(tài)寄存器寄存器 0x01的 bit5自協(xié)商完成和 bit2link 狀態(tài)。很多驅(qū)動把第 2 步和第 4 步混在一起先寫 BMCR 使能自協(xié)商再寫軟復(fù)位結(jié)果復(fù)位把自協(xié)商使能又清掉了。順序上一定要先復(fù)位復(fù)位完成后再寫配置。5.4 多 PHY 場景的復(fù)位策略一塊板子上有多個 PHY 時盡量逐個復(fù)位、逐個確認。同時對所有 PHY 發(fā)軟復(fù)位雖然省時間但多個 PHY 同時重啟自協(xié)商可能帶來電源沖擊也可能讓 MDIO 總線出現(xiàn)短時間異常。逐個復(fù)位的代價是初始化時間變長但如果把每個復(fù)位之間的等待時間優(yōu)化好實際影響很小。另外多 PHY 場景下一定要維護一張 PHY 地址映射表把“邏輯端口號”和“MDIO PHY 地址”對應(yīng)起來不要把 PHY 地址硬編碼散落在各處。調(diào)試時打印復(fù)位前后每個 PHY 的狀態(tài)能省非常多時間。6. 調(diào)試軟復(fù)位的三條硬件經(jīng)驗6.1 新板卡先測時鐘和復(fù)位釋放拿到新板卡的第一件事不是燒代碼而是用示波器測三樣?xùn)|西PHY 參考時鐘是否起振、頻率是否準確、RST# 復(fù)位釋放時間是否滿足 datasheet 要求。這三樣如果沒問題軟件層面的排查才有意義。我見過因為晶振虛焊導(dǎo)致 PHY 軟復(fù)位不生效的也見過 RST# 上拉電阻沒貼導(dǎo)致 PHY 一直處于復(fù)位狀態(tài)的。這些問題單純靠軟件是發(fā)現(xiàn)不了的。6.2 寄存器快照改動前先留證據(jù)調(diào)試 PHY 問題時建議在每次改動寄存器前把當(dāng)前 BMCR、ANAR、狀態(tài)寄存器的值打印出來。后面如果改了配置發(fā)現(xiàn)鏈路異??梢詫Ρ瓤煺张袛嗍悄膫€配置被覆蓋了。軟復(fù)位失效場景下快照尤其有用能確認復(fù)位前后寄存器變化是否符合預(yù)期。6.3 把軟復(fù)位做成單獨調(diào)試接口不要把軟復(fù)位邏輯藏在完整初始化函數(shù)深處。把它做成一個獨立接口比如調(diào)試串口命令phy_reset addr這樣無論什么時候懷疑軟復(fù)位有問題都能單獨觸發(fā)觀察結(jié)果。完整的初始化流程里調(diào)用這個接口最小化腳本里也調(diào)用這個接口調(diào)試一致性和復(fù)現(xiàn)路徑都會清晰很多。在實際項目里我最后都是靠這種分離式設(shè)計快速定位問題的。一旦懷疑軟復(fù)位不生效先通過調(diào)試接口單獨復(fù)位如果單獨復(fù)位正常再回頭查驅(qū)動初始化的上下文邏輯。6.4 軟復(fù)位失效但讀 ID 正常優(yōu)先查邏輯如果軟復(fù)位失敗但 PHY ID 能正常讀到那可以確定 MDIO 總線基本通路是好的問題大概率在邏輯層地址配錯、時序不夠、配置覆蓋。如果 PHY ID 都讀不到先別急著查軟復(fù)位總線電氣和硬件復(fù)位才是重點。這是我調(diào)試以來總結(jié)得最實用的一條排查順序ID 能讀查邏輯ID 不能讀查硬件。