跳轉到

第一個專案#

工具裝好了、編輯器配好了、設定檔看懂了——現在動手寫第一個 Veryl 專案。我們從 veryl new 開始,寫一個 Counter module,看它如何被轉譯成 SystemVerilog。

建立專案#

$ veryl new first_proj

這會產生一個最小專案骨架:

first_proj/
├── Veryl.toml
└── src/
    └── lib.veryl

結構與 cargo new 產出的專案如出一轍:根目錄一個 Veryl.toml(上章介紹過),原始碼集中在 src/。打開 Veryl.toml 確認 [project] name = "first_proj",然後進入專案目錄開始編輯。

寫一個 Counter#

把 src/lib.veryl 改寫成一個帶參數寬度的計數器:

module Counter #(
    param WIDTH: u32 = 8,
) (
    port clk    : input clock,
    port rst    : input reset,
    port o_count: output logic<WIDTH>,
) {
    var count: logic<WIDTH>;

    always_ff (clk, rst) {
        if_reset {
            count = 0;
        } else {
            count = count + 1;
        }
    }

    assign o_count = count;
}

幾個語法點值得停留。第一,param WIDTH: u32 = 8 是 module 參數,帶型別與預設值——注意所有參數與 port 都有顯式的型別,而且 logic<WIDTH> 的寬度可以是參數:位寬進入型別系統,這是 Veryl 的招牌特徵。第二,clk 的型別是 clock、rst 是 reset,不是普通的 logic:時脈與重置在型別層面就被區分,把資料訊號誤接上時脈 port 是編譯錯誤。第三,var count 宣告可變的內部訊號(若用 let 則是單一賦值),第四,always_ff (clk, rst) 描述時序邏輯,敏感列表由所用的 clock/reset 型別決定展開方式;if_reset 是重置分支的抽象寫法,具體展開成同步或非同步、high active 或 low active,由 Veryl.toml 的 reset_type 全域決定——你在原始碼裡完全不必寫 posedge rst_n 這類細節。第五,組合驅動用 assign o_count = count;,與 SV 的語意相同但型別檢查更嚴格。

轉譯:veryl build#

$ veryl build

Veryl 會做型別檢查,然後把整個專案轉譯成 SystemVerilog,輸出放在 target/ 目錄下(對應 cargo 的 target 慣例),同時產生 filelist。用編輯器打開 target/ 裡對應 Counter 的 .sv 檔案,你會看到大致如下的 SystemVerilog(細節依 Veryl 版本與 Veryl.toml 設定略有差異):

module Counter #(
    parameter WIDTH = 8
) (
    input var logic        clk,
    input var logic        rst,
    output var logic [WIDTH-1:0] o_count
);
    logic [WIDTH-1:0] count;

    always_ff @(posedge clk, negedge rst) begin
        if (!rst) begin
            count <= 0;
        end else begin
            count <= count + 1;
        end
    end

    assign o_count = count;
endmodule

請仔細端詳這份輸出,因為它是 Veryl 哲學的具體呈現:這是一份人類會寫、也願意 review 的 SystemVerilog。if_reset { count = 0; } 被展開成 if (!rst) 加上非同步重置的敏感列表——這是 Veryl.toml 裡 reset_type 的全域決定;如果設定改為同步重置,展開會變成時脈分支內的 if,原始碼一行都不用改。logic<WIDTH> 對應成 [WIDTH-1:0]。轉譯是一對一的,沒有暴肥的中間碼,從 SV 讀回 Veryl 的心智映射毫不費力。這就是「零額外成本的可讀轉譯」:你可以把 target/ 的輸出直接交給任何模擬器、綜合器或公司既有流程,也可以在 code review 時讓不熟 Veryl 的同事直接讀 SV。

文件註解與更多觀察#

再補一個細節:Veryl 的文件註解用 ///,寫在 module 或 item 之前:

/// 8-bit free-running counter.
module Counter #(

veryl doc 會把這些註解收進產生的 HTML 文件(rustdoc 模式),而轉譯出的 SV 裡也會保留對應的註解——文件不會在轉譯的過程中消失。至於 let/var 的對應:var 轉成一般的 logic 訊號宣告,let 的單一賦值語意則由編譯器檢查後照常輸出,差異在編譯期的保證而非輸出的形態。

驗證與下一步#

$ veryl check

veryl check 做靜態檢查而不產生輸出,適合放進編輯器的儲存後鉤子與 CI。寫測試、跑模擬用 veryl test(後續章節專述);更新格式用 veryl fmt。到此你已經跑完 Veryl 的最小迴路:new → 編輯 → build → 檢查輸出。下一步,我們把這個迴路升級成有測試的迴路。

本章小結#

  • veryl new first_proj 產生 Veryl.toml + src/ 的最小專案,結構比照 cargo。
  • Counter 範例展示 param、clock/reset 型別、var/always_ff/if_reset/assign 的基本語法。
  • veryl build 轉譯出人類可讀的 SystemVerilog 到 target/,if_reset 依 Veryl.toml 的 reset_type 全域展開。
  • /// 文件註解會進入 veryl doc 輸出並保留在 SV 中。
  • 轉譯輸出可直接進任何 SystemVerilog 工具鏈——相容性零成本。