参考
BD
axidma_sg_system.tcl_xc7z020.tcl
axidma_three_step_system.tcl_xc7z010.tcl
AXI 地址映射表
| 模块 |
接口 |
Slave Interface |
Address Segment |
Base Address |
Range |
High Address |
| axi_dma_0 |
Data_S2MM |
S_AXI_HP0 |
HP0_DDR_LOWOCM |
0x0000_0000 |
1G |
0x3FFF_FFFF |
| axi_dma_0 |
Data_SG |
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
## 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]]
# led
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports O_LED]
top.v
`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-Stream 输出宽度,可设为 32 或 64。
parameter P_M_AXIS_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 S2MM 或 AXI-Stream FIFO。
output wire [P_M_AXIS_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_M_AXIS_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;
// AXIS 生成器输出的是一整帧宽数据,这里再拆成 32/64 位送 DMA。
localparam integer C_AXIS_GEN_WORDS = 22;
localparam integer C_AXIS_GEN_DATA_WIDTH = C_AXIS_GEN_WORDS * 32;
wire [C_AXIS_GEN_DATA_WIDTH-1:0] w_axis_gen_tdata;
wire w_axis_gen_tvalid;
wire w_axis_gen_tready;
////////////////////////////////////////////////////////////////////////////
// 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)
);
//模拟生成采集流
axis_generator#(
.P_CHANNEL_COUNT(C_AXIS_GEN_WORDS-1)
) u_axis_generator(
.i_clk(s_axi_aclk),
.i_rst_n(s_axi_aresetn),
.m_axis_tdata(w_axis_gen_tdata),
.m_axis_tvalid(w_axis_gen_tvalid),
.m_axis_tready(w_axis_gen_tready)
);
//采集流位宽转换,8行数据1个bd(生成m_axis_tlast)
axis_width_adapter #(
.P_S_AXIS_DATA_WIDTH(C_AXIS_GEN_DATA_WIDTH),
.P_M_AXIS_DATA_WIDTH(P_M_AXIS_DATA_WIDTH)
) u_axis_width_adapter (
.i_clk (s_axi_aclk),
.i_rst_n (s_axi_aresetn),
.s_axis_tdata (w_axis_gen_tdata),
.s_axis_tvalid (w_axis_gen_tvalid),
.s_axis_tready (w_axis_gen_tready),
.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)
);
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)
);
endmodule
axilite_slave_smmr_bridge.v
//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
////////////////////////////////////////////////////////////////////////////////
// 简单内存映射寄存器路由模块
// - 将字节地址转换成 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
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
);
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
axis_generator.v
`timescale 1ns / 1ps
//
// 示例:
// P_CHANNEL_COUNT = 21
// seq ad0 ad1 ad2 ad3 ad4 ad5 ad6 ad7 ad8 ad9 ad10 ad11 ad12 ad13 ad14 ad15 ad16 ad17 ad18 ad19 ad20
// 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 0020 0021
// 0001 0022 0023 0024 0025 0026 0027 0028 0029 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 0040 0041 0042
module axis_generator #(
// 时钟频率
parameter P_CLK_FREQ = 100_000_000,
// 采样周期
//10ms采样1次 10ms*50=500ms 写1次ddr
parameter P_SAMPLE_PERIOD = 1000000,
// AD数量
parameter P_CHANNEL_COUNT = 21
)(
input wire i_clk,
input wire i_rst_n,
//(P_CHANNEL_COUNT+1)个u32
output reg [(P_CHANNEL_COUNT+1)*32-1:0] m_axis_tdata,
output reg m_axis_tvalid,
input wire m_axis_tready
);
localparam P_AXIS_WIDTH = (P_CHANNEL_COUNT+1)*32;
// 采样计时器
reg [31:0] r_timer;
// 行号
reg [31:0] r_seq;
// AD数据计数
reg [31:0] r_ad_counter;
integer i;
////////////////////////////////////////////////////////
// AXIS DATA GENERATOR
////////////////////////////////////////////////////////
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n) begin
r_timer <= 0;
r_seq <= 0;
r_ad_counter <= 0;
m_axis_tdata <= 0;
m_axis_tvalid <= 0;
end
else begin
//--------------------------------------------------
// AXIS握手
//--------------------------------------------------
if(m_axis_tvalid) begin
if(m_axis_tready) m_axis_tvalid <= 0;
end
//--------------------------------------------------
//旧数据收到后,才生成新数据
//--------------------------------------------------
else if(r_timer >= P_SAMPLE_PERIOD-1) begin
r_timer <= 0;
// 清空
m_axis_tdata <= 0;
// seq
m_axis_tdata[P_AXIS_WIDTH-1 -:32] <= r_seq;
//AD
for(i=0;i<P_CHANNEL_COUNT;i=i+1) begin
m_axis_tdata[P_AXIS_WIDTH-33-i*32 -:32]<= r_ad_counter+i+1;
end
//数据有效
m_axis_tvalid <= 1'b1;
// 行号+1
r_seq <= r_seq + 1;
// AD计数增加AD数量
r_ad_counter <= r_ad_counter + P_CHANNEL_COUNT;
end
else begin
r_timer <= r_timer + 1;
end
end
end
endmodule
axis_width_adapter.v
`timescale 1ns / 1ps
// -----------------------------------------------------------------------------
// 功能说明
// 这个模块把一行宽总线输入拆成多拍窄总线输出。
// 例如:704bit 输入、32bit 输出时,一行会拆成 22 个 beat。
// 数据顺序采用 MSB 先出,与 axis_generator 的打包顺序一致。
// m_axis_tlast 不再按"每行都拉高",而是按"每 P_LINES_PER_TLAST 行拉高一次"。
// 默认 P_LINES_PER_TLAST=8,即 8 行产生 1 个 last。
// -----------------------------------------------------------------------------
module axis_width_adapter #(
parameter P_S_AXIS_DATA_WIDTH = 704, // 输入 AXIS 宽度,通常对应一整行数据
parameter P_M_AXIS_DATA_WIDTH = 32, // 输出 AXIS 宽度,通常对应单个 beat
parameter P_LINES_PER_TLAST = 8 // 每多少行输出一次 tlast,最小值为 1
)(
input wire i_clk,
input wire i_rst_n,
input wire [P_S_AXIS_DATA_WIDTH-1:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
output wire [P_M_AXIS_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_M_AXIS_DATA_WIDTH/8)-1:0] m_axis_tkeep
);
// 一行输入可以拆出的 beat 数。
localparam integer P_BEAT_COUNT = P_S_AXIS_DATA_WIDTH / P_M_AXIS_DATA_WIDTH;
// 用于检查输入宽度是否能被输出宽度整除。
localparam integer P_REMAINDER = P_S_AXIS_DATA_WIDTH % P_M_AXIS_DATA_WIDTH;
// 0 基准下,分组最后一行的索引。
localparam integer P_LAST_LINE = P_LINES_PER_TLAST - 1;
// 当前缓存的一整行输入数据。
reg [P_S_AXIS_DATA_WIDTH-1:0] r_frame_data;
// 当前行已经输出到第几个 beat。
reg [31:0] r_beat_idx;
// 当前处在第几行,用于控制 tlast 的行分组。
reg [31:0] r_line_idx;
// 当前缓存里是否还有未输出完的一整行数据。
reg r_frame_valid;
// 输出侧握手成功,表示当前 beat 被下游真正接收。
wire w_axis_fire = m_axis_tvalid && m_axis_tready;
// 当前行是否已经输出到最后一个 beat。
wire w_last_beat = (r_beat_idx == P_BEAT_COUNT-1);
// 当前行是否正好是本轮 tlast 分组中的最后一行。
wire w_last_line = (r_line_idx == P_LAST_LINE);
// 只有当前一整行已经输出完,模块才允许接收下一行。
wire w_accept_new = !r_frame_valid || (w_axis_fire && w_last_beat);
// 输入端始终以"一行"为单位握手。
assign s_axis_tready = w_accept_new;
// 只要有一整行缓存,输出就保持 valid。
assign m_axis_tvalid = r_frame_valid;
// 只有在"最后一行"的"最后一个 beat"上,才拉高 tlast。
assign m_axis_tlast = r_frame_valid && w_last_beat && w_last_line;
// 本模块输出的是纯数据拍,没有字节裁剪,所有 byte lane 都有效。
assign m_axis_tkeep = {(P_M_AXIS_DATA_WIDTH/8){1'b1}};
// 从缓存的一整行里,按 MSB 先出的顺序切出当前 beat。
assign m_axis_tdata = r_frame_data[P_S_AXIS_DATA_WIDTH-1 - r_beat_idx*P_M_AXIS_DATA_WIDTH -: P_M_AXIS_DATA_WIDTH];
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
// 复位后先把行索引放到"最后一行"的位置,
// 这样第一次接收新行时会回卷到 0,逻辑写法更统一。
r_frame_data <= {P_S_AXIS_DATA_WIDTH{1'b0}};
r_beat_idx <= 32'd0;
r_line_idx <= P_LAST_LINE;
r_frame_valid <= 1'b0;
end else begin
if (s_axis_tvalid && s_axis_tready) begin
// 新的一行输入被接受:
// 1) 锁存整行数据
// 2) beat 索引清零,准备从最高位开始输出
// 3) 行计数加一/回卷,用来决定这一行是否触发 tlast
r_frame_data <= s_axis_tdata;
r_beat_idx <= 32'd0;
if (r_line_idx == P_LAST_LINE) begin
r_line_idx <= 32'd0;
end else begin
r_line_idx <= r_line_idx + 32'd1;
end
// 标记当前有一整行数据等待输出。
r_frame_valid <= 1'b1;
end else if (w_axis_fire) begin
// 当前 beat 已被下游接收,继续输出下一拍。
if (w_last_beat) begin
// 一整行已经输出结束,清空 valid,等待下一行。
r_beat_idx <= 32'd0;
r_frame_valid <= 1'b0;
end else begin
// 还没到最后一个 beat,beat 索引加一继续拆包。
r_beat_idx <= r_beat_idx + 32'd1;
end
end
end
end
generate
if (P_LINES_PER_TLAST < 1) begin : g_tlast_error
// 参数非法时直接报错,避免出现负索引或死逻辑。
initial begin
$fatal(1, "axis_width_adapter: P_LINES_PER_TLAST must be >= 1");
end
end
if ((P_S_AXIS_DATA_WIDTH < P_M_AXIS_DATA_WIDTH) || (P_REMAINDER != 0)) begin : g_width_error
// 输入宽度必须是输出宽度的整数倍,否则无法整拍拆分。
initial begin
$fatal(1, "axis_width_adapter: source width must be an integer multiple of output width");
end
end
endgenerate
endmodule