0.1 導言:硬體描述語言的困境與Veryl的出現(1984-2026)#
一個特殊的語言種類#
軟體工程裡,程式語言的選擇通常是工程管理問題:這個語言的工具鏈是否成熟、團隊是否熟悉、除錯是否方便、效能是否達標。只要語言的語意是明確的,錯誤就會被編譯器或測試框架攔下,修正成本也侷限在「重新部署」的代價以內。
硬體描述語言(Hardware Description Language, HDL)不是這樣。
一份 HDL 原始碼同時扮演兩個角色:
- 它是描述。工程師用它說出「我要造什麼電路」——一組在時間上展開的資料流、狀態機、算術單元與匯流排協定。
- 它也是實作。同一份程式碼會被綜合器(synthesizer)轉成閘級網路,被模擬器(simulator)拿來跑功能驗證,被形式驗證工具拿來證明性質,被時序分析工具拿來估計關鍵路徑。
這是硬體描述語言在整個軟體世界裡獨一無二的位置。它既不是「寫給機器看的程式碼」,也不是「寫給人看的規格」,它是兩者的混合物,而這個混合帶來了後續四十年所有的麻煩。
更關鍵的是:語言不只是記號系統,它是工程方法論的載體。同樣要寫一個非同步 FIFO,在 Verilog 裡你要手動管理指標位寬、在 SystemVerilog 裡你要寫 always_ff 加 logic 加結構體、在 Veryl 裡你寫 fifo 加一個型別參數。三種語言不只是「寫法不同」,而是錯誤的種類不同、驗證方法不同、除錯時間不同、團隊技能需求不同。語言選擇潛在地決定了一個專案的工程形態。
因此本書不是「Veryl 語法手冊」。它的核心工作是回答一個更上位的問題:
為什麼硬體描述語言在四十年裡不斷地「修補」,卻一次比一次更複雜?而 Veryl 為什麼可能是一個不同的方向?
語言演化史的三個階段(與第四階段)#
一張時間線#
timeline
title 硬體描述語言的四個階段
1984-1995 : Verilog 誕生 : Gateway Design Automation : 事件式模擬語言 : C 風格語法 : 為模擬而設計
1995-2005 : Verilog-95 與 Verilog-2001 : ANSI 埠宣告 : generate 與 genvar : PLI 與 configuration : 兼具模擬與描述
2005-2017 : SystemVerilog 與 IEEE 1800 : 類別與介面 : 泛型與封裝 : 斷言與 SVA : DPI 與 UVM
2017-2026 : 複雜度危機 : 工具子集分歧 : 語意不可預測 : Alt-HDL 挑戰
2022-2026 : Veryl : 語義等價 SystemVerilog : 語法大幅簡化 : 產生可讀 SystemVerilog第一階段(1984-1995):Verilog——為模擬而生的語言#
Verilog 的血統不是硬體設計,而是電路模擬。1984 年,Gateway Design Automation 發表 Verilog,作為他們的類比電路模擬器 Verilog-XL 的描述語言層。這個出身決定了它的 DNA:Verilog 最優秀的地方是事件式模擬效率,最薄弱的地方是設計語意。
具體地說,Verilog 的語法幾乎是 C 的翻版——同樣的 if、case、for、while、function、task、module。這大幅降低了軟體工程師進入硬體領域的門檻,是 Verilog 能在二十世紀末統治產業的關鍵。
但它帶來了兩個原生的、至今仍存留的問題:
- 四態邏輯( \(\{0,1,x,z\}\) ):語言為了偵測「未初始化」與「高阻抗」而引入
x與z,代價是所有比較運算子都要定義在四態語意上,x會像傳染病一樣傳播。 - 求值順序依賴程式碼位置:因為一切都是「事件驅動的」,
always區塊的執行先後決定了最終值。同一份 RTL 在不同模擬器、不同最佳化設定下可能給出不同結果——這是競態(race)的根源。
Verilog 的優點是「寫起來像 C」,代價是「讀起來像 C,但語意不是 C」。
第二階段(1995-2005):Verilog-95 與 Verilog-2001——從模擬語言變成設計語言#
Verilog-95(IEEE 1364-1995)與 Verilog-2001(IEEE 1364-2001)加入了幾項關鍵機制,把 Verilog 從「模擬語言」推成了「設計語言」:
- 阻塞賦值(blocking assignment,
=)與非阻擋賦值(non-blocking assignment,<=)的明確區分; - ANSI 風格埠宣告,讓埠的位寬與方向在模組介面上一次說清楚;
generate/genvar,把「硬體迴圈」語法化;- PLI(Programming Language Interface),把 Verilog 接到 C 語言;
- 配置(configuration)機制,讓同一組模組能組出不同組態。
到 2001 年,Verilog 已經兼具模擬與描述能力,成為產業不約而同的預設選擇。值得注意的是,IEEE 1364 正式標準晚了近十年才追上實務——這是硬體產業常見的現象:語言由使用行為決定,標準由既成事實追認。
第三階段(2005-2017):SystemVerilog——「把所有東西放進來」#
2005 年,IEEE 1800-2005 將 Verilog-2001 併入 SystemVerilog,並一次吸納了 C++ 的類別系統、PSL/OpenVera 的斷言語言、以及 DPI。SystemVerilog 幾乎是「Verilog + C++ + 硬體斷言 + 約束求解」的大集合。
這是一場豪賭,也是硬體語言史上最大的一次擴張。豪賭成功了大約十二年:SystemVerilog 確實解決了 Verilog 的所有結構性缺陷,也確實孕育了 UVM 這個至今仍主導驗證工程的主流方法學。但豪賭的帳單在 2017 年之後開始到期:完整語言規格超過一千八百頁,各家 EDA 工具只支援互相不重疊的子集,工程師被迫在「交集」而非「語言」裡寫程式。
第四階段(2022-至今):Veryl——回到可預測性#
Veryl 於 2022 年公開,是一套語義等價於 SystemVerilog、語法大幅簡化、且保證輸出可讀 SystemVerilog 的硬體描述語言。
它的核心洞見不是「語法要更漂亮」,而是:
語言的價值不來自表達力的最大值,而來自「讀程式碼的人能預測這會變成什麼電路」的程度。
核心命題:表達力與可預測性的取捨#
價值函數#
我們用一個簡化的模型來描述硬體描述語言的工程價值。令:
- \(E\) :表達力(expressiveness),語言能描述的硬體建構範圍與抽象層級;
- \(C\) :複雜度成本(complexity),包含實作成本、學習成本與歧義(ambiguity)成本;
- \(U\) :不確定性(uncertainty),指工程師無法從程式碼預測最終硬體的程度。
則語言的工程價值可以粗略地寫成
其中 \(\alpha, \beta, \gamma > 0\) 為權重。這個式子不追求嚴謹的數學意義,它的用途是把常見的、直覺性的工程爭論攤開成可以辯論的形式。
用它檢視歷史:
- Verilog: \(E\) 偏低(沒有介面、沒有類別、沒有參數化泛型), \(C\) 也不高,但 \(U\) 已經不小(四態邏輯、競態、隱含網路)。總值中等。
- SystemVerilog: \(E\) 被推到歷史最高(幾乎沒有它描述不了的硬體建構),但 \(C\) 與 \(U\) 同樣爆炸。結果是——在大多數真實專案裡, \(V\) 反而低於它的前一代。
- Veryl:刻意把 \(E\) 限制在「工程上剛好夠用」的範圍,換取極低的 \(C\) 與極低的 \(U\) 。
為什麼硬體語言的複雜度代價更高#
這個不等式在硬體領域的斜率比軟體更陡,原因有三個硬體特有的性質。
第一,錯誤成本高且不可逆。 軟體的 bug 通常可以熱更新修掉;ASIC 的掩膜(mask)一旦投片(tape-out),修改一顆 gate 的成本是數百萬美元量級,量產中的矽片甚至已經在客戶手上。因此語言層級的一個語意歧義,在硬體裡對應的不是「錯的值」,而是「錯的電路」,且必須靠 ECO 或整個重投片來收拾。
第二,產出物是物理的。 軟體的「效能」抽象成時間複雜度;硬體的產出物受矽物理限制:面積決定成本,時序(timing)決定最高工作頻率,功耗決定散熱與電池壽命。這些量都無法靠「多跑幾次」來改善。當語言允許過度 generality 時(例如任意寬度的算術運算子),設計者容易寫出「能跑但面積爆炸」的碼,而工具未必會警告。
第三,語意不明確帶來的風險是不確定的。 這是最容易被低估的一點。軟體裡的邏輯錯誤多數是確定性的:同樣輸入、同樣版本,必定同樣輸出,工程師可以重現、除錯、修正。硬體裡的語意不明確(例如阻塞/非阻擋用錯)產生的錯誤,是只在罕見情境觸發的:位元切換時序、特定的初始化值、特定的重置解除順序。這類 bug 可以通過幾乎所有回歸測試,只在量產後才現形。
用一句話總結這個差異:
軟體語言的 bug 通常是「算出來的值不對」;硬體語言的 bug 常常是「長出來的不是你想像的那個電路」。
一個最小的例子#
// A:正確的流水線風格
always_ff @(posedge clk) begin
q <= d;
end
// B:把非阻塞改成阻塞
always_ff @(posedge clk) begin
q = d;
end
在功能模擬上,若 d 依賴 q,兩者會在某些情境產生不同結果。在合成上:
| 寫法 | 合成結果 |
|---|---|
q <= d | 一個 D 觸發器(FF) |
q = d | 一條組合邏輯路徑(若放在時脈觸發區塊中,工具會發出警告,但形式上仍可能產生非預期結構) |
這不是「寫法風格」的問題,而是語法決定硬體拓撲的問題。整本書會反覆回到這一點。
硬體語言的三個結構性難題#
在進入歷史之前,我們先把整本書會用到的三個難題講清楚,因為它們是後面所有語言決定的源頭。
難題一:時間不在程式碼裡#
在軟體裡,一次「執行」是一個全序的指令序列。在硬體裡,「時間」是獨立的物理軸:所有訊號的值都是「時間 \(t\) 的函數」。
這意味著硬體語言必須回答一個軟體語言永遠不需要回答的問題:
在同一個時間點,兩個敘述誰先發生?
Verilog 的答案是「不確定,取決於程式碼順序與模擬器排程」;SystemVerilog 試圖用 always_comb / always_ff / always_latch 與模擬器關鍵字 program 來限制它;Veryl 的答案是用不同語法強迫設計者宣告這三種意圖,把模糊的 always 消滅。
難題二:硬體是無限平行的#
軟體的 for 迴圈是「執行很多次」;硬體的 for 迴圈通常是「複製很多份硬體」。這個差異衍生出語言裡最難處理的一類問題:參數化。
Verilog 用 parameter 與 defparam(後者事後被廣詬病為「就地修改」,是模擬器非標準擴充的產物);SystemVerilog 用 parameter type 與泛型類別;Veryl 用 gen 與型別參數,把「複製硬體」變成一個宣告式的、可檢查的動作。
難題三:語言必須同時是描述與驗證#
硬體的驗證環境往往比 DUT(Device Under Test)本身大上一個數量級。如果語言只有模組與連線,就沒法寫複雜的激勵器、記分板(scoreboard)與斷言。
SystemVerilog 之所以走向類別與物件導向,是因為驗證環境的複雜度壓力。但這也意味著語言被硬生生拉進了軟體工程的領域——帶來了軟體工程全部的複雜度,也帶來了軟體工程全部的誘惑(繼承、虛函式、動態派發)。
Veryl 的定位#
Veryl 官方把設計哲學歸納為三支柱:
| 支柱 | 內涵 | 對工程師的意義 |
|---|---|---|
| Optimized Syntax(最佳化語法) | 消除隱含網路、隱含型別、隱含語意;每一條語法規則都只有一種解讀 | 讀程式碼就是在讀硬體 |
| Interoperability(互操作) | 生成可讀的 SystemVerilog,可與既有 SV 設計、驗證環境、IP 自由混用 | 不必推倒重來 |
| Productivity(生產力) | 建置、格式化(format)、檢查(lint)、文件產生與模擬器整合為一條工具鏈 | 團隊可持續 |
本書會把這三支柱拆開、驗證、並批評它們在實務上的邊界(第 2 章)。
Veryl 有一個「消極的」設計選擇值得特別指出:它刻意不提供自動流水線(automatic pipelining)之類會偷偷插入觸發器的功能。理由是這類功能會破壞整條因果鏈——「我改 Veryl 程式碼,工具替我插 FF」意味著「我改 Veryl 程式碼,硬體結構可能非預期地改變」,而這正是第 1.3 節要論證的核心問題。
一個 Veryl 與 SystemVerilog 的等價片段#
module Top (
i_clk : clock,
i_rst_n : reset,
i_d : logic [7:0],
o_q : logic [7:0],
) {
var r_q : logic [7:0]
always_ff {
r_q = i_d
}
assign o_q = r_q
}
它生成的 SystemVerilog 會是可直接交給既有流程的、可讀的 SystemVerilog。這個「等價性」是 Veryl 一切設計決策的基礎,也是第 9 章「互操作與遷移」的技術前提。
全書地圖與閱讀路線#
十章分派邏輯#
flowchart TD
A[第0章 導言] --> B[第1章 歷史與複雜度危機]
B --> C[第2章 設計哲學與語言定位]
C --> D[第3章 模組 介面 專案組織]
C --> E[第4章 時序邏輯與時鐘重置抽象]
C --> F[第5章 資料型別 泛型 表達式]
E --> G[第6章 時鐘域與跨時鐘域安全]
D --> H[第7章 工具鏈 專案管理 CI]
F --> I[第8章 驗證流程]
G --> I
H --> I
I --> J[第9章 互操作與遷移]
J --> K[第10章 生態 實戰 未來]- 第 0 章(導言):建立「語言決定工程方法論」的論題。
- 第 1 章(歷史與複雜度危機):三節,分別處理 Verilog 時代、SystemVerilog 時代、以及 2017 年之後的三重危機。這是本卷的理據。
- 第 2 章(設計哲學):Veryl 的誕生、三支柱、以及「為什麼不是 Chisel」。
- 第 3-5 章(語言各論):模組與介面、時序邏輯與時鐘重置抽象、資料型別與泛型。這是核心語法。
- 第 6 章(時鐘域):跨時鐘域——硬體設計中最難的一類 bug。
- 第 7 章(工具鏈):
verylup、build/fmt/lint/doc、CI 與相依性管理。 - 第 8 章(驗證):原生測試框架、內建模擬器、隨機測試與 cocotb 嵌入。
- 第 9 章(互操作):混合語言設計、
translate自動轉換、標準函式庫。 - 第 10 章(生態):Heliodor(RISC-V 多核)、周邊生態、以及未解問題。
三條建議閱讀路線#
路線一:Verilog 背景者(讀過 Verilog、寫過幾個模組、對類別不熟)
- 第 1 章全章——你需要知道 Verilog 的四態邏輯與競態是怎麼回事。
- 第 2.2 節(三大支柱)→ 第 3 章 → 第 4 章 → 第 5 章。
- 第 7.2 節(工具鏈與 CI)→ 第 8.1 節(原生測試框架)。
- 第 9 章(若你已有 SystemVerilog 程式碼需要遷移)。
路線二:SystemVerilog 專家(熟悉類別、UVM、介面、隨機驗證)
- 第 1.2 與 1.3 節——這兩節會把你日常感覺到的痛點(工具子集、
always_ff的爭論、UVM 的笨重)給出機制性解釋。 - 第 2.3 節(為什麼不是 Chisel)——直接對話你的既有工作流。
- 第 4 章(時序邏輯)與第 6 章(時鐘域)——這兩章是 Veryl 與 SystemVerilog 差異最大的地方。
- 第 8 章(驗證)——了解 Veryl 如何在不引入 UVM 式類別階層的前提下做驗證。
路線三:軟體背景轉硬體者
- 第 1.1 節(Verilog 的語意核心)與第 1.3 節(語意不可預測)——這兩節專門拆解「為什麼硬體不能用軟體的直覺」。
- 第 2.1 節(誕生與動機)。
- 第 4 章與第 5 章。
- 第 10.1 節(Heliodor)——看一個真實規模的軟體背景者成果。
術語與記號約定#
| 記號 | 意義 |
|---|---|
| \(I\) | 輸入(Input) |
| \(O\) | 輸出(Output) |
| \(CLK\) | 時鐘訊號(Clock) |
| \(F\) | 正規表達式/正規語法(Formal syntax) |
| \(\mathcal{L}\) | 語意(Semantics) |
| \(F \models \varphi\) | 系統 \(F\) 滿足性質 \(\varphi\) (形式驗證記號) |
| \(E\) | 表達力(Expressiveness) |
| \(C\) | 複雜度成本(Complexity) |
| \(U\) | 不確定性(Uncertainty) |
| \(V\) | 語言的工程價值 |
| DUT | 被驗證的設計本體(Device Under Test) |
| RTL | 暫存器傳輸層(Register Transfer Level) |
| UVM | 通用驗證方法學(Universal Verification Methodology) |
| ECO | 工程變更單(Engineering Change Order),投片後的硬體修改 |
| CDC | 跨時鐘域(Clock Domain Crossing) |
| SVA | SystemVerilog Assertions |
| DPI | 直接程式介面(Direct Programming Interface) |
補充說明:書中描述語法時,「Verilog 片段」一律標註為 verilog,「SystemVerilog 片段」標註為 systemverilog,「Veryl 片段」標註為 veryl,C/C++ 片段標註為 cpp。行內數學一律使用 \(...\) 包裹,並在內側保留空白,以確保 GitHub 與 Obsidian 兩邊的渲染一致。
結語:從「語言是糖」到「語言是契約」#
軟體界有一句常見的流行語:「語言是糖,演算法是 guts」(the language is sugar, the algorithm is the guts)。這句話背後的價值觀是:語言只是一層便利的外衣,換掉它不影響事情的本質。
對硬體來說,這句話幾乎是錯的。
硬體語言的每一條語法規則,都直接決定了多少硬體、多少個狀態、什麼拓撲。always @(posedge clk) q <= d; 與 always @* q = d; 的差別不是寫法,而是「一顆 FF」與「一條組合路徑」的差別。語言不是包裝,而是與矽之間的契約。
而一份不可預測的契約,比沒有契約更危險:沒有契約時,工程師至少知道自己在猜;有了一份不可預測的契約,工程師會誤以為自己知道。
這就是 Veryl 出現的時機與理由。它不是要造出表達力最強的硬體語言,而是要造出最不容易騙人的硬體語言。
接下來的三節,將把這四十二年的因果鏈攤開:Verilog 如何贏得標準地位(1.1)、SystemVerilog 如何贏得工具支援並因此負債(1.2)、以及這筆負債如何在 2017 年之後引爆成三場危機(1.3)。理解這條鏈,才能理解 Veryl 的每一條語法決定為什麼長成那個樣子。
下一節:第 1.1 節「從 Verilog 到 SystemVerilog:一門語言的三十年演進(1984-2005)」