跳轉到

以 Interface 建構 Master 與 Slave#

Veryl 的 interface 對應 SystemVerilog 的 interface,但加上兩個工具級的強化:generics(可參數化)與 modport(明確的存取方向宣告)。這一節我們用 Veryl interface 完整定義 AXI4-Lite 五通道。

定義 AXI4-Lite interface#

package axi_pkg {
    // AXI4-Lite 資料寬度慣例:32 或 64,此處參數化
    pub const ADDR_W: u32 = 16;
    pub const DATA_W: u32 = 32;
}

interface Axi4Lite::<ADDR_W: u32, DATA_W: u32> {
    // AW: 寫位址通道
    var awaddr:  logic<ADDR_W>;
    var awvalid: logic;
    var awready: logic;
    // W: 寫資料通道
    var wdata:   logic<DATA_W>;
    var wstrb:   logic<DATA_W / 8>;
    var wvalid:  logic;
    var wready:  logic;
    // B: 寫回應通道
    var bresp:   logic<2>;
    var bvalid:  logic;
    var bready:  logic;
    // AR: 讀位址通道
    var araddr:  logic<ADDR_W>;
    var arvalid: logic;
    var arready: logic;
    // R: 讀資料通道
    var rdata:   logic<DATA_W>;
    var rresp:   logic<2>;
    var rvalid:  logic;
    var rready:  logic;

    modport master {
        awaddr: output, awvalid: output, awready: input,
        wdata: output, wstrb: output, wvalid: output, wready: input,
        bresp: input, bvalid: input, bready: output,
        araddr: output, arvalid: output, arready: input,
        rdata: input, rresp: input, rvalid: input, rready: output,
    }

    modport slave {
        awaddr: input, awvalid: input, awready: output,
        wdata: input, wstrb: input, wvalid: input, wready: output,
        bresp: output, bvalid: output, bready: input,
        araddr: input, arvalid: input, arready: output,
        rdata: output, rresp: output, rvalid: output, rready: input,
    }
}

幾個值得體會的設計點。第一,DATA_W / 8 直接在型別參數裡做運算——wstrb(write strobe,每一位元組一個 write enable)的位寬自動跟著資料寬度走,這是 generics 相對 parameter 巨集式展開的優雅之處。第二,modport 讓「誰驅動誰」變成編譯期檢查:master 企圖驅動 rdata 會直接編譯錯誤,不用等到模擬期才發現兩個 driver 打架。第三,interface 本身可 generics 化,一份定義服務 32 位與 64 位兩種 AXI4-Lite。

實例化與連接#

module Top (
    clk: input clock,
    rst: input reset,
) {
    inst axi: Axi4Lite::<16, 32>;

    // master 側(例如一個簡單的暫存器寫入器)
    inst cpu_bridge: CpuBridge (axi);

    // slave 側(下一節的暫存器模組)
    inst regs: Regs (axi);
}

inst axi: Axi4Lite::<16, 32>; 建立一個 interface 實例,然後以 inst u: Module (axi); 的形式把它交給 module 的 modport port。整條匯流排 seventeen 根訊號只用一個識別字牽線——這就是 interface 存在的理由:把「一群一起移動的訊號」封裝成一個概念單位。

master 側的最小骨架#

一個只會做單筆寫入的 master,狀態機只需要三態:

module CpuBridge (
    bus: modport Axi4Lite::<16, 32>::master,
) {
    enum St: logic<2> { Idle = 0, Write, WaitB }
    var st: St;

    always_comb {
        bus.awvalid = st == St::Write;
        bus.wvalid = st == St::Write;
        bus.bready = st == St::WaitB;
        bus.arvalid = 0;
        bus.rready = 1;
    }

    always_ff (clk, rst) {
        if_reset {
            st = St::Idle;
        } else {
            case st {
                St::Idle: if bus.awready && bus.wready { st = St::WaitB; }
                St::WaitB: if bus.bvalid { st = St::Idle; }
                default: {}
            }
        }
    }
}

(骨架略去了位址與資料的載入細節,讀者可自行補完。)下一節我們寫 slave 側的重頭戲:暫存器模組。