上一个章节我们新建工程,然后进行基本的初始化操作,这个章节将在上个工程的基础上进行突发读写
因ddr3读写部分控制信号比较多,所以ddr3读写控制模块比较复杂,本章节着重于一个256位数据的突发读写,ddr读写控制模块暂不引出行复位部分,简化了ddr读写控制模块也让各种童鞋理解更清晰。
因本章的工程是在上一篇博客的基础上进行改进的,加入了ddr读写控制模块,和突发读写模块,所以务必按照我博客的顺序来看
ddr3突发读写工程顶层
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/20 20:24:41
// Design Name:
// Module Name: ddr3_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ddr3_top(
//clock
input sys_clk_i,//200M
//reset
input sys_rst,
// Inouts
inout [31:0] ddr3_dq,
inout [3:0] ddr3_dqs_n,
inout [3:0] ddr3_dqs_p,
// Outputs
output [14:0] ddr3_addr,
output [2:0] ddr3_ba,
output ddr3_ras_n,
output ddr3_cas_n,
output ddr3_we_n,
output ddr3_reset_n,
output [0:0] ddr3_ck_p,
output [0:0] ddr3_ck_n,
output [0:0] ddr3_cke,
output [0:0] ddr3_cs_n,
output [3:0] ddr3_dm,
output [0:0] ddr3_odt
);
ddr_test ddr_test_inst (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [14:0] ddr3_addr
.ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba
.ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n
.ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p
.ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke
.ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n
.ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n
.ddr3_we_n (ddr3_we_n), // output ddr3_we_n
.ddr3_dq (ddr3_dq), // inout [31:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n), // inout [3:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p), // inout [3:0] ddr3_dqs_p
.ddr3_cs_n (ddr3_cs_n), // output [0:0] ddr3_cs_n
.ddr3_dm (ddr3_dm), // output [3:0] ddr3_dm
.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
// System Clock Ports
.sys_clk_i (sys_clk_i),
.sys_rst (sys_rst) // input sys_rst
);
endmodule
ddr3测试模块(测试模块包含:mig控制器,ddr3读写控制模块,ddr3突发读写模块)
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/21 18:58:16
// Design Name:
// Module Name: ddr_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ddr_test(
input sys_clk_i,//200M
//reset
input sys_rst,
// Inouts
inout [31:0] ddr3_dq,
inout [3:0] ddr3_dqs_n,
inout [3:0] ddr3_dqs_p,
// Outputs
output [14:0] ddr3_addr,
output [2:0] ddr3_ba,
output ddr3_ras_n,
output ddr3_cas_n,
output ddr3_we_n,
output ddr3_reset_n,
output [0:0] ddr3_ck_p,
output [0:0] ddr3_ck_n,
output [0:0] ddr3_cke,
output [0:0] ddr3_cs_n,
output [3:0] ddr3_dm,
output [0:0] ddr3_odt
);
wire [28:0] app_addr; //DDR3地址
wire [2:0] app_cmd; //MIG IP核操作命令,读或者写
wire app_en; //MIG IP发送命令使能
wire app_rdy; //MIG 命令接收准备好标志
wire [255:0] app_rd_data; //用户读数据
wire app_rd_data_end; //突发读当前时钟最后一个数据
wire app_rd_data_valid; //读数据有效
wire [255:0] app_wdf_data; //用户写数据
wire app_wdf_end; //突发写当前时钟最后一个数据
wire [31:0] app_wdf_mask;
wire app_wdf_rdy; //MIG数据接收准备好
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack;
wire app_wdf_wren;
wire ui_clk; //用户时钟
wire ui_clk_sync_rst; //复位,高有效
wire [24:0] ddr_addr_max;
//mig控制器
mig_7series_0 u_mig_7series_0 (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [14:0] ddr3_addr
.ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba
.ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n
.ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p
.ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke
.ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n
.ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n
.ddr3_we_n (ddr3_we_n), // output ddr3_we_n
.ddr3_dq (ddr3_dq), // inout [31:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n), // inout [3:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p), // inout [3:0] ddr3_dqs_p
.init_calib_complete (init_calib_complete), // output init_calib_complete
.ddr3_cs_n (ddr3_cs_n), // output [0:0] ddr3_cs_n
.ddr3_dm (ddr3_dm), // output [3:0] ddr3_dm
.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
// Application interface ports
.app_addr (app_addr), // input [28:0] app_addr
.app_cmd (app_cmd), // input [2:0] app_cmd
.app_en (app_en), // input app_en
.app_wdf_data (app_wdf_data), // input [255:0] app_wdf_data
.app_wdf_end (app_wdf_end), // input app_wdf_end
.app_wdf_wren (app_wdf_wren), // input app_wdf_wren
.app_rd_data (app_rd_data), // output [255:0] app_rd_data
.app_rd_data_end (app_rd_data_end), // output app_rd_data_end
.app_rd_data_valid (app_rd_data_valid), // output app_rd_data_valid
.app_rdy (app_rdy), // output app_rdy
.app_wdf_rdy (app_wdf_rdy), // output app_wdf_rdy
.app_sr_req (1'b0), // input app_sr_req
.app_ref_req (1'b0), // input app_ref_req
.app_zq_req (1'b0), // input app_zq_req
.app_sr_active (app_sr_active), // output app_sr_active
.app_ref_ack (app_ref_ack), // output app_ref_ack
.app_zq_ack (app_zq_ack), // output app_zq_ack
.ui_clk (ui_clk), // output ui_clk
.ui_clk_sync_rst (ui_clk_sync_rst), // output ui_clk_sync_rst
.app_wdf_mask (32'd0), // input [31:0] app_wdf_mask
// System Clock Ports
.sys_clk_i (sys_clk_i),
.sys_rst (sys_rst) // input sys_rst
);
assign ddr_addr_max = 32-8;
//读写控制模块
ddr3_rw_control ddr3_rw_control_inst (
.ui_clk (ui_clk), //用户时钟
.ui_clk_sync_rst (ui_clk_sync_rst), //复位,高有效
.init_calib_complete (init_calib_complete), //DDR3初始化完成
.app_rdy (app_rdy), //MIG 命令接收准备好标致
.app_wdf_rdy (app_wdf_rdy), //MIG数据接收准备好
.app_rd_data_valid (app_rd_data_valid), //读数据有效
.app_addr (app_addr), //DDR3地址
.app_en (app_en), //MIG IP发送命令使能
.app_wdf_wren (app_wdf_wren), //用户写数据使能
.app_wdf_end (app_wdf_end), //突发写当前时钟最后一个数据
.app_cmd (app_cmd), //MIG IP核操作命令,读或者写
//用户接口
.ddr_addr_max (ddr_addr_max),
.wr_len_en (wr_len_en),
.rd_len_en (rd_len_en),
.wr_len_done (wr_len_done),
.rd_len_done (rd_len_done),
.ddr_wr_end (ddr_wr_end),
.ddr_rd_end (ddr_rd_end)
);
//突发读写
burst_rw burst_rw_inst(
.ui_clk (ui_clk),
.i_rst_n (!ui_clk_sync_rst),
.init_calib_complete (init_calib_complete),
//突发读写控制信号
.wr_len_en (wr_len_en),//突发写使能
.rd_len_en (rd_len_en),//突发读使能
.wr_len_done (wr_len_done),//突发写完成
.rd_len_done (rd_len_done),//突发读完成
.ddr_wr_end (ddr_wr_end),//ddr写结束
.ddr_rd_end (ddr_rd_end),//ddr读结束
//读写数据接口
.app_wdf_wren (app_wdf_wren),//ddr写有效
.app_wdf_data (app_wdf_data),//ddr写入的数据
.app_rd_data_valid (app_rd_data_valid),//ddr读有效
.app_rd_data (app_rd_data)//ddr读出的数据
);
endmodule
ddr3读写控制模块
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/22 08:27:58
// Design Name:
// Module Name: ddr3_rw_control
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ddr3_rw_control (
input ui_clk, //用户时钟
input ui_clk_sync_rst, //复位,高有效
input init_calib_complete, //DDR3初始化完成
input app_rdy, //MIG 命令接收准备好标致
input app_wdf_rdy, //MIG数据接收准备好
input app_rd_data_valid, //读数据有效
output [27:0] app_addr, //DDR3地址
output app_en, //MIG IP发送命令使能
output app_wdf_wren, //用户写数据使能
output app_wdf_end, //突发写当前时钟最后一个数据
output [2:0] app_cmd, //MIG IP核操作命令,读或者写
output reg [2 :0] state, //读写状态
output reg [23:0] rd_addr_cnt, //用户读地址计数
output reg [23:0] wr_addr_cnt, //用户写地址计数
input [24:0] ddr_addr_max,
input wr_len_en,
input rd_len_en,
output wr_len_done,
output rd_len_done,
output reg ddr_wr_end,
output reg ddr_rd_end,
input vin_vs,
input vout_vs,
output reg wr_reset,
output reg rd_reset
);
//parameter define
parameter WRITE_LENGTH = 4;
parameter READ_LENGTH = 4;
parameter IDLE = 3'd0; //空闲状态
parameter WRITE = 3'd1; //写状态
parameter WRITE_DONE = 3'd2; //读到写过度等待
parameter READ = 3'd3; //读状态
parameter READ_DONE = 3'd4;
wire rst_n; //复位,低有效
reg [27:0] app_addr_wr;
reg [27:0] app_addr_rd;
reg [2:0] wr_page;
reg [2:0] rd_page;
reg [15:0] wr_load_r;
reg [15:0] rd_load_r;
//*****************************************************
//** main code
//*****************************************************
assign rst_n = ~ui_clk_sync_rst;
//在写状态MIG IP 命令接收和数据接收都准备好,或者在读状态命令接收准备好,此时拉高使能信号,
assign app_en = ((state == WRITE && (app_rdy && app_wdf_rdy))
||(state == READ && app_rdy)) ? 1'b1:1'b0;
//在写状态,命令接收和数据接收都准备好,此时拉高写使能
assign app_wdf_wren = (state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
//由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
assign app_wdf_end = app_wdf_wren;
//处于读的时候命令值为1,其他时候命令值为0
assign app_cmd = (state == READ) ? 3'd1 :3'd0;
assign app_addr = (state == WRITE && (app_rdy && app_wdf_rdy))? {wr_page,app_addr_wr[24:0]}:{rd_page,app_addr_rd[24:0]};
assign wr_len_done = ((state == WRITE_DONE) && wr_len_en)? 1'b1:1'b0;
assign rd_len_done = ((state == READ_DONE) && rd_len_en)? 1'b1:1'b0;
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n)
ddr_wr_end <= 1'b0;
else if(wr_reset)
ddr_wr_end <= 1'b0;
else if((state == WRITE_DONE) && app_addr_wr == ddr_addr_max)
ddr_wr_end <= 1'b1;
end
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n)
ddr_rd_end <= 1'b0;
else if(rd_reset)
ddr_rd_end <= 1'b0;
else if((state == READ_DONE) && app_addr_rd == ddr_addr_max)
ddr_rd_end <= 1'b1;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
wr_load_r <= 1'd0;
else
wr_load_r <= {wr_load_r[14:0],vin_vs};
end
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n)
wr_page <= 3'd0;
else if(wr_load_r[0] && !wr_load_r[14])
wr_page <= wr_page + 1'b1;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
wr_reset <= 1'd0;
else if(wr_load_r[0] && !wr_load_r[14])
wr_reset <= 1'd1;
else if(app_addr_wr == 0 && !(wr_load_r[0] && !wr_load_r[14]))
wr_reset <= 1'd0;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
rd_load_r <= 1'd0;
else
rd_load_r <= {rd_load_r[14:0],vout_vs};
end
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n)
rd_page <= 3'd0;
else if(rd_load_r[0] && !rd_load_r[14])
rd_page <= wr_page - 1'b1;
end
always@(posedge ui_clk or negedge rst_n)begin
if(!rst_n)
rd_reset <= 1'd0;
else if(rd_load_r[0] && !rd_load_r[14])
rd_reset <= 1'd1;
else if(app_addr_rd == 0 && !(rd_load_r[0] && !rd_load_r[14]))
rd_reset <= 1'd0;
end
//DDR3读写逻辑实现
always @(posedge ui_clk or negedge rst_n) begin
if(!rst_n) begin
state <= IDLE;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr_wr <= 25'd0;
app_addr_rd <= 25'd0;
end
else if(init_calib_complete)begin //MIG IP核初始化完成
case(state)
IDLE:
if(rd_reset)begin
rd_addr_cnt <= 24'd0;
app_addr_rd <= 25'd0;
end
else if(wr_reset)begin
wr_addr_cnt <= 24'd0;
app_addr_wr <= 25'd0;
end
else if(rd_len_en)begin
state <= READ;
rd_addr_cnt <= 24'd0;
end
else if(wr_len_en)begin
state <= WRITE;
wr_addr_cnt <= 24'd0;
end
else
state <= IDLE;
WRITE:begin
if(wr_addr_cnt == WRITE_LENGTH - 1 &&(app_rdy && app_wdf_rdy))
state <= WRITE_DONE; //写到设定的长度跳到等待状态
else if(app_rdy && app_wdf_rdy)begin //写条件满足
wr_addr_cnt <= wr_addr_cnt + 1; //写地址自加
app_addr_wr <= app_addr_wr + 8; //DDR3 地址加8
end
else begin //写条件不满足,保持当前值
wr_addr_cnt <= wr_addr_cnt;
app_addr_wr <= app_addr_wr;
end
end
WRITE_DONE:begin
if(app_addr_wr == ddr_addr_max)begin
app_addr_wr <= 25'd0;
wr_addr_cnt <= 24'd0;
state <= IDLE;
end
else begin
app_addr_wr <= app_addr_wr + 8;
wr_addr_cnt <= 24'd0;
state <= IDLE;
end
end
READ:begin //读到设定的地址长度
if(rd_addr_cnt == READ_LENGTH - 1 && app_rdy)begin
state <= READ_DONE;
end //则跳到空闲状态
else if(app_rdy)begin //若MIG已经准备好,则开始读
rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
app_addr_rd <= app_addr_rd + 8; //DDR3地址加8
end
else begin //若MIG没准备好,则保持原值
rd_addr_cnt <= rd_addr_cnt;
app_addr_rd <= app_addr_rd;
end
end
READ_DONE:begin
if(app_addr_rd == ddr_addr_max)begin
app_addr_rd <= 25'd0;
rd_addr_cnt <= 24'd0;
state <= IDLE;
end
else begin
app_addr_rd <= app_addr_rd + 8;
rd_addr_cnt <= 24'd0;
state <= IDLE;
end
end
default:begin
state <= IDLE;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr_wr <= 25'd0;
app_addr_rd <= 25'd0;
end
endcase
end
end
endmodule
ddr3突发读写模块
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/21 20:00:10
// Design Name:
// Module Name: burst_rw
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module burst_rw(
input ui_clk,
input i_rst_n,
input init_calib_complete,
output reg wr_len_en,//突发写使能
output reg rd_len_en,//突发读使能
input wr_len_done,//突发写完成
input rd_len_done,//突发读完成
input ddr_wr_end,//ddr写结束
input ddr_rd_end,//ddr读结束
input app_wdf_wren,//ddr写有效
output [255:0] app_wdf_data,//ddr写入的数据
input app_rd_data_valid,//ddr读有效
input [255:0] app_rd_data//ddr读出的数据
);
reg [5:0] count;
reg [255:0] ddr3_w_data;
wire [255:0] ddr3_read_in;
assign app_wdf_data = ddr3_w_data;//ddr3写入的数据
assign ddr3_read_in = (app_rd_data_valid)? app_rd_data:256'd0;//ddr3读出的数据
//通过计数器产生突发读写使能信号
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
count <= 6'd0;
else if(count == 6'd32)
count <= count;
else if(init_calib_complete)
count <= count + 1'b1;
end
//ddr写入的数据
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
ddr3_w_data <= 256'd0;
else if(init_calib_complete && app_wdf_wren)
ddr3_w_data <= ddr3_w_data + 1'b1;
else
ddr3_w_data <= 256'd0;
end
//突发写使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
wr_len_en <= 1'd0;
else if(ddr_wr_end)
wr_len_en <= 1'd0;
else if(rd_len_en)
wr_len_en <= 1'd0;
else if(wr_len_done)
wr_len_en <= 1'd0;
else if(count == 6'd16)
wr_len_en <= 1'd1;
end
//突发读使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
rd_len_en <= 1'd0;
else if(ddr_rd_end)
rd_len_en <= 1'd0;
else if(wr_len_en)
rd_len_en <= 1'd0;
else if(rd_len_done)
rd_len_en <= 1'd0;
else if(count == 6'd32)
rd_len_en <= 1'd1;
end
endmodule
从硬件上看两片16位ddr加在一起是32位的
可以看到读写的数据位宽都是[255:0]也就是256位,那是因为ddr3突发读写一般都是8位,ddr的数据位宽是32位,也就是32*8=256位,所以突发读或者突发写一次就是256位
因突发一次相当于写8个32位数据,也就是256位宽数据,所以ddr控制模块读写地址每次都是加8位
ddr型号为MT41J256M16,可以看出行为[14:0],列为[9:0]
从下图可以看出ddr一共有8个bank,也就是bank0~bank7,而且每个bank的存储空间都一样
行为[14:0],列为[9:0],那么一个bank的最大寻址空间为
2^15*2^10 = 32768*1024= 33554432=2^25
数据位宽相当于[24:0]
从下图可以看出bank选择,由BA0,BA1,BA2这三个管脚组成,也就是2^3=8(bank0~bank7)
再来看8个bank的寻址空间计算
bank0
33554432*1-1 = 33554431([24:0]),加上BA0,BA1,BA2,也就是[27:0],将33554431化为二进制
0 0 0 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 000
同样bank1为
33554432*2-1 = 67108863
0 0 1 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 001
同样bank2为
33554432*3-1 = 100663295
0 1 0 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 010
同样bank3为
33554432*4-1 = 134217727
0 1 1 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 011
同样bank4为
33554432*5-1 = 167772159
1 0 0 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 100
同样bank5为
33554432*6-1 = 201326591
1 0 1 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 101
同样bank6为
33554432*7-1 = 234881023
1 1 0 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 110
同样bank7为
33554432*8-1 = 268435455
1 1 1 1111111111111111111111111
ba2 ba1 ba0 ([24:0])
所以[ba2,ba1,ba0] = 111
从上面可以看出,ddr寻址范围[24:0]为每个bank的寻址空间,而高三位[27:25]为ba2,ba1,ba0为bank选择
所以ddr控制模块里面读写寻址空间为[27:0],后[24:0]为寻址空间,前面三位为bank选择,因本ddr一共有8个bank,所以可以缓存8页数据
assign app_addr = (state == WRITE && (app_rdy && app_wdf_rdy))? {wr_page,app_addr_wr[24:0]}:{rd_page,app_addr_rd[24:0]};
再来看工程仿真波形
当count计数器计数到32时产生一次wr_len_en突发写使能
//突发写使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
wr_len_en <= 1'd0;
else if(ddr_wr_end)
wr_len_en <= 1'd0;
else if(rd_len_en)
wr_len_en <= 1'd0;
else if(wr_len_done)
wr_len_en <= 1'd0;
else if(count == 8'd32)
wr_len_en <= 1'd1;
end
当发出wr_len_en写突发使能后,可以看到app_rdy和app_wdf_rdy都为1,表示可以写数据,app_cmd为0表示写命令,这里一共进行四次突发写,所以地址为0,8,10,18(所以ddr地址为32-8),wr_addr_cnt为突发写的次数,这个次数是可以设置的,但我们这里设置为4,截图中可以看到进行了四次计数分别是000000,000001,000002,000003。
当wr_len_en为1时,app_wdf_wren为高写有效,可以看到app_wdf_data写入了四个256位宽度的数据,分别为0,1,2,3这四个数据,写完成后产生一个wr_len_done突发写完成信号,因前面我们设置了写四个数据,数据写完成后会产生一个ddr_wr_end写结束信号。
//ddr写入的数据
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
ddr3_w_data <= 256'd0;
else if(init_calib_complete && app_wdf_wren)
ddr3_w_data <= ddr3_w_data + 1'b1;
else
ddr3_w_data <= 256'd0;
end
当count计数器计数到64时产生一次rd_len_en突发读使能
//突发读使能
always@(posedge ui_clk or negedge i_rst_n)begin
if(!i_rst_n)
rd_len_en <= 1'd0;
else if(ddr_rd_end)
rd_len_en <= 1'd0;
else if(wr_len_en)
rd_len_en <= 1'd0;
else if(rd_len_done)
rd_len_en <= 1'd0;
else if(count == 8'd64)
rd_len_en <= 1'd1;
end
突发读时,当app_rdy和app_wdf_rdy为高时,app_en使能为高,写入需要读数据的地址,也就是0,8,10,18这四个地址,而且此时的app_cmd为1表示进行ddr读命令,rd_addr_cnt为突发读次数,可以看到进行了四次突发读分别是000000,000001,000002,000003,并产生ddr_rd_end读突发结束信号。
读出数据,可以看到app_rd_data_valid为高表示读出数据有效,app_rd_data读出0,1,2,3四个数据和写入的数据一致(一般读出数据是有延迟的,并不是写完控制命令后,立即就能读出数据,具体需要多少个周期,可以参考ddr3数据手册)
因怕很多人对突发读写不理解,所以将ddr读写控制和突发读写模块简化了很多,本来想将突发读写,写一篇博客的,但感觉涉及的东西太多了,写到这里还没有将ddr复位信号加入进去,之后使用hdmi显示再加入场信号复位,如果写的有问题欢迎各位指正。
如若转载,请注明出处