黑金例程pl_read_write_ps_ddr学习以及代码精读
最近学习一下DDR的使用,先学习了AXI4总线协议,又看了看DDRx相关资料,做个总结
先声明一下,黑金的例程可以,视频教程可以,pdf用户手册有点垃圾
可以先看看视频学个大概
【34】ALINX Zynq MPSoC XILINX FPGA视频教程 SDK 裸机开发—AXI总线协议介绍_哔哩哔哩_bilibili
【34】ALINX Zynq MPSoC XILINX FPGA视频教程 SDK 裸机开发—AXI总线协议介绍_哔哩哔哩_bilibili
https://www.bilibili.com/video/BV1Ha411H7Hc/?spm_id_from=333.788
推荐文章
了解AXI4总线协议,看着很详细,调着看
【数字IC基础】一文搞懂AXI (Advanced eXtensible Interface) 协议_数字IC前端入门-优快云专栏
学习算DDR容量
DDR-SDRAM内存颗粒 容量计算2-SDRAM,DDR3,DDR4实例计算,兼容设计_哔哩哔哩_bilibili
另外一些Debug博客
在vivado环境中如何debug? - 知乎 (zhihu.com)
FPGA的开发流程梳理: Vivado的RTL分析(RTL analysis)、综合(Synthesis)和实现(Implementation)的区别?分别负责什么工作-优快云博客
然后废话就不多说了直接上代码,几乎都有注释,我都注明了作用
top.v
`timescale 1ns / 1ps
//
// 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 top(
(*mark_debug="true"*) output error
);
// AXI4协议学习 https://download.youkuaiyun.com/blog/column/11976230/129024896
wire rst_n;
wire M_AXI_ACLK;
// Master Write Address
wire [0:0] M_AXI_AWID;
wire [31:0] M_AXI_AWADDR;
wire [7:0] M_AXI_AWLEN; // Burst Length: 0-255
wire [2:0] M_AXI_AWSIZE; // Burst Size: Fixed 2'b011 数据宽度64位,也就是2^(3)字节
wire [1:0] M_AXI_AWBURST; // Burst Type: Fixed 2'b01(Incremental Burst) (Incrementing-address 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 自定义用户信息
wire M_AXI_AWVALID; //写地址有效信号
wire M_AXI_AWREADY; //写地址准备信号,等待从机返回
// Master Write Data
wire [63:0] M_AXI_WDATA; //64位写数据
wire [7:0] M_AXI_WSTRB; //数据选通,对上面64位数据进行有效使能,每一位对应上面一个字节
wire M_AXI_WLAST; //表示一次突发的最后一个数据
wire [0:0] M_AXI_WUSER; // 自定义的用户信息
wire M_AXI_WVALID; // 写数据有效信号
wire M_AXI_WREADY; // 写数据准备信号,等待从机返回
// Master Write Response
wire [0:0] M_AXI_BID; // 响应ID,必须和AWID主机ID保持一致,等待从设备返回
wire [1:0] M_AXI_BRESP; // 写响应信号,从设备告诉主机写传输的状态
wire [0:0] M_AXI_BUSER; // 从设备自定义用户信号
wire M_AXI_BVALID; // 写响应有效信号,从设备发出。
wire M_AXI_BREADY; // 主机发出准备信号,表示可以接受响应信号
// Master Read Address
wire [0:0] M_AXI_ARID; // 读通道 ID 这个信号是读地址信号组的ID标签
wire [31:0] M_AXI_ARADDR; // 读通道地址
wire [7:0] M_AXI_ARLEN; // Burst Length: 0-255
wire [2:0] M_AXI_ARSIZE; // Burst Size: Fixed 2'b011 数据宽度64位,也就是2^(3)字节
wire [1:0] M_AXI_ARBURST; // Burst Type: Fixed 2'b01(Incremental Burst) (Incrementing-address burst :可指定长度的增量式突发传输)
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; // 主机自定义用户信号
wire M_AXI_ARVALID; // 地址有效信号
wire M_AXI_ARREADY; // 地址准备好接收信号,从机发出
// Master Read Data
wire [0:0] M_AXI_RID; // 响应ID,必须和AWID主机ID保持一致,等待从设备返回
wire [63:0] M_AXI_RDATA; // 所读出的数据,从机发出
wire [1:0] M_AXI_RRESP; // 读响应信号,从设备告诉主机写传输的状态
wire M_AXI_RLAST; // 表示一次突发的最后一个数据
wire [0:0] M_AXI_RUSER; // 主机自定义用户信号
wire M_AXI_RVALID; // 数据有效信号
wire M_AXI_RREADY; // 读数据准备信号,主机发出
wire [31:0] DEBUG;
(*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;
(*mark_debug="true"*)wire[23 : 0] debug_reg_wr_len;
(*mark_debug="true"*)wire[2 : 0] debug_wr_state;
(*mark_debug="true"*)wire[2 : 0] debug_rd_state;
assign debug_reg_wr_len = DEBUG[31:8];
assign debug_wr_state = DEBUG[6:4];
assign debug_rd_state = DEBUG[2:0];
mem_test
#(
.MEM_DATA_BITS(64), //数据位宽64
.ADDR_BITS(27) //地址位宽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}), // 在地址后面补3位,因为地址按64位宽设置的,但是在存取DDR的过程中,每位地址储存8位数据,故地址要*8
.WR_LEN({wr_burst_len,3'd0}), // 后面增加3位选通信号,好像没有起什么作用
.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(DEBUG) //
);
design_1_wrapper ps_block
(
.S_AXI_HP0_araddr (M_AXI_ARADDR ),
.S_AXI_HP0_arburst (M_AXI_ARBURST ),
.S_AXI_HP0_arcache (M_AXI_ARCACHE ),
.S_AXI_HP0_arid (M_AXI_ARID ),
.S_AXI_HP0_arlen (M_AXI_ARLEN ),
.S_AXI_HP0_arlock (M_AXI_ARLOCK ),
.S_AXI_HP0_arprot (M_AXI_ARPROT ),
.S_AXI_HP0_arqos (M_AXI_ARQOS ),
.S_AXI_HP0_arready (M_AXI_ARREADY ),
.S_AXI_HP0_arsize (M_AXI_ARSIZE ),
.S_AXI_HP0_arvalid (M_AXI_ARVALID ),
.S_AXI_HP0_rdata (M_AXI_RDATA ),
.S_AXI_HP0_rid (M_AXI_RID ),
.S_AXI_HP0_rlast (M_AXI_RLAST ),
.S_AXI_HP0_rready (M_AXI_RREADY ),
.S_AXI_HP0_rresp (M_AXI_RRESP ),
.S_AXI_HP0_rvalid (M_AXI_RVALID ),
.S_AXI_HP0_awaddr (M_AXI_AWADDR ),
.S_AXI_HP0_awburst (M_AXI_AWBURST ),
.S_AXI_HP0_awcache (M_AXI_AWCACHE ),
.S_AXI_HP0_awid (M_AXI_AWID ),
.S_AXI_HP0_awlen (M_AXI_AWLEN ),
.S_AXI_HP0_awlock (M_AXI_AWLOCK ),
.S_AXI_HP0_awprot (M_AXI_AWPROT ),
.S_AXI_HP0_awqos (M_AXI_AWQOS ),
.S_AXI_HP0_awready (M_AXI_AWREADY ),
.S_AXI_HP0_awsize (M_AXI_AWSIZE ),
.S_AXI_HP0_awvalid (M_AXI_AWVALID ),
.S_AXI_HP0_bid (M_AXI_BID ),
.S_AXI_HP0_bready (M_AXI_BREADY ),
.S_AXI_HP0_bresp (M_AXI_BRESP ),
.S_AXI_HP0_bvalid (M_AXI_BVALID ),
.S_AXI_HP0_wdata (M_AXI_WDATA ),
.S_AXI_HP0_wlast (M_AXI_WLAST ),
.S_AXI_HP0_wready (M_AXI_WREADY ),
.S_AXI_HP0_wstrb (M_AXI_WSTRB ),
.S_AXI_HP0_wvalid (M_AXI_WVALID ),
.axim_rst_n(rst_n),
.pl_clk0(M_AXI_ACLK),
.axi_hp_clk(M_AXI_ACLK)
);
endmodule
mem_test.v
//
// 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}});
/*错误检测,检测是否读出的数据与所构想设计的一样*/
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}}; //初始化写数据写入64‘d0,写计数为零
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}}; //开始设计写入数据,记得debug是先写入的1还是0???
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
aq_axi_master.v
/*
* 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, //64位写数据
output [7:0] M_AXI_WSTRB, //数据选通,对上面64位数据进行有效使能,每一位对应上面一个字节
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, // 响应ID,必须和AWID主机ID保持一致,等待从设备返回
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, // 读通道 ID 这个信号是读地址信号组的ID标签
output [31:0] M_AXI_ARADDR, // 读通道地址
output [7:0] M_AXI_ARLEN, // Burst Length: 0-255
output [2:0] M_AXI_ARSIZE, // Burst Size: Fixed 2'b011 数据宽度64位,也就是2^(3)字节
output [1:0] M_AXI_ARBURST, // Burst Type: Fixed 2'b01(Incremental Burst) (Incrementing-address burst :可指定长度的增量式突发传输)
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, // 响应ID,必须和AWID主机ID保持一致,等待从设备返回
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); //写数据请求,给men模块,让其发送数据可能是先让其输出一个数据占据寄存器,有效的时候能够立即输出,经过Debug验证正确
//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; //写入长度,固定,32位,与传入信号长度并不匹配,
reg_awvalid <= 1'b0; //写地址有效信号
reg_wvalid <= 1'b0; //写数据有效信号
reg_w_last <= 1'b0; //一次突发写入的最后一次发送
reg_w_len[7:0] <= 8'd0; //写入数据长度,但是只有在127的时候awvalid有效,之后按照传输进行累减,表明传输数据。
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; //看不懂,应该和fifo有关,但是此代码也没用到,只是模拟
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; //初始化写长度,0-255,从0开始故要减一,在输入的时候已经后面补了3’b000,数据个数在[10:3]内
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 //如果FIFO不空,里面有数据,或者 一次突发长度小于256开始写地址,因为FIFO没用,没理解或的作用
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 //如果一次突发长度大于256
reg_w_len[7:0] <= 8'hFF; //此次传输最大长度256
reg_w_last <= 1'b0; //不是这次突发传输的结束
reg_w_stb[7:0] <= 8'hFF; //选通,64位数据全都有效
end else begin //突发传输长度小于256
reg_w_len[7:0] <= reg_wr_len[10:3]; //赋值正确的传输数据长度
reg_w_last <= 1'b1; //是最后一次传输
reg_w_stb[7:0] <= 8'hFF; //64位全部有效
/*
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 //如果写数据准备信号OK,且FIFO不口,这只是判断,硬件综合后,无论是否进入此阶段,只要awready有效就能开始写入,此阶段只是进行写数据计数,感觉有点别扭不可控
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; //地址累加 128*8 128个数据 每个数据64位也就是8个8位
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; //UID,默认,不去修改
assign M_AXI_AWADDR[31:0] = reg_wr_adrs[31:0]; //写地址赋值
assign M_AXI_AWLEN[7:0] = reg_w_len[7:0]; //虽然reg_w_len在累减,但是只有在127的时候valid有效
assign M_AXI_AWSIZE[2:0] = 2'b011; //一个数据的大小 64位 = 8个8位 8=2^3
assign M_AXI_AWBURST[1:0] = 2'b01; //Burst type
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; //一次突发的最后一个数据 和reg_w_last不同
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; //读长度赋值,减一也是因为0-255
end //
reg_arvalid <= 1'b0; //写地址有效信号失效
reg_r_len[7:0] <= 8'd0; //读长度归零
end //
S_RA_WAIT: begin //读地址等待阶段
if(~RD_FIFO_AFULL) begin //如果FIFO不满,可以往里面写
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 //判断是否超过256
reg_r_last <= 1'b0; //超过,不是最后一次传输
reg_r_len[7:0] <= 8'd255; //长度最大值为256
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; //主机已经准备好接受的信号,与从机发送的数据有效信号同步, debug看下时序,就是相当于连起来了,同步执行
assign RD_READY = (rd_state == S_RD_IDLE)?1'b1:1'b0; //读数据准备
assign RD_FIFO_WE = M_AXI_RVALID; //传给FIFO,读有效
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
有两个时序值得说一下
第一个 这个判断是下一个时序才会起效,req在上一个时钟使能,在下一个时钟判断起效
第二个 valid
在valid和ready使能的同时,此时钟下的信号就是将要写进去的信号