valid/ready 握手協定#
上一節的架構圖裡,資料順流、控制逆流。要把這張圖變成可驗證的合約,我們需要一套精確的規則——這就是 valid/ready 握手協定。它簡單到只有兩條線,卻是整個現代 SoC 互連的基石。
協定規則#
master(發送方)拉高 valid 表示「資料有效」,同時把資料放在 data 上;slave(接收方)拉高 ready 表示「我這一拍可以收」。兩者同時為高的那一個時脈沿,一次傳輸完成。三條不變量:
valid拉高後,在握手完成(valid && ready同拍)之前不得撤回,資料也必須保持穩定。- master 不得等
ready才拉valid(valid不得組合依賴ready),否則兩邊互相等待形成組合迴圈或死鎖。 - slave 的
ready可以自由依賴valid(例如「見到 valid 且我空閒就收」),也可以完全獨立。
其中第 2 條是 AXI 規範明文要求的相依性規則;第 1 條則是 AXI 對 VALID 的穩定性要求。違反第 1 條是最常見的新手 bug:資料已經被下游「看到」卻又縮回去,系統在模擬裡偶爾過、上板後偶爾壞,成為最難抓的 flaky 錯誤。
為什麼需要:backpressure 與 skid buffer#
同步電路的節奏由單一 clock 統一,但各模組的消化速度不同:記憶體可能要等 refresh,DMA 可能搶走匯流排,快取可能 miss。如果快的一方強塞資料給慢的一方,資料就掉了。握手協定讓慢的一方可以「喊停」,這就是 backpressure。
但喊停有代價。最簡單的做法是整條流水線一起停(global stall),代價是所有人都停。更好的做法是在停住點前放一個 skid buffer——一級額外的暫存器,讓上游在下游暫停後還能多塞一筆資料「滑行」一拍再停,如此 master 的時脈不必被下游的 ready 直接綁死,timing 收斂容易得多。skid buffer 是工業界耳熟能詳的電路樣式,Cummings 在 SNUG 論文裡對各種 pipeline/fifo 握手變形有系統性的整理(見附錄參考文獻)。
常見錯誤#
- 組合迴圈穿過 ready:
my_ready = !upstream_valid || consume之類把別人的 valid 直接組合進自己的 ready,再串上一長串模組,形成跨模組的組合環。綜合器會報 combinational loop,或更糟——默默做出震盪電路。解法是把 ready 打一拍,或用 skid buffer 切斷組合鏈。 - valid 因 ready 拉低而撤回:多發生在「資料來自一個會被覆寫的暫存器」時。正確寫法是:一旦 valid 拉高,暫存器就鎖住,直到握手完成才允許更新。
- 用
valid && ready之外的方式判定傳輸(例如只看valid),導致重複消費。
歷史:flow control 的血脈#
流量控制不是晶片工程師的發明。電傳(teletype)時代,紙帶閱讀機與打孔機速度不匹配,操作員用 XON/XOFF 這兩個控制字元喊停——這是「接收方告訴發送方暫停」最早的形式。RS-232 序列通訊繼承了 RTS/CTS 硬體握手線,字面上就是 ready 訊號的祖先。Ethernet 從最早的 CSMA/CD「碰撞了就退避重試」,演化到 PAUSE frame、再到 PCIe 與乙太網交換器的 credit-based flow control,思想一貫:發送前先確認接收方有容量。
晶片內部走同一條路。1990 年代的 PCI 匯流排用 FRAME#/TRDY#/IRDY# 三線握手;ARM 在 AMBA 裡先用簡單的 valid/ready(AHB 稱 HREADY),到 AXI 把每個通道都標準化為 VALID/READY 對。今天 AXI 的五通道、NVMe over PCIe 的 credit 機制、NoC(network-on-chip)裡的虛通道流控,全是同一套思想的變奏。學會 valid/ready,你學到的不只是一個 HDL 慣例,而是六十年來數位系統處理「快慢不匹配」的通解。
下一節,我們把這套協定寫進兩級流水線的 Veryl 程式碼。