参考
AHB-Lite 的信号定义
SMMR 接口为异步返回,主机需轮询状态标志完成交互。
AHB-Lite 基于同步时序实现读写,采用“地址/控制阶段 + 数据阶段”的传输机制。对于一个最简单的零等待 AHB-Lite 从设备,可以在一个有效传输周期内完成访问;
如果从设备无法立即完成访问,则通过 HREADY=0 插入等待周期。
s_ahb_hclk:AHB-Lite 总线时钟s_ahb_hresetn:AHB-Lite 总线复位,低电平有效s_ahb_haddr:AHB-Lite 访问地址s_ahb_htrans:AHB-Lite 传输类型2'b00:IDLE2'b01:BUSY2'b10:NONSEQ,新的传输开始2'b11:SEQ,连续传输
s_ahb_hwrite:AHB-Lite 读写控制信号1'b1:写操作1'b0:读操作
s_ahb_hsize:传输数据大小s_ahb_hburst:突发类型s_ahb_hprot:访问保护属性s_ahb_hwdata:AHB-Lite 写数据s_ahb_hrdata:AHB-Lite 读数据s_ahb_hready:传输完成/从设备就绪信号s_ahb_hresp:传输响应信号1'b0:OKAY1'b1:ERROR
AHB-Lite 传输机制
AHB-Lite 没有 APB 的 SETUP/ACCESS 两阶段。
主机首先给出地址和控制信号,然后进入数据阶段。
HREADY=1表示当前传输完成。
HREADY=0表示从设备暂时无法完成传输,需要插入等待周期。读操作时从设备通过
HRDATA返回数据。写操作时主设备通过
HWDATA提供写入数据。
# AHB-Lite 传输阶段
地址/控制阶段 数据阶段
时钟 T1 T2
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ 地址 + 控制 │ │ 数据阶段 │
│ │ │ │
HADDR ────┤ 地址 │─────────┤ 保持/传输 │
HWRITE ────┤ 读写控制 │ │ │
HTRANS ────┤ NONSEQ/SEQ │ │ │
└───────────────┘ │ │
│ 写:HWDATA │
│ 读:HRDATA │
│ │
│ HREADY │
└───────┬───────┘
│
▼
HREADY = 1
│
▼
完成
# 写事务
地址/控制阶段 数据阶段
│ │
▼ ▼
HADDR ======= 地址 ========================
HWRITE ======= 1 ==========================
HTRANS ======= NONSEQ =====================
HWDATA = 写数据
============
HREADY = 1
============
# 读事务
地址/控制阶段 数据阶段
│ │
▼ ▼
HADDR ======= 地址 ========================
HWRITE ======= 0 ==========================
HTRANS ======= NONSEQ =====================
HRDATA = 读数据
============
HREADY = 1
============
# HTRANS 传输类型
HTRANS = 2'b00
│
└── IDLE
当前没有传输
HTRANS = 2'b01
│
└── BUSY
当前主机暂时无法继续传输
HTRANS = 2'b10
│
└── NONSEQ
一次新的传输开始
HTRANS = 2'b11
│
└── SEQ
连续传输
AHB-Lite 写事务 - 主机发起方(Master)
// ==============================================
// AHB-Lite 写事务 - 主机发起方
// ==============================================
// 1️⃣ 地址/控制阶段
// 输出目标地址和控制信息
s_ahb_haddr <= write_addr; // 📍 目标写地址
s_ahb_hwrite <= 1'b1; // ✏️ 写操作
s_ahb_htrans <= 2'b10; // 🚀 NONSEQ,新传输
wait (posedge s_ahb_hclk);
// 2️⃣ 数据阶段
// 提供需要写入的数据
s_ahb_hwdata <= write_data; // 📝 待写入数据
wait (s_ahb_hready); // ⏳ 等待从设备完成
// 3️⃣ 检查响应
write_error = s_ahb_hresp; // 📋 获取错误状态
// 4️⃣ 结束事务
s_ahb_htrans <= 2'b00; // 🔚 返回 IDLE
// 💡 AHB-Lite 写事务完成
AHB-Lite 写事务 - 从机响应方(Slave)
// ==============================================
// AHB-Lite 写事务 - 从机响应方
// ==============================================
// 1️⃣ 检测有效传输
if (s_ahb_htrans[1]) begin
if (s_ahb_hwrite) begin
wr_addr = s_ahb_haddr; // 📍 获取写地址
end
end
// 2️⃣ 数据阶段
// 下一周期 HWDATA 有效
wait (posedge s_ahb_hclk);
// 3️⃣ 执行写操作
if (wr_valid) begin
reg_file[wr_addr] <= s_ahb_hwdata; // 💾 写入寄存器
end
// 4️⃣ 返回响应
s_ahb_hready <= 1'b1; // ✅ 传输完成
s_ahb_hresp <= 1'b0; // ✔️ OKAY
// 💡 AHB-Lite 写事务结束
AHB-Lite 读事务 - 主机发起方(Master)
// ==============================================
// AHB-Lite 读事务 - 主机发起方
// ==============================================
// 1️⃣ 地址/控制阶段
// 输出目标地址和控制信息
s_ahb_haddr <= read_addr; // 📍 目标读地址
s_ahb_hwrite <= 1'b0; // 📖 读操作
s_ahb_htrans <= 2'b10; // 🚀 NONSEQ,新传输
wait (posedge s_ahb_hclk);
// 2️⃣ 等待从设备完成
wait (s_ahb_hready);
// 3️⃣ 获取读数据
read_data = s_ahb_hrdata; // 📥 接收读数据
read_error = s_ahb_hresp; // 📋 获取错误状态
// 4️⃣ 结束事务
s_ahb_htrans <= 2'b00; // 🔚 返回 IDLE
// 💡 AHB-Lite 读事务完成
AHB-Lite 读事务 - 从机响应方(Slave)
// ==============================================
// AHB-Lite 读事务 - 从机响应方
// ==============================================
// 1️⃣ 检测有效传输
if (s_ahb_htrans[1]) begin
if (!s_ahb_hwrite) begin
rd_addr = s_ahb_haddr; // 📍 获取读地址
end
end
// 2️⃣ 准备读数据
s_ahb_hrdata <= reg_file[rd_addr]; // 📤 返回寄存器数据
// 3️⃣ 返回响应
s_ahb_hready <= 1'b1; // ✅ 数据有效
s_ahb_hresp <= 1'b0; // ✔️ OKAY
// 💡 AHB-Lite 读事务结束
ahblite_slave_smmr_bridge.v
ahblite_slave转成smmr
// =============================================================================
// 模块名称 : ahblite_slave_smmr_bridge
// 功能说明 : AHB-Lite Slave 接口到 SMMR(Simple Memory Mapped Register)接口的桥接模块
//
// 设计要点 :
// 1. AHB-Lite 总线是地址相位/数据相位流水结构。
// - 写传输时,HADDR/HWRITE/HTRANS 在地址相位给出;
// - 对应的 HWDATA 在下一拍的数据相位给出。
// 2. 本模块把 AHB 字节地址转换成 slot0 使用的寄存器编号。
// - AHB 侧访问地址 : 0x0, 0x4, 0x8, 0xC
// - SMMR 侧寄存器地址 : 0, 1, 2, 3
// 3. 本模块为零等待 AHB-Lite Slave。
// - HREADYOUT 固定为 1;
// - HRESP 固定为 OKAY;
// - 每个有效 NONSEQ/SEQ beat 都会转换成一次 SMMR 读或写。
// 4. 支持 AHB-Lite burst 访问。
// - 模块不主动解析 HBURST 计数;
// - AHB master 在 burst 中逐拍给出递增后的 HADDR;
// - 本模块对每个有效 beat 独立处理,因此 INCR4 写/读 4 个寄存器可以连续完成。
//
// 典型连接 :
// o_reg_wr_en -> slot0.i_reg_wr_en
// o_reg_wr_addr -> slot0.i_reg_wr_addr
// o_reg_wr_data -> slot0.i_reg_wr_data
// o_reg_rd_addr -> slot0.i_reg_rd_addr
// i_reg_rd_data <- slot0.o_reg_rd_data
// =============================================================================
module ahblite_slave_smmr_bridge #(
parameter P_S_AHB_ADDR_WIDTH = 32, // AHB-Lite 地址总线宽度,单位 bit
parameter P_S_AHB_DATA_WIDTH = 32, // AHB-Lite 数据总线宽度,当前按 32-bit word 访问设计
parameter P_S_MEM_SIZE = 16, // 当前映射空间大小,单位 byte;16 byte 对应 4 个 32-bit 寄存器
parameter P_REG_ADDR_WIDTH = 7 // SMMR 寄存器地址宽度,需要和 slot0.P_ADDR_WIDTH 保持一致
)(
// =========================
// AHB-Lite Slave 接口,master -> slave
// =========================
input wire s_ahb_hclk, // AHB-Lite 时钟,所有时序逻辑在该时钟上升沿工作
input wire s_ahb_hresetn, // AHB-Lite 异步低有效复位
input wire s_ahb_hsel, // Slave 片选,高有效;为 1 表示当前地址命中本模块
input wire [P_S_AHB_ADDR_WIDTH-1:0] s_ahb_haddr, // 地址相位地址,AHB 侧为 byte address
input wire [1:0] s_ahb_htrans, // 传输类型:00=IDLE,01=BUSY,10=NONSEQ,11=SEQ
input wire s_ahb_hwrite, // 传输方向:1=写,0=读;在地址相位有效
input wire [2:0] s_ahb_hsize, // 传输大小;当前桥按 32-bit word 使用,通常应为 3'b010
input wire [2:0] s_ahb_hburst, // 突发类型;本模块不计数,只按每个有效 beat 处理
input wire [P_S_AHB_DATA_WIDTH-1:0] s_ahb_hwdata, // 写数据;对应上一拍地址相位的写传输
input wire s_ahb_hready, // 上一级/总线全局 ready;为 1 时地址相位和数据相位才能推进
output wire s_ahb_hreadyout, // 本 slave ready 输出;本模块零等待,固定拉高
output reg [P_S_AHB_DATA_WIDTH-1:0] s_ahb_hrdata, // 读数据;在读传输的数据相位返回给 master
output wire s_ahb_hresp, // 传输响应;0=OKAY,1=ERROR,本模块固定 OKAY
// =========================
// SMMR 简单寄存器接口
// =========================
output reg o_reg_wr_en, // 寄存器写使能,高有效;写数据相位产生 1 拍脉冲
output reg [P_REG_ADDR_WIDTH-1:0] o_reg_wr_addr, // 寄存器写地址;由上一拍 HADDR[...:2] 转换得到
output reg [P_S_AHB_DATA_WIDTH-1:0] o_reg_wr_data, // 寄存器写数据;直接来自当前数据相位 HWDATA
output reg [P_REG_ADDR_WIDTH-1:0] o_reg_rd_addr, // 寄存器读地址;读地址相位锁存后送给下游
input wire [P_S_AHB_DATA_WIDTH-1:0] i_reg_rd_data // 寄存器读数据;由 slot0 等下游寄存器模块返回
);
// 32-bit word 访问时,AHB byte address 的 bit[1:0] 是字节偏移。
// slot0 的地址是寄存器编号,因此需要右移 2 bit:
// HADDR 0x00 -> reg addr 0
// HADDR 0x04 -> reg addr 1
// HADDR 0x08 -> reg addr 2
// HADDR 0x0c -> reg addr 3
localparam C_REG_ADDR_LSB = 2;
// AHB-Lite 的写数据/读数据都发生在地址相位的下一拍。
// 因此需要把上一拍地址相位中的“是否有效、读写方向、寄存器地址”锁存下来,
// 到当前数据相位使用。
reg r_data_phase_valid; // 当前数据相位是否对应一个有效传输
reg r_data_phase_write; // 当前数据相位对应的传输方向:1=写,0=读
reg [P_REG_ADDR_WIDTH-1:0] r_data_phase_addr; // 当前数据相位对应的 SMMR 寄存器地址
// 地址相位有效判断:
// s_ahb_hready 为 1,表示流水线允许推进;
// s_ahb_hsel 为 1,表示本 slave 被选中;
// HTRANS[1] 为 1,表示 NONSEQ 或 SEQ,有真实传输。
// IDLE/BUSY 不会产生寄存器访问。
wire w_addr_phase_valid = s_ahb_hready && s_ahb_hsel && s_ahb_htrans[1];
// AHB byte address -> SMMR register index。
// 对 slot0 的 4 个寄存器,master 应访问 0x0/0x4/0x8/0xC,
// 这里转换后分别得到 0/1/2/3。
wire [P_REG_ADDR_WIDTH-1:0] w_addr_phase_reg_addr =
s_ahb_haddr[P_REG_ADDR_WIDTH+C_REG_ADDR_LSB-1:C_REG_ADDR_LSB];
// 零等待响应:
// HREADYOUT 固定为 1,表示本 slave 不插入 wait state;
// HRESP 固定为 0,表示所有访问均返回 OKAY。
// 地址越界的过滤由 slot0 的地址范围判断完成,越界读返回 0,越界写不生效。
assign s_ahb_hreadyout = 1'b1;
assign s_ahb_hresp = 1'b0;
// 写通路:
// AHB 写数据在数据相位出现,也就是地址相位后一拍。
// r_data_phase_* 保存的正是上一拍地址相位信息。
//
// 这里组合产生 o_reg_wr_en,是为了让 slot0 这种 posedge 写寄存器的模块,
// 能在当前 HWDATA 数据相位的同一个时钟上升沿采样到写使能、写地址和写数据。
//
// burst 写时,每个有效 beat 都会形成一次写脉冲:
// addr phase : 0x0 0x4 0x8 0xC
// data phase : D0 D1 D2 D3
// reg write : R0 R1 R2 R3
always @(*) begin
o_reg_wr_en = s_ahb_hresetn && s_ahb_hready && r_data_phase_valid && r_data_phase_write;
o_reg_wr_addr = r_data_phase_addr;
o_reg_wr_data = s_ahb_hwdata;
end
// 读数据返回通路:
// 读地址在地址相位锁存到 o_reg_rd_addr;
// slot0 的读数据是组合逻辑输出;
// 下一拍进入读数据相位时,直接把 i_reg_rd_data 返回到 HRDATA。
//
// 这样 INCR4 burst 读时,可以每拍返回一个寄存器值,不需要插入等待周期。
always @(*) begin
if (s_ahb_hresetn && r_data_phase_valid && !r_data_phase_write) begin
s_ahb_hrdata = i_reg_rd_data;
end else begin
s_ahb_hrdata = {P_S_AHB_DATA_WIDTH{1'b0}};
end
end
always @(posedge s_ahb_hclk or negedge s_ahb_hresetn) begin
if (!s_ahb_hresetn) begin
r_data_phase_valid <= 1'b0;
r_data_phase_write <= 1'b0;
r_data_phase_addr <= {P_REG_ADDR_WIDTH{1'b0}};
o_reg_rd_addr <= {P_REG_ADDR_WIDTH{1'b0}};
end else begin
if (s_ahb_hready) begin
// 把当前地址相位信息锁存到下一拍的数据相位。
// 连续 burst 中,NONSEQ 和 SEQ 都会被锁存,因此每拍都能处理一个 beat。
r_data_phase_valid <= w_addr_phase_valid;
r_data_phase_write <= s_ahb_hwrite;
r_data_phase_addr <= w_addr_phase_reg_addr;
// 读传输需要提前把读地址送到下游寄存器模块。
// 到下一拍数据相位时,i_reg_rd_data 已经对应这个地址。
if (w_addr_phase_valid && !s_ahb_hwrite) begin
o_reg_rd_addr <= w_addr_phase_reg_addr;
end
end
end
end
endmodule
slot0.v
module slot0 #(
parameter P_ADDR_WIDTH = 7,
parameter P_DATA_WIDTH = 32,
parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 0,
parameter P_REG_COUNT = 4
)(
input wire i_clk,
input wire i_rst_n,
input wire i_reg_wr_en,
input wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr,
input wire [P_DATA_WIDTH-1:0] i_reg_wr_data,
input wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr,
output reg [P_DATA_WIDTH-1:0] o_reg_rd_data
);
reg [P_DATA_WIDTH-1:0] r_mem [0:P_REG_COUNT-1];
//写
integer i;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
for (i = 0; i < P_REG_COUNT; i = i + 1) begin
r_mem[i] <= {P_DATA_WIDTH{1'b0}};
end
end else if (i_reg_wr_en && (i_reg_wr_addr >= P_SLOT_BASE) && (i_reg_wr_addr < (P_SLOT_BASE + P_REG_COUNT))) begin
r_mem[i_reg_wr_addr-P_SLOT_BASE] <= i_reg_wr_data;
end
end
//读
always @(*) begin
if ((i_reg_rd_addr >= P_SLOT_BASE) && (i_reg_rd_addr < (P_SLOT_BASE + P_REG_COUNT))) begin
o_reg_rd_data = r_mem[i_reg_rd_addr-P_SLOT_BASE];
end else begin
o_reg_rd_data = {P_DATA_WIDTH{1'b0}};
end
end
endmodule
tb_ahblite_slave_smmr_bridge.v
读写 读写 slot.v 的4个寄存器
`timescale 1ns/1ps
module tb;
localparam HTRANS_IDLE = 2'b00;
localparam HTRANS_NONSEQ = 2'b10;
localparam HTRANS_SEQ = 2'b11;
localparam HBURST_SINGLE = 3'b000;
localparam HBURST_INCR4 = 3'b011;
localparam HSIZE_WORD = 3'b010;
reg hclk;
reg hresetn;
reg hsel;
reg [31:0] haddr;
reg [1:0] htrans;
reg hwrite;
reg [2:0] hsize;
reg [2:0] hburst;
reg [31:0] hwdata;
wire hreadyout;
wire [31:0] hrdata;
wire hresp;
wire reg_wr_en;
wire [6:0] reg_wr_addr;
wire [31:0] reg_wr_data;
wire [6:0] reg_rd_addr;
wire [31:0] reg_rd_data;
ahblite_slave_smmr_bridge #(
.P_S_AHB_ADDR_WIDTH(32),
.P_S_AHB_DATA_WIDTH(32),
.P_S_MEM_SIZE(16),
.P_REG_ADDR_WIDTH(7)
) u_bridge (
.s_ahb_hclk (hclk),
.s_ahb_hresetn (hresetn),
.s_ahb_hsel (hsel),
.s_ahb_haddr (haddr),
.s_ahb_htrans (htrans),
.s_ahb_hwrite (hwrite),
.s_ahb_hsize (hsize),
.s_ahb_hburst (hburst),
.s_ahb_hwdata (hwdata),
.s_ahb_hready (hreadyout),
.s_ahb_hreadyout(hreadyout),
.s_ahb_hrdata (hrdata),
.s_ahb_hresp (hresp),
.o_reg_wr_en (reg_wr_en),
.o_reg_wr_addr (reg_wr_addr),
.o_reg_wr_data (reg_wr_data),
.o_reg_rd_addr (reg_rd_addr),
.i_reg_rd_data (reg_rd_data)
);
slot0 #(
.P_ADDR_WIDTH(7),
.P_DATA_WIDTH(32),
.P_SLOT_BASE(0),
.P_REG_COUNT(4)
) u_slot0 (
.i_clk (hclk),
.i_rst_n (hresetn),
.i_reg_wr_en (reg_wr_en),
.i_reg_wr_addr(reg_wr_addr),
.i_reg_wr_data(reg_wr_data),
.i_reg_rd_addr(reg_rd_addr),
.o_reg_rd_data(reg_rd_data)
);
always #5 hclk = ~hclk;
task ahb_idle;
begin
@(negedge hclk);
hsel = 1'b0;
haddr = 32'h0;
htrans = HTRANS_IDLE;
hwrite = 1'b0;
hsize = HSIZE_WORD;
hburst = HBURST_SINGLE;
hwdata = 32'h0;
end
endtask
task check_word;
input [255:0] name;
input [31:0] got;
input [31:0] exp;
begin
if (got !== exp) begin
$display("FAIL %0s got=%08x exp=%08x", name, got, exp);
$finish;
end
end
endtask
task ahb_write_single;
input [31:0] addr;
input [31:0] data;
begin
@(negedge hclk);
hsel = 1'b1;
haddr = addr;
htrans = HTRANS_NONSEQ;
hwrite = 1'b1;
hsize = HSIZE_WORD;
hburst = HBURST_SINGLE;
@(negedge hclk);
hsel = 1'b0;
htrans = HTRANS_IDLE;
hwrite = 1'b0;
hwdata = data;
@(negedge hclk);
hwdata = 32'h0;
end
endtask
task ahb_read_single;
input [31:0] addr;
output [31:0] data;
begin
@(negedge hclk);
hsel = 1'b1;
haddr = addr;
htrans = HTRANS_NONSEQ;
hwrite = 1'b0;
hsize = HSIZE_WORD;
hburst = HBURST_SINGLE;
@(negedge hclk);
hsel = 1'b0;
htrans = HTRANS_IDLE;
data = hrdata;
end
endtask
task ahb_burst_write4;
input [31:0] base_addr;
input [31:0] data0;
input [31:0] data1;
input [31:0] data2;
input [31:0] data3;
begin
@(negedge hclk);
hsel = 1'b1;
haddr = base_addr;
htrans = HTRANS_NONSEQ;
hwrite = 1'b1;
hsize = HSIZE_WORD;
hburst = HBURST_INCR4;
@(negedge hclk);
haddr = base_addr + 32'd4;
htrans = HTRANS_SEQ;
hwdata = data0;
@(negedge hclk);
haddr = base_addr + 32'd8;
htrans = HTRANS_SEQ;
hwdata = data1;
@(negedge hclk);
haddr = base_addr + 32'd12;
htrans = HTRANS_SEQ;
hwdata = data2;
@(negedge hclk);
hsel = 1'b0;
htrans = HTRANS_IDLE;
hwrite = 1'b0;
hburst = HBURST_SINGLE;
hwdata = data3;
@(negedge hclk);
hwdata = 32'h0;
end
endtask
task ahb_burst_read4;
input [31:0] base_addr;
output [31:0] data0;
output [31:0] data1;
output [31:0] data2;
output [31:0] data3;
begin
@(negedge hclk);
hsel = 1'b1;
haddr = base_addr;
htrans = HTRANS_NONSEQ;
hwrite = 1'b0;
hsize = HSIZE_WORD;
hburst = HBURST_INCR4;
@(negedge hclk);
haddr = base_addr + 32'd4;
htrans = HTRANS_SEQ;
data0 = hrdata;
@(negedge hclk);
haddr = base_addr + 32'd8;
htrans = HTRANS_SEQ;
data1 = hrdata;
@(negedge hclk);
haddr = base_addr + 32'd12;
htrans = HTRANS_SEQ;
data2 = hrdata;
@(negedge hclk);
hsel = 1'b0;
htrans = HTRANS_IDLE;
hburst = HBURST_SINGLE;
data3 = hrdata;
end
endtask
reg [31:0] rd0;
reg [31:0] rd1;
reg [31:0] rd2;
reg [31:0] rd3;
initial begin
hclk = 1'b0;
hresetn = 1'b0;
hsel = 1'b0;
haddr = 32'h0;
htrans = HTRANS_IDLE;
hwrite = 1'b0;
hsize = HSIZE_WORD;
hburst = HBURST_SINGLE;
hwdata = 32'h0;
repeat (4) @(negedge hclk);
hresetn = 1'b1;
ahb_write_single(32'h0000_0000, 32'h1122_3344);
ahb_read_single(32'h0000_0000, rd0);
check_word("single_read", rd0, 32'h1122_3344);
ahb_burst_write4(32'h0000_0000,
32'h0000_0001,
32'h0000_0002,
32'h0000_0003,
32'h0000_0004);
ahb_burst_read4(32'h0000_0000, rd0, rd1, rd2, rd3);
$display("burst read back: %0d %0d %0d %0d", rd0, rd1, rd2, rd3);
check_word("burst_read0", rd0, 32'h0000_0001);
check_word("burst_read1", rd1, 32'h0000_0002);
check_word("burst_read2", rd2, 32'h0000_0003);
check_word("burst_read3", rd3, 32'h0000_0004);
$display("PASS ahblite_slave_smmr_bridge single and INCR4 burst access");
$finish;
end
endmodule
ahblite_master.v
// =============================================================================
// 模块名称 : ahblite_master
// 功能说明 : AHB-Lite master 自测试模块
//
// 测试流程 :
// 1. 检测 i_start 上升沿;
// 2. 发起一次 INCR4 写突发,向地址 0x0/0x4/0x8/0xC 写入 1/2/3/4;
// 3. 发起一次 INCR4 读突发,从地址 0x0/0x4/0x8/0xC 读回 4 个数据;
// 4. 比较读回数据:
// - 全部一致:o_done=1,o_led=1,表示 PASS;
// - 任意错误:o_done=1,o_led=0,表示 FAIL。
//
// 说明 :
// AHB-Lite 写数据 HWDATA 比写地址 HADDR 晚一个数据相位。
// 因此写突发时,本模块会先发第 0 个地址,然后下一拍开始给第 0 个写数据。
// =============================================================================
module ahblite_master #(
parameter P_M_AHB_ADDR_WIDTH = 32,
parameter P_M_AHB_DATA_WIDTH = 32,
parameter [P_M_AHB_ADDR_WIDTH-1:0] P_TEST_BASE_ADDR = {P_M_AHB_ADDR_WIDTH{1'b0}}
)(
input wire i_clk,
input wire i_rst_n,
input wire i_start,
output reg o_done,
output reg o_led,
// =========================
// AHB-Lite Master interface
// =========================
output reg m_ahb_hsel,
output reg [P_M_AHB_ADDR_WIDTH-1:0] m_ahb_haddr,
output reg [1:0] m_ahb_htrans,
output reg m_ahb_hwrite,
output reg [2:0] m_ahb_hsize,
output reg [2:0] m_ahb_hburst,
output reg [P_M_AHB_DATA_WIDTH-1:0] m_ahb_hwdata,
input wire m_ahb_hready,
input wire [P_M_AHB_DATA_WIDTH-1:0] m_ahb_hrdata,
input wire m_ahb_hresp
);
localparam [1:0] C_HTRANS_IDLE = 2'b00;
localparam [1:0] C_HTRANS_NONSEQ = 2'b10;
localparam [1:0] C_HTRANS_SEQ = 2'b11;
localparam [2:0] C_HBURST_SINGLE = 3'b000;
localparam [2:0] C_HBURST_INCR4 = 3'b011;
localparam [2:0] C_HSIZE_WORD = 3'b010;
localparam [P_M_AHB_DATA_WIDTH-1:0] C_TEST_DATA0 = 32'd1;
localparam [P_M_AHB_DATA_WIDTH-1:0] C_TEST_DATA1 = 32'd2;
localparam [P_M_AHB_DATA_WIDTH-1:0] C_TEST_DATA2 = 32'd3;
localparam [P_M_AHB_DATA_WIDTH-1:0] C_TEST_DATA3 = 32'd4;
localparam [3:0] S_IDLE = 4'd0;
localparam [3:0] S_WR_A0 = 4'd1;
localparam [3:0] S_WR_A1_D0 = 4'd2;
localparam [3:0] S_WR_A2_D1 = 4'd3;
localparam [3:0] S_WR_A3_D2 = 4'd4;
localparam [3:0] S_WR_IDLE_D3 = 4'd5;
localparam [3:0] S_RD_A0 = 4'd6;
localparam [3:0] S_RD_A1_D0 = 4'd7;
localparam [3:0] S_RD_A2_D1 = 4'd8;
localparam [3:0] S_RD_A3_D2 = 4'd9;
localparam [3:0] S_RD_IDLE_D3 = 4'd10;
reg [3:0] r_state;
reg r_start_d;
reg r_fail;
wire w_start_posedge = i_start && !r_start_d;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_state <= S_IDLE;
r_start_d <= 1'b0;
r_fail <= 1'b0;
o_done <= 1'b0;
o_led <= 1'b0;
m_ahb_hsel <= 1'b0;
m_ahb_haddr <= {P_M_AHB_ADDR_WIDTH{1'b0}};
m_ahb_htrans <= C_HTRANS_IDLE;
m_ahb_hwrite <= 1'b0;
m_ahb_hsize <= C_HSIZE_WORD;
m_ahb_hburst <= C_HBURST_SINGLE;
m_ahb_hwdata <= {P_M_AHB_DATA_WIDTH{1'b0}};
end else begin
r_start_d <= i_start;
case (r_state)
S_IDLE: begin
m_ahb_hsel <= 1'b0;
m_ahb_haddr <= P_TEST_BASE_ADDR;
m_ahb_htrans <= C_HTRANS_IDLE;
m_ahb_hwrite <= 1'b0;
m_ahb_hsize <= C_HSIZE_WORD;
m_ahb_hburst <= C_HBURST_SINGLE;
m_ahb_hwdata <= {P_M_AHB_DATA_WIDTH{1'b0}};
if (w_start_posedge) begin
r_fail <= 1'b0;
o_done <= 1'b0;
o_led <= 1'b0;
m_ahb_hsel <= 1'b1;
m_ahb_haddr <= P_TEST_BASE_ADDR;
m_ahb_htrans <= C_HTRANS_NONSEQ;
m_ahb_hwrite <= 1'b1;
m_ahb_hburst <= C_HBURST_INCR4;
r_state <= S_WR_A0;
end
end
// 当前周期:写突发第 0 个地址相位,尚无有效 HWDATA。
S_WR_A0: begin
if (m_ahb_hready) begin
m_ahb_hsel <= 1'b1;
m_ahb_haddr <= P_TEST_BASE_ADDR + 32'd4;
m_ahb_htrans <= C_HTRANS_SEQ;
m_ahb_hwrite <= 1'b1;
m_ahb_hburst <= C_HBURST_INCR4;
m_ahb_hwdata <= C_TEST_DATA0;
r_state <= S_WR_A1_D0;
end
end
// 当前周期:写地址 0x4,同时给上一拍地址 0x0 的写数据 1。
S_WR_A1_D0: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp;
m_ahb_haddr <= P_TEST_BASE_ADDR + 32'd8;
m_ahb_htrans <= C_HTRANS_SEQ;
m_ahb_hwdata <= C_TEST_DATA1;
r_state <= S_WR_A2_D1;
end
end
// 当前周期:写地址 0x8,同时给上一拍地址 0x4 的写数据 2。
S_WR_A2_D1: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp;
m_ahb_haddr <= P_TEST_BASE_ADDR + 32'd12;
m_ahb_htrans <= C_HTRANS_SEQ;
m_ahb_hwdata <= C_TEST_DATA2;
r_state <= S_WR_A3_D2;
end
end
// 当前周期:写地址 0xC,同时给上一拍地址 0x8 的写数据 3。
S_WR_A3_D2: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp;
m_ahb_hsel <= 1'b0;
m_ahb_htrans <= C_HTRANS_IDLE;
m_ahb_hwrite <= 1'b0;
m_ahb_hburst <= C_HBURST_SINGLE;
m_ahb_hwdata <= C_TEST_DATA3;
r_state <= S_WR_IDLE_D3;
end
end
// 当前周期:总线地址相位空闲,同时完成上一拍地址 0xC 的写数据 4。
S_WR_IDLE_D3: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp;
m_ahb_hsel <= 1'b1;
m_ahb_haddr <= P_TEST_BASE_ADDR;
m_ahb_htrans <= C_HTRANS_NONSEQ;
m_ahb_hwrite <= 1'b0;
m_ahb_hburst <= C_HBURST_INCR4;
m_ahb_hwdata <= {P_M_AHB_DATA_WIDTH{1'b0}};
r_state <= S_RD_A0;
end
end
// 当前周期:读突发第 0 个地址相位,尚无有效 HRDATA。
S_RD_A0: begin
if (m_ahb_hready) begin
m_ahb_haddr <= P_TEST_BASE_ADDR + 32'd4;
m_ahb_htrans <= C_HTRANS_SEQ;
r_state <= S_RD_A1_D0;
end
end
// 当前周期:读地址 0x4,同时采样上一拍地址 0x0 的读数据。
S_RD_A1_D0: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp | (m_ahb_hrdata != C_TEST_DATA0);
m_ahb_haddr <= P_TEST_BASE_ADDR + 32'd8;
m_ahb_htrans <= C_HTRANS_SEQ;
r_state <= S_RD_A2_D1;
end
end
// 当前周期:读地址 0x8,同时采样上一拍地址 0x4 的读数据。
S_RD_A2_D1: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp | (m_ahb_hrdata != C_TEST_DATA1);
m_ahb_haddr <= P_TEST_BASE_ADDR + 32'd12;
m_ahb_htrans <= C_HTRANS_SEQ;
r_state <= S_RD_A3_D2;
end
end
// 当前周期:读地址 0xC,同时采样上一拍地址 0x8 的读数据。
S_RD_A3_D2: begin
if (m_ahb_hready) begin
r_fail <= r_fail | m_ahb_hresp | (m_ahb_hrdata != C_TEST_DATA2);
m_ahb_hsel <= 1'b0;
m_ahb_htrans <= C_HTRANS_IDLE;
m_ahb_hburst <= C_HBURST_SINGLE;
r_state <= S_RD_IDLE_D3;
end
end
// 当前周期:总线地址相位空闲,同时采样上一拍地址 0xC 的读数据。
S_RD_IDLE_D3: begin
if (m_ahb_hready) begin
o_done <= 1'b1;
o_led <= !(r_fail | m_ahb_hresp | (m_ahb_hrdata != C_TEST_DATA3));
m_ahb_hsel <= 1'b0;
m_ahb_haddr <= P_TEST_BASE_ADDR;
m_ahb_htrans <= C_HTRANS_IDLE;
m_ahb_hwrite <= 1'b0;
m_ahb_hburst <= C_HBURST_SINGLE;
m_ahb_hwdata <= {P_M_AHB_DATA_WIDTH{1'b0}};
r_state <= S_IDLE;
end
end
default: begin
r_state <= S_IDLE;
r_fail <= 1'b1;
o_done <= 1'b1;
o_led <= 1'b0;
m_ahb_hsel <= 1'b0;
m_ahb_haddr <= P_TEST_BASE_ADDR;
m_ahb_htrans <= C_HTRANS_IDLE;
m_ahb_hwrite <= 1'b0;
m_ahb_hsize <= C_HSIZE_WORD;
m_ahb_hburst <= C_HBURST_SINGLE;
m_ahb_hwdata <= {P_M_AHB_DATA_WIDTH{1'b0}};
end
endcase
end
end
endmodule
tb_ahblite_master.v
同样是读写 slot0.v的4个寄存器
`timescale 1ns/1ps
module tb;
reg hclk;
reg hresetn;
reg start;
wire done;
wire led;
wire hsel;
wire [31:0] haddr;
wire [1:0] htrans;
wire hwrite;
wire [2:0] hsize;
wire [2:0] hburst;
wire [31:0] hwdata;
wire hready;
wire [31:0] hrdata;
wire hresp;
wire reg_wr_en;
wire [6:0] reg_wr_addr;
wire [31:0] reg_wr_data;
wire [6:0] reg_rd_addr;
wire [31:0] reg_rd_data;
ahblite_master #(
.P_M_AHB_ADDR_WIDTH(32),
.P_M_AHB_DATA_WIDTH(32),
.P_TEST_BASE_ADDR(32'h0000_0000)
) u_master (
.i_clk (hclk),
.i_rst_n (hresetn),
.i_start (start),
.o_done (done),
.o_led (led),
.m_ahb_hsel (hsel),
.m_ahb_haddr (haddr),
.m_ahb_htrans(htrans),
.m_ahb_hwrite(hwrite),
.m_ahb_hsize (hsize),
.m_ahb_hburst(hburst),
.m_ahb_hwdata(hwdata),
.m_ahb_hready(hready),
.m_ahb_hrdata(hrdata),
.m_ahb_hresp (hresp)
);
ahblite_slave_smmr_bridge #(
.P_S_AHB_ADDR_WIDTH(32),
.P_S_AHB_DATA_WIDTH(32),
.P_S_MEM_SIZE(16),
.P_REG_ADDR_WIDTH(7)
) u_bridge (
.s_ahb_hclk (hclk),
.s_ahb_hresetn (hresetn),
.s_ahb_hsel (hsel),
.s_ahb_haddr (haddr),
.s_ahb_htrans (htrans),
.s_ahb_hwrite (hwrite),
.s_ahb_hsize (hsize),
.s_ahb_hburst (hburst),
.s_ahb_hwdata (hwdata),
.s_ahb_hready (hready),
.s_ahb_hreadyout(hready),
.s_ahb_hrdata (hrdata),
.s_ahb_hresp (hresp),
.o_reg_wr_en (reg_wr_en),
.o_reg_wr_addr (reg_wr_addr),
.o_reg_wr_data (reg_wr_data),
.o_reg_rd_addr (reg_rd_addr),
.i_reg_rd_data (reg_rd_data)
);
slot0 #(
.P_ADDR_WIDTH(7),
.P_DATA_WIDTH(32),
.P_SLOT_BASE(0),
.P_REG_COUNT(4)
) u_slot0 (
.i_clk (hclk),
.i_rst_n (hresetn),
.i_reg_wr_en (reg_wr_en),
.i_reg_wr_addr(reg_wr_addr),
.i_reg_wr_data(reg_wr_data),
.i_reg_rd_addr(reg_rd_addr),
.o_reg_rd_data(reg_rd_data)
);
always #5 hclk = ~hclk;
integer timeout_cnt;
initial begin
hclk = 1'b0;
hresetn = 1'b0;
start = 1'b0;
repeat (5) @(negedge hclk);
hresetn = 1'b1;
repeat (2) @(negedge hclk);
start = 1'b1;
@(negedge hclk);
start = 1'b0;
timeout_cnt = 0;
while (!done && timeout_cnt < 100) begin
@(negedge hclk);
timeout_cnt = timeout_cnt + 1;
end
if (!done) begin
$display("FAIL timeout, master did not assert done");
$finish;
end
if (led) begin
$display("PASS master burst write/read test, led=%0d done=%0d", led, done);
end else begin
$display("FAIL master burst write/read test, led=%0d done=%0d", led, done);
$finish;
end
repeat (5) @(negedge hclk);
$finish;
end
endmodule
`

5402

被折叠的 条评论
为什么被折叠?



