以 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 側的重頭戲:暫存器模組。