Xilinx MIG 控制器使用详解(三)

 关于DDR3的基本知识在这里我就不详细说了,只有在相关的地方会提上一嘴。本教程的目的只是教会大家如何使用MIG控制器,大家一定不要觉得MIG控制器有多难,其实很简单的,跟着我在心里默念“MIG就像BRAM一样简单”。确实哈,当你回过头来看,MIG控制器的使用基本和BRAM的使用方法很像。
 
VIVADO版本:2016.4

芯片:zynq7035

读写测试总模块如下图:

外部晶振输入的是100MHz,但是MIG需要400MHz的输入,所以需要经过一个始终管理模块进行倍频到400MHz。DDR_CTRL的主要作用就是产生第二节中说明的那些读写控制信号。

代码结构如下:

现在让我们来分析分析代码吧,先从top.v开始。

top.v
`timescale 1ns / 1ps

module top(
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 ,
output ddr3_ras_n ,
output ddr3_cas_n ,
output ddr3_we_n ,
output ddr3_reset_n ,
output ddr3_ck_p ,
output ddr3_ck_n ,
output ddr3_cke ,
output ddr3_cs_n ,
output [3:0] ddr3_dm ,
output ddr3_odt ,

input sys_clk_i //外部输入的100MHz时钟
);

wire clk400;
wire rst_n;
clk_wiz_0 clk_wiz_0(
.clk_out1 ( clk400 ),
.locked ( rst_n ),
.clk_in1 ( sys_clk_i )
);

(*keep = "true"*)(*mark_debug = "true"*)wire [28:0] app_addr ;
(*keep = "true"*)(*mark_debug = "true"*)wire [2:0] app_cmd ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_en ;
(*keep = "true"*)(*mark_debug = "true"*)wire [255:0] app_wdf_data ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_wdf_end ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_wdf_wren ;
(*keep = "true"*)(*mark_debug = "true"*)wire [255:0] app_rd_data ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_rd_data_end ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_rd_data_valid ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_rdy ;
(*keep = "true"*)(*mark_debug = "true"*)wire app_wdf_rdy ;

(*keep = "true"*)(*mark_debug = "true"*)wire ddrdata_test_err ;

wire [31:0] app_wdf_mask ;
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 init_calib_complete ;

//例化ddr_ctrl模块
ddr_ctrl ddr_ctrl(
.clk ( ui_clk ),
.rst_n ( rst_n ),

.app_addr ( app_addr ),
.app_cmd ( app_cmd ),
.app_en ( app_en ),
.app_wdf_data ( app_wdf_data ),
.app_wdf_mask ( app_wdf_mask ),

.app_rd_data ( app_rd_data ),
.app_rd_data_end ( app_rd_data_end ),
.app_rd_data_valid ( app_rd_data_valid ),

.app_rdy ( app_rdy ),
.app_wdf_rdy ( app_wdf_rdy ),
.app_wdf_end ( app_wdf_end ),
.app_wdf_wren ( app_wdf_wren ),

.init_calib_complete ( init_calib_complete ),
.ddrdata_test_err ( ddrdata_test_err )
);

//例化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 (0), // input app_sr_req
.app_ref_req (0), // input app_ref_req
.app_zq_req (0), // 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 (clk400),
// Reference Clock Ports
.clk_ref_i (clk400),
.sys_rst (rst_n) // input sys_rst
);

endmodule

ddr_ctrl.v模块
`timescale 1ns / 1ps

module ddr_ctrl(
input clk ,
input rst_n ,

output [28:0] app_addr ,
output reg [2:0] app_cmd ,
output reg app_en ,
output reg [255:0] app_wdf_data ,
output [31:0] app_wdf_mask ,

input [255:0] app_rd_data ,
input app_rd_data_end ,
input app_rd_data_valid ,

input app_rdy ,
input app_wdf_rdy ,
output app_wdf_end ,
output app_wdf_wren ,
input init_calib_complete , // DDR3 鍒濆鍖栧畬鎴�
output reg ddrdata_test_err
);

reg [3:0] test_state ;
reg [15:0] send_cnt ;
reg [28:0] write_addr ;
reg [28:0] read_addr ;
reg [255:0] data_buff ;

assign app_wdf_wren = app_en & app_wdf_rdy & app_rdy & (app_cmd == 3'd0);
assign app_wdf_end = app_wdf_wren;

assign app_addr = (app_cmd == 3'd0) ? write_addr : read_addr;

assign app_wdf_mask = 32'd0;//这个直赋0
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
test_state <= 4'd0;
send_cnt <= 16'd0;
write_addr <= 29'd0;
read_addr <= 29'd0;
app_cmd <= 3'd0;
app_en <= 1'b0;
app_wdf_data <= 256'd0;
end
else
begin
case (test_state)
4'd0 :
begin
app_cmd <= 3'd0;
app_en <= 1'b0;
app_wdf_data <= 256'd0;
send_cnt <= 16'd0;
write_addr <= 29'd0;
read_addr <= 29'd0;
if(init_calib_complete)
test_state <= 4'd1; //如果DDR初始化完成就进入下一个状态
else
test_state <= 4'd0; //否则等待DDR初始化完成
end

4'd1 :
begin
if(app_rdy & app_wdf_rdy) //等待这两个信号拉高就使命令有效
begin
app_cmd <= 3'd0;
app_en <= 1'b1;
send_cnt <= send_cnt + 1'b1;
test_state <= 4'd2;
end
end

4'd2 :
begin
if(app_rdy & app_wdf_rdy)
begin
if(send_cnt == 16'd199)
begin
app_wdf_data <= 256'd0;
write_addr <= 29'd0;
send_cnt <= 16'd0;
test_state <= 4'd3; //进入读状态
app_en <= 1'b0;
end
else
begin
send_cnt <= send_cnt + 1'b1;
app_cmd <= 3'd0;
app_en <= 1'b1;
write_addr <= write_addr + 29'd8;
app_wdf_data <= app_wdf_data + 256'd1;//写入的数据,从1到198
end
end
end

4'd3 :
begin
if(app_rdy & app_wdf_rdy)
begin
app_cmd <= 3'd1; //读命令有效,实际上这时候已经有数据出来了
app_en <= 1'b1;
send_cnt <= send_cnt + 1'b1;
test_state <= 4'd4;
end
end

4'd4 :
begin
if(app_rdy & app_wdf_rdy)
begin
if(send_cnt == 16'd199)
begin
read_addr <= 29'd0;
send_cnt <= 16'd0;
test_state <= 4'd5;
app_en <= 1'b0;
end
else
begin
send_cnt <= send_cnt + 1'b1;
app_cmd <= 3'd1;
app_en <= 1'b1;
read_addr <= read_addr + 29'd8; //读地址

end
end
end

4'd5 ://不读也不写
begin
app_cmd <= 3'd0;
app_en <= 1'b0;
send_cnt <= send_cnt + 1'b1;
if(send_cnt == 16'd200)
begin
send_cnt <= 16'd0;
test_state <= 4'd1;
end
end

default : test_state <= 4'd0;

endcase
end
end
//比较写入与独处的数据是否相等
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
data_buff <= 256'd0;
ddrdata_test_err <= 1'b0;
end
else if (test_state == 4'd3)
begin
data_buff <= 256'd0;
end
else
begin
if(app_rd_data_valid)
begin
data_buff <= data_buff + 256'd1;
if(data_buff != app_rd_data) //如果写入与独处的数据不相等就拉高
ddrdata_test_err <= 1'b1;
end
end
end
endmodule

到此程序已经分析完毕了,想到什么问题再补充吧。

工程代码。

---------------------
作者:maochuangan
来源:CSDN
原文: https://blog.csdn.net/MaoChuangAn/article/details/85269291

最新文章

最新文章