跳轉到

05.3 Reset 極性與非同步:一場持續二十年的工程論戰#

重置為什麼難#

重置(reset)聽起來是最簡單的功能:把狀態歸零。但它在實作上是整個數位設計中最容易出事的訊號之一。原因在於重置同時踩在兩個世界的邊界上:它是「控制訊號」(要在所有時刻都有效,不受時脈節拍限制),卻又牽涉「時序約束」(recovery/removal timing 檢查)。這一章我們看 Veryl 如何用型別家族把這個問題結構化。

兩大陣營:同步 vs 非同步#

非同步重置(asynchronous reset)的寫法在 Verilog 中是 always @(posedge clk or negedge rst_n):重置一有效,flip-flop 立刻歸零,不等時脈。它的優點是不需要時脈在跑就能重置(上電初期時脈尚未穩定時尤其重要)、不消耗資料路徑的邏輯資源。缺點有三:

  • recovery/removal timing:重置釋放(deassert)的時間點相對時脈邊緣必須滿足 setup/hold 類的約束,否則某些 flip-flop 這一拍重置、某些下一拍才重置,狀態機直接走進非法狀態。因此非同步重置必須搭配「重置同步器」(reset synchronizer):重置釋放前先由兩級正反器同步。
  • reset domain crossing(RDC):當晶片上有多個重置域,跨域的訊號在對方重置期間可能處於不確定狀態,需要額外的跨域保護邏輯。
  • 非同步重置的 assertion 無法被 STA 的普通路徑檢查涵蓋,需要專門的工具與約束。

同步重置(synchronous reset)則把重置當成資料路徑上的一個 mux 輸入:if (!rst_n) q <= 0; 出現在時脈邊緣觸發的程序塊中。優點是完全在 STA 的管轄之下,且能過濾掉窄於一個 clock period 的重置毛刺。缺點是時脈必須在跑才能重置,且每個 flip-flop 的 D 輸入多一層邏輯,對面積與時序都有影響。

這場論戰在 2000 年代的 SNUG(Synopsys Users Group)年會上是常設議題,Cummings 等人的重置設計論文被業界當成事實標準參考。一般共識是:ASIC 傾向非同步重置(assert 非同步、deassert 同步化),FPGA 廠商則普遍建議用同步重置——Xilinx 與 Altera 的 FPGA 硬體架構中,flip-flop 的 set/reset 資源有限且非同步重置會妨礙某些最佳化,廠商文件長年建議同步重置、能不用非同步就不用。ASIC 與 FPGA 對 reset 策略的這種結構性差異,正是 Veryl 把 reset 策略做成可配置選項的現實背景。

Veryl 的 reset 型別家族#

Veryl 提供一組 reset 型別,涵蓋極性與同步/非同步兩個維度:

型別 極性 屬性
reset 由全域設定決定 跟隨專案預設
reset_async_high active-high 非同步
reset_async_low active-low 非同步
reset_sync_high active-high 同步
reset_sync_low active-low 同步

使用方式:

module Foo (
    clk : input clock,
    rst : input reset_async_low,
) {
    var r_q: logic<8>;
    always_ff (clk, rst) {
        if_reset {
            r_q = 0;
        } else {
            r_q = r_q + 1;
        }
    }
}

注意 if_reset 裡完全不出現極性。極性寫在型別名稱裡,由編譯器在產生 Verilog 時正確展開成 negedge rst 與 if (!rst)(或相應的極性)。在 Verilog 中「事件列表寫 negedge、if 條件忘寫 !」這個經典錯誤,在 Veryl 中是結構上不可能出現的,因為工程師根本沒有機會分開寫這兩處。

全域設定:Veryl.toml 的 reset_type#

更進一步,Veryl 允許在專案設定檔 Veryl.toml 的 [build] 區段設定全域的 reset 策略(reset_type)。宣告為抽象 reset 型別的埠,會依這個全域設定被轉譯為具體的極性與同步屬性。其意義在於:同一份 RTL 可以在「ASIC 式非同步重置」與「FPGA 式同步重置」之間切換,而不需要修改任何一行設計程式碼。對一個同時要投 ASIC 又要跑 FPGA prototype 的專案,這省下的是整個重置結構的改寫與回歸驗證。

[build]
reset_type = "async_low" # 概念示意;實際值請參考 Veryl 文件

與 always_ff 的協作#

回到上一章的骨架,一個典型的時序塊:

always_ff (clk, rst) {
    if_reset {
        // 重置值
    } else {
        // 正常行為
    }
}

工具鏈依 rst 的型別(或全域 reset_type)決定:非同步重置就展開成帶 negedge/posedge rst 的事件列表;同步重置就展開成純時脈邊緣觸發、重置判斷放在資料路徑。工程師的行為描述不變,reset 語意成為後端配置。

歷史小結#

從 1990 年代 ASIC 流程確立「非同步重置 + reset synchronizer」的主流做法,到 2000 年代 SNUG 論文把 reset 問題系統化,再到 FPGA 廠商以硬體架構理由推廣同步重置,重置策略始終是「沒有標準答案、只有與目標製程配合的答案」的典型工程決策。Verilog 的回應是「留給工程師手寫」,結果是無窮的極性錯誤與 domain 問題;Veryl 的回應是把答案空間列舉成型別,讓錯誤與切換成本都消失在編譯期。這是語言設計向工程實務學習的一個漂亮案例。