本文完全参考野火的DDR3读写控制设计,原文十分详细,需要的可以去看看。
一、AXI4接口详解
AXI接口由5个独立的通道构成,分别是读地址、读数据、写地址、写数据、写响应。
如下是读传输过程示意图,使用读地址与读数据通道。主机首先在读地址通道给出读地址和控制信号,然后从机由读数据通道返回读出的数据。且可以看到,主机只给出了一个地址,从该地址连续突发读出4个数据。
写传输过程如下图所示。使用写地址、写数据和写响应三个通道。主机在写地址通道给出写地址和控制信号,然后在写数据通道连续突发写四个数据。从机在接收数据之后,在写响应通道给出响应信号。
如下图是AXI4的接口描述,左侧是IP核生成的接口信号,括号里的是本工程定义的信号。可以发现,接口信号中以“s_axi_aw”开头的是写地址通道信号,以“s_axi_w”开头的是写数据通道信号,以“s_axi_b”开头的是写响应通道信号,以“s_axi_ar”开头的是读地址通道信号,以“s_axi_r”开头的是读数据响应通道信号。
这里也顺便将DDR3 IP核所有的接口描述一下。带ddr3的信号是引脚信号,与外部ddr3存储器相连,不需要管;带app的信号是本地接口维护命令信号,不用使用,输入信号给0,输出信号空接;带ui的信号是给用户侧使用的时钟信号和复位信号,注意复位信号是高电平有效;sys_clk_i是ip核系统时钟,一般设置为200M,这样参考时钟可以选择use system clk,所以这里的clk_ref_i被注释了;还要注意ip系统复位输入信号sys_rst,是低电平有效。
突发写时序
写数据时序图如下。
AXI4突发写可以分为7个状态,分别为:
1.写空闲:等待突发写触发信号。
2.写通道地址等待:准备好写地址AWADDR,然后拉高AWVALID。
3.写通道写地址:从机接受到AWVALID,发出AWREADY。
4.写数据等待:准备好写数据WDATA,拉高WVALID。
5.写数据循环:从机接受WVALID,确认数据WDATA有效并且接受,发出WREADY,AXI是突发传输:循环该操作到接受到WLAST最后一个数据标志位。
6.接受写应答:接受到从机发出的BVALID,主机发出BREADY。
7.写结束:拉低未拉低的信号,进入写空闲。
突发读时序
读数据时序图如下。
AXI4突发读可以分为6个状态,分别为:
1.读空闲:等待突发读触发信号。
2.读通道写地址等待:准备好写地址ARADDR,然后拉高ARVALID。
3.读通道写地址:从机接收到ARVALID,发出ARREADY。
4.读数据等待:从机准备好读数据RDATA,并拉高RVALID。
5.读数据循环:主机接收RVALID,确认数据RDATA有效并接收,发出RREADY给从机,AXI是突发传输:循环该操作到接收到RLAST最后一个数据标志位。
6.读结束:拉低未拉低的信号,进入读空闲。
接口信号详解
- VALID和READY信号:AXI接口的读写在于握手信号VALID与READY。VALID信号由源端产生,表示当前地址或数据线上的信息是有效的;READY信号由目的端产生,表示已经准备好接收地址、数据以及控制信息。VALID和READY信号提供了3种握手机制,即VALIDbeforeREADY握手,READYbeforeVALID握手,VALIDwithREADY握手。
- AWID与ARID:对于只有一个主从设备,该值可设置为任意值。
- AWADDR与ARADDR:主机只给出突发传输第一个字节的地址,从机必须计算突发传输后续的地址。突发传输不能跨4KB边界。这是因为每一个从机分配的是4KB空间。以32位地址为例,[31:12]相等的地址都是同一个page,没有跨4K边界。即[11:0]可以为0~0xFFF。 例如0x1000和0x2000就是在不同的page,跨了4K边界。0x1000和0x1FFF则是在同一个page,没有跨4K边界。同理,0x1FFF和0x2000,虽然他们是相邻的byte,但也跨了4K边界。所以,这是为了防止一次突发跨越两个从机的边界。
- AWLEN与ARLEN:读写传输的突发长度。突发长度==AxLEN[7:0]+1。
- ARSIZE与AWSIZE:读写突发传输大小。突发大小==2^(AxSIZE[2:0])字节。
- AWBURST与ARBURST:突发类型。2'b00:FIXED,2'b01:INCR,2'b10:WRAP,2'b11:Reserved。FIXED:突发传输过程中地址固定;INCR:增量突发,传输过程中,地址递增,增加量取决于AxSIZE的值。WRAP:回环突发,和增量突发类似,但会在特定高地址的边界处回到低地址处。
- WSTRB:有效字节,WSTRB[n:0]对应于对应的写字节,WSTRB[n]对应WDATA[8n+7:8n],也就是对于的数据宽度的字节数是否有效。对于一般应用,将WSTRB全部置1即可,保证全部数据有效。读通道无该信号。
- WLAST与RLAST:拉高表示传输最后一个数据,意味着传输结束。
IP核配置
选择AXI4 Interface。
Data Width根据开发板板载DDR3数据位宽设置即可,在我们的程序设计中没有体现,关注点还应该是AXI。
接下来是AXI接口的参数配置界面,本工程将AXI的Data_Width设置为64。
200M的IP核系统时钟。
参考时钟选择系统时钟。
这里选择第二个。
其他选项保持默认即可,生成IP核。
这里强调一点,AXI接口的地址是一个字节一个地址的,千万不要和native接口的ddr芯片的地址混淆。
二、代码设计
模块框图如下。具体流程为:用户将需要存储的数据存入写fifo,axi_ctrl模块根据写fifo的状态产生写突发信号控制axi_master_write模块,完成写操作;用户给出读请求,axi_ctrl模块根据读fifo的状态产生读突发信号控制axi_master_read模块,完成读操作。
axi_master_write模块
点击查看代码
module axi_master_write
(
input ARESETN , //axi复位
input ACLK , //axi总时钟
//axi4写通道地址通道
output [3:0] M_AXI_AWID , //写地址ID,用来标志一组写信号
output [31:0] M_AXI_AWADDR , //写地址,给出一次写突发传输的写地址
output [7:0] M_AXI_AWLEN , //突发长度,给出突发传输的次数
output [2:0] M_AXI_AWSIZE , //突发大小,给出每次突发传输的字节数
output [1:0] M_AXI_AWBURST, //突发类型
output M_AXI_AWLOCK , //总线锁信号,可提供操作的原子性
output [3:0] M_AXI_AWCACHE, //内存类型,表明一次传输是怎样通过系统的
output [2:0] M_AXI_AWPROT , //保护类型,表明一次传输的特权级及安全等级
output [3:0] M_AXI_AWQOS , //质量服务QoS
output M_AXI_AWVALID, //有效信号,表明此通道的地址控制信号有效
input M_AXI_AWREADY, //表明“从”可以接收地址和对应的控制信号
//axi4写通道数据通道
output [63:0] M_AXI_WDATA , //写数据
output [7:0] M_AXI_WSTRB , //写数据有效的字节线
output M_AXI_WLAST , //表明此次传输是最后一个突发传输
output M_AXI_WVALID , //写有效,表明此次写有效
input M_AXI_WREADY , //表明从机可以接收写数据
//axi4写通道应答通道
input [3:0] M_AXI_BID , //写响应ID TAG
input [1:0] M_AXI_BRESP , //写响应,表明写传输的状态
input M_AXI_BVALID , //写响应有效
output M_AXI_BREADY , //表明主机能够接收写响应
//用户端信号
input WR_START , //写突发触发信号
input [31:0] WR_ADRS , //地址
input [9:0] WR_LEN , //长度
output WR_READY , //写空闲
output WR_FIFO_RE , //连接到写fifo的读使能
input [63:0] WR_FIFO_DATA , //连接到fifo的读数据
output WR_DONE //完成一次突发
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
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 define
reg [2:0] wr_state ; //状态寄存器
reg [31:0] reg_wr_adrs; //地址寄存器
reg reg_awvalid; //地址有效握手信号
reg reg_wvalid ; //数据有效握手信号
reg reg_w_last ; //传输最后一个数据
reg [7:0] reg_w_len ; //突发长度最大256,实测128最佳
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//写完成信号的写状态完成
assign WR_DONE = (wr_state == S_WR_DONE);
//写fifo的读使能为axi数据握手成功
assign WR_FIFO_RE = ((reg_wvalid & M_AXI_WREADY ));
//只有一个主机,可随意设置
assign M_AXI_AWID = 4'b1111;
//把地址赋予总线
assign M_AXI_AWADDR[31:0] = reg_wr_adrs[31:0];
//一次突发传输1长度
assign M_AXI_AWLEN[7:0] = WR_LEN-'d1;
//表示AXI总线每个数据宽度是8字节,64位
assign M_AXI_AWSIZE[2:0] = 3'b011;
//01代表地址递增,10代表递减
assign M_AXI_AWBURST[1:0] = 2'b01;
assign M_AXI_AWLOCK = 1'b0;
assign M_AXI_AWCACHE[3:0] = 4'b0000;
assign M_AXI_AWPROT[2:0] = 3'b000;
assign M_AXI_AWQOS[3:0] = 4'b0000;
//地址握手信号AWVALID
assign M_AXI_AWVALID = reg_awvalid;
//fifo数据赋予总线
assign M_AXI_WDATA[63:0] = WR_FIFO_DATA[63:0];
assign M_AXI_WSTRB[7:0] = 8'hFF;
//写到最后一个数据
assign M_AXI_WLAST =(reg_w_len[7:0] == 8'd0)?'b1:'b0;
//数据握手信号WVALID
assign M_AXI_WVALID = reg_wvalid;
//这个信号是告诉AXI我收到你的应答
assign M_AXI_BREADY = M_AXI_BVALID;
//axi状态机空闲信号
assign WR_READY = (wr_state == S_WR_IDLE)?1'b1:1'b0;
//axi写过程状态机
always @(posedge ACLK or negedge ARESETN) begin
if(!ARESETN) begin
wr_state <= S_WR_IDLE;
reg_wr_adrs[31:0] <= 32'd0;
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b0;
reg_w_last <= 1'b0;
reg_w_len[7:0] <= 8'd0;
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];
end
reg_awvalid <= 1'b0;
reg_wvalid <= 1'b0;
reg_w_len[7:0] <= 8'd0;
end
S_WA_WAIT: begin//写地址等待
wr_state <= S_WA_START;//等待一个周期
end
S_WA_START: begin
wr_state <= S_WD_WAIT;//写数据等待
reg_awvalid <= 1'b1; //拉高地址有效信号
reg_wvalid <= 1'b1;//拉高数据有效信号
end
S_WD_WAIT: begin
if(M_AXI_AWREADY) begin//等待写地址就绪
wr_state <= S_WD_PROC;
reg_w_len<=WR_LEN-'d1;//127代表128个长度,0代表1个长度
reg_awvalid <= 1'b0;
end
end
S_WD_PROC: begin//等待AXI写数据就绪信号
if(M_AXI_WREADY) begin//拉高了就可以输出fifo使能信号开始读
if(reg_w_len[7:0] == 8'd0) begin//完成数据写过程
wr_state <= S_WR_WAIT;
reg_wvalid <= 1'b0;//此信号拉低,写fifo读使能无效
reg_w_last<='b1;
//读到最后一个数据,拉高这个标志信号告诉AXI总线这是最后一个
//如果不拉高传输不会成功
end
else begin
reg_w_len[7:0] <= reg_w_len[7:0] -8'd1;
end
end
end
S_WR_WAIT: begin//等待写的AXI应答信号
reg_w_last<='b0;
//M_AXI_BVALID拉高表示写成功,然后状态机完成一次突发传输
if(M_AXI_BVALID) begin
wr_state <= S_WR_DONE;
end
end
S_WR_DONE: begin //写完成
wr_state <= S_WR_IDLE;
end
default: begin
wr_state <= S_WR_IDLE;
end
endcase
end
end
endmodule
axi_master_read模块
点击查看代码
module axi_master_read
(
input ARESETN,//axi复位
input ACLK, //axi时钟
//axi读通道写地址
output [3:0] M_AXI_ARID , //读地址ID,用来标志一组写信号
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 , //质量服务QOS
output M_AXI_ARVALID, //有效信号,表明此通道的地址控制信号有效
input M_AXI_ARREADY, //表明“从”可以接收地址和对应的控制信号
//axi读通道读数据
input [3:0] M_AXI_RID , //读ID tag
input [63:0] M_AXI_RDATA , //读数据
input [1:0] M_AXI_RRESP , //读响应,表明读传输的状态
input M_AXI_RLAST , //表明读突发的最后一次传输
input M_AXI_RVALID, //表明此通道信号有效
output M_AXI_RREADY, //表明主机能够接收读数据和响应信息
//用户端fifo接口
input RD_START , //读突发触发信号
input [31:0] RD_ADRS , //地址
input [9:0] RD_LEN , //长度
output RD_READY , //读空闲
output RD_FIFO_WE , //连接到读fifo的写使能
output [63:0] RD_FIFO_DATA, //连接到读fifo的写数据
output RD_DONE //完成一次突发
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
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 define
reg [2:0] rd_state ; //状态寄存器
reg [31:0] reg_rd_adrs; //地址寄存器
reg [31:0] reg_rd_len ; //突发长度寄存器
reg reg_arvalid; //地址有效寄存器
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
assign RD_DONE = (rd_state == S_RD_DONE) ;
assign M_AXI_ARID = 4'b1111;//地址id
assign M_AXI_ARADDR[31:0] = reg_rd_adrs[31:0];//地址
assign M_AXI_ARLEN[7:0] = RD_LEN-32'd1;//突发长度
assign M_AXI_ARSIZE[2:0] = 3'b011;//表示AXI总线每个数据宽度是8字节,64位
assign M_AXI_ARBURST[1:0] = 2'b01;//地址递增方式传输
assign M_AXI_ARLOCK = 1'b0;
assign M_AXI_ARCACHE[3:0] = 4'b0000;
assign M_AXI_ARPROT[2:0] = 3'b000;
assign M_AXI_ARQOS[3:0] = 4'b0000;
assign M_AXI_ARVALID = reg_arvalid;
assign M_AXI_RREADY = M_AXI_RVALID;
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];//读fifo的写数据信号
// 读状态机
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;
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[9:0] -32'd1;
end
reg_arvalid <= 1'b0;
end
S_RA_WAIT: begin//写地址等待
rd_state <= S_RA_START;
end
S_RA_START: begin//写地址
rd_state <= S_RD_WAIT;
reg_arvalid <= 1'b1;//拉高地址有效
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 //收到最后一个数据
rd_state<= S_RD_DONE;
end
end
end
S_RD_DONE:begin
rd_state <= S_RD_IDLE;
end
endcase
end
end
endmodule
axi_ctrl模块
点击查看代码
module axi_ctrl
#(
parameter DDR_WR_LEN=128,//写突发长度 128个64bit
parameter DDR_RD_LEN=128 //读突发长度 128个64bit
)
(
input wire ui_clk , //时钟
input wire ui_rst , //复位,高电平有效
input wire pingpang , //乒乓操作
//写fifo写数据端
input wire [31:0] wr_b_addr , //写DDR首地址
input wire [31:0] wr_e_addr , //写DDR末地址
input wire user_wr_clk, //写FIFO写时钟
input wire data_wren , //写FIFO写请求
//写进fifo数据长度,可根据写fifo的写端口数据长度自行修改
//写FIFO写数据 16位,此时用64位是为了兼容32,64位
input wire [63:0] data_wr ,
input wire wr_rst ,
//读fifo读数据端
input wire [31:0] rd_b_addr , //读DDR首地址
input wire [31:0] rd_e_addr , //读DDR末地址
input wire user_rd_clk, //读FIFO读时钟
input wire data_rden , //读FIFO读请求
//读出fifo数据长度,可根据读fifo的读端口数据长度自行修改
//读FIFO读数据,16位,此时用64位是为了兼容32,64位
output wire [63:0] data_rd ,
input wire rd_rst ,
input wire read_enable,//读ddr使能
output wire data_rd_valid,
//突发写模块控制信号
output wire wr_burst_req , //写突发触发信号
output wire[31:0] wr_burst_addr , //地址
output wire[9:0] wr_burst_len , //长度
input wire wr_ready , //写空闲
input wire wr_fifo_re , //连接到写fifo的读使能
output wire [63:0] wr_fifo_data , //连接到fifo的读数据
input wire wr_burst_finish , //完成一次突发
//突发读模块控制信号
output wire rd_burst_req , //读突发触发信号
output wire[31:0] rd_burst_addr , //地址
output wire[9:0] rd_burst_len , //长度
input wire rd_ready , //读空闲
input wire rd_fifo_we , //连接到读fifo的写使能
input wire[63:0] rd_fifo_data , //连接到读fifo的写数据
input wire rd_burst_finish //完成一次突发
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg define
reg wr_burst_req_reg ; //写突发寄存器
reg [31:0]wr_burst_addr_reg; //写地址寄存器
reg [9:0] wr_burst_len_reg ; //写长度寄存器
reg rd_burst_req_reg ; //读突发寄存器
reg [31:0]rd_burst_addr_reg; //读地址寄存器
reg [9:0] rd_burst_len_reg ; //读长度寄存器
//读写地址复位打拍寄存器
reg wr_rst_reg1;
reg wr_rst_reg2;
reg rd_rst_reg1;
reg rd_rst_reg2;
reg pingpang_reg;//乒乓操作指示寄存器
//wire define
//写fifo信号
wire wr_fifo_wr_clk ;
wire wr_fifo_rd_clk ;
wire [63:0] wr_fifo_din ;
wire wr_fifo_wr_en ;
wire wr_fifo_rd_en ;
wire [63:0] wr_fifo_dout ;
wire wr_fifo_full ;
wire wr_fifo_almost_full ;
wire wr_fifo_empty ;
wire wr_fifo_almost_empty ;
wire [9:0] wr_fifo_rd_data_count ;
wire [11:0] wr_fifo_wr_data_count;
//读fifo信号
wire rd_fifo_wr_clk ;
wire rd_fifo_rd_clk ;
wire [63:0] rd_fifo_din ;
wire rd_fifo_wr_en ;
wire rd_fifo_rd_en ;
wire [63:0] rd_fifo_dout ;
wire rd_fifo_full ;
wire rd_fifo_almost_full ;
wire rd_fifo_empty ;
wire rd_fifo_almost_empty ;
wire [11:0]rd_fifo_rd_data_count ;
wire [9:0] rd_fifo_wr_data_count ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
assign wr_burst_req = wr_burst_req_reg; //写突发请求
assign wr_burst_addr = wr_burst_addr_reg; //写地址
assign wr_burst_len = DDR_WR_LEN; //写长度
assign rd_burst_req = rd_burst_req_reg; //读突发请求
assign rd_burst_addr = rd_burst_addr_reg; //读地址
assign rd_burst_len = DDR_RD_LEN; //读长度
//写fifo写时钟位用户端时钟
assign wr_fifo_wr_clk = user_wr_clk;
//写fifo读时钟位axi总时钟
assign wr_fifo_rd_clk = ui_clk; //axi接口时钟是ui_clk
//写fifo非满为用户输入数据
assign wr_fifo_din = data_wr;
//写fifo非满为用户输入数据使能
assign wr_fifo_wr_en = data_wren;
//写fifo非空为axi写主机读取使能
assign wr_fifo_rd_en = wr_fifo_re;
//写fifo非空为axi写主机读取数据
assign wr_fifo_data = wr_fifo_dout;
//读fifo写时钟位axi读主机时钟
assign rd_fifo_wr_clk=ui_clk; //axi接口时钟是ui_clk
//读fifo读时钟位用户时钟
assign rd_fifo_rd_clk=user_rd_clk;
//读fifo读使能为用户使能
assign rd_fifo_rd_en =data_rden;
//读fifo读数据为用户使能
assign data_rd =rd_fifo_dout;
//读fifo写使能为axi读主机写使能
assign rd_fifo_wr_en =rd_fifo_we;
//读fifo写使能为axi读主机写数据
assign rd_fifo_din =rd_fifo_data;
assign data_rd_valid=~rd_fifo_empty;
//对写复位信号的跨时钟域打2拍
always@(posedge ui_clk or posedge ui_rst) begin
if(ui_rst==1'b1)begin
wr_rst_reg1<=1'b0;
wr_rst_reg2<=1'b0;
end
else begin
wr_rst_reg1<=wr_rst;
wr_rst_reg2<=wr_rst_reg1;
end
end
//对读复位信号的跨时钟域打2拍
always@(posedge ui_clk or posedge ui_rst) begin
if(ui_rst==1'b1)begin
rd_rst_reg1<=1'b0;
rd_rst_reg2<=1'b0;
end
else begin
rd_rst_reg1<=rd_rst;
rd_rst_reg2<=rd_rst_reg1;
end
end
//写burst请求产生
always@(posedge ui_clk or posedge ui_rst) begin
if(ui_rst==1'b1)begin
wr_burst_req_reg<=1'b0;
end
//fifo数据长度大于一次突发长度并且axi写空闲
else if((wr_fifo_rd_data_count+9'd2)>=DDR_WR_LEN && wr_ready==1'b1 ) begin
wr_burst_req_reg<=1'b1;
end
else begin
wr_burst_req_reg<=1'b0;
end
end
//完成一次突发对地址进行相加
//相加地址长度=突发长度x8,64位等于8字节
//128*8=1024
always@(posedge ui_clk or posedge ui_rst) begin
if(ui_rst==1'b1)begin
wr_burst_addr_reg<=wr_b_addr;
pingpang_reg<=1'b0;
end
//写复位信号上升沿
else if(wr_rst_reg1&(~wr_rst_reg2)) begin
wr_burst_addr_reg<=wr_b_addr;
end
else if(wr_burst_finish==1'b1)begin
wr_burst_addr_reg<=wr_burst_addr_reg+DDR_WR_LEN*8;
//判断是否是乒乓操作
if(pingpang==1'b1) begin
//结束地址为2倍的接受地址,有两块区域
if(wr_burst_addr_reg>=(
(wr_e_addr-wr_b_addr)*2+wr_b_addr-DDR_WR_LEN*8))
begin
wr_burst_addr_reg<=wr_b_addr;
end
//根据地址,pingpang_reg为0或者1
//用于指示读操作与写操作地址不冲突
if(wr_burst_addr_reg<wr_e_addr) begin
pingpang_reg<=1'b0;
end
else begin
pingpang_reg<=1'b1;
end
end
//非乒乓操作
else begin
if(wr_burst_addr_reg>=(wr_e_addr-DDR_WR_LEN*8))
begin
wr_burst_addr_reg<=wr_b_addr;
end
end
end
else begin
wr_burst_addr_reg<=wr_burst_addr_reg;
end
end
//读burst请求产生
always@(posedge ui_clk or posedge ui_rst) begin
if(ui_rst==1'b1)begin
rd_burst_req_reg<=1'b0;
end
//fifo可写长度大于一次突发长度并且axi读空闲,fifo总长度1024
else if(rd_fifo_wr_data_count<=(10'd1000-DDR_RD_LEN)
&& rd_ready==1'b1 &&read_enable==1'b1)
begin
rd_burst_req_reg<=1'b1;
end
else begin
rd_burst_req_reg<=1'b0;
end
end
//完成一次突发对地址进行相加
//相加地址长度=突发长度x8,64位等于8字节
//128*8=1024
always@(posedge ui_clk or posedge ui_rst) begin
if(ui_rst==1'b1)begin
if(pingpang==1'b1) rd_burst_addr_reg<=rd_e_addr;
else rd_burst_addr_reg<=rd_b_addr;
end
else if(rd_rst_reg1&(~rd_rst_reg2)) begin
rd_burst_addr_reg<=rd_b_addr;
end
else if(rd_burst_finish==1'b1)begin
rd_burst_addr_reg<=rd_burst_addr_reg+DDR_RD_LEN*8;//地址累加
//乒乓操作
if(pingpang==1'b1) begin
//到达结束地址
if((rd_burst_addr_reg==(rd_e_addr-DDR_RD_LEN*8))||
(rd_burst_addr_reg==((rd_e_addr-rd_b_addr)*2+rd_b_addr-DDR_RD_LEN*8)))
begin
//根据写指示地址信号,对读信号进行复位
if(pingpang_reg==1'b1) rd_burst_addr_reg<=rd_b_addr;
else rd_burst_addr_reg<=rd_e_addr;
end
end
else begin //非乒乓操作
if(rd_burst_addr_reg>=(rd_e_addr-DDR_RD_LEN*8))
begin
rd_burst_addr_reg<=rd_b_addr;
end
end
end
else begin
rd_burst_addr_reg<=rd_burst_addr_reg;
end
end
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- wr_fifo_inst -------------
//写fifo
wr_fifo wr_fifo_inst (
.wr_rst(wr_rst||ui_rst), // 写复位
.rd_rst(wr_rst||ui_rst), //读复位
.wr_clk(wr_fifo_wr_clk), // 写时钟
.rd_clk(wr_fifo_rd_clk), // 读时钟
.din (wr_fifo_din ), // 外部写进fifo的数据 16位
.wr_en (wr_fifo_wr_en ), // 写使能
.rd_en (wr_fifo_rd_en ), // 读使能
.dout (wr_fifo_dout ), // 输出给ddr的axi写数据,写进ddr 64位
.full (wr_fifo_full ), // fifo满信号
.almost_full (wr_fifo_almost_full ), //fifo几乎满信号
.empty (wr_fifo_empty ), //fifo空信号
.almost_empty (wr_fifo_almost_empty ),
.rd_data_count(wr_fifo_rd_data_count), // 可读数据个数
.wr_data_count(wr_fifo_wr_data_count) // 可写数据个数
);
//------------- rd_fifo_inst -------------
//读fifo
rd_fifo rd_fifo_inst (
.wr_rst(rd_rst||ui_rst), // 写复位
.rd_rst(rd_rst||ui_rst), //读复位
.wr_clk(rd_fifo_wr_clk), // 写时钟
.rd_clk(rd_fifo_rd_clk), // 读时钟
.din (rd_fifo_din ), // ddr读出的数据,写进fifo 64位
.wr_en (rd_fifo_wr_en ), // 写使能
.rd_en (rd_fifo_rd_en ), // 读使能
.dout (rd_fifo_dout ), // 最终我们读出的数据 64位
.full (rd_fifo_full ), // fifo满信号
.almost_full (rd_fifo_almost_full ),//fifo几乎满信号
.empty (rd_fifo_empty ), //空信号
.almost_empty (rd_fifo_almost_empty ),
.rd_data_count(rd_fifo_rd_data_count), // 可读数据个数
.wr_data_count(rd_fifo_wr_data_count) // 可写数据个数
);
endmodule
对于读突发请求的产生,也是一样的道理,当读fifo中的可写数据长度大于一次突发长度并且axi读空闲,且用户侧给ddr3读使能read_enable有效,就拉高突发读请求。代码中fifo总长度设置的是1024,所以用了一个1000,这个数据要求并不严格,只要能保证我再进行一次突发。不会将读fifo存满即可。突发读模块检测到有效信号开始突发读,读出的数据直接存进读fifo中,当检测到突发读完成信号,就需要更新突发地址。
axi_ddr_top模块
该模块将各个模块以及IP核集成,封装成一个类FIFO的结构。
点击查看代码
module axi_ddr_top #
(
parameter DDR_WR_LEN=128,//写突发长度 最大128个64bit
parameter DDR_RD_LEN=128//读突发长度 最大128个64bit
)
(
//50m的时钟与复位信号
input wire ddr3_clk , //ddr3 ip核时钟200M
input wire sys_rst_n , //外部复位
inout [31:0] ddr3_dq , //数据线
inout [3:0] ddr3_dqs_n , //数据选取脉冲差分信号
inout [3:0] ddr3_dqs_p , //数据选取脉冲差分信号
output [14:0] ddr3_addr , //地址线
output [2:0] ddr3_ba , //bank线
output ddr3_ras_n , //行使能信号,低电平有效
output ddr3_cas_n , //列使能信号,低电平有效
output ddr3_we_n , //写使能信号,低电平有效
output ddr3_reset_n , //ddr3复位
output [0:0] ddr3_ck_p , //ddr3差分时钟
output [0:0] ddr3_ck_n , //ddr3差分时钟
output [0:0] ddr3_cke , //ddr3时钟使能信号
output [0:0] ddr3_cs_n , //ddr3片选信号
output [3:0] ddr3_dm , //ddr3掩码
output [0:0] ddr3_odt , //odt阻抗
input wire pingpang , //乒乓操作,1使能,0不使能
input wire[31:0]wr_b_addr , //写DDR首地址
input wire[31:0]wr_e_addr , //写DDR末地址
input wire user_wr_clk, //写FIFO写时钟
input wire data_wren , //写FIFO写请求
//写进fifo数据长度,可根据写fifo的写端口数据长度自行修改
//写FIFO写数据 16位,此时用64位是为了兼容32,64位
input wire[63:0]data_wr , //写数据 低16有效
input wire wr_rst , //写地址复位
input wire[31:0]rd_b_addr , //读DDR首地址
input wire[31:0]rd_e_addr , //读DDR末地址
input wire user_rd_clk, //读FIFO读时钟
input wire data_rden , //读FIFO读请求
//读出fifo数据长度,可根据读fifo的读端口数据长度自行修改
//读FIFO读数据,16位,此时用64位是为了兼容32,64位
output wire[63:0]data_rd , //读数据 低16有效
input wire rd_rst , //读地址复位
input wire read_enable, //读使能
output wire data_rd_valid,
output wire ui_clk , //输出时钟125m
output wire ui_rst , //输出复位,高有效
output wire calib_done //ddr初始化完成
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire define
//axi写通道写地址
wire [3:0] M_AXI_WR_awid; //写地址ID,用来标志一组写信号
wire [31:0]M_AXI_WR_awaddr; //写地址,给出一次写突发传输的写地址
wire [7:0] M_AXI_WR_awlen; //突发长度,给出突发传输的次数
wire [2:0] M_AXI_WR_awsize; //突发大小,给出每次突发传输的字节数
wire [1:0] M_AXI_WR_awburst;//突发类型
wire [0:0] M_AXI_WR_awlock; //总线锁信号,可提供操作的原子性
wire [3:0] M_AXI_WR_awcache;//内存类型,表明一次传输是怎样通过系统的
wire [2:0] M_AXI_WR_awprot; //保护类型,表明一次传输的特权级及安全等级
wire [3:0] M_AXI_WR_awqos; //质量服务QoS
wire M_AXI_WR_awvalid;//有效信号,表明此通道的地址控制信号有效
wire M_AXI_WR_awready;//表明“从”可以接收地址和对应的控制信号
//axi写通道读数据
wire [63:0]M_AXI_WR_wdata; //写数据
wire [7:0] M_AXI_WR_wstrb; //写数据有效的字节线
//用来表明哪8bits数据是有效的
wire M_AXI_WR_wlast; //表明此次传输是最后一个突发传输
wire M_AXI_WR_wvalid; //写有效,表明此次写有效
wire M_AXI_WR_wready; //表明从机可以接收写数据
//axi写通道读应答
wire [3:0] M_AXI_WR_bid; //写响应ID TAG
wire [1:0] M_AXI_WR_bresp; //写响应,表明写传输的状态
wire M_AXI_WR_bvalid; //写响应有效
wire M_AXI_WR_bready; //表明主机能够接收写响应
//axi读通道写地址
wire [3:0] M_AXI_RD_arid; //读地址ID,用来标志一组写信号
wire [31:0]M_AXI_RD_araddr; //读地址,给出一次写突发传输的读地址
wire [7:0] M_AXI_RD_arlen; //突发长度,给出突发传输的次数
wire [2:0] M_AXI_RD_arsize; //突发大小,给出每次突发传输的字节数
wire [1:0] M_AXI_RD_arburst;//突发类型
wire [1:0] M_AXI_RD_arlock; //总线锁信号,可提供操作的原子性
wire [3:0] M_AXI_RD_arcache;//内存类型,表明一次传输是怎样通过系统的
wire [2:0] M_AXI_RD_arprot; //保护类型,表明一次传输的特权级及安全等级
wire [3:0] M_AXI_RD_arqos; //质量服务QOS
wire M_AXI_RD_arvalid;//有效信号,表明此通道的地址控制信号有效
wire M_AXI_RD_arready;//表明“从”可以接收地址和对应的控制信号
//axi读通道读数据
wire [3:0] M_AXI_RD_rid; //读ID tag
wire [63:0]M_AXI_RD_rdata; //读数据
wire [1:0] M_AXI_RD_rresp; //读响应,表明读传输的状态
wire M_AXI_RD_rlast; //表明读突发的最后一次传输
wire M_AXI_RD_rvalid; //表明此通道信号有效
wire M_AXI_RD_rready; //表明主机能够接收读数据和响应信息
//axi主机用户写控制信号
wire wr_burst_req ;
wire [31:0] wr_burst_addr ;
wire [9:0] wr_burst_len ;
wire wr_ready ;
//axi写数据与使能fifo接口
wire wr_fifo_re ;
wire [63:0] wr_fifo_data ;
wire wr_burst_finish;
//axi主机用户读控制信号
wire rd_burst_req ;
wire [31:0] rd_burst_addr ;
wire [9:0] rd_burst_len ;
wire rd_ready ;
//axi读数据与使能fifo接口
wire rd_fifo_we ;
wire [63:0] rd_fifo_data ;
wire rd_burst_finish;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- axi_ctrl_inst -------------
axi_ctrl
#(
.DDR_WR_LEN(DDR_WR_LEN),//写突发长度 128个64bit
.DDR_RD_LEN(DDR_RD_LEN)//读突发长度 128个64bit
)
axi_ctrl_inst
(
.ui_clk (ui_clk ),
.ui_rst (ui_rst ),
.pingpang (pingpang ),//乒乓操作
.wr_b_addr (wr_b_addr ), //写DDR首地址
.wr_e_addr (wr_e_addr ), //写DDR末地址
.user_wr_clk(user_wr_clk), //写FIFO写时钟
.data_wren (data_wren ), //写FIFO写请求
.data_wr (data_wr ), //写FIFO写数据 16位
//此时用64位是为了兼容32,64位
.wr_rst (wr_rst ), //写地址复位
.rd_b_addr (rd_b_addr ), //读DDR首地址
.rd_e_addr (rd_e_addr ), //读DDR末地址
.user_rd_clk(user_rd_clk), //读FIFO读时钟
.data_rden (data_rden ), //读FIFO读请求
.data_rd (data_rd ), //读FIFO读数据,16位,此时用64位是为了兼容32位
//64位,增强复用性,只需修改fifo即可
.rd_rst (rd_rst ),
.read_enable(read_enable),
.data_rd_valid(data_rd_valid),
//连接到axi写主机
.wr_burst_req (wr_burst_req ),
.wr_burst_addr (wr_burst_addr ),
.wr_burst_len (wr_burst_len ),
.wr_ready (wr_ready ),
.wr_fifo_re (wr_fifo_re ),
.wr_fifo_data (wr_fifo_data ),
.wr_burst_finish (wr_burst_finish),
//连接到axi读主机
.rd_burst_req (rd_burst_req ),
.rd_burst_addr (rd_burst_addr ),
.rd_burst_len (rd_burst_len ),
.rd_ready (rd_ready ),
.rd_fifo_we (rd_fifo_we ),
.rd_fifo_data (rd_fifo_data ),
.rd_burst_finish (rd_burst_finish)
);
//------------- axi_master_write_inst -------------
axi_master_write axi_master_write_inst
(
.ARESETN (~ui_rst ), //axi复位
.ACLK (ui_clk ), //axi总时钟
.M_AXI_AWID (M_AXI_WR_awid ), //写地址ID
.M_AXI_AWADDR (M_AXI_WR_awaddr ), //写地址
.M_AXI_AWLEN (M_AXI_WR_awlen ), //突发长度
.M_AXI_AWSIZE (M_AXI_WR_awsize ), //突发大小
.M_AXI_AWBURST(M_AXI_WR_awburst), //突发类型
.M_AXI_AWLOCK (M_AXI_WR_awlock ), //总线锁信号
.M_AXI_AWCACHE(M_AXI_WR_awcache), //内存类型
.M_AXI_AWPROT (M_AXI_WR_awprot ), //保护类型
.M_AXI_AWQOS (M_AXI_WR_awqos ), //质量服务QoS
.M_AXI_AWVALID(M_AXI_WR_awvalid), //有效信号
.M_AXI_AWREADY(M_AXI_WR_awready), //握手信号awready
.M_AXI_WDATA (M_AXI_WR_wdata ), //写数据
.M_AXI_WSTRB (M_AXI_WR_wstrb ), //写数据有效的字节线
.M_AXI_WLAST (M_AXI_WR_wlast ), //表明此次传输是最后一个突发传输
.M_AXI_WVALID(M_AXI_WR_wvalid ), //写有效
.M_AXI_WREADY(M_AXI_WR_wready ), //表明从机可以接收写数据
.M_AXI_BID (M_AXI_WR_bid ), //写响应ID TAG
.M_AXI_BRESP (M_AXI_WR_bresp ), //写响应
.M_AXI_BVALID(M_AXI_WR_bvalid ), //写响应有效
.M_AXI_BREADY(M_AXI_WR_bready ), //表明主机能够接收写响应
.WR_START (wr_burst_req ), //写突发触发信号
.WR_ADRS (wr_burst_addr ), //地址
.WR_LEN (wr_burst_len ), //长度
.WR_READY (wr_ready ), //写空闲
.WR_FIFO_RE (wr_fifo_re ), //连接到写fifo的读使能
.WR_FIFO_DATA(wr_fifo_data ), //连接到fifo的读数据
.WR_DONE (wr_burst_finish ) //完成一次突发
);
//------------- axi_master_read_inst -------------
axi_master_read axi_master_read_inst
(
. ARESETN (~ui_rst),
. ACLK (ui_clk),
. M_AXI_ARID (M_AXI_RD_arid ), //读地址ID
. M_AXI_ARADDR (M_AXI_RD_araddr ), //读地址
. M_AXI_ARLEN (M_AXI_RD_arlen ), //突发长度
. M_AXI_ARSIZE (M_AXI_RD_arsize ), //突发大小
. M_AXI_ARBURST(M_AXI_RD_arburst), //突发类型
. M_AXI_ARLOCK (M_AXI_RD_arlock ), //总线锁信号
. M_AXI_ARCACHE(M_AXI_RD_arcache), //内存类型
. M_AXI_ARPROT (M_AXI_RD_arprot ), //保护类型
. M_AXI_ARQOS (M_AXI_RD_arqos ), //质量服务QOS
. M_AXI_ARVALID(M_AXI_RD_arvalid), //有效信号
. M_AXI_ARREADY(M_AXI_RD_arready), //握手信号arready
. M_AXI_RID (M_AXI_RD_rid ), //读ID tag
. M_AXI_RDATA (M_AXI_RD_rdata ), //读数据
. M_AXI_RRESP (M_AXI_RD_rresp ), //读响应,表明读传输的状态
. M_AXI_RLAST (M_AXI_RD_rlast ), //表明读突发的最后一次传输
. M_AXI_RVALID(M_AXI_RD_rvalid), //表明此通道信号有效
. M_AXI_RREADY(M_AXI_RD_rready), //表明主机能够接收读数据和响应信息
. RD_START (rd_burst_req ), //读突发触发信号
. RD_ADRS (rd_burst_addr ), //地址
. RD_LEN (rd_burst_len ), //长度
. RD_READY (rd_ready ), //读空闲
. RD_FIFO_WE (rd_fifo_we ), //连接到读fifo的写使能
. RD_FIFO_DATA(rd_fifo_data ), //连接到读fifo的写数据
. RD_DONE (rd_burst_finish) //完成一次突发
);
//------------- u_axi_ddr -------------
//xilinx提供的mig ip核,开启axi4接口
axi_ddr u_axi_ddr
(
.ddr3_dq (ddr3_dq ), //数据线
.ddr3_dqs_n(ddr3_dqs_n), //数据选取脉冲差分信号
.ddr3_dqs_p(ddr3_dqs_p), //数据选取脉冲差分信号
.ddr3_addr (ddr3_addr ), //地址线
.ddr3_ba (ddr3_ba ), //bank线
.ddr3_ras_n(ddr3_ras_n), //行使能信号,低电平有效
.ddr3_cas_n(ddr3_cas_n), //列使能信号,低电平有效
.ddr3_we_n (ddr3_we_n ), //写使能信号,低电平有效
.ddr3_reset_n(ddr3_reset_n), //ddr3复位
.ddr3_ck_p (ddr3_ck_p ), //ddr3差分时钟
.ddr3_ck_n (ddr3_ck_n ), //ddr3差分时钟
.ddr3_cke (ddr3_cke ), //ddr3时钟使能信号
.ddr3_cs_n (ddr3_cs_n ), //ddr3片选信号
.ddr3_dm (ddr3_dm ), //ddr3掩码
.ddr3_odt (ddr3_odt ), //odt阻抗
.sys_clk_i(ddr3_clk), //ip核时钟
// .clk_ref_i(ddr3_clk), //ip核参考时钟
.ui_clk (ui_clk), //用户端口时钟
.ui_clk_sync_rst(ui_rst), //复位
.mmcm_locked(),
.aresetn (sys_rst_n), //异步复位
.app_sr_req ('b0),
.app_ref_req('b0),
.app_zq_req ('b0),
.app_sr_active(),
.app_ref_ack(),
.app_zq_ack(),
//axi写通道地址与控制信号
.s_axi_awid (M_AXI_WR_awid ), //写地址ID 输入[3:0]
.s_axi_awaddr (M_AXI_WR_awaddr ), //写地址 输入[29:0]
.s_axi_awlen (M_AXI_WR_awlen ), //突发长度 输入[7:0]
.s_axi_awsize (M_AXI_WR_awsize ), //突发大小 输入[2:0]
.s_axi_awburst (M_AXI_WR_awburst), //突发类型 输入[1:0]
.s_axi_awlock (M_AXI_WR_awlock ), //总线锁信号 输入[0:0]
.s_axi_awcache (M_AXI_WR_awcache), //内存类型 输入[3:0]
.s_axi_awprot (M_AXI_WR_awprot ), //保护类型 输入[2:0]
.s_axi_awqos (M_AXI_WR_awqos ), //质量服务QoS 输入[3:0]
.s_axi_awvalid (M_AXI_WR_awvalid), //有效信号 输入[0:0]
.s_axi_awready (M_AXI_WR_awready), //握手信号awready 输出[0:0]
//axi写通道数据
.s_axi_wdata (M_AXI_WR_wdata ), //写数据 输入[63:0]
.s_axi_wstrb (M_AXI_WR_wstrb ), //写数据有效的字节线 输入[7:0]
.s_axi_wlast (M_AXI_WR_wlast ), //表明此次传输是最后一个突发传输 输入[0:0]
.s_axi_wvalid (M_AXI_WR_wvalid ), //写有效,表明此次写有效 输入[0:0]
.s_axi_wready (M_AXI_WR_wready ), //表明从机可以接收写数据 输出[0:0]
//axi写通道应答
.s_axi_bid (M_AXI_WR_bid ), //写响应ID TAG 输出[3:0]
.s_axi_bresp (M_AXI_WR_bresp ), //写响应,表明写传输的状态 输出[1:0]
.s_axi_bvalid (M_AXI_WR_bvalid ), //写响应有效 输出[0:0]
.s_axi_bready (M_AXI_WR_bready ), //表明主机能够接收写响应 输入[0:0]
//axi读通道地址与控制信号
.s_axi_arid (M_AXI_RD_arid ), //读地址ID 输入[3:0]
.s_axi_araddr (M_AXI_RD_araddr ), //读地址 输入[29:0]
.s_axi_arlen (M_AXI_RD_arlen ), //突发长度 输入[7:0]
.s_axi_arsize (M_AXI_RD_arsize ), //突发大小 输入[2:0]
.s_axi_arburst (M_AXI_RD_arburst), //突发类型 输入[1:0]
.s_axi_arlock (M_AXI_RD_arlock ), //总线锁信号 输入[0:0]
.s_axi_arcache (M_AXI_RD_arcache), //内存类型 输入[3:0]
.s_axi_arprot (M_AXI_RD_arprot ), //保护类型 输入[2:0]
.s_axi_arqos (M_AXI_RD_arqos ), //质量服务QOS 输入[3:0]
.s_axi_arvalid (M_AXI_RD_arvalid), //有效信号 输入[0:0]
.s_axi_arready (M_AXI_RD_arready), //握手信号arready 输出[0:0]
//axi读通道数据,包括应答
.s_axi_rid (M_AXI_RD_rid ), //读ID tag 输出[3:0]
.s_axi_rdata (M_AXI_RD_rdata ), //读数据 输出[63:0]
.s_axi_rresp (M_AXI_RD_rresp ), //读响应,表明读传输的状态 输出[1:0]
.s_axi_rlast (M_AXI_RD_rlast ), //表明读突发的最后一次传输 输出[0:0]
.s_axi_rvalid (M_AXI_RD_rvalid ), //表明此通道信号有效 输出[0:0]
.s_axi_rready (M_AXI_RD_rready ), //表明主机能够接收读数据 输入[0:0]
.init_calib_complete(calib_done) , //ip核初始化完成
.sys_rst(sys_rst_n) //ip核复位
);
endmodule