AXI4-Lite Slave 暫存器模組#
匯流排的另一端是 slave。這一節實作一個完整的 AXI4-Lite slave:位址解碼、讀寫 FSM、一組 memory-mapped 暫存器。這個模組幾乎可以原封不動地搬進任何 SoC 專案——它是 SoC 設計裡重複率最高的程式碼之一。
設計#
暫存器組包含:
CTRL(位址 0x00):控制暫存器,bit0 = enable,bit1 = opcode 選擇。STATUS(位址 0x04):唯讀狀態,bit0 = busy。DATA_A(0x08)、DATA_B(0x0C):運算元。RESULT(0x10):唯讀結果。SCRATCH(0x14):可讀寫的草稿暫存器,測試匯流排用。
寫入流程:master 先送 AW 通道(位址),再送 W 通道(資料),slave 兩者都收齊後寫入暫存器,最後在 B 通道回 OKAY。讀取流程:master 送 AR,slave 解碼位址、把暫存器值放上 R 通道,回 OKAY。
完整程式碼#
import axi_pkg::*;
module Regs (
clk: input clock,
rst: input reset,
bus: modport Axi4Lite::<16, 32>::slave,
) {
const ADDR_CTRL: u32 = 16'h0000;
const ADDR_STATUS: u32 = 16'h0004;
const ADDR_DATA_A: u32 = 16'h0008;
const ADDR_DATA_B: u32 = 16'h000C;
const ADDR_RESULT: u32 = 16'h0010;
const RESP_OKAY: logic<2> = 2'b00;
// 暫存器本體
var ctrl: logic<32>;
var data_a: logic<32>;
var data_b: logic<32>;
var result: logic<32>;
var busy: logic;
// 讀寫 FSM
enum St: logic<2> { Idle = 0, Write, Read, Resp }
var st: St;
var addr_q: logic<16>;
always_comb {
bus.awready = st == St::Idle;
bus.wready = st == St::Idle;
bus.arready = st == St::Idle;
bus.bvalid = st == St::Resp;
bus.rvalid = st == St::Resp;
bus.bresp = RESP_OKAY;
bus.rresp = RESP_OKAY;
}
// 位址解碼 + 寫入
always_ff (clk, rst) {
if_reset {
st = St::Idle;
addr_q = 0;
ctrl = 0;
data_a = 0;
data_b = 0;
} else {
case st {
St::Idle: {
if bus.awvalid && bus.wvalid {
addr_q = bus.awaddr;
if bus.wstrb[0] { ctrl[7:0] = bus.wdata[7:0]; }
case bus.awaddr {
ADDR_CTRL: ctrl = bus.wdata;
ADDR_DATA_A: data_a = bus.wdata;
ADDR_DATA_B: data_b = bus.wdata;
default: {}
}
st = St::Resp;
}
if bus.arvalid {
addr_q = bus.araddr;
st = St::Resp;
}
}
St::Resp: {
if bus.bvalid && bus.bready || bus.rvalid && bus.rready {
st = St::Idle;
}
}
default: {}
}
}
}
// 讀資料 mux
always_comb {
case addr_q {
ADDR_CTRL: bus.rdata = ctrl;
ADDR_STATUS: bus.rdata = 32'd0 | busy as logic<32>;
ADDR_DATA_A: bus.rdata = data_a;
ADDR_DATA_B: bus.rdata = data_b;
ADDR_RESULT: bus.rdata = result;
default: bus.rdata = 0;
}
}
}
(範例為可讀性做了取捨:wstrb 的逐 byte 處理、寫入優先於讀取等細節,讀者可依需求強化。)
CSR 設計慣例與文件制度#
CSR(control/status register)設計有一套工程界累積出來的慣例,值得寫進任何專案的第一頁文件:
- 位址表即規格:每個暫存器的位址、欄位、讀寫屬性(RW/RO/WO)、reset 值,寫成一張表格,放在 IP 的 README 或
doc/。動什麼位元、有什麼副作用,必須一行講清。 - 保留位恆為 0:軟體寫 1 到保留位不應炸掉硬體;硬體讀回保留位必為 0。
- W1C(write-1-to-clear)中斷暫存器、set/clear 對、shadow register 這些樣式,在 ARM 的文件與 Linux devicetree 綁定文件裡都有標準寫法。
- 大公司用內部語言(如 Excel 或自製 DSL)定義 CSR,再自動產生 RTL、軟體 header、文件——三者同源,永不失同步。這是「單一事實來源」思想在暫存器上的體現。開源世界對應的工具是 regtool 等暫存器碼產生器;Veryl 專案則常用 package 的
const位址定義搭配自動產生 markdown 文件。
暫存器是硬體與軟體的邊界。邊界處的紀律,決定的是整個團隊的速度。下一章,我們從接合暫存器,走向更宏大的題目:用 Veryl 打造一部 RISC-V 處理器。