目录
- zynq中axi_dma的三个实验-第1课:PL搭建.csdn
- zynq中axi_dma的三个实验-第2课:Vitis裸机测试.csdn
- zynq中axi_dma的三个实验-第3课:Linux驱动测试.csdn
BD

AXI 地址映射表
| 模块 | 接口 | Slave Interface | Address Segment | Base Address | Range | High Address |
|---|---|---|---|---|---|---|
| axi_dma_0 | Data_MM2S | S_AXI_HP0 | HP0_DDR_LOWOCM | 0x0000_0000 |
1G | 0x3FFF_FFFF |
| axi_dma_0 | Data_S2MM | S_AXI_HP0 | HP0_DDR_LOWOCM | 0x0000_0000 |
1G | 0x3FFF_FFFF |
| processing_system7_0 | axi_dma_0/S_AXI_LITE | S_AXI_LITE | Reg | 0x4040_0000 |
64K | 0x4040_FFFF |
| processing_system7_0 | top_0/s_axi | s_axi | reg0 | 0x43C0_0000 |
4K | 0x43C0_0FFF |
PIN.xdc
xc7z020
bash
## IIC
set_property -dict {PACKAGE_PIN T11 IOSTANDARD LVCMOS33} [get_ports IIC_EMIO_scl_io]
set_property -dict {PACKAGE_PIN V5 IOSTANDARD LVCMOS33} [get_ports IIC_EMIO_sda_io]
## GPIO_EMIO
set_property -dict {PACKAGE_PIN U5 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[0]]
set_property -dict {PACKAGE_PIN V7 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[1]]
set_property -dict {PACKAGE_PIN W8 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[2]]
set_property -dict {PACKAGE_PIN U9 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[3]]
set_property -dict {PACKAGE_PIN U10 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[4]]
set_property -dict {PACKAGE_PIN W6 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[5]]
set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[6]]
set_property -dict {PACKAGE_PIN Y9 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[7]]
# 中断和脉冲产生
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports O_LED]
set_property -dict {PACKAGE_PIN J14 IOSTANDARD LVCMOS33} [get_ports o_debug]
top.v
verilog
`timescale 1ns/1ps
module top #(
// AXI-Lite 地址宽度,常见 FPGA SoC 系统中一般为 32 位。
parameter P_S_AXI_ADDR_WIDTH = 32,
// AXI-Lite 数据宽度,本项目使用 32 位寄存器访问。
parameter P_S_AXI_DATA_WIDTH = 32,
// AXI-Lite 可访问的寄存器窗口大小,单位是字节。
// 1024 字节可覆盖 slot0 的 129 个 32 位寄存器和后续 slot。
parameter P_S_MEM_SIZE = 1024,
// 内部 SMMR 字节地址宽度。
// 10 位地址可以覆盖 1024 字节空间。
parameter P_REG_ADDR_WIDTH = 10,
// 系统时钟频率。
// 当前顶层主要把 AXI 时钟传给各级模块,此参数预留给需要时序计算的模块使用。
parameter P_CLK_FREQ = 50_000_000
)(
// AXI-Lite 全局时钟和低有效复位。
input wire s_axi_aclk,
input wire s_axi_aresetn,
// AXI-Lite 写地址通道。
input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_awaddr,
input wire s_axi_awvalid,
output wire s_axi_awready,
// AXI-Lite 写数据通道。
input wire [P_S_AXI_DATA_WIDTH-1:0] s_axi_wdata,
input wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axi_wstrb,
input wire s_axi_wvalid,
output wire s_axi_wready,
// AXI-Lite 写响应通道。
output wire [1:0] s_axi_bresp,
output wire s_axi_bvalid,
input wire s_axi_bready,
// AXI-Lite 读地址通道。
input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_araddr,
input wire s_axi_arvalid,
output wire s_axi_arready,
// AXI-Lite 读数据通道。
output wire [P_S_AXI_DATA_WIDTH-1:0] s_axi_rdata,
output wire [1:0] s_axi_rresp,
output wire s_axi_rvalid,
input wire s_axi_rready,
// slot0 AXI-Stream 数据输入,接 AXI DMA MM2S。
input wire [P_S_AXI_DATA_WIDTH-1:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axis_tkeep,
// slot0 AXI-Stream 数据输出,接 AXI DMA S2MM 或 AXI-Stream FIFO。
output wire [P_S_AXI_DATA_WIDTH-1:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [(P_S_AXI_DATA_WIDTH/8)-1:0] m_axis_tkeep,
// 示例外设输出。
// 该信号由 slot1 内部的控制寄存器驱动。
output wire o_led
);
localparam C_SLOT_ADDR_WIDTH = P_REG_ADDR_WIDTH - 2;
////////////////////////////////////////////////////////////////////////////
// AXI-Lite 桥接模块到 SMMR 路由模块之间的信号
////////////////////////////////////////////////////////////////////////////
// AXI-Lite 写事务完成后产生的单周期写脉冲。
wire w_reg_wr_en;
// SMMR 写地址和读地址。
// 这里仍然是字节地址,后面由 smmr_slot_router 转换成寄存器索引。
wire [P_REG_ADDR_WIDTH-1:0] w_reg_wr_addr;
wire [P_REG_ADDR_WIDTH-1:0] w_reg_rd_addr;
// SMMR 写数据和读数据。
wire [P_S_AXI_DATA_WIDTH-1:0] w_reg_wr_data;
wire [P_S_AXI_DATA_WIDTH-1:0] w_reg_rd_data;
////////////////////////////////////////////////////////////////////////////
// SMMR 路由模块到各个 slot 外设之间的信号
////////////////////////////////////////////////////////////////////////////
// slot 地址以 32 位寄存器为单位,因此比字节地址少 2 位。
// 例如字节地址 20 右移 2 位后变成寄存器地址 5。
wire [C_SLOT_ADDR_WIDTH-1:0] w_slot_wr_addr;
wire [C_SLOT_ADDR_WIDTH-1:0] w_slot_rd_addr;
// 写数据会同时送到所有 slot,真正写入哪个 slot 由对应写使能决定。
wire [P_S_AXI_DATA_WIDTH-1:0] w_slot_wr_data;
// slot0 的写使能和读数据。
wire w_slot0_wr_en;
wire [P_S_AXI_DATA_WIDTH-1:0] w_slot0_rd_data;
// slot1 的写使能和读数据。
wire w_slot1_wr_en;
wire [P_S_AXI_DATA_WIDTH-1:0] w_slot1_rd_data;
////////////////////////////////////////////////////////////////////////////
// AXI-Lite 从接口转 SMMR 桥接模块
//
// 该模块负责处理 AXI-Lite 的地址、数据、响应和握手信号。
// 对内部模块来说,它只输出简单的 SMMR 寄存器读写接口。
////////////////////////////////////////////////////////////////////////////
axilite_slave_smmr_bridge #(
.P_S_AXI_ADDR_WIDTH(P_S_AXI_ADDR_WIDTH),
.P_S_AXI_DATA_WIDTH(P_S_AXI_DATA_WIDTH),
.P_S_MEM_SIZE (P_S_MEM_SIZE),
.P_REG_ADDR_WIDTH (P_REG_ADDR_WIDTH)
) u_axilite_slave_smmr_bridge (
.s_axi_aclk (s_axi_aclk),
.s_axi_aresetn (s_axi_aresetn),
.s_axi_awaddr (s_axi_awaddr),
.s_axi_awvalid (s_axi_awvalid),
.s_axi_awready (s_axi_awready),
.s_axi_wdata (s_axi_wdata),
.s_axi_wstrb (s_axi_wstrb),
.s_axi_wvalid (s_axi_wvalid),
.s_axi_wready (s_axi_wready),
.s_axi_bresp (s_axi_bresp),
.s_axi_bvalid (s_axi_bvalid),
.s_axi_bready (s_axi_bready),
.s_axi_araddr (s_axi_araddr),
.s_axi_arvalid (s_axi_arvalid),
.s_axi_arready (s_axi_arready),
.s_axi_rdata (s_axi_rdata),
.s_axi_rresp (s_axi_rresp),
.s_axi_rvalid (s_axi_rvalid),
.s_axi_rready (s_axi_rready),
.o_reg_wr_en (w_reg_wr_en),
.o_reg_wr_addr (w_reg_wr_addr),
.o_reg_wr_data (w_reg_wr_data),
.o_reg_rd_addr (w_reg_rd_addr),
.i_reg_rd_data (w_reg_rd_data)
);
////////////////////////////////////////////////////////////////////////////
// SMMR slot 路由模块
//
// 该模块根据 SMMR 地址范围选择目标 slot。
// 写操作时,它给对应 slot 产生写使能。
// 读操作时,它把被选中 slot 的读数据返回给 AXI-Lite 桥接模块。
//
// 当前地址映射:
// slot0:寄存器地址 0 到 128
// 0 保留,1..64 为读区,65..128 为写区
// slot1:寄存器地址 129 到 132
////////////////////////////////////////////////////////////////////////////
smmr_slot_router #(
.P_ADDR_WIDTH (P_REG_ADDR_WIDTH),
.P_DATA_WIDTH (P_S_AXI_DATA_WIDTH),
.P_SLOT0_BASE (0),
.P_SLOT0_SIZE (129),
.P_SLOT1_BASE (129),
.P_SLOT1_SIZE (4)
) u_smmr_slot_router (
.i_clk (s_axi_aclk),
.i_rst_n (s_axi_aresetn),
.i_reg_wr_en (w_reg_wr_en),
.i_reg_wr_addr (w_reg_wr_addr),
.i_reg_wr_data (w_reg_wr_data),
.i_reg_rd_addr (w_reg_rd_addr),
.o_reg_rd_data (w_reg_rd_data),
.o_slot_wr_addr (w_slot_wr_addr),
.o_slot_wr_data (w_slot_wr_data),
.o_slot_rd_addr (w_slot_rd_addr),
.o_slot0_wr_en (w_slot0_wr_en),
.i_slot0_rd_data(w_slot0_rd_data),
.o_slot1_wr_en (w_slot1_wr_en),
.i_slot1_rd_data(w_slot1_rd_data)
);
////////////////////////////////////////////////////////////////////////////
// slot0 外设
//
// slot0 是一个简单寄存器块。
// 它可以作为新增 SMMR 外设的模板:只需要接入写使能、地址、写数据、
// 读地址和读数据,就可以挂到系统中。
////////////////////////////////////////////////////////////////////////////
slot0 #(
.P_ADDR_WIDTH (C_SLOT_ADDR_WIDTH),
.P_DATA_WIDTH (P_S_AXI_DATA_WIDTH),
.P_SLOT_BASE (0),
.P_REG_COUNT (129)
) u_slot0 (
.i_clk (s_axi_aclk),
.i_rst_n (s_axi_aresetn),
.i_reg_wr_en (w_slot0_wr_en),
.i_reg_wr_addr (w_slot_wr_addr),
.i_reg_wr_data (w_slot_wr_data),
.i_reg_rd_addr (w_slot_rd_addr),
.o_reg_rd_data (w_slot0_rd_data),
.s_axis_tdata (s_axis_tdata),
.s_axis_tvalid (s_axis_tvalid),
.s_axis_tready (s_axis_tready),
.s_axis_tlast (s_axis_tlast),
.s_axis_tkeep (s_axis_tkeep),
.m_axis_tdata (m_axis_tdata),
.m_axis_tvalid (m_axis_tvalid),
.m_axis_tready (m_axis_tready),
.m_axis_tlast (m_axis_tlast),
.m_axis_tkeep (m_axis_tkeep)
);
////////////////////////////////////////////////////////////////////////////
// slot1 外设
//
// slot1 也是一个 SMMR 寄存器块,同时多了一个实际硬件输出 o_led。
// 这展示了如何通过寄存器写入来控制 FPGA 内部或外部硬件信号。
////////////////////////////////////////////////////////////////////////////
slot1 #(
.P_ADDR_WIDTH (C_SLOT_ADDR_WIDTH),
.P_DATA_WIDTH (P_S_AXI_DATA_WIDTH),
.P_SLOT_BASE (129),
.P_REG_COUNT (4)
) u_slot1 (
.i_clk (s_axi_aclk),
.i_rst_n (s_axi_aresetn),
.i_reg_wr_en (w_slot1_wr_en),
.i_reg_wr_addr (w_slot_wr_addr),
.i_reg_wr_data (w_slot_wr_data),
.i_reg_rd_addr (w_slot_rd_addr),
.o_reg_rd_data (w_slot1_rd_data),
.o_led (o_led)
);
endmodule
axilite_master.v
verilog
`timescale 1ns/1ps
module axilite_master #(
parameter P_AXI_ADDR_WIDTH = 32,
parameter P_AXI_DATA_WIDTH = 32
)(
output reg [P_AXI_ADDR_WIDTH-1:0] m_axi_awaddr,
output reg m_axi_awvalid,
input wire m_axi_awready,
output reg [P_AXI_DATA_WIDTH-1:0] m_axi_wdata,
output reg [(P_AXI_DATA_WIDTH/8)-1:0] m_axi_wstrb,
output reg m_axi_wvalid,
input wire m_axi_wready,
input wire [1:0] m_axi_bresp,
input wire m_axi_bvalid,
output reg m_axi_bready,
output reg [P_AXI_ADDR_WIDTH-1:0] m_axi_araddr,
output reg m_axi_arvalid,
input wire m_axi_arready,
input wire [P_AXI_DATA_WIDTH-1:0] m_axi_rdata,
input wire [1:0] m_axi_rresp,
input wire m_axi_rvalid,
output reg m_axi_rready,
input wire i_clk,
input wire i_rst_n,
input wire i_wr_start,
input wire [P_AXI_ADDR_WIDTH-1:0] i_wr_addr,
input wire [P_AXI_DATA_WIDTH-1:0] i_wr_data,
output reg o_wr_busy,
output reg o_wr_done,
output reg [1:0] o_wr_resp,
input wire i_rd_start,
input wire [P_AXI_ADDR_WIDTH-1:0] i_rd_addr,
output reg o_rd_busy,
output reg o_rd_done,
output reg [P_AXI_DATA_WIDTH-1:0] o_rd_data,
output reg [1:0] o_rd_resp
);
localparam [1:0] WR_IDLE = 2'd0, WR_ADDR = 2'd1, WR_RESP = 2'd2;
localparam [1:0] RD_IDLE = 2'd0, RD_ADDR = 2'd1, RD_DATA = 2'd2;
reg [1:0] r_wr_state;
reg [1:0] r_rd_state;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_wr_state <= WR_IDLE; o_wr_busy <= 1'b0; o_wr_done <= 1'b0; o_wr_resp <= 2'b00;
m_axi_awaddr <= {P_AXI_ADDR_WIDTH{1'b0}}; m_axi_awvalid <= 1'b0;
m_axi_wdata <= {P_AXI_DATA_WIDTH{1'b0}}; m_axi_wstrb <= {(P_AXI_DATA_WIDTH/8){1'b0}}; m_axi_wvalid <= 1'b0;
m_axi_bready <= 1'b0;
end else begin
o_wr_done <= 1'b0;
case (r_wr_state)
WR_IDLE: begin
o_wr_busy <= 1'b0; m_axi_awvalid <= 1'b0; m_axi_wvalid <= 1'b0; m_axi_bready <= 1'b0;
if (i_wr_start) begin
o_wr_busy <= 1'b1; m_axi_awaddr <= i_wr_addr; m_axi_awvalid <= 1'b1;
m_axi_wdata <= i_wr_data; m_axi_wstrb <= {(P_AXI_DATA_WIDTH/8){1'b1}}; m_axi_wvalid <= 1'b1;
r_wr_state <= WR_ADDR;
end
end
WR_ADDR: begin
if (m_axi_awvalid && m_axi_awready) m_axi_awvalid <= 1'b0;
if (m_axi_wvalid && m_axi_wready) m_axi_wvalid <= 1'b0;
if ((!m_axi_awvalid || m_axi_awready) && (!m_axi_wvalid || m_axi_wready)) begin
m_axi_bready <= 1'b1; r_wr_state <= WR_RESP;
end
end
WR_RESP: begin
if (m_axi_bvalid) begin
o_wr_resp <= m_axi_bresp; o_wr_done <= 1'b1; o_wr_busy <= 1'b0; m_axi_bready <= 1'b0; r_wr_state <= WR_IDLE;
end
end
default: r_wr_state <= WR_IDLE;
endcase
end
end
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_rd_state <= RD_IDLE; o_rd_busy <= 1'b0; o_rd_done <= 1'b0; o_rd_data <= {P_AXI_DATA_WIDTH{1'b0}}; o_rd_resp <= 2'b00;
m_axi_araddr <= {P_AXI_ADDR_WIDTH{1'b0}}; m_axi_arvalid <= 1'b0; m_axi_rready <= 1'b0;
end else begin
o_rd_done <= 1'b0;
case (r_rd_state)
RD_IDLE: begin
o_rd_busy <= 1'b0; m_axi_arvalid <= 1'b0; m_axi_rready <= 1'b0;
if (i_rd_start) begin
o_rd_busy <= 1'b1; m_axi_araddr <= i_rd_addr; m_axi_arvalid <= 1'b1; r_rd_state <= RD_ADDR;
end
end
RD_ADDR: begin
if (m_axi_arvalid && m_axi_arready) begin
m_axi_arvalid <= 1'b0; m_axi_rready <= 1'b1; r_rd_state <= RD_DATA;
end
end
RD_DATA: begin
if (m_axi_rvalid) begin
o_rd_data <= m_axi_rdata; o_rd_resp <= m_axi_rresp; o_rd_done <= 1'b1; o_rd_busy <= 1'b0; m_axi_rready <= 1'b0; r_rd_state <= RD_IDLE;
end
end
default: r_rd_state <= RD_IDLE;
endcase
end
end
endmodule
axilite_slave_smmr_bridge.v
verilog
//AXI-Lite Slave 接口转 SMMR 简单寄存器接口 桥接模块
module axilite_slave_smmr_bridge #(
parameter P_S_AXI_ADDR_WIDTH = 32,
parameter P_S_AXI_DATA_WIDTH = 32,
parameter P_S_MEM_SIZE = 512, //内部存储器大小(单位:字节),对应128个32位寄存器
parameter P_REG_ADDR_WIDTH = 9
)(
// =========================
// AXI-Lite (PS -> PL)
// =========================
input wire s_axi_aclk,
input wire s_axi_aresetn,
input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_awaddr,
input wire s_axi_awvalid,
output reg s_axi_awready,
input wire [P_S_AXI_DATA_WIDTH-1:0] s_axi_wdata,
input wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axi_wstrb,
input wire s_axi_wvalid,
output reg s_axi_wready,
output reg [1:0] s_axi_bresp,
output reg s_axi_bvalid,
input wire s_axi_bready,
input wire [P_S_AXI_ADDR_WIDTH-1:0] s_axi_araddr,
input wire s_axi_arvalid,
output reg s_axi_arready,
output reg [P_S_AXI_DATA_WIDTH-1:0] s_axi_rdata,
output reg [1:0] s_axi_rresp,
output reg s_axi_rvalid,
input wire s_axi_rready,
// =========================
// Simple register bus
// =========================
output reg o_reg_wr_en,
output reg [P_REG_ADDR_WIDTH-1:0] o_reg_wr_addr,
output reg [P_S_AXI_DATA_WIDTH-1:0] o_reg_wr_data,
output reg [P_REG_ADDR_WIDTH-1:0] o_reg_rd_addr,
input wire [P_S_AXI_DATA_WIDTH-1:0] i_reg_rd_data
);
reg [P_S_AXI_ADDR_WIDTH-1:0] r_write_addr; // 写地址锁存:AW 通道先到、W 通道后到时,暂存本次写地址。
reg [P_S_AXI_DATA_WIDTH-1:0] r_write_data; // 写数据锁存:W 通道先到、AW 通道后到时,暂存本次写数据。
reg r_aw_valid; // 写地址锁存有效标志:AWADDR 已锁存,等待 WDATA 到齐后写寄存器。
reg r_w_valid; // 写数据锁存有效标志:WDATA 已锁存,等待 AWADDR 到齐后写寄存器。
reg r_read_pending; // 读请求处理中标志:ARADDR 发给下游后一拍,再采样读数据返回 AXI R 通道。
wire w_aw_fire = s_axi_awvalid && s_axi_awready; // 写地址通道握手完成,本拍接收 AWADDR。
wire w_w_fire = s_axi_wvalid && s_axi_wready; // 写数据通道握手完成,本拍接收 WDATA。
wire w_ar_fire = s_axi_arvalid && s_axi_arready; // 读地址通道握手完成,本拍接收 ARADDR。
// 写地址和写数据可能不同拍到达;这里先判断本拍是否已经具备完整写事务。
wire w_write_addr_ready = r_aw_valid || w_aw_fire;
wire w_write_data_ready = r_w_valid || w_w_fire;
wire w_write_ready = !s_axi_bvalid && w_write_addr_ready && w_write_data_ready;
wire [P_S_AXI_ADDR_WIDTH-1:0] w_write_addr = w_aw_fire ? s_axi_awaddr : r_write_addr;
wire [P_S_AXI_DATA_WIDTH-1:0] w_write_data = w_w_fire ? s_axi_wdata : r_write_data;
// 读写通道独立仲裁。AXI-Lite 允许读写同时发起,这里不让读事务等待写事务结束。
always @(*) begin
s_axi_awready = s_axi_aresetn && !r_aw_valid && !s_axi_bvalid;
s_axi_wready = s_axi_aresetn && !r_w_valid && !s_axi_bvalid;
s_axi_arready = s_axi_aresetn && !r_read_pending && !s_axi_rvalid;
end
always @(posedge s_axi_aclk or negedge s_axi_aresetn) begin
if (!s_axi_aresetn) begin
s_axi_bresp <= 2'b00;
s_axi_bvalid <= 1'b0;
s_axi_rdata <= {P_S_AXI_DATA_WIDTH{1'b0}};
s_axi_rresp <= 2'b00;
s_axi_rvalid <= 1'b0;
r_write_addr <= {P_S_AXI_ADDR_WIDTH{1'b0}};
r_write_data <= {P_S_AXI_DATA_WIDTH{1'b0}};
r_aw_valid <= 1'b0;
r_w_valid <= 1'b0;
r_read_pending <= 1'b0;
o_reg_wr_en <= 1'b0;
o_reg_wr_addr <= {P_REG_ADDR_WIDTH{1'b0}};
o_reg_wr_data <= {P_S_AXI_DATA_WIDTH{1'b0}};
o_reg_rd_addr <= {P_REG_ADDR_WIDTH{1'b0}};
end else begin
o_reg_wr_en <= 1'b0;
if (w_aw_fire) begin
r_aw_valid <= 1'b1;
r_write_addr <= s_axi_awaddr;
end
if (w_w_fire) begin
r_w_valid <= 1'b1;
r_write_data <= s_axi_wdata;
end
if (w_write_ready) begin
r_aw_valid <= 1'b0;
r_w_valid <= 1'b0;
s_axi_bresp <= 2'b00;
s_axi_bvalid <= 1'b1;
o_reg_wr_en <= 1'b1;
o_reg_wr_addr <= w_write_addr[P_REG_ADDR_WIDTH-1:0];
o_reg_wr_data <= w_write_data;
end else if (s_axi_bvalid && s_axi_bready) begin
s_axi_bvalid <= 1'b0;
end
if (w_ar_fire) begin
r_read_pending <= 1'b1;
o_reg_rd_addr <= s_axi_araddr[P_REG_ADDR_WIDTH-1:0];
end else if (r_read_pending) begin
r_read_pending <= 1'b0;
s_axi_rdata <= i_reg_rd_data;
s_axi_rresp <= 2'b00;
s_axi_rvalid <= 1'b1;
end else if (s_axi_rvalid && s_axi_rready) begin
s_axi_rvalid <= 1'b0;
end
end
end
endmodule
smmr_slot_router.v
verilog
////////////////////////////////////////////////////////////////////////////////
// 简单内存映射寄存器路由模块
// - 将字节地址转换成 32 位寄存器索引
// - 将一路 SMMR 接口路由到多个寄存器槽
////////////////////////////////////////////////////////////////////////////////
module smmr_slot_router #(
// AXI-Lite 侧使用字节地址,寄存器槽侧使用寄存器索引。
parameter P_ADDR_WIDTH = 10,
parameter P_DATA_WIDTH = 32,
// 寄存器槽地址范围以 32 位寄存器为单位。
parameter P_SLOT0_BASE = 0,
parameter P_SLOT0_SIZE = 129,
parameter P_SLOT1_BASE = 129,
parameter P_SLOT1_SIZE = 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,
// 下游寄存器槽总线,地址为 32 位寄存器索引。
output wire [P_ADDR_WIDTH-3:0] o_slot_wr_addr,
output wire [P_DATA_WIDTH-1:0] o_slot_wr_data,
output wire [P_ADDR_WIDTH-3:0] o_slot_rd_addr,
output wire o_slot0_wr_en,
input wire [P_DATA_WIDTH-1:0] i_slot0_rd_data,
output wire o_slot1_wr_en,
input wire [P_DATA_WIDTH-1:0] i_slot1_rd_data
);
wire [P_ADDR_WIDTH-3:0] w_reg_wr_addr_u32 = i_reg_wr_addr >> 2;
wire [P_ADDR_WIDTH-3:0] w_reg_rd_addr_u32 = i_reg_rd_addr >> 2;
reg r_slot0_wr_sel;
reg r_slot1_wr_sel;
reg r_slot0_rd_sel;
reg r_slot1_rd_sel;
always @(*) begin
r_slot0_wr_sel = 1'b0;
r_slot1_wr_sel = 1'b0;
if ((w_reg_wr_addr_u32 >= P_SLOT0_BASE) && (w_reg_wr_addr_u32 < (P_SLOT0_BASE + P_SLOT0_SIZE))) begin
r_slot0_wr_sel = 1'b1;
end else if ((w_reg_wr_addr_u32 >= P_SLOT1_BASE) && (w_reg_wr_addr_u32 < (P_SLOT1_BASE + P_SLOT1_SIZE))) begin
r_slot1_wr_sel = 1'b1;
end
end
always @(*) begin
r_slot0_rd_sel = 1'b0;
r_slot1_rd_sel = 1'b0;
if ((w_reg_rd_addr_u32 >= P_SLOT0_BASE) && (w_reg_rd_addr_u32 < (P_SLOT0_BASE + P_SLOT0_SIZE))) begin
r_slot0_rd_sel = 1'b1;
end else if ((w_reg_rd_addr_u32 >= P_SLOT1_BASE) && (w_reg_rd_addr_u32 < (P_SLOT1_BASE + P_SLOT1_SIZE))) begin
r_slot1_rd_sel = 1'b1;
end
end
assign o_slot0_wr_en = i_reg_wr_en && r_slot0_wr_sel;
assign o_slot1_wr_en = i_reg_wr_en && r_slot1_wr_sel;
assign o_slot_wr_addr = w_reg_wr_addr_u32;
assign o_slot_wr_data = i_reg_wr_data;
assign o_slot_rd_addr = w_reg_rd_addr_u32;
always @(*) begin
if (r_slot0_rd_sel) begin
o_reg_rd_data = i_slot0_rd_data;
end else if (r_slot1_rd_sel) begin
o_reg_rd_data = i_slot1_rd_data;
end else begin
o_reg_rd_data = {P_DATA_WIDTH{1'b0}};
end
end
endmodule
slot0.v
verilog
//------------------------------------------------------------------------------
// slot0
//------------------------------------------------------------------------------
// 功能概述:
// 这是一个同时支持三种访问方式的寄存器槽:
// 1. 简单寄存器总线读写:PS 通过 AXI-Lite -> SMMR 路由访问 slot0 寄存器。
// 2. AXI-Stream 从接口 s_axis:接收 AXI DMA MM2S 送来的 64 个 32bit 数据,
// 写入 slot0[65]..slot0[128]。
// 3. AXI-Stream 主接口 m_axis:把 slot0[1]..slot0[64] 作为 64 个 32bit
// 数据输出给 AXI DMA S2MM。
//
// 寄存器约定:
// slot0[0] :控制触发寄存器,不作为普通数据保存。
// bit0 = 1:启动一次 m_axis 64-word 输出包。
// bit1 = 1:清零 s_axis 写入索引。
// slot0[1..64] :读窗口,m_axis 每次从这里顺序输出 64 word。
// slot0[65..128]:写窗口,s_axis 每次顺序写入这里 64 word。
//
// 关键时序点:
// m_axis_tvalid 不能常高。若 DMA S2MM 还没有正式启动,而 tvalid 常高,
// DMA 或互连在空闲期短暂拉高 tready 时会提前消耗数据,导致读指针错位。
// 所以这里使用 r_m_axis_active 控制 tvalid,只在软件写 slot0[0].bit0 后
// 输出一包 64 word,最后一拍 tlast 后自动停住并把读指针回到 0。
//------------------------------------------------------------------------------
module slot0 #(
// 槽内部寄存器索引宽度。当前顶层传入 8bit,可覆盖 0..255 个 32bit 寄存器索引。
parameter P_ADDR_WIDTH = 8,
// 数据宽度。当前工程使用 32bit,与 AXI-Lite 数据宽度和 AXI-Stream tdata 一致。
parameter P_DATA_WIDTH = 32,
// 本 slot 在全局寄存器索引空间中的起始地址。slot0 当前固定从 0 开始。
parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 0,
// slot0 寄存器数量:0..128,共 129 个 32bit 寄存器索引。
parameter P_REG_COUNT = 129
)(
// 与 AXI-Lite/SMMR 和 AXI-Stream 共用同一时钟域。
input wire i_clk,
// 低有效复位。
input wire i_rst_n,
// 简单寄存器写接口:上游 AXI-Lite 桥在完成一次写事务时给一个写使能脉冲。
input wire i_reg_wr_en,
// 寄存器索引地址,不是字节地址。比如 slot0[65] 的地址就是 65。
input wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr,
input wire [P_DATA_WIDTH-1:0] i_reg_wr_data,
// 简单寄存器读接口:组合读,地址命中时直接返回 r_mem 中的数据。
input wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr,
output reg [P_DATA_WIDTH-1:0] o_reg_rd_data,
// AXI-Stream Slave:接 AXI DMA 的 MM2S 输出,数据方向 DDR/PS -> DMA -> slot0。
input wire [P_DATA_WIDTH-1:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [(P_DATA_WIDTH/8)-1:0] s_axis_tkeep,
// AXI-Stream Master:接 AXI DMA 的 S2MM 输入,数据方向 slot0 -> DMA -> DDR/PS。
output wire [P_DATA_WIDTH-1:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [(P_DATA_WIDTH/8)-1:0] m_axis_tkeep
);
// slot0[0]:控制触发寄存器。变量名仍叫 RSVD,是为了保持原结构,
// 但现在 bit0/bit1 已作为一次性控制位使用,不保存到 r_mem[0]。
localparam [P_ADDR_WIDTH-1:0] C_RSVD_REG = P_SLOT_BASE;
// slot0[1]:m_axis 输出窗口起始寄存器。
localparam [P_ADDR_WIDTH-1:0] C_READ_REG_BASE = P_SLOT_BASE + 1;
// slot0[65]:s_axis 输入窗口起始寄存器。
localparam [P_ADDR_WIDTH-1:0] C_WRITE_REG_BASE = P_SLOT_BASE + 65;
// 64 word AXI-Stream 包的最后一个索引:0..63。
localparam [5:0] C_AXIS_LAST_IDX = 6'd63;
// 实际寄存器存储区。索引 0 对应 slot0[0],索引 128 对应 slot0[128]。
reg [P_DATA_WIDTH-1:0] r_mem [0:P_REG_COUNT-1];
// s_axis 当前写入 slot0[65..128] 的偏移,范围 0..63。
reg [5:0] r_s_axis_wr_idx;
// m_axis 当前读取 slot0[1..64] 的偏移,范围 0..63。
reg [5:0] r_m_axis_rd_idx;
// m_axis 一包数据发送中的状态标志。为 1 时才允许 tvalid 拉高。
reg r_m_axis_active;
// 写 slot0[0] 时认为是控制写,不进入普通数据寄存器写路径。
wire w_reg_ctrl_wr = i_reg_wr_en && (i_reg_wr_addr == C_RSVD_REG);
// 普通寄存器写命中:slot0[1]..slot0[128] 可写,slot0[0] 被排除。
wire w_reg_wr_hit = (i_reg_wr_addr > C_RSVD_REG) &&
(i_reg_wr_addr < (P_SLOT_BASE + P_REG_COUNT));
// 寄存器读命中:slot0[0]..slot0[128] 都可读。slot0[0] 当前读到的是 r_mem[0]。
wire w_reg_rd_hit = (i_reg_rd_addr >= P_SLOT_BASE) &&
(i_reg_rd_addr < (P_SLOT_BASE + P_REG_COUNT));
// AXI-Stream 握手成功的一拍:valid 和 ready 同时为 1 时数据才被消费。
wire w_s_axis_fire = s_axis_tvalid && s_axis_tready;
wire w_m_axis_fire = m_axis_tvalid && m_axis_tready;
// 根据 tkeep 按字节合并写入数据。
// keep[b]=1 表示写入对应字节;keep[b]=0 表示保留旧字节。
// 对于常见 32bit 全字写入,tkeep 通常是 4'b1111。
function [P_DATA_WIDTH-1:0] f_keep_merge;
input [P_DATA_WIDTH-1:0] old_data;
input [P_DATA_WIDTH-1:0] new_data;
input [(P_DATA_WIDTH/8)-1:0] keep;
integer b;
begin
f_keep_merge = old_data;
for (b = 0; b < (P_DATA_WIDTH/8); b = b + 1) begin
if (keep[b]) begin
f_keep_merge[b*8 +: 8] = new_data[b*8 +: 8];
end
end
end
endfunction
// s_axis 接收端当前没有背压逻辑,复位释放后始终 ready。
// AXI DMA MM2S 连续发送 64 word 时,每个有效拍都会被接收。
assign s_axis_tready = i_rst_n;
// m_axis 只有在软件写 slot0[0].bit0 触发后才输出一包数据。
// 这样可以避免 DMA 尚未启动时,由 tready 空闲抖动导致读指针提前递增。
assign m_axis_tvalid = r_m_axis_active;
// 当前输出数据来自 slot0[1..64],由 r_m_axis_rd_idx 顺序选择。
assign m_axis_tdata = r_mem[(C_READ_REG_BASE - P_SLOT_BASE) + r_m_axis_rd_idx];
// 第 64 个 word 时拉高 tlast,通知 AXI DMA S2MM 一包结束。
assign m_axis_tlast = r_m_axis_active && (r_m_axis_rd_idx == C_AXIS_LAST_IDX);
// m_axis 每拍都输出完整 32bit,所以 tkeep 全 1。
assign m_axis_tkeep = {(P_DATA_WIDTH/8){1'b1}};
// 主时序逻辑:
// - 复位时清空寄存器和两个 AXI-Stream 索引。
// - 普通寄存器写:写 slot0[1..128]。
// - s_axis 收到数据:顺序写 slot0[65..128]。
// - 写 slot0[0].bit0:启动一次 m_axis 输出 slot0[1..64]。
// - m_axis 发完第 64 个 word:停止输出并把读索引归零。
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
r_s_axis_wr_idx <= 6'd0;
r_m_axis_rd_idx <= 6'd0;
r_m_axis_active <= 1'b0;
end else begin
// 控制寄存器 bit1:手动清零 s_axis 写索引。
// 主要用于调试或异常恢复,正常 64 word 包结束时也会自动归零。
if (w_reg_ctrl_wr && i_reg_wr_data[1]) begin
r_s_axis_wr_idx <= 6'd0;
end
// PS/CPU 通过 AXI-Lite 直接写 slot0[1..128]。
// slot0[0] 不在这里写入,避免控制触发值污染普通数据区。
if (i_reg_wr_en && w_reg_wr_hit) begin
r_mem[i_reg_wr_addr-P_SLOT_BASE] <= i_reg_wr_data;
end
// AXI DMA MM2S -> slot0:
// 每收到一个 stream word,就写入 slot0[65 + r_s_axis_wr_idx]。
// 若收到 tlast,或者已经写到第 64 个 word,则下一包从 65 重新开始。
if (w_s_axis_fire) begin
r_mem[(C_WRITE_REG_BASE - P_SLOT_BASE) + r_s_axis_wr_idx] <=
f_keep_merge(r_mem[(C_WRITE_REG_BASE - P_SLOT_BASE) + r_s_axis_wr_idx],
s_axis_tdata,
s_axis_tkeep);
if (s_axis_tlast || (r_s_axis_wr_idx == C_AXIS_LAST_IDX)) begin
r_s_axis_wr_idx <= 6'd0;
end else begin
r_s_axis_wr_idx <= r_s_axis_wr_idx + 6'd1;
end
end
// 写 slot0[0].bit0:启动 m_axis 一次 64 word 输出。
// 启动时强制读索引回到 0,保证每次 dma_read 都从 slot0[1] 开始。
if (w_reg_ctrl_wr && i_reg_wr_data[0]) begin
r_m_axis_active <= 1'b1;
r_m_axis_rd_idx <= 6'd0;
end else if (w_m_axis_fire) begin
// AXI DMA S2MM 真正接收一个 word 后,读索引才递增。
// 最后一拍发出后停止 valid,等待下一次 slot0[0].bit0 触发。
if (r_m_axis_rd_idx == C_AXIS_LAST_IDX) begin
r_m_axis_rd_idx <= 6'd0;
r_m_axis_active <= 1'b0;
end else begin
r_m_axis_rd_idx <= r_m_axis_rd_idx + 6'd1;
end
end
end
end
// 寄存器组合读:
// 命中 slot0 地址范围时返回对应 r_mem。
// 超出范围时返回 0,避免上游读到不确定值。
always @(*) begin
if (w_reg_rd_hit) 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
slot1.v
verilog
module slot1 #(
parameter P_ADDR_WIDTH = 8,
parameter P_DATA_WIDTH = 32,
parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 129,
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,
output reg o_led
);
// 控制寄存器:bit0:o_led
localparam [P_ADDR_WIDTH-1:0] ADDR_CONTROL = P_SLOT_BASE;
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
o_led<=0;
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;
case (i_reg_wr_addr)
ADDR_CONTROL: begin
o_led <= i_reg_wr_data[0];
end
endcase
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.v
verilog
`timescale 1ns/1ps
module tb;
localparam P_AXI_ADDR_WIDTH = 32;
localparam P_AXI_DATA_WIDTH = 32;
reg i_clk;
reg i_rst_n;
reg r_wr_start;
reg [P_AXI_ADDR_WIDTH-1:0] r_wr_addr;
reg [P_AXI_DATA_WIDTH-1:0] r_wr_data;
wire w_wr_busy;
wire w_wr_done;
wire [1:0] w_wr_resp;
reg r_rd_start;
reg [P_AXI_ADDR_WIDTH-1:0] r_rd_addr;
wire w_rd_busy;
wire w_rd_done;
wire [P_AXI_DATA_WIDTH-1:0] w_rd_data;
wire [1:0] w_rd_resp;
wire [P_AXI_ADDR_WIDTH-1:0] w_axi_awaddr;
wire w_axi_awvalid;
wire w_axi_awready;
wire [P_AXI_DATA_WIDTH-1:0] w_axi_wdata;
wire [(P_AXI_DATA_WIDTH/8)-1:0] w_axi_wstrb;
wire w_axi_wvalid;
wire w_axi_wready;
wire [1:0] w_axi_bresp;
wire w_axi_bvalid;
wire w_axi_bready;
wire [P_AXI_ADDR_WIDTH-1:0] w_axi_araddr;
wire w_axi_arvalid;
wire w_axi_arready;
wire [P_AXI_DATA_WIDTH-1:0] w_axi_rdata;
wire [1:0] w_axi_rresp;
wire w_axi_rvalid;
wire w_axi_rready;
reg [P_AXI_DATA_WIDTH-1:0] r_s_axis_tdata;
reg r_s_axis_tvalid;
wire w_s_axis_tready;
reg r_s_axis_tlast;
reg [(P_AXI_DATA_WIDTH/8)-1:0] r_s_axis_tkeep;
wire [P_AXI_DATA_WIDTH-1:0] w_m_axis_tdata;
wire w_m_axis_tvalid;
reg r_m_axis_tready;
wire w_m_axis_tlast;
wire [(P_AXI_DATA_WIDTH/8)-1:0] w_m_axis_tkeep;
wire w_led;
axilite_master u_axilite_master (
.i_clk(i_clk), .i_rst_n(i_rst_n),
.i_wr_start(r_wr_start), .i_wr_addr(r_wr_addr), .i_wr_data(r_wr_data),
.o_wr_busy(w_wr_busy), .o_wr_done(w_wr_done), .o_wr_resp(w_wr_resp),
.i_rd_start(r_rd_start), .i_rd_addr(r_rd_addr),
.o_rd_busy(w_rd_busy), .o_rd_done(w_rd_done), .o_rd_data(w_rd_data), .o_rd_resp(w_rd_resp),
.m_axi_awaddr(w_axi_awaddr), .m_axi_awvalid(w_axi_awvalid), .m_axi_awready(w_axi_awready),
.m_axi_wdata(w_axi_wdata), .m_axi_wstrb(w_axi_wstrb), .m_axi_wvalid(w_axi_wvalid), .m_axi_wready(w_axi_wready),
.m_axi_bresp(w_axi_bresp), .m_axi_bvalid(w_axi_bvalid), .m_axi_bready(w_axi_bready),
.m_axi_araddr(w_axi_araddr), .m_axi_arvalid(w_axi_arvalid), .m_axi_arready(w_axi_arready),
.m_axi_rdata(w_axi_rdata), .m_axi_rresp(w_axi_rresp), .m_axi_rvalid(w_axi_rvalid), .m_axi_rready(w_axi_rready)
);
top u_top (
.s_axi_aclk(i_clk), .s_axi_aresetn(i_rst_n),
.s_axi_awaddr(w_axi_awaddr), .s_axi_awvalid(w_axi_awvalid), .s_axi_awready(w_axi_awready),
.s_axi_wdata(w_axi_wdata), .s_axi_wstrb(w_axi_wstrb), .s_axi_wvalid(w_axi_wvalid), .s_axi_wready(w_axi_wready),
.s_axi_bresp(w_axi_bresp), .s_axi_bvalid(w_axi_bvalid), .s_axi_bready(w_axi_bready),
.s_axi_araddr(w_axi_araddr), .s_axi_arvalid(w_axi_arvalid), .s_axi_arready(w_axi_arready),
.s_axi_rdata(w_axi_rdata), .s_axi_rresp(w_axi_rresp), .s_axi_rvalid(w_axi_rvalid), .s_axi_rready(w_axi_rready),
.s_axis_tdata(r_s_axis_tdata), .s_axis_tvalid(r_s_axis_tvalid), .s_axis_tready(w_s_axis_tready),
.s_axis_tlast(r_s_axis_tlast), .s_axis_tkeep(r_s_axis_tkeep),
.m_axis_tdata(w_m_axis_tdata), .m_axis_tvalid(w_m_axis_tvalid), .m_axis_tready(r_m_axis_tready),
.m_axis_tlast(w_m_axis_tlast), .m_axis_tkeep(w_m_axis_tkeep),
.o_led(w_led)
);
initial begin
i_clk = 1'b0;
forever #10 i_clk = ~i_clk;
end
task reset_dut;
begin
i_rst_n <= 1'b0;
r_wr_start <= 1'b0; r_wr_addr <= 32'd0; r_wr_data <= 32'd0;
r_rd_start <= 1'b0; r_rd_addr <= 32'd0;
r_s_axis_tdata <= 32'd0; r_s_axis_tvalid <= 1'b0; r_s_axis_tlast <= 1'b0;
r_s_axis_tkeep <= {(P_AXI_DATA_WIDTH/8){1'b1}};
r_m_axis_tready <= 1'b0;
repeat (5) @(posedge i_clk);
i_rst_n <= 1'b1;
repeat (2) @(posedge i_clk);
end
endtask
task axi_write;
input [P_AXI_ADDR_WIDTH-1:0] addr;
input [P_AXI_DATA_WIDTH-1:0] data;
begin
@(posedge i_clk);
r_wr_addr <= addr; r_wr_data <= data; r_wr_start <= 1'b1;
@(posedge i_clk);
r_wr_start <= 1'b0;
wait (w_wr_done);
$display("[%0t ns] AXI WRITE addr=0x%08x data=0x%08x resp=%0d", $time, addr, data, w_wr_resp);
if (w_wr_resp != 2'b00) $fatal(1, "AXI write response error");
end
endtask
task axi_read_check;
input [P_AXI_ADDR_WIDTH-1:0] addr;
input [P_AXI_DATA_WIDTH-1:0] exp_data;
begin
@(posedge i_clk);
r_rd_addr <= addr; r_rd_start <= 1'b1;
@(posedge i_clk);
r_rd_start <= 1'b0;
wait (w_rd_done);
$display("[%0t ns] AXI READ addr=0x%08x data=0x%08x resp=%0d led=%0b", $time, addr, w_rd_data, w_rd_resp, w_led);
if (w_rd_resp != 2'b00) $fatal(1, "AXI read response error");
if (w_rd_data !== exp_data) $fatal(1, "AXI read mismatch addr=0x%08x exp=0x%08x got=0x%08x", addr, exp_data, w_rd_data);
end
endtask
task axis_write64;
integer j;
begin
for (j = 0; j < 64; j = j + 1) begin
@(negedge i_clk);
r_s_axis_tdata = 32'hA000_0000 + j;
r_s_axis_tvalid = 1'b1;
r_s_axis_tlast = (j == 63);
r_s_axis_tkeep = {(P_AXI_DATA_WIDTH/8){1'b1}};
@(posedge i_clk);
if (!w_s_axis_tready) $fatal(1, "S_AXIS not ready");
end
@(negedge i_clk);
r_s_axis_tvalid = 1'b0;
r_s_axis_tlast = 1'b0;
end
endtask
task axis_read64_check;
integer j;
begin
@(negedge i_clk);
r_m_axis_tready = 1'b1;
for (j = 0; j < 64; j = j + 1) begin
@(posedge i_clk);
if (!w_m_axis_tvalid) $fatal(1, "M_AXIS not valid");
if ((j == 0) && (w_m_axis_tdata !== 32'hB000_0001)) begin
$fatal(1, "M_AXIS first word mismatch got=0x%08x", w_m_axis_tdata);
end
if ((j == 63) && (w_m_axis_tdata !== 32'hB000_0040)) begin
$fatal(1, "M_AXIS last word mismatch got=0x%08x", w_m_axis_tdata);
end
if (w_m_axis_tlast !== (j == 63)) begin
$fatal(1, "M_AXIS tlast mismatch index=%0d tlast=%0b", j, w_m_axis_tlast);
end
end
@(negedge i_clk);
r_m_axis_tready = 1'b0;
end
endtask
task axis_idle_check;
integer k;
begin
for (k = 0; k < 4; k = k + 1) begin
@(negedge i_clk);
r_m_axis_tready = 1'b1;
@(posedge i_clk);
if (w_m_axis_tvalid !== 1'b0) $fatal(1, "M_AXIS valid while idle");
end
@(negedge i_clk);
r_m_axis_tready = 1'b0;
end
endtask
initial begin
reset_dut;
axi_write(32'd1 << 2, 32'd123);
axi_read_check(32'd1 << 2, 32'd123);
axis_write64;
axi_read_check(32'd65 << 2, 32'hA000_0000);
axi_read_check(32'd128 << 2, 32'hA000_003f);
axi_write(32'd1 << 2, 32'hB000_0001);
axi_write(32'd64 << 2, 32'hB000_0040);
axis_idle_check;
axi_write(32'd0 << 2, 32'd1);
axis_read64_check;
axi_write(32'd129 << 2, 32'd1);
axi_read_check(32'd129 << 2, 32'd1);
repeat (5) @(posedge i_clk);
$display("AXI-Lite top test PASS");
$finish;
end
endmodule
测试
bash
$:0
AtShell commands:
0.help - list cmd
1.clean - clean screen
2.read - read reg
3.write - write reg
4.dma_read - dma_read
5.dma_write - dma_write
6.app_init_reg - app_init_reg
# AXI_DMA读64个u32耗时4479ns
$:4
dma_read: regs=1..64 words=64 bytes=256 ns=4479
# 轮询读64个u32耗时34197ns
$:2
read: src=0x43C00000 words=129 ns=34197