1、 IP核设置注意事项
⭐时钟选500MHz-即DQS频率为500MHz。
⭐Phy to controller clock ratio选择4:1-此处4代表的是DQS时钟,1代表逻辑收发时钟,此处为4:1的关系,即逻辑收发时钟为500/4=125MHz。此设置也会影响数据端口的位宽,如下分别为4:1和2:1时的UI数据位宽和DDR数据位宽的关系,本实验DDR数据位宽为32位,由于时上下沿采样,所以4:1的关系实际为8:1,所以对应的UI数据位宽位32*8=256bit。
⭐此处注意Input Clock Period 此时钟为输入FPGA的主时钟,也就是system clock,此处有很多值可以选择,本实验选择200MHz,选择这个时钟频率的好处是Reference clock可以直接选择使用system clock。
IP核设置的注意事项到此完毕。
2.读写时序分别如下图所示,此处注意DDR3的burst length 只能为8
下面是读写状态机:
module DDR_TEST(
output reg[255:0] read_data,
input sys_clk_p,
input sys_clk_n,
input sys_rst,
output [14:0] ddr3_addr, // output [14:0] ddr3_addr
output [2:0] ddr3_ba, // output [2:0] ddr3_ba
output ddr3_cas_n, // output ddr3_cas_n
output [0:0] ddr3_ck_n, // output [0:0] ddr3_ck_n
output [0:0] ddr3_ck_p, // output [0:0] ddr3_ck_p
output [0:0] ddr3_cke, // output [0:0] ddr3_cke
output ddr3_ras_n, // output ddr3_ras_n
output ddr3_reset_n, // output ddr3_reset_n
output ddr3_we_n, // output ddr3_we_n
inout [31:0] ddr3_dq, // inout [31:0] ddr3_dq
inout [3:0] ddr3_dqs_n, // inout [3:0] ddr3_dqs_n
inout [3:0] ddr3_dqs_p, // inout [3:0] ddr3_dqs_p
output init_calib_complete, // output init_calib_complete
output [0:0] ddr3_cs_n, // output [0:0] ddr3_cs_n
output [3:0] ddr3_dm, // output [3:0] ddr3_dm
output ddr3_odt // output [0:0] ddr3_odt
);
reg [28:0] app_addr;
reg [2:0] app_cmd;
reg app_en;
reg [255:0] app_wdf_data;
reg app_wdf_end;
reg 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;
reg [2:0] state_c;
reg [2:0] state_n;
reg [28:0] cnt;
wire add_cnt;
wire end_cnt;
wire rst_h;
wire clk;
wire idl2write_start;
wire write2read_start;
wire read2idle_start;
wire write_vld;
wire read_vld;
assign rst_n = !rst_h;
assign write_vld = app_rdy && app_wdf_rdy;
assign read_vld = app_rdy;
///
localparam IDLE = 3'b001;
localparam READ = 3'b010;
localparam WRITE = 3'b100;
///
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 8;
end
end
assign add_cnt = (state_c==WRITE && write_vld)||(state_c==READ && read_vld && app_rd_data_valid);
assign end_cnt = add_cnt && cnt== 64-8;
//第一段:
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段:
always@(*)begin
case(state_c)
IDLE:begin
if(idl2write_start)begin
state_n = WRITE;
end
else begin
state_n = state_c;
end
end
WRITE:begin
if(write2read_start)begin
state_n = READ;
end
else begin
state_n = state_c;
end
end
READ:begin
if(read2idle_start)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default:begin
state_n = IDLE;
end
endcase
end
//第三段:
assign idl2write_start = state_c==IDLE && init_calib_complete && write_vld;
assign write2read_start = state_c==WRITE && end_cnt && read_vld;
assign read2idle_start = state_c==READ && end_cnt;
//--app_addr
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
app_addr <= 29'h0;
end
else if(state_c!=IDLE && (write_vld ||read_vld)) begin
app_addr <= cnt;
end
end
//--app_cmd
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
app_cmd<=3'h0;
end
else if (state_c==WRITE && write_vld)begin
app_cmd<=3'h0;
end
else if (state_c==READ && read_vld)begin
app_cmd<=3'h1;
end
end
//--app_en
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
app_en <=1'b0;
end
else if(state_c!=IDLE && (write_vld ||read_vld))begin
app_en <=1'b1;
end
else if(state_c == IDLE)begin
app_en <=1'b0;//app_en need to hold, in case the app_rdy pull down suddenly
end
end
//--app_wdf_data
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
app_wdf_data<=256'd0;
end
else if (state_c==WRITE && write_vld)begin
case(cnt)
29'd0:app_wdf_data<=256'd1;
29'd8:app_wdf_data<=256'd2;
29'd16:app_wdf_data<=256'd3;
29'd24:app_wdf_data<=256'd4;
29'd32:app_wdf_data<=256'd5;
29'd40:app_wdf_data<=256'd6;
29'd48:app_wdf_data<=256'd7;
29'd56:app_wdf_data<=256'd8;
default: app_wdf_data<=256'd0;
endcase
end
end
//--app_wdf_end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
app_wdf_end <=1'b0;
end
else if(state_c==WRITE)begin
app_wdf_end <=1'b1;
end
else begin
app_wdf_end <=1'b0;
end
end
//--app_wdf_wren
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
app_wdf_wren <=1'b0;
end
else if(state_c==WRITE && write_vld)begin
app_wdf_wren <=1'b1;
end
else begin
app_wdf_wren <=1'b0;
end
end
//--read_data
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
read_data <=1'b0;
end
else if(app_rd_data_valid)begin
read_data <=app_rd_data;
end
end
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,This output indicates that the UI is ready to accept commands
.app_wdf_rdy (app_wdf_rdy ), // output app_wdf_rdy,This output indicates that the write data FIFO is ready to receive data
.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 ( ), // output app_sr_active
.app_ref_ack ( ), // output app_ref_ack
.app_zq_ack ( ), // output app_zq_ack
.ui_clk (clk ), // output ui_clk 500M/4=125M
.ui_clk_sync_rst (rst_h ), // output ui_clk_sync_rst
.app_wdf_mask (32'b0 ), // input [31:0] app_wdf_mask
// System Clock Ports
.sys_clk_p (sys_clk_p ), // input sys_clk_p 200M
.sys_clk_n (sys_clk_n ), // input sys_clk_n
.sys_rst (sys_rst ) // input sys_rst
);
endmodule
状态机对从0开始的连续8个地址分别写入1-8的数字,写完成后再读出。
3、仿真
DDR的仿真需要DDR的Module,模型可以通过example design获得,但直接拿过来并不能用,需要进行修改,后面会给出包括TB的完整工程链接,这里就不做过多阐述,下面是仿真结果:可以看到结果完全符合预期。
包括仿真文件的完整工程见下面链接:
https://download.csdn.net/download/weixin_45002573/11998076