前言
笔者在之前通过microblaze软核的方式实现了DDR3芯片的读写测试,当时对于Xilinx MIG DDR控制器的理解还比较肤浅。还是想通过控制用户接口时序的方式来读写DDR,扩展和加深自己对DDR的理解。
MIG IP核配置请看我的前一篇文章
【FPGA测试】Microblaze测试DDR读写_microblaze ddr-CSDN博客
里面关于MIG参考时钟输入有错误的地方,这里改正一下。
MIG的输入时钟有2种,一个是系统时钟,如下图所示,input Clock period。还有一个时钟为参考时钟,固定为200MHz。
DDR读写时序
首先是命令,如图,只有当app_rdy信号为高时,用户给MIG的指令才有效。
然后是写指令,一种是写数据和写命令同时进行。第二种是写的数据慢于写命令。第三种是写的数据快于写指令。一般来说采用同时写数据和写命令即可。
当输入读命令和读地址时,延迟几个时钟周期后,开始读出数据。当app_rd_data_vaild为高时,读出的数据才是正确且有效的。
测试代码如下:
每给一个地址,会往ddr里写8个32bit的数据。这样一次写命令就可以写满8个地址。
所以每写一次,地址都要加8。
同时。每读一次,会读出8个地址的数据,所以每次读一次,地址也要加8。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/12/23 14:03:36
// Design Name:
// Module Name: DDR3
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module DDR3(
ddr3_addr,
ddr3_ba,
ddr3_cas_n,
ddr3_ck_n,
ddr3_ck_p,
ddr3_cke,
ddr3_cs_n,
ddr3_dm,
ddr3_dq,
ddr3_dqs_n,
ddr3_dqs_p,
ddr3_odt,
ddr3_ras_n,
ddr3_reset_n,
ddr3_we_n,
clk_100M,
rst_n
);
output [14:0]ddr3_addr;
output [2:0]ddr3_ba;
output ddr3_cas_n;
output [0:0]ddr3_ck_n;
output [0:0]ddr3_ck_p;
output [0:0]ddr3_cke;
output [0:0]ddr3_cs_n;
output [3:0]ddr3_dm;
inout [31:0]ddr3_dq;
inout [3:0]ddr3_dqs_n;
inout [3:0]ddr3_dqs_p;
output [0:0]ddr3_odt;
output ddr3_ras_n;
output ddr3_reset_n;
output ddr3_we_n;
input clk_100M;
input rst_n;
wire [14:0]ddr3_addr;
wire [2:0]ddr3_ba;
wire ddr3_cas_n;
wire [0:0]ddr3_ck_n;
wire [0:0]ddr3_ck_p;
wire [0:0]ddr3_cke;
wire [0:0]ddr3_cs_n;
wire [3:0]ddr3_dm;
wire [31:0]ddr3_dq;
wire [3:0]ddr3_dqs_n;
wire [3:0]ddr3_dqs_p;
wire [0:0]ddr3_odt;
wire ddr3_ras_n;
wire ddr3_reset_n;
wire ddr3_we_n;
wire clk_100M;
wire rst_n;
wire init_calib_complete;
wire [28:0] app_addr;
wire [2:0] app_cmd;
wire app_en;
wire [255:0] app_wdf_data;
wire app_wdf_end;
wire app_wdf_wren;
wire [255:0] app_rd_data;
wire app_rd_data_end;
wire app_rd_data_valid;
wire app_rdy;
wire app_wdf_rdy;
wire app_sr_req;
wire app_ref_req;
wire app_zq_req;
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack;
wire ui_clk;
wire ui_clk_sync_rst;
wire [31:0] app_wdf_mask;
wire sys_clk_i;
wire clk_ref_i;
wire sys_rst;
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 (app_sr_req), // input app_sr_req
.app_ref_req (app_ref_req), // input app_ref_req
.app_zq_req (app_zq_req), // 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 (app_wdf_mask), // input [31:0] app_wdf_mask
// System Clock Ports
.sys_clk_i (sys_clk_i),
// Reference Clock Ports
.clk_ref_i (clk_ref_i),
.sys_rst (sys_rst) // input sys_rst
);
wire clk_200M;
wire rst;
wire locked;
clk_wiz_0 instance_name
(
// Clock out ports
.clk_out1(clk_200M), // output clk_out1
// Status and control signals
.locked(locked), // output locked
// Clock in ports
.clk_in1(clk_100M));
assign sys_clk_i = clk_200M;
assign clk_ref_i = clk_200M;
assign rst = rst_n;
assign sys_rst = rst_n;
parameter [2:0]IDLE =3'd0;
parameter [2:0]WRITE =3'd1;
parameter [2:0]CLC=3'd2;
parameter [2:0]WAIT =3'd3;
parameter [2:0]READ =3'd4;
parameter [2:0]CMD_WRITE =3'd0;
parameter [2:0]CMD_READ =3'd1;
parameter TEST_DATA_RANGE =28'd1000;//测试1000个地址
reg [2 :0]state=0;
reg [31:0]Count_64;
reg [27:0]app_addr_begin;
assign app_wdf_end =app_wdf_wren;
assign app_en =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : ((state==READ)&&app_rdy);
assign app_wdf_wren =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : 1'b0;
assign app_cmd =(state==WRITE) ? CMD_WRITE : CMD_READ;
assign app_addr =app_addr_begin;
assign app_wdf_data ={
Count_64[31:0],(Count_64[31:0]+32'd1),(Count_64[31:0]+32'd2),(Count_64[31:0]+32'd3),
(Count_64[31:0]+32'd4),(Count_64[31:0]+32'd5),(Count_64[31:0]+32'd6),(Count_64[31:0]+32'd7)
};//一次写入8个地址
always@(posedge clk_100M)
if(!rst&!init_calib_complete)
begin
state <=IDLE;
app_addr_begin <=28'd0;
Count_64 <=32'd0;
end
else case(state)
IDLE: begin
state <=WRITE;
if(app_addr_begin >= TEST_DATA_RANGE)
app_addr_begin <=28'd0;
Count_64 <=32'd0;
end
WRITE: begin
state <=(Count_64>=TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy ? CLC:state;
Count_64 <=app_rdy&&app_wdf_rdy?(Count_64+32'd8):Count_64;
app_addr_begin <=app_rdy&&app_wdf_rdy?(app_addr_begin+28'd8):app_addr_begin;8*32=256
end
CLC: begin
state <=WAIT;
Count_64 <=32'd0;
app_addr_begin <=28'd0;
end
WAIT: begin
state <=READ;
Count_64 <=32'd0;
app_addr_begin <=28'd0;
end
READ: begin
state <=(Count_64>=TEST_DATA_RANGE)&&app_rdy? IDLE:state;
Count_64 <=app_rdy?(Count_64+32'd8):Count_64;
app_addr_begin <=app_rdy?(app_addr_begin+28'd8):app_addr_begin;
end
default:begin
state <=IDLE;
app_addr_begin <=28'd0;
Count_64 <=32'd0;
end
endcase
reg [27:0] count;
always@(posedge clk_100M)
if(!rst)
count <= 28'b0;
else if(app_rd_data_valid)begin
if(app_rd_data[255:224]==32'd0)
count <= 28'd8;
else
count <= count + 28'd8;
end
else if(count >= 28'd1000)
count <= 28'b0;
ila_0 u1 (
.clk(clk_100M), // input wire clk
.probe0(app_cmd), // input wire [2:0] probe0
.probe1(state), // input wire [1:0] probe1
.probe2(Count_64), // input wire [23:0] probe2
.probe3(app_addr_begin), // input wire [27:0] probe3
.probe4(app_rd_data), // input wire [255:0] probe4
.probe10(app_rd_data_valid), // input wire [0:0] probe5
.probe6(app_en), // input wire [0:0] probe6
.probe7(app_wdf_wren), // input wire [0:0] probe7
.probe8(app_rdy), // input wire [0:0] probe8
.probe9(app_wdf_rdy), // input wire [0:0] probe9
.probe5(app_wdf_data),
.probe11(app_rd_data[31:0]),
.probe12(app_rd_data[255:224]),
.probe13(count),
.probe14(app_rd_data_valid)
);
endmodule
首先看一下写的情况,从零地址开始,每个地址写上地址数,共写1000个地址。
再看一下读的情况,每次读出8个地址的32位数据。
如上图所示,黄线处开始读命令,但是此时并没有有效数据读出,需要等待几个时钟周期,才能读出正确数据。
文章来源:https://www.toymoban.com/news/detail-815325.html
如上图所示,读出的数据是正确的。文章来源地址https://www.toymoban.com/news/detail-815325.html
到了这里,关于【FPGA】MIG DDR3读写逻辑测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!