FPGA:自定义AXI-FIFO主机接口(含versal读写工程)
日常·唠嗑
最近在操作AXI接口的DDR控制器,参考了ALINX的做法,相当好用,做法很玄妙,AXI被封装成类FIFO的简易接口,不用懂AXI也可以操作(当然了解原理才知道具体含义,不会一头雾水),在这里分享给大家。
一、代码框架
代码框架很简单,总共三个module:

数据留向:
现有代码用的计数器,所以fifo只是信号名,各位如果想读写其他数据,可以同个FIFO缓冲一下,怼到mem_test接口上就好。

这样设计有个好处,代码的第 96-120 行定义的 Local Bus 接口,它的作用是屏蔽 AXI 协议的复杂性。
如果不使用这个 Local Bus 接口,你的用户逻辑代码就需要自己去处理 AXI 的时序(比如判断 AWREADY 何时变高,计算 WLAST 信号,处理 4KB 边界等)。
这个模块将 AXI Master 变成了一个简单的“命令-数据流”模式:
控制信号 (WR_START, WR_ADRS, WR_LEN 等):
用户只需要给出一个“开始写”信号、起始地址和长度,这个模块内部的状态机(wr_state)就会自动产生符合 AXI 规范的一系列时序。
它会自动把长数据传输切分成多个 AXI Burst 传输(代码 215-223 行处理了长度递减和 Burst 拆分)。
FIFO 接口 (WR_FIFO_DATA, WR_FIFO_RE 等):
对于数据传输,它通过 FIFO 接口与用户逻辑交互。
写操作时:用户逻辑只需要把数据写入一个 FIFO,这个模块会自动从 FIFO 读数据 (WR_FIFO_RE) 并发送到 AXI 总线 (M_AXI_WDATA)。
读操作时:这个模块从 AXI 总线收到数据 (M_AXI_RDATA) 后,会自动写使能 (RD_FIFO_WE) 将数据推送到用户的 FIFO 中。
总结 “Local Bus” 的作用
Local Bus 的作用是充当“用户逻辑”和“AXI 总线”之间的桥梁(适配器)。
输入端 (Local Bus): 简单的握手(Start/Done)、地址/长度配置、FIFO 数据流接口。
输出端 (AXI Master): 复杂的 AXI4 协议时序。
这样设计的好处是,你在编写其余的逻辑代码(比如 mem_test.v)时,完全不需要了解 AXI 协议的细节,只需要操作简单的 FIFO 和寄存器即可完成对 DDR 等高速存储器的读写。
二、程序设计:
三个模块:请各位尊重他人成果,保留ALINX作者信息及相关信息。
mem_test.v中,我在ALINX的基础上加了相关注释,方便大家理解。
1、top
`timescale 1ns / 1ps
module top(
output [0:0] DDR4_act_n,
output [16:0] DDR4_adr,
output [1:0] DDR4_ba,
output [0:0] DDR4_bg,
output [0:0] DDR4_ck_c,
output [0:0] DDR4_ck_t,
output [0:0] DDR4_cke,
output [0:0] DDR4_cs_n,
inout [7:0] DDR4_dm_n,
inout [63:0] DDR4_dq,
inout [7:0] DDR4_dqs_c,
inout [7:0] DDR4_dqs_t,
output [0:0] DDR4_odt,
output [0:0] DDR4_reset_n,
input [0:0] sys_clk_n,
input [0:0] sys_clk_p
);
(*mark_debug="true"*) wire error;
wire rst_n;
wire M_AXI_ACLK;
// Master Write Address
wire [0:0] M_AXI_AWID;
(*mark_debug="true"*)wire [31:0] M_AXI_AWADDR;
(*mark_debug="true"*)wire [7:0] M_AXI_AWLEN; // Burst Length: 0-255
(*mark_debug="true"*)wire [2:0] M_AXI_AWSIZE; // Burst Size: Fixed 2'b011
(*mark_debug="true"*)wire [1:0] M_AXI_AWBURST; // Burst Type: Fixed 2'b01(Incremental Burst)
wire M_AXI_AWLOCK; // Lock: Fixed 2'b00
wire [3:0] M_AXI_AWCACHE; // Cache: Fiex 2'b0011
wire [2:0] M_AXI_AWPROT; // Protect: Fixed 2'b000
wire [3:0] M_AXI_AWQOS; // QoS: Fixed 2'b0000
wire [0:0] M_AXI_AWUSER; // User: Fixed 32'd0
(*mark_debug="true"*)wire M_AXI_AWVALID;
(*mark_debug="true"*)wire M_AXI_AWREADY;
// Master Write Data
(*mark_debug="true"*)wire [63:0] M_AXI_WDATA;
(*mark_debug="true"*)wire [7:0] M_AXI_WSTRB;
(*mark_debug="true"*)wire M_AXI_WLAST;
(*mark_debug="true"*)wire [0:0] M_AXI_WUSER;
(*mark_debug="true"*)wire M_AXI_WVALID;
(*mark_debug="true"*)wire M_AXI_WREADY;
// Master Write Response
wire [0:0] M_AXI_BID;
(*mark_debug="true"*)wire [1:0] M_AXI_BRESP;
(*mark_debug="true"*)wire [0:0] M_AXI_BUSER;
(*mark_debug="true"*)wire M_AXI_BVALID;
(*mark_debug="true"*)wire M_AXI_BREADY;
// Master Read Address
wire [0:0] M_AXI_ARID;
(*mark_debug="true"*)wire [31:0] M_AXI_ARADDR;
(*mark_debug="true"*)wire [7:0] M_AXI_ARLEN;
(*mark_debug="true"*)wire [2:0] M_AXI_ARSIZE;
(*mark_debug="true"*)wire [1:0] M_AXI_ARBURST;
wire M_AXI_ARLOCK;
wire [3:0] M_AXI_ARCACHE;
wire [2:0] M_AXI_ARPROT;
wire [3:0] M_AXI_ARQOS;
wire [0:0] M_AXI_ARUSER;
(*mark_debug="true"*)wire M_AXI_ARVALID;
(*mark_debug="true"*)wire M_AXI_ARREADY;
// Master Read Data
wire [0:0] M_AXI_RID;
(*mark_debug="true"*)wire [63:0] M_AXI_RDATA;
(*mark_debug="true"*)wire [1:0] M_AXI_RRESP;
(*mark_debug="true"*)wire M_AXI_RLAST;
(*mark_debug="true"*)wire [0:0] M_AXI_RUSER;
(*mark_debug="true"*)wire M_AXI_RVALID;
(*mark_debug="true"*)wire M_AXI_RREADY;
(*mark_debug="true"*)wire wr_burst_data_req;
(*mark_debug="true"*)wire wr_burst_finish;
(*mark_debug="true"*)wire rd_burst_finish;
(*mark_debug="true"*)wire rd_burst_req;
(*mark_debug="true"*)wire wr_burst_req;
(*mark_debug="true"*)wire[9:0] rd_burst_len;
(*mark_debug="true"*)wire[9:0] wr_burst_len;
(*mark_debug="true"*)wire[31:0] rd_burst_addr;
(*mark_debug="true"*)wire[31:0] wr_burst_addr;
(*mark_debug="true"*)wire rd_burst_data_valid;
(*mark_debug="true"*)wire[63 : 0] rd_burst_data;
(*mark_debug="true"*)wire[63 : 0] wr_burst_data;
mem_test
#(
.MEM_DATA_BITS(64),
.ADDR_BITS(27)
)
mem_test_m0
(
.rst(~rst_n),
.mem_clk(M_AXI_ACLK),
.rd_burst_req(rd_burst_req),
.wr_burst_req(wr_burst_req),
.rd_burst_len(rd_burst_len),
.wr_burst_len(wr_burst_len),
.rd_burst_addr(rd_burst_addr),
.wr_burst_addr(wr_burst_addr),
.rd_burst_data_valid(rd_burst_data_valid),
.wr_burst_data_req(wr_burst_data_req),
.rd_burst_data(rd_burst_data),
.wr_burst_data(wr_burst_data),
.rd_burst_finish(rd_burst_finish),
.wr_burst_finish(wr_burst_finish),
.error(error)
);
aq_axi_master u_aq_axi_master
(
.ARESETN(rst_n),
.ACLK(M_AXI_ACLK),
.M_AXI_AWID(M_AXI_AWID),
.M_AXI_AWADDR(M_AXI_AWADDR),
.M_AXI_AWLEN(M_AXI_AWLEN),
.M_AXI_AWSIZE(M_AXI_AWSIZE),
.M_AXI_AWBURST(M_AXI_AWBURST),
.M_AXI_AWLOCK(M_AXI_AWLOCK),
.M_AXI_AWCACHE(M_AXI_AWCACHE),
.M_AXI_AWPROT(M_AXI_AWPROT),
.M_AXI_AWQOS(M_AXI_AWQOS),
.M_AXI_AWUSER(M_AXI_AWUSER),
.M_AXI_AWVALID(M_AXI_AWVALID),
.M_AXI_AWREADY(M_AXI_AWREADY),
.M_AXI_WDATA(M_AXI_WDATA),
.M_AXI_WSTRB(M_AXI_WSTRB),
.M_AXI_WLAST(M_AXI_WLAST),
.M_AXI_WUSER(M_AXI_WUSER),
.M_AXI_WVALID(M_AXI_WVALID),
.M_AXI_WREADY(M_AXI_WREADY),
.M_AXI_BID(M_AXI_BID),
.M_AXI_BRESP(M_AXI_BRESP),
.M_AXI_BUSER(M_AXI_BUSER),
.M_AXI_BVALID(M_AXI_BVALID),
.M_AXI_BREADY(M_AXI_BREADY),
.M_AXI_ARID(M_AXI_ARID),
.M_AXI_ARADDR(M_AXI_ARADDR),
.M_AXI_ARLEN(M_AXI_ARLEN),
.M_AXI_ARSIZE(M_AXI_ARSIZE),
.M_AXI_ARBURST(M_AXI_ARBURST),
.M_AXI_ARLOCK(M_AXI_ARLOCK),
.M_AXI_ARCACHE(M_AXI_ARCACHE),
.M_AXI_ARPROT(M_AXI_ARPROT),
.M_AXI_ARQOS(M_AXI_ARQOS),
.M_AXI_ARUSER(M_AXI_ARUSER),
.M_AXI_ARVALID(M_AXI_ARVALID),
.M_AXI_ARREADY(M_AXI_ARREADY),
.M_AXI_RID(M_AXI_RID),
.M_AXI_RDATA(M_AXI_RDATA),
.M_AXI_RRESP(M_AXI_RRESP),
.M_AXI_RLAST(M_AXI_RLAST),
.M_AXI_RUSER(M_AXI_RUSER),
.M_AXI_RVALID(M_AXI_RVALID),
.M_AXI_RREADY(M_AXI_RREADY),
.MASTER_RST(~rst_n),
.WR_START(wr_burst_req),
.WR_ADRS({wr_burst_addr[28:0],3'd0}),
.WR_LEN({wr_burst_len,3'd0}),
.WR_READY(),
.WR_FIFO_RE(wr_burst_data_req),
.WR_FIFO_EMPTY(1'b0),
.WR_FIFO_AEMPTY(1'b0),
.WR_FIFO_DATA(wr_burst_data),
.WR_DONE(wr_burst_finish),
.RD_START(rd_burst_req),
.RD_ADRS({rd_burst_addr[28:0],3'd0}),
.RD_LEN({rd_burst_len,3'd0}),
.RD_READY(),
.RD_FIFO_WE(rd_burst_data_valid),
.RD_FIFO_FULL(1'b0),
.RD_FIFO_AFULL(1'b0),
.RD_FIFO_DATA(rd_burst_data),
.RD_DONE(rd_burst_finish),
.DEBUG()
);
design_1_wrapper ps_block
(
.S_AXI_araddr (M_AXI_ARADDR ),
.S_AXI_arburst (M_AXI_ARBURST ),
.S_AXI_arcache (M_AXI_ARCACHE ),
.S_AXI_arid (M_AXI_ARID ),
.S_AXI_arlen (M_AXI_ARLEN ),
.S_AXI_arlock (M_AXI_ARLOCK ),
.S_AXI_arprot (M_AXI_ARPROT ),
.S_AXI_arqos (M_AXI_ARQOS ),
.S_AXI_arready (M_AXI_ARREADY ),
.S_AXI_arsize (M_AXI_ARSIZE ),
.S_AXI_arvalid (M_AXI_ARVALID ),
.S_AXI_rdata (M_AXI_RDATA ),
.S_AXI_rid (M_AXI_RID ),
.S_AXI_rlast (M_AXI_RLAST ),
.S_AXI_rready (M_AXI_RREADY ),
.S_AXI_rresp (M_AXI_RRESP ),
.S_AXI_rvalid (M_AXI_RVALID ),
.S_AXI_awaddr (M_AXI_AWADDR ),
.S_AXI_awburst (M_AXI_AWBURST ),
.S_AXI_awcache (M_AXI_AWCACHE ),
.S_AXI_awid (M_AXI_AWID ),
.S_AXI_awlen (M_AXI_AWLEN ),
.S_AXI_awlock (M_AXI_AWLOCK ),
.S_AXI_awprot (M_AXI_AWPROT ),
.S_AXI_awqos (M_AXI_AWQOS ),
.S_AXI_awready (M_AXI_AWREADY ),
.S_AXI_awsize (M_AXI_AWSIZE ),
.S_AXI_awvalid (M_AXI_AWVALID ),
.S_AXI_bid (M_AXI_BID ),
.S_AXI_bready (M_AXI_BREADY ),
.S_AXI_bresp (M_AXI_BRESP ),
.S_AXI_bvalid (M_AXI_BVALID ),
.S_AXI_wdata (M_AXI_WDATA ),
.S_AXI_wlast (M_AXI_WLAST ),
.S_AXI_wready (M_AXI_WREADY ),
.S_AXI_wstrb (M_AXI_WSTRB ),
.S_AXI_wvalid (M_AXI_WVALID ),
.DDR4_act_n (DDR4_act_n ),
.DDR4_adr (DDR4_adr ),
.DDR4_ba (DDR4_ba ),
.DDR4_bg (DDR4_bg ),
.DDR4_ck_c (DDR4_ck_c ),
.DDR4_ck_t (DDR4_ck_t ),
.DDR4_cke (DDR4_cke ),
.DDR4_cs_n (DDR4_cs_n ),
.DDR4_dm_n (DDR4_dm_n ),
.DDR4_dq (DDR4_dq ),
.DDR4_dqs_c (DDR4_dqs_c ),
.DDR4_dqs_t (DDR4_dqs_t ),
.DDR4_odt (DDR4_odt ),
.DDR4_reset_n (DDR4_reset_n ),
.sys_clk_p(sys_clk_p),
.sys_clk_n(sys_clk_n),
.axi_resetn(rst_n),
.axi_clk(M_AXI_ACLK)
);
endmodule
2、mem_test
//////////////////////////////////////////////////////////////////////////////////
// Company: ALINX黑金
// Engineer: 老梅
//
// Create Date: 2016/11/17 10:27:06
// Design Name:
// Module Name: mem_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module mem_test
#(
parameter MEM_DATA_BITS = 64, // 数据总线宽度
parameter ADDR_BITS = 32 // 地址总线宽度
)
(
input rst, // 复位信号,高有效
input mem_clk, // 接口时钟
output reg rd_burst_req, // 读突发请求
output reg wr_burst_req, // 写突发请求
output reg[9:0] rd_burst_len, // 读突发长度
output reg[9:0] wr_burst_len, // 写突发长度
output reg[ADDR_BITS - 1:0] rd_burst_addr, // 读突发首地址
output reg[ADDR_BITS - 1:0] wr_burst_addr, // 写突发首地址
input rd_burst_data_valid, // 读出数据有效
input wr_burst_data_req, // 写数据请求
input[MEM_DATA_BITS - 1:0] rd_burst_data, // 读出的数据
output[MEM_DATA_BITS - 1:0] wr_burst_data, // 写入的数据
input rd_burst_finish, // 读突发完成
input wr_burst_finish, // 写突发完成
output reg error // 错误标志
);
// 状态机状态定义
localparam IDLE = 3'd0; // 空闲状态
localparam MEM_READ = 3'd1; // 读状态
localparam MEM_WRITE = 3'd2; // 写状态
localparam BURST_LEN = 128; // 每次突发的数据长度
reg[2:0] state; // 当前状态机状态
reg[7:0] wr_cnt; // 写数据计数
reg[MEM_DATA_BITS - 1:0] wr_burst_data_reg; // 写数据寄存器
assign wr_burst_data = wr_burst_data_reg; // 输出写入数据
reg[7:0] rd_cnt; // 读数据计数
reg[31:0] write_read_len; // 累计写读长度
//assign error = (state == MEM_READ) && rd_burst_data_valid && (rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}});
// 错误检查逻辑: 读状态且数据有效且数据内容不匹配时,置error为1
always@(posedge mem_clk or posedge rst)
begin
if(rst)
error <= 1'b0;
else if(state == MEM_READ && rd_burst_data_valid && rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}})
error <= 1'b1;
end
// 写端数据生成逻辑
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
wr_burst_data_reg <= {MEM_DATA_BITS{1'b0}}; // 写数据寄存器清零
wr_cnt <= 8'd0; // 写数据计数清零
end
else if(state == MEM_WRITE)
begin
if(wr_burst_data_req) // 需要写数据时
begin
wr_burst_data_reg <= {(MEM_DATA_BITS/8){wr_cnt}}; // 每个字节填写cnt
wr_cnt <= wr_cnt + 8'd1;
end
else if(wr_burst_finish) // 写突发完成,清空写计数
wr_cnt <= 8'd0;
end
end
// 读端数据计数
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
rd_cnt <= 8'd0; // 读计数清零
end
else if(state == MEM_READ)
begin
if(rd_burst_data_valid)
begin
rd_cnt <= rd_cnt + 8'd1;
end
else if(rd_burst_finish)
rd_cnt <= 8'd0;
end
else
rd_cnt <= 8'd0; // 非读状态下强制清零
end
// 状态机
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
state <= IDLE; // 初始状态
wr_burst_req <= 1'b0; // 关闭写请求
rd_burst_req <= 1'b0; // 关闭读请求
rd_burst_len <= BURST_LEN; // 设置突发长度
wr_burst_len <= BURST_LEN;
rd_burst_addr <= 0;
wr_burst_addr <= 0;
write_read_len <= 32'd0; // 写读累计长度清零
end
else
begin
case(state)
IDLE:
begin
// 启动第一个写突发
state <= MEM_WRITE;
wr_burst_req <= 1'b1;
wr_burst_len <= BURST_LEN;
wr_burst_addr <='h2000000; // 写起始地址
write_read_len <= 32'd0;
end
MEM_WRITE:
begin
if(wr_burst_finish)
begin
// 写完成切换到读
state <= MEM_READ;
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b1;
rd_burst_len <= BURST_LEN;
rd_burst_addr <= wr_burst_addr;
write_read_len <= write_read_len + BURST_LEN; // 累加长度
end
end
MEM_READ:
begin
if(rd_burst_finish)
begin
if(write_read_len == 32'h2000000)
begin
// 所有数据写读结束
rd_burst_req <= 1'b0;
state <= IDLE;
end
else
begin
// 继续下一个突发写
state <= MEM_WRITE;
wr_burst_req <= 1'b1;
wr_burst_len <= BURST_LEN;
rd_burst_req <= 1'b0;
wr_burst_addr <= wr_burst_addr + BURST_LEN;
end
end
end
default:
state <= IDLE;
endcase
end
end
endmodule
3、aq_axi_master
/*
* Copyright (C)2014-2015 AQUAXIS TECHNOLOGY.
* Don't remove this header.
* When you use this source, there is a need to inherit this header.
*
* License
* For no commercial -
* License: The Open Software License 3.0
* License URI: http://www.opensource.org/licenses/OSL-3.0
*
* For commmercial -
* License: AQUAXIS License 1.0
* License URI: http://www.aquaxis.com/licenses
*
* For further information please contact.
* URI: http://www.aquaxis.com/
* E-Mail: info(at)aquaxis.com
*/
//////////////////////////////////////////////////////////////////////////////////
// Company: ALINX榛戦噾
// Engineer: 鑰佹��
//
// Create Date: 2016/11/17 10:27:06
// Design Name:
// Module Name: mem_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module aq_axi_master(
// Reset, Clock
input ARESETN,
input ACLK,
// Master Write Address
output [0:0] M_AXI_AWID,
output [31:0] M_AXI_AWADDR,
output [7:0] M_AXI_AWLEN, // Burst Length: 0-255
output [2:0] M_AXI_AWSIZE, // Burst Size: Fixed 2'b011
output [1:0] M_AXI_AWBURST, // Burst Type: Fixed 2'b01(Incremental Burst)
output M_AXI_AWLOCK, // Lock: Fixed 2'b00
output [3:0] M_AXI_AWCACHE, // Cache: Fiex 2'b0011
output [2:0] M_AXI_AWPROT, // Protect: Fixed 2'b000
output [3:0] M_AXI_AWQOS, // QoS: Fixed 2'b0000
output [0:0] M_AXI_AWUSER, // User: Fixed 32'd0
output M_AXI_AWVALID,
input M_AXI_AWREADY,
// Master Write Data
output [63:0] M_AXI_WDATA,
output [7:0] M_AXI_WSTRB,
output M_AXI_WLAST,
output [0:0] M_AXI_WUSER,
output M_AXI_WVALID,
input M_AXI_WREADY,
// Master Write Response
input [0:0] M_AXI_BID,
input [1:0] M_AXI_BRESP,
input [0:0] M_AXI_BUSER,
input M_AXI_BVALID,
output M_AXI_BREADY,
// Master Read Address
output [0:0] M_AXI_ARID,
output [31:0] M_AXI_ARADDR,
output [7:0] M_AXI_ARLEN,
output [2:0] M_AXI_ARSIZE,
output [1:0] M_AXI_ARBURST,
output [1:0] M_AXI_ARLOCK,
output [3:0] M_AXI_ARCACHE,
output [2:0] M_AXI_ARPROT,
output [3:0] M_AXI_ARQOS,
output [0:0] M_AXI_ARUSER,
output M_AXI_ARVALID,
input M_AXI_ARREADY,
// Master Read Data
input [0:0] M_AXI_RID,
input [63:0] M_AXI_RDATA,
input [1:0] M_AXI_RRESP,
input M_AXI_RLAST,
input [0:0] M_AXI_RUSER,
input M_AXI_RVALID,
output M_AXI_RREADY,
// Local Bus
input MASTER_RST,
input WR_START,
input [31:0] WR_ADRS,
input [31:0] WR_LEN,
output WR_READY,
output WR_FIFO_RE,
input WR_FIFO_EMPTY,
input WR_FIFO_AEMPTY,
input [63:0] WR_FIFO_DATA,
output WR_DONE,
input RD_START,
input [31:0] RD_ADRS,
input [31:0] RD_LEN,
output RD_READY,
output RD_FIFO_WE,
input RD_FIFO_FULL,
input RD_FIFO_AFULL,
output [63:0] RD_FIFO_DATA,
output RD_DONE,
output [31:0] DEBUG
);
localparam S_WR_IDLE = 3'd0;
localparam S_WA_WAIT = 3'd1;
localparam S_WA_START = 3'd2;
localparam S_WD_WAIT = 3'd3;
localparam S_WD_PROC = 3'd4;
localparam S_WR_WAIT = 3'd5;
localparam S_WR_DONE = 3'd6;
reg [2:0] wr_state;
reg [31:0] reg_wr_adrs;
reg [31:0] reg_wr_len;
reg reg_awvalid, reg_wvalid, reg_w_last;
reg [7:0] reg_w_len;
reg [7:0] reg_w_stb;
reg [1:0] reg_wr_status;
reg [3:0] reg_w_count, reg_r_count;
reg [7:0] rd_chkdata, wr_chkdata;
reg [1:0] resp;
reg rd_first_data;
reg rd_fifo_enable;
reg[31:0] rd_fifo_cnt;
assign WR_DONE = (wr_state == S_WR_DONE);
assign WR_FIFO_RE = rd_first_data | (reg_wvalid & ~WR_FIFO_EMPTY & M_AXI_WREADY & rd_fifo_enable);
//assign WR_FIFO_RE = reg_wvalid & ~WR_FIFO_EMPTY & M_AXI_WREADY;
always @(posedge ACLK or negedge ARESETN)
begin
if(!ARESETN)
rd_fifo_cnt <= 32'd0;
else if(WR_FIFO_RE)
rd_fifo_cnt <= rd_fifo_cnt + 32'd1;
else if(wr_state == S_WR_IDLE)
rd_fifo_cnt <= 32'd0;
end
always @(posedge ACLK or negedge ARESETN)
begin
if(!ARESETN)
rd_fifo_enable <= 1'b0;
else if(wr_state == S_WR_IDLE && WR_START)
rd_fifo_enable <= 1'b1;
else if(WR_FIFO_RE && (rd_fifo_cnt == RD_LEN[31:3] - 32'd1) )
rd_fifo_enable <= 1'b0;
end
// Write State
always @(posedge ACLK or negedge ARESETN) begin
if(!ARESETN) begin
wr_state <= S_WR_IDLE;
reg_wr_adrs[31:0] <= 32'd0;
reg_wr_len[31:0] <= 32'd0;
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b0;
reg_w_last <= 1'b0;
reg_w_len[7:0] <= 8'd0;
reg_w_stb[7:0] <= 8'd0;
reg_wr_status[1:0] <= 2'd0;
reg_w_count[3:0] <= 4'd0;
reg_r_count[3:0] <= 4'd0;
wr_chkdata <= 8'd0;
rd_chkdata <= 8'd0;
resp <= 2'd0;
rd_first_data <= 1'b0;
end else begin
if(MASTER_RST) begin
wr_state <= S_WR_IDLE;
end else begin
case(wr_state)
S_WR_IDLE: begin
if(WR_START) begin
wr_state <= S_WA_WAIT;
reg_wr_adrs[31:0] <= WR_ADRS[31:0];
reg_wr_len[31:0] <= WR_LEN[31:0] -32'd1;
rd_first_data <= 1'b1;
end
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b0;
reg_w_last <= 1'b0;
reg_w_len[7:0] <= 8'd0;
reg_w_stb[7:0] <= 8'd0;
reg_wr_status[1:0] <= 2'd0;
end
S_WA_WAIT: begin
if(!WR_FIFO_AEMPTY | (reg_wr_len[31:11] == 21'd0)) begin
wr_state <= S_WA_START;
end
rd_first_data <= 1'b0;
end
S_WA_START: begin
wr_state <= S_WD_WAIT;
reg_awvalid <= 1'b1;
reg_wr_len[31:11] <= reg_wr_len[31:11] - 21'd1;
if(reg_wr_len[31:11] != 21'd0) begin
reg_w_len[7:0] <= 8'hFF;
reg_w_last <= 1'b0;
reg_w_stb[7:0] <= 8'hFF;
end else begin
reg_w_len[7:0] <= reg_wr_len[10:3];
reg_w_last <= 1'b1;
reg_w_stb[7:0] <= 8'hFF;
/*
case(reg_wr_len[2:0]) begin
case 3'd0: reg_w_stb[7:0] <= 8'b0000_0000;
case 3'd1: reg_w_stb[7:0] <= 8'b0000_0001;
case 3'd2: reg_w_stb[7:0] <= 8'b0000_0011;
case 3'd3: reg_w_stb[7:0] <= 8'b0000_0111;
case 3'd4: reg_w_stb[7:0] <= 8'b0000_1111;
case 3'd5: reg_w_stb[7:0] <= 8'b0001_1111;
case 3'd6: reg_w_stb[7:0] <= 8'b0011_1111;
case 3'd7: reg_w_stb[7:0] <= 8'b0111_1111;
default: reg_w_stb[7:0] <= 8'b1111_1111;
endcase
*/
end
end
S_WD_WAIT: begin
if(M_AXI_AWREADY) begin
wr_state <= S_WD_PROC;
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b1;
end
end
S_WD_PROC: begin
if(M_AXI_WREADY & ~WR_FIFO_EMPTY) begin
if(reg_w_len[7:0] == 8'd0) begin
wr_state <= S_WR_WAIT;
reg_wvalid <= 1'b0;
reg_w_stb[7:0] <= 8'h00;
end else begin
reg_w_len[7:0] <= reg_w_len[7:0] -8'd1;
end
end
end
S_WR_WAIT: begin
if(M_AXI_BVALID) begin
reg_wr_status[1:0] <= reg_wr_status[1:0] | M_AXI_BRESP[1:0];
if(reg_w_last) begin
wr_state <= S_WR_DONE;
end else begin
wr_state <= S_WA_WAIT;
reg_wr_adrs[31:0] <= reg_wr_adrs[31:0] + 32'd2048;
end
end
end
S_WR_DONE: begin
wr_state <= S_WR_IDLE;
end
default: begin
wr_state <= S_WR_IDLE;
end
endcase
/*
if(WR_FIFO_RE) begin
reg_w_count[3:0] <= reg_w_count[3:0] + 4'd1;
end
if(RD_FIFO_WE)begin
reg_r_count[3:0] <= reg_r_count[3:0] + 4'd1;
end
if(M_AXI_AWREADY & M_AXI_AWVALID) begin
wr_chkdata <= 8'hEE;
end else if(M_AXI_WSTRB[7] & M_AXI_WVALID) begin
wr_chkdata <= WR_FIFO_DATA[63:56];
end
if(M_AXI_AWREADY & M_AXI_AWVALID) begin
rd_chkdata <= 8'hDD;
end else if(M_AXI_WSTRB[7] & M_AXI_WREADY) begin
rd_chkdata <= WR_FIFO_DATA[63:56];
end
if(M_AXI_BVALID & M_AXI_BREADY) begin
resp <= M_AXI_BRESP;
end
*/
end
end
end
assign M_AXI_AWID = 1'b0;
assign M_AXI_AWADDR[31:0] = reg_wr_adrs[31:0];
assign M_AXI_AWLEN[7:0] = reg_w_len[7:0];
assign M_AXI_AWSIZE[2:0] = 2'b011;
assign M_AXI_AWBURST[1:0] = 2'b01;
assign M_AXI_AWLOCK = 1'b0;
assign M_AXI_AWCACHE[3:0] = 4'b0011;
assign M_AXI_AWPROT[2:0] = 3'b000;
assign M_AXI_AWQOS[3:0] = 4'b0000;
assign M_AXI_AWUSER[0] = 1'b1;
assign M_AXI_AWVALID = reg_awvalid;
assign M_AXI_WDATA[63:0] = WR_FIFO_DATA[63:0];
// assign M_AXI_WSTRB[7:0] = (reg_w_len[7:0] == 8'd0)?reg_w_stb[7:0]:8'hFF;
// assign M_AXI_WSTRB[7:0] = (wr_state == S_WD_PROC)?8'hFF:8'h00;
assign M_AXI_WSTRB[7:0] = (reg_wvalid & ~WR_FIFO_EMPTY)?8'hFF:8'h00;
assign M_AXI_WLAST = (reg_w_len[7:0] == 8'd0)?1'b1:1'b0;
assign M_AXI_WUSER = 1;
assign M_AXI_WVALID = reg_wvalid & ~WR_FIFO_EMPTY;
// assign M_AXI_WVALID = (wr_state == S_WD_PROC)?1'b1:1'b0;
assign M_AXI_BREADY = M_AXI_BVALID;
assign WR_READY = (wr_state == S_WR_IDLE)?1'b1:1'b0;
// assign WR_FIFO_RE = (wr_state == S_WD_PROC)?M_AXI_WREADY:1'b0;
localparam S_RD_IDLE = 3'd0;
localparam S_RA_WAIT = 3'd1;
localparam S_RA_START = 3'd2;
localparam S_RD_WAIT = 3'd3;
localparam S_RD_PROC = 3'd4;
localparam S_RD_DONE = 3'd5;
reg [2:0] rd_state;
reg [31:0] reg_rd_adrs;
reg [31:0] reg_rd_len;
reg reg_arvalid, reg_r_last;
reg [7:0] reg_r_len;
assign RD_DONE = (rd_state == S_RD_DONE) ;
// Read State
always @(posedge ACLK or negedge ARESETN) begin
if(!ARESETN) begin
rd_state <= S_RD_IDLE;
reg_rd_adrs[31:0] <= 32'd0;
reg_rd_len[31:0] <= 32'd0;
reg_arvalid <= 1'b0;
reg_r_len[7:0] <= 8'd0;
end else begin
case(rd_state)
S_RD_IDLE: begin
if(RD_START) begin
rd_state <= S_RA_WAIT;
reg_rd_adrs[31:0] <= RD_ADRS[31:0];
reg_rd_len[31:0] <= RD_LEN[31:0] -32'd1;
end
reg_arvalid <= 1'b0;
reg_r_len[7:0] <= 8'd0;
end
S_RA_WAIT: begin
if(~RD_FIFO_AFULL) begin
rd_state <= S_RA_START;
end
end
S_RA_START: begin
rd_state <= S_RD_WAIT;
reg_arvalid <= 1'b1;
reg_rd_len[31:11] <= reg_rd_len[31:11] -21'd1;
if(reg_rd_len[31:11] != 21'd0) begin
reg_r_last <= 1'b0;
reg_r_len[7:0] <= 8'd255;
end else begin
reg_r_last <= 1'b1;
reg_r_len[7:0] <= reg_rd_len[10:3];
end
end
S_RD_WAIT: begin
if(M_AXI_ARREADY) begin
rd_state <= S_RD_PROC;
reg_arvalid <= 1'b0;
end
end
S_RD_PROC: begin
if(M_AXI_RVALID) begin
if(M_AXI_RLAST) begin
if(reg_r_last) begin
rd_state <= S_RD_DONE;
end else begin
rd_state <= S_RA_WAIT;
reg_rd_adrs[31:0] <= reg_rd_adrs[31:0] + 32'd2048;
end
end else begin
reg_r_len[7:0] <= reg_r_len[7:0] -8'd1;
end
end
end
S_RD_DONE:begin
rd_state <= S_RD_IDLE;
end
endcase
end
end
// Master Read Address
assign M_AXI_ARID = 1'b0;
assign M_AXI_ARADDR[31:0] = reg_rd_adrs[31:0];
assign M_AXI_ARLEN[7:0] = reg_r_len[7:0];
assign M_AXI_ARSIZE[2:0] = 3'b011;
assign M_AXI_ARBURST[1:0] = 2'b01;
assign M_AXI_ARLOCK = 1'b0;
assign M_AXI_ARCACHE[3:0] = 4'b0011;
assign M_AXI_ARPROT[2:0] = 3'b000;
assign M_AXI_ARQOS[3:0] = 4'b0000;
assign M_AXI_ARUSER[0] = 1'b1;
assign M_AXI_ARVALID = reg_arvalid;
assign M_AXI_RREADY = M_AXI_RVALID & ~RD_FIFO_FULL;
assign RD_READY = (rd_state == S_RD_IDLE)?1'b1:1'b0;
assign RD_FIFO_WE = M_AXI_RVALID;
assign RD_FIFO_DATA[63:0] = M_AXI_RDATA[63:0];
assign DEBUG[31:0] = {reg_wr_len[31:8],
1'd0, wr_state[2:0], 1'd0, rd_state[2:0]};
endmodule
4、verilog语法(位复制)
1、 a <= {b{1’b0}}
mem_test.v 有个语法需要注意一下:
在 Verilog 中,这行代码 a <= {b{1’b0}}; 表示位复制(Replication)赋值。
其具体含义如下:
位复制运算符 {n{m}}:
这是 Verilog 的一种缩写语法,表示将大括号内的值 m 重复 n 次。
在此例中,b 是重复次数。
1’b0 是被重复的内容(即一个位宽为 1 的二进制 0)。
具体操作:
该语句会将 0 复制 b 次。例如:
如果 b 等于 4,则 {4{1’b0}} 等同于 4’b0000。
如果 b 等于 8,则 {8{1’b0}} 等同于 8’b00000000。
赋值符号 <=:
这是非阻塞赋值(Non-blocking Assignment),通常用于时序逻辑(always @(posedge clk))中。它表示在当前时钟步进结束时,将右侧计算出的“全 0”结果赋给信号 a。
总结
这行代码的作用是:将信号 a 的所有位(共 b 位)清零。
注意: 在标准 Verilog 中,重复次数 b 必须是一个常量(编译时可确定),不能是运行时的变量。如果你需要根据变量动态清零,通常直接写作 a <= 0;,编译器会自动匹配 a 的位宽。
2、a <= {(b/8){c}}
这行代码 a <= {(b/8){c}}; 依然使用的是 Verilog 中的位复制(Replication)语法,表示将信号 c 重复拼接 b/8 次,并将结果赋给 a。
具体解析如下:
- 核心语法:{num{value}}
num (重复次数):这里是 b/8。它定义了括号内的内容要重复多少次。
value (被重复的内容):这里是 c。c 可以是一个单比特信号(如 1’b1),也可以是一个多比特向量。 - 运算逻辑
假设 b 是 32,c 是一个字节(8位信号 8’hAA):
重复次数 = 32 / 8 = 4 次。
运算结果 = {4{8’hAA}},即 32’hAAAAAAAA。
最后通过非阻塞赋值 <= 将这个值赋给 a。 - 重要限制与注意事项
在编写这段代码时,必须满足以下 Verilog 语法要求:
次数必须是常量:
b/8 的计算结果在编译阶段必须是一个确定的常数。如果 b 是一个变量(wire 或 reg),编译器会报错,因为硬件电路的位宽在运行时是无法动态改变的。通常 b 是通过 parameter 或 localparam 定义的。
整数除法:
如果 b 不能被 8 整除(例如 b=20),b/8 会进行截断取整(20/8 = 2)。
位宽匹配:
最终生成的总位宽(c 的位宽 * (b/8))应该与目标信号 a 的位宽一致,否则会发生截断或补零。
常见应用场景
这种写法常用于总线位宽扩展或内存对齐。例如,当你有一个 64 位宽的数据总线 a,你想用同一个字节 c(8 位)填充整个总线时,可以使用:
a <= {8{c}}; // 这里的 8 就是 64/8 的结果
三、工程下载
通过网盘分享的文件:02_pl_rw_ddr
链接: https://pan.baidu.com/s/1brcFxnT4RLFIq6HwDRPfdw?pwd=jwmf 提取码: jwmf
–来自百度网盘超级会员v8的分享
更多推荐
所有评论(0)