
1. 先搞清楚死鎖AXI總線不是數(shù)據(jù)庫也會卡死干驗證或者做SoC集成的朋友應該都經(jīng)歷過這種畫面仿真跑到某個點時鐘還在翻波形里的valid全部高懸ready全部紋絲不動整條總線像被人按了暫停鍵。抓下來一看AWVALID、WVALID、AWREADY、WREADY這幾根信號排列組合出一個死局最常見的元兇之一就是AXI總線死鎖里的AW-W依賴。這篇文章就把這個場景掰開揉碎講清楚它怎么產(chǎn)生、怎么復現(xiàn)、怎么定位以及RTL里怎么防、驗證里怎么查。AXI總線死鎖聽起來像是個“協(xié)議小概率事件”但只要系統(tǒng)里有多個Master、多個Outstanding事務、或者經(jīng)過一層帶內(nèi)部FIFO的橋接邏輯它就會從理論變成現(xiàn)實。最容易踩到的坑是大家把“AXI允許outstanding”理解成“隨便發(fā)多少個都不會卡”結果忽略了一個事實從設備和互連邏輯里的FIFO深度、地址槽數(shù)量、狀態(tài)機處理能力都是有限的。當這些有限資源遇上AW通道和W通道之間的依賴死鎖的四個條件就湊齊了。1.1 死鎖四定律在AXI里長什么樣死鎖不是數(shù)據(jù)庫和線程并發(fā)編程的專利。操作系統(tǒng)課上講的那四個必要條件放在AXI總線上照樣成立互斥、持有并等待、不可搶占、循環(huán)等待。在AXI場景里“資源”可以是一個地址槽、一條寫數(shù)據(jù)FIFO、一個內(nèi)部事務表項甚至是一段W通道的調度權?;コ獾囊馑际沁@些資源同一時刻只能被一個事務占用持有并等待是一個Master已經(jīng)占住了AW地址槽但還在等W通道讓出不可搶占是AXI的valid/ready握手一旦沒有完成誰也沒法強行把數(shù)據(jù)塞進從設備循環(huán)等待就是A事務在等B事務占的資源B事務又在等A事務占的資源。只要這四個條件同時成立總線就必然變成一潭死水。很多工程師第一次接觸死鎖是在數(shù)據(jù)庫或者多線程編程里覺得鎖表、互斥量這些東西離硬件很遠。其實從系統(tǒng)角度看去AXI總線上的死鎖和數(shù)據(jù)庫行鎖死鎖是同一套模型資源被多個請求方競爭每個請求方都攥著自己已經(jīng)拿到的資源不肯放最后誰也推進不下去。理解了這層共性后面看波形和畫等待圖的時候就會順手很多。1.2 寫通道里的“AW-W依賴”到底指什么AXI寫操作有三條通道AW發(fā)地址W發(fā)數(shù)據(jù)B回響應。從架構上看這三條通道是獨立握手的所以Master可以先發(fā)AW再發(fā)W也可以在特定實現(xiàn)下把W數(shù)據(jù)和AW相對獨立地推進。協(xié)議的靈活性給性能帶來了好處也給死鎖埋了引線。所謂AW-W依賴指的是寫地址通道是否能夠繼續(xù)推進取決于寫數(shù)據(jù)通道上某個事務的數(shù)據(jù)是否已經(jīng)被接收。最常見的實現(xiàn)是從設備內(nèi)部只有一個寫事務地址槽或者一個事務表項AW進來之后從設備把它記錄在案然后必須等對應的W數(shù)據(jù)全部到齊內(nèi)部寫狀態(tài)機才能把這個事務清空空出來的地址槽才能接收下一個AW。換句話說AW通道能不能向前走本質上被W通道的完成情況鎖住了。這種設計從單筆事務看沒什么問題先有地址再有數(shù)據(jù)順序合理。但一旦系統(tǒng)里有多個Master、多個Outstanding事務W通道又不是專門給同一筆事務服務的就可能出現(xiàn)“AW在等WW又在等另一個事務的AW”的閉環(huán)。這就是我們把這類死鎖統(tǒng)稱為AW-W依賴型死鎖的原因。2. AW-W依賴型死鎖的完整推演光說定義不夠直觀我直接搭一個最小復現(xiàn)模型。這個模型我至少在兩個真實項目里見過等價版本每次根因都一樣從設備把地址槽的釋放條件和W數(shù)據(jù)的接收綁死而W通道又被另一個Master的突發(fā)數(shù)據(jù)長期占據(jù)。2.1 一個最小復現(xiàn)模型兩個Master、一個從設備、一個地址槽假設系統(tǒng)里有兩個MasterM0和M1它們都向同一個從設備S發(fā)寫事務。從設備S的寫通道內(nèi)部資源如下只有一個寫事務地址槽地址槽被占住后AWREADY不會再為新的AW拉高地址槽要等當前事務的W數(shù)據(jù)被接收、內(nèi)部狀態(tài)機清空后才釋放W通道仲裁采用嚴格優(yōu)先級M1優(yōu)先級高于M0從設備內(nèi)的寫數(shù)據(jù)FIFO非常淺只能暫存少量數(shù)據(jù)且必須拿到對應AW才能決定數(shù)據(jù)往哪個目標去。第一眼看上去這不過是一個“outstanding能力為1”的從設備。問題是M0和M1并不知道這個限制它們都認為AXI協(xié)議允許自己同時發(fā)多筆寫。于是總線就按照下面的節(jié)奏一步步走進死鎖。2.2 一步一步進入死鎖我用一個表格記錄這個死鎖過程的每一步方便你在調試時對著波形找步驟M0 行為M1 行為從設備/互連狀態(tài)1發(fā)出AW0握手成功還沒動作地址槽被AW0占用等待W02準備好W0等待W通道授權發(fā)出AW1但AWREADY拉低AW1停在總線上地址槽不釋放AW1無法進入3W0一直等W通道W1突發(fā)被W通道仲裁選中開始傳輸W通道被W1占用從設備數(shù)據(jù)FIFO開始接收W14W0繼續(xù)等W1繼續(xù)傳直到從設備數(shù)據(jù)FIFO寫滿數(shù)據(jù)FIFO滿WREADY拉低W1也無法前進5等WREADY變高等從設備消費W1而消費W1需要AW1從設備等W0完成釋放地址槽W0又被W通道擋住走到第5步總線實際上已經(jīng)凍住了。從表面上看起來每一方都在等一個“很正?!钡臈l件但所有條件互相咬死誰都不會先松手。2.3 循環(huán)等待的拆解把上面的等待關系畫成一張圖會更清楚M0.W0 -- 等待 W 通道 W 通道 -- 被 M1.W1 占用 M1.W1 -- 等待 從設備 WREADY 從設備 WREADY -- 等待 數(shù)據(jù) FIFO 空位 數(shù)據(jù) FIFO 空位 -- 等待 AW1 AW1 -- 等待 地址槽釋放 地址槽釋放 -- 等待 W0 完成這個依賴鏈正好是一個環(huán)W0等通道通道被W1占著W1又從設備數(shù)據(jù)FIFO滿被卡住數(shù)據(jù)FIFO要等AW1來消費AW1要等地址槽釋放地址槽要等W0完成W0又回到了等待通道。這就是教科書意義上的循環(huán)等待只不過資源從數(shù)據(jù)庫鎖變成了AXI的通道和FIFO。同樣的道理也適用于另一個方向的依賴也就是W先到、從設備必須先等AW才能接收數(shù)據(jù)。如果AW通道因為別的事務的W卡住而W通道又在等AWREADY最后也會形成等價的死鎖環(huán)。所以說AW-W依賴不是單指某一種狀態(tài)機寫法而是指地址通道和數(shù)據(jù)通道之間互相鉗制的這一類問題。3. 實際調試中怎么把這種死鎖揪出來偉哥說得好遇到死鎖別慌先抓波形。AXI死鎖的特征其實非常明顯時鐘還在跑所有通道都處于valid和ready僵持的狀態(tài)而且這個狀態(tài)能持續(xù)幾百甚至幾千個周期不動。只要看到這種情況十有八九是死鎖而不是單純性能問題。3.1 從波形里認出“僵硬”的握手拿到波形后先把關鍵的寫通道信號拉出來AWVALID、AWREADY、WVALID、WREADY、BVALID、BREADY。一個典型的AW-W依賴死鎖快照長這樣一個Master的AWVALID為1AWREADY為0它的AW已經(jīng)被從設備拒之門外很長時間另一個Master的WVALID為1WREADY為0它的W數(shù)據(jù)被從設備的WREADY卡住再往前翻能看到最早一個AW已經(jīng)握手成功但對應的W數(shù)據(jù)遲遲沒有完成所有Master的write outstanding計數(shù)都停在某個值既不變多也不變少。這種狀態(tài)和普通擁塞最大的區(qū)別是“完全沒有周期級的抖動”。正常擁塞下即使整體吞吐低總會有某個通道偶爾握手成功而死鎖狀態(tài)下所有相關握手信號像焊死了一樣一個周期都不動??吹竭@種畫面基本就可以進入根因定位了。3.2 畫一張等待圖別靠猜定位死鎖最忌諱上來就改代碼或者隨便調仲裁優(yōu)先級。正確做法是把當前系統(tǒng)里所有寫事務的狀態(tài)列出來畫一張等待圖。具體操作分四步第一步列出每個Master當前還沒收到B響應的寫事務記錄每筆事務的AW是否已握手、W是否已開始、W是否已結束第二步去從設備側看內(nèi)部資源占用情況比如地址槽被哪個事務占著數(shù)據(jù)FIFO里存的是哪個事務的數(shù)據(jù)第三步看互連邏輯的仲裁狀態(tài)W通道當前在服務哪個Master的哪筆事務第四步把這些信息整理成“誰在等誰”的依賴邊。等你把依賴圖畫完如果發(fā)現(xiàn)它能繞成一個環(huán)死鎖結論就成立了。這個方法看著笨但在復雜SoC里特別管用。很多死鎖問題看起來像“某個Master卡住了”其實問題根本不在那個Master身上而在另一個Master的W突發(fā)把整個通道占死了。3.3 數(shù)據(jù)庫死鎖、線程死鎖同樣的模型不同的馬甲AXI總線死鎖、數(shù)據(jù)庫死鎖、線程死鎖本質上都是資源分配不當導致的循環(huán)等待。我在調試AXI死鎖的時候經(jīng)常直接借用并發(fā)編程里的術語只不過把“鎖”換成“地址槽”和“W通道授權”。類型典型資源持有者等待對象常見解法數(shù)據(jù)庫死鎖行鎖、表鎖事務另一個事務持有的鎖鎖順序、超時回滾、死鎖檢測線程死鎖互斥量、條件變量線程另一個線程持有的鎖鎖順序、trylock、層級鎖AXI總線死鎖地址槽、數(shù)據(jù)FIFO、W通道授權Master/從設備狀態(tài)機另一個事務占用的通道資源限制outstanding、解耦AW/W釋放條件、公平仲裁這張表不是生搬硬套。數(shù)據(jù)庫里兩個事務互相等鎖和AXI里兩個Master互相等通道資源從檢測思路上完全一致找到環(huán)打破環(huán)。數(shù)據(jù)庫死鎖有超時回滾硬件死鎖沒有“回滾”這個選項一旦總線凍住整個系統(tǒng)只能復位重啟。所以硬件上更依賴在設計階段就把死鎖條件拆掉。4. 堵住AW-W依賴從協(xié)議、RTL到驗證的完整打法知道了死鎖怎么形成預防思路就清晰了要么讓AW和W不互相等要么保證在資源有限的情況下不會形成循環(huán)等待。下面從不同層面講我實際用過的做法。4.1 協(xié)議與架構層面的約束架構上最容易犯的錯誤是覺得AXI協(xié)議允許outstanding就無腦讓Master往死里發(fā)。實際做系統(tǒng)集成的時候一定要先確認從設備和互連到底支持多少個outstanding寫事務。如果某個從設備的地址槽必須等W數(shù)據(jù)完成才能釋放那么這個從設備實際outstanding能力就是1所有OS看不到這一層的Master都會踩雷。我在項目里定過一個規(guī)矩凡是內(nèi)部有“AW接收依賴W完成”的模塊對外必須把這個限制暴露出來要么通過outstanding count配置限制主端要么在互連的QoS/調度層面保證前一筆W數(shù)據(jù)能優(yōu)先占用W通道。不能一邊只給一個地址槽一邊要求多個Master并發(fā)寫還指望不出事。另外還要注意W數(shù)據(jù)和AW的順序約束。如果從設備不支持W先到Master就不能把W數(shù)據(jù)提前推到通道上反之如果從設備必須先收AW再等W那么Master在發(fā)出AW之后必須保證W數(shù)據(jù)能很快跟上不能發(fā)出AW就晾在那里去等別的通道。很多死鎖其實是Master的調度器“只發(fā)AW不發(fā)W”造成的跟從設備關系不大。4.2 RTL設計里的關鍵取舍RTL設計上最直接的解耦辦法是把“地址槽釋放條件”從“W數(shù)據(jù)完全接收”改成“W數(shù)據(jù)已經(jīng)進入一個足夠深的數(shù)據(jù)緩沖”。比如AXI-to-AHB橋里AW進來后先把地址轉換成AHB總線事務W數(shù)據(jù)進FIFO只要FIFO沒滿就允許下一個AW進來。這樣AW通道的推進不再被W通道的即時完成狀態(tài)綁死循環(huán)等待的一個關鍵邊就被切斷了。如果確實因為后端存儲或數(shù)據(jù)通路限制必須等W數(shù)據(jù)到齊才能接受下一個AW那就要從資源數(shù)量下手。把地址槽深度從1加到2或者把寫數(shù)據(jù)FIFO加深到能容納一筆完整突發(fā)都能打破死鎖環(huán)。注意這里不是隨便加一個深度就完事而是要結合最長突發(fā)長度和outstanding事務數(shù)計算W數(shù)據(jù)緩沖深度至少應該能容納可能被堵在中間的所有突發(fā)數(shù)據(jù)。仲裁器設計同樣重要。W通道仲裁如果采用嚴格優(yōu)先級高優(yōu)先級Master一直有數(shù)據(jù)低優(yōu)先級Master可能永遠拿不到W通道就為循環(huán)等待創(chuàng)造了條件。至少要做到公平輪轉最好還能配合“等待超時提升優(yōu)先級”的機制。我在前面那個最小模型里特意設置了嚴格優(yōu)先級就是因為實際項目里這種配置非常常見而且最容易出事。4.3 驗證與斷言讓死鎖在回歸前現(xiàn)形驗證層面除了跑隨機壓力用例一定要加liveness檢查。協(xié)議檢查通常只查handshake規(guī)則比如valid不能在沒有ready時亂拉低但這類檢查查不出“雙方都合法但誰都不往前走”的死鎖。我常用的一個簡單斷言是只要AW完成握手最終必須看到對應的WLAST和B響應。property p_write_txn_complete(slave_if s); (posedge s.aclk) disable iff (!s.aresetn) (s.awvalid s.awready) |- ##[1:$] (s.wvalid s.wready s.wlast); endproperty property p_write_response_after_wlast(slave_if s); (posedge s.aclk) disable iff (!s.aresetn) (s.wvalid s.wready s.wlast) |- ##[1:$] (s.bvalid s.bready); endproperty這種屬性只會在死鎖出現(xiàn)后超時觸發(fā)定位時作用有限但能在回歸階段第一時間把問題炸出來。除了斷言還可以在testbench里加一個deadlock watchdog設定一個最大等待周期數(shù)某個通道的握手如果超過該周期數(shù)還沒有完成就dump當前所有Master和從設備的狀態(tài)到日志并打印等待圖。實測下來這種監(jiān)視器比事后拿waveform翻幾百周期要快得多。5. 復盤一次真實的AXI寫通道死鎖講完方法論分享一個我印象很深的案例。這個案例不是虛構是在一個帶CPU和DMA的子系統(tǒng)驗證里遇到的現(xiàn)象、定位和修復都非常典型。5.1 現(xiàn)場現(xiàn)象與第一印象當時測的是一個SoC子系統(tǒng)CPU核和DMA會同時訪問掛在AXI-to-APB橋后面的外設寄存器區(qū)。隨機壓力測試跑了幾千個事務后仿真永遠停在同一個地方不是報錯也不是超時就是整條總線卡死。第一眼看到波形時CPU側已經(jīng)發(fā)出了AW正在等W通道DMA側則在做一個大塊數(shù)據(jù)搬運WVALID一直為高但WREADY長期為低。橋接模塊內(nèi)部的地址FIFO顯示只有一項而且這項被CPU的AW占著。第一印象是“DMA把總線擠爆了”因為DMA的數(shù)據(jù)量確實很大。但如果只是擁塞總線至少會在某些周期讓一個握手成功。實際情況是AWREADY和WREADY都紋絲不動說明這不是性能問題而是死鎖。5.2 根因定位一個“看起來沒問題”的狀態(tài)機把等待圖畫出來后發(fā)現(xiàn)根因和模型里的場景幾乎一樣橋接模塊的狀態(tài)機為了省事設計成“收到AW幀后必須等當前寫事務的W數(shù)據(jù)全部到達才允許接收下一個AW”。也就是說地址FIFO雖然只有一項但在這一項被占住后橋不會回收它除非W通道把數(shù)據(jù)送完。與此同時DMA的W突發(fā)被仲裁器判定為高優(yōu)先級持續(xù)占據(jù)W通道CPU的W0根本無法進入。DMA的W數(shù)據(jù)不斷地往橋的數(shù)據(jù)FIFO里灌但橋的消費邏輯又需要拿到DMA的AW1才能知道數(shù)據(jù)該寫到哪個外設地址而AW1被地址FIFO擋住。依賴環(huán)就此形成CPU等W通道W通道被DMA占住DMA等AW1AW1等地址FIFO釋放地址FIFO釋放又等CPU的W0完成。這里最坑的地方在于每一個模塊單獨看都“符合協(xié)議”橋的狀態(tài)機沒有違反AXI握手規(guī)則DMA也只是在發(fā)正常的寫突發(fā)仲裁器嚴格按優(yōu)先級調度也不違反AXI規(guī)范。但把它們組合在一起就湊齊了死鎖的全部條件。5.3 修復與回歸一行代碼的代價和兩個驗證點修復方案不是把DMA優(yōu)先級調低也不是讓CPU多等一會而是直接把地址FIFO深度從1改成2同時把地址槽的釋放條件從“W數(shù)據(jù)全部接收”改成“W數(shù)據(jù)進入數(shù)據(jù)FIFO即可”。改完之后即使CPU的W0還堵在路上DMA的AW1也能進入地址FIFO橋的消費邏輯拿到AW1后就能消費DMA的W數(shù)據(jù)W通道一旦釋放CPU的W0自然跟著走了。代碼改動其實很小但回歸驗證需要補兩個點第一加一條斷言保證橋的地址FIFO只要還有空位AWREADY就必須在有限周期內(nèi)拉高第二隨機測試里把CPU和DMA的寫優(yōu)先級、突發(fā)長度、outstanding數(shù)量都設成不同組合連續(xù)跑了幾百個種子。修改后這些用例全部通過之后類似的死鎖沒再復現(xiàn)過。這個案例給我最大的教訓是協(xié)議上“合法”不代表系統(tǒng)里“安全”AXI死鎖從來不是某一條通道自己的問題而是通道間依賴關系的問題。6. 常見問題速查表與最后幾點心得調試AXI死鎖是個熟練活很多問題看一眼現(xiàn)象就能猜到大概方向。這里整理了一張速查表也是我平時給人排查死鎖時的第一份checklist。現(xiàn)象可能原因排查方向AWVALID為1且長時間不握手從設備地址槽滿或地址槽被某筆未完成事務占住查從設備內(nèi)部事務表項、地址FIFO占用WVALID為1且WREADY長時間為0從設備數(shù)據(jù)FIFO滿或W通道被更高優(yōu)先級突發(fā)占用查數(shù)據(jù)FIFO深度、W通道仲裁狀態(tài)AW和W各卡在不同Master上基本就是AW-W依賴型循環(huán)等待畫等待圖找“AW等W、W等AW”的環(huán)改變一個Master優(yōu)先級后死鎖消失仲裁問題掩蓋了資源不足不要只調優(yōu)先級要查資源深度和釋放條件斷言只報超時不報位置Liveness檢查缺少狀態(tài)dump增加deadlock watchdog和等待圖打印6.1 最后幾點心得我在實際項目中處理完這類死鎖后最深的體會是AXI死鎖問題很難靠“看一眼代碼”發(fā)現(xiàn)因為它往往發(fā)生在多個模塊的交互邊界上。習慣性做三件事能省很多時間一是所有寫通道都加liveness斷言不要只做handshake協(xié)議檢查二是設計評審時重點審“地址槽釋放條件”和“數(shù)據(jù)FIFO消費條件”凡是兩者形成依賴的都要標黃三是出現(xiàn)卡死先畫等待圖不要急著改仲裁優(yōu)先級優(yōu)先級很可能只是壓死駱駝的最后一根稻草。另一個小技巧是在testbench里專門做一個“事務超時監(jiān)控器”按Master分別統(tǒng)計所有outstanding寫事務的年齡。年齡超過閾值的Master把它當前AW、W、B三個通道的狀態(tài)全部打印出來。這個監(jiān)視器寫起來不復雜但每次都能在死鎖產(chǎn)生后立刻給出第一手的現(xiàn)場證據(jù)比事后翻波形高效太多。最后想強調的是AW-W依賴不是某一種從設備實現(xiàn)獨有的bug它是通道獨立性帶來的固有風險。只要系統(tǒng)里存在多個Master、多筆outstanding寫事務、以及有限深度的內(nèi)部緩沖就有可能出現(xiàn)這種環(huán)。理解了模型之后再回去看那些“偶爾卡死但復現(xiàn)不出來”的老問題往往會有一種豁然開朗的感覺。