自学内容网 自学内容网

DDR3的使用(二)利用XILINX MIGIP核(native)读写DDR3

我们上一节介绍了mig ip核的时钟结构,搞清楚时钟结构后我们尝试着读写下ddr3芯片。我们使用黑金的AX7325平台进行实验。
1.我们建立vivado该工程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.我们建立好工程,然后添加设计文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述3.添加代码

`timescale 1ps/1ps

module top
  (
   // Inouts
   inout [63:0]                       ddr3_dq,
   inout [7:0]                        ddr3_dqs_n,
   inout [7: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 [7:0]                      ddr3_dm,  
   output [0:0]                      ddr3_odt,  
   //Differential system clocks
   input                              sys_clk_p,
   input                              sys_clk_n,
   input                              sys_clk, 
   output                             error,
   output                             fan_pwm,
   input                              rst_n
   );

localparam nCK_PER_CLK           = 4;
localparam DQ_WIDTH              = 64;
localparam ADDR_WIDTH            = 29;
localparam DATA_WIDTH            = 64;
localparam PAYLOAD_WIDTH         = 64;

localparam APP_DATA_WIDTH        = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;
localparam APP_MASK_WIDTH        = APP_DATA_WIDTH / 8;
 wire init_calib_complete;
assign fan_pwm                   = 1'b0;
  // Wire declarations
 wire sys_clk_200MHz;   
 IBUFDS sys_clk_ibufgds
 (
     .O  (sys_clk_200MHz),
     .I  (sys_clk_p),
     .IB (sys_clk_n)
 );
 //下面接口与ddr核相连  
  wire [ADDR_WIDTH-1:0]                 app_addr;
  wire [2:0]                            app_cmd;
  wire                                  app_en;
  wire                                  app_rdy;
  wire [APP_DATA_WIDTH-1:0]             app_rd_data;
  wire                                  app_rd_data_end;
  wire                                  app_rd_data_valid;
  wire [APP_DATA_WIDTH-1:0]             app_wdf_data;
  wire                                  app_wdf_end;
  wire [APP_MASK_WIDTH-1:0]             app_wdf_mask;
  wire                                  app_wdf_rdy;
  wire                                  app_sr_active;
  wire                                  app_ref_ack;
  wire                                  app_zq_ack;
  wire                                  app_wdf_wren;

  wire                                  clk;
  wire                                  rst;
  ddr3 u_ddr3   //参考 Xilinx 提供的文档“ug586_7Series_MIS.pdf”
  (
   // Memory interface ports
   .ddr3_addr                      (ddr3_addr),
   .ddr3_ba                        (ddr3_ba),
   .ddr3_cas_n                     (ddr3_cas_n),
   .ddr3_ck_n                      (ddr3_ck_n),
   .ddr3_ck_p                      (ddr3_ck_p),
   .ddr3_cke                       (ddr3_cke),
   .ddr3_ras_n                     (ddr3_ras_n),
   .ddr3_we_n                      (ddr3_we_n),
   .ddr3_dq                        (ddr3_dq),
   .ddr3_dqs_n                     (ddr3_dqs_n),
   .ddr3_dqs_p                     (ddr3_dqs_p),
   .ddr3_reset_n                   (ddr3_reset_n),
   .init_calib_complete            (init_calib_complete),
  
   .ddr3_cs_n                      (ddr3_cs_n),
   .ddr3_dm                        (ddr3_dm),
   .ddr3_odt                       (ddr3_odt),
    // Application interface ports
   .app_addr                       (app_addr),
   .app_cmd                        (app_cmd),
   .app_en                         (app_en),
   .app_wdf_data                   (app_wdf_data),
   .app_wdf_end                    (app_wdf_end),
   .app_wdf_wren                   (app_wdf_wren),
   .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_sr_req                     (1'b0),
   .app_ref_req                    (1'b0),
   .app_zq_req                     (1'b0),
   .app_sr_active                  (app_sr_active),
   .app_ref_ack                    (app_ref_ack),
   .app_zq_ack                     (app_zq_ack),
   .ui_clk                         (clk),
   .ui_clk_sync_rst                (rst),
  
   .app_wdf_mask                   (app_wdf_mask),
   // System Clock Ports
   .sys_clk_i                       (sys_clk_200MHz),
   .sys_rst                        (rst_n)
   );
// End of User Design top instance
  wire                              wr_burst_data_req;
  wire                              wr_burst_finish;
  wire                              rd_burst_finish;
  wire                              rd_burst_req;
  wire                              wr_burst_req;
  wire[9:0]                         rd_burst_len;
  wire[9:0]                         wr_burst_len;
  wire[28:0]                        rd_burst_addr;
  wire[28:0]                        wr_burst_addr;
  wire                              rd_burst_data_valid;
 wire[512 - 1 : 0]                  rd_burst_data;
 wire[512 - 1 : 0]                  wr_burst_data;
 //对ddr ip核包装,只用对注释的接口进行控制就可实现对ddr的控制
mem_burst
#(
.MEM_DATA_BITS(APP_DATA_WIDTH),
.ADDR_BITS(ADDR_WIDTH)
)
 mem_burst_m0
(
.rst(rst),                                  /*复位*/
.mem_clk(clk),                              /*接口时钟*/
.rd_burst_req(rd_burst_req),                /*读请求*/
.wr_burst_req(wr_burst_req),                /*写请求*/
.rd_burst_len(rd_burst_len),                /*读数据长度*/
.wr_burst_len(wr_burst_len),                 /*写数据长度*/
.rd_burst_addr(rd_burst_addr),               /*读首地址*/
.wr_burst_addr(wr_burst_addr),               /*写首地址*/
.rd_burst_data_valid(rd_burst_data_valid),   /*读出数据有效*/
.wr_burst_data_req(wr_burst_data_req),       /*写数据信号*/
.rd_burst_data(rd_burst_data),               /*读出的数据*/
.wr_burst_data(wr_burst_data),               /*写入的数据*/
.rd_burst_finish(rd_burst_finish),           /*读完成*/
.wr_burst_finish(wr_burst_finish),           /*写完成*/
.burst_finish(),                             /*读或写完成*/

///
   .app_addr(app_addr),
   .app_cmd(app_cmd),
   .app_en(app_en),
   .app_wdf_data(app_wdf_data),
   .app_wdf_end(app_wdf_end),
   .app_wdf_mask(app_wdf_mask),
   .app_wdf_wren(app_wdf_wren),
   .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),
   .ui_clk_sync_rst(),  
   .init_calib_complete(init_calib_complete)
);
//测试文件,读写数据
mem_test
#(
.MEM_DATA_BITS(APP_DATA_WIDTH),
.ADDR_BITS(ADDR_WIDTH)
)
mem_test_m0
(
.rst(rst),                                 
.mem_clk(clk),                               
.rd_burst_req(rd_burst_req),                         
.wr_burst_req(wr_burst_req),                         
.rd_burst_len(rd_burst_len),                     
.wr_burst_len(wr_burst_len),                    
.rd_burst_addr(rd_burst_addr),        
.wr_burst_addr(wr_burst_addr),        
.rd_burst_data_valid(rd_burst_data_valid),                 
.wr_burst_data_req(wr_burst_data_req),                   
.rd_burst_data(rd_burst_data),   
.wr_burst_data(wr_burst_data),    
.rd_burst_finish(rd_burst_finish),                     
.wr_burst_finish(wr_burst_finish),                      

.error(error)
);

wire probe0;
wire probe1;
wire probe2;
wire probe3;
wire probe4;
wire probe5;
wire probe6;
wire probe7;
wire [511 : 0] probe8;
wire [511 : 0] probe9;
wire [28 : 0] probe10;


ila_0 u_ila_0(
.clk(clk),
.probe0(probe0),
.probe1(probe1),
.probe2(probe2),
.probe3(probe3),
.probe4(probe4),
.probe5(probe5),
.probe6(probe6),
.probe7(probe7),
.probe8(probe8),
.probe9(probe9),
    .probe10(probe10)
);
assign probe0 = rd_burst_req;
assign probe1 = wr_burst_req;
assign probe2 = rd_burst_data_valid;
assign probe3 = wr_burst_data_req;
assign probe4 = rd_burst_finish;
assign probe5 = wr_burst_finish;
assign probe6 = error;
assign probe7 = init_calib_complete;
assign probe8 = wr_burst_data[511:0];
assign probe9 = rd_burst_data[511:0];
assign probe10 = app_addr[28:0];
endmodule

module mem_test
#(
parameter MEM_DATA_BITS = 64,
parameter ADDR_BITS = 24
)
(
input rst,                                         /*复位*/
input mem_clk,                                     /*接口时钟*/
output reg rd_burst_req,                          /*读请求*/
output reg wr_burst_req,                          /*写请求*/
output reg[9:0] rd_burst_len,                     /*读数据长度*/
output reg[9:0] wr_burst_len,                     /*写数据长度*/
output reg[ADDR_BITS - 1:0] rd_burst_addr,        /*读首地址*/
output reg[ADDR_BITS - 1:0] wr_burst_addr,        /*写首地址*/
input rd_burst_data_valid,                         /*读出数据有效*/
input wr_burst_data_req,                           /*写数据信号*/
input[MEM_DATA_BITS - 1:0] rd_burst_data,          /*读出的数据*/
output[MEM_DATA_BITS - 1:0] wr_burst_data,         /*写入的数据*/
input rd_burst_finish,                             /*读完成*/
input wr_burst_finish,                             /*写完成*/
output reg error
);
localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_WRITE  = 3'd2;

reg[2:0] state;
reg[7:0] wr_cnt;
reg[MEM_DATA_BITS - 1:0] wr_burst_data_reg;
assign wr_burst_data = wr_burst_data_reg;
reg[7:0] rd_cnt;
always@(posedge mem_clk or posedge rst)
begin
if(rst)
   error <= 1'b0;
else if((state == MEM_READ) && rd_burst_data_valid && (rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}}))
   error <= 1'b1;
else
   error <= error;//写入数据和读出数据不一致
end
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
wr_burst_data_reg <= {MEM_DATA_BITS{1'b0}};
wr_cnt <= 8'd0;
end
else if(state == MEM_WRITE)
begin
if(wr_burst_data_req)
begin
wr_burst_data_reg <= {(MEM_DATA_BITS/8){wr_cnt}};//写入数据
wr_cnt <= wr_cnt + 8'd1;
end
else if(wr_burst_finish)
wr_cnt <= 8'd0;
end
end
//读数据
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
rd_cnt <= 8'd0;
end
else if(state == MEM_READ)
begin
if(rd_burst_data_valid)
begin
rd_cnt <= rd_cnt + 8'd1;
end
else if(rd_burst_finish)
rd_cnt <= 8'd0;
end
else
rd_cnt <= 8'd0;
end
//状态机
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
state <= IDLE;
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b0;
rd_burst_len <= 10'd128;//数据长度
wr_burst_len <= 10'd128;
rd_burst_addr <= 0;
wr_burst_addr <= 0;
end
else
begin
case(state)
IDLE:
begin
state <= MEM_WRITE;
wr_burst_req <= 1'b1;
wr_burst_len <= 10'd128;
end
MEM_WRITE:
begin
if(wr_burst_finish)
begin
state <= MEM_READ;
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b1;
rd_burst_len <= 10'd128;
rd_burst_addr <= wr_burst_addr;//地址
end
end
MEM_READ:
begin
if(rd_burst_finish)
begin
state <= MEM_WRITE;
wr_burst_req <= 1'b1;
wr_burst_len <= 10'd128;
rd_burst_req <= 1'b0;
wr_burst_addr <= wr_burst_addr + 128;
end
end
default:
state <= IDLE;
endcase
end
end

endmodule
/*本模块完成对ddr2 IP的包装,方便后续模块使用,也方便程序的移植,如果更换平台,更新这个文件即可
*/
module mem_burst
#(
parameter MEM_DATA_BITS = 64,
parameter ADDR_BITS = 24
)
(
input rst,                                 /*复位*/
input mem_clk,                               /*接口时钟*/
input rd_burst_req,                          /*读请求*/
input wr_burst_req,                          /*写请求*/
input[9:0] rd_burst_len,                     /*读数据长度*/
input[9:0] wr_burst_len,                     /*写数据长度*/
input[ADDR_BITS - 1:0] rd_burst_addr,        /*读首地址*/
input[ADDR_BITS - 1:0] wr_burst_addr,        /*写首地址*/
output rd_burst_data_valid,                  /*读出数据有效*/
output wr_burst_data_req,                    /*写数据信号*/
output[MEM_DATA_BITS - 1:0] rd_burst_data,   /*读出的数据*/
input[MEM_DATA_BITS - 1:0] wr_burst_data,    /*写入的数据*/
output rd_burst_finish,                      /*读完成*/
output wr_burst_finish,                      /*写完成*/
output burst_finish,                         /*读或写完成*/

///与ddrIP核相连的接口
   output[ADDR_BITS-1:0]                       app_addr,
   output[2:0]                                 app_cmd,
   output                                      app_en,
   output [MEM_DATA_BITS-1:0]                  app_wdf_data,
   output                                      app_wdf_end,
   output [MEM_DATA_BITS/8-1:0]                app_wdf_mask,
   output                                      app_wdf_wren,
   input [MEM_DATA_BITS-1:0]                   app_rd_data,
   input                                       app_rd_data_end,
   input                                       app_rd_data_valid,
   input                                       app_rdy,
   input                                       app_wdf_rdy,
   input                                       ui_clk_sync_rst,  
   input                                       init_calib_complete
);

assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};

localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_READ_WAIT = 3'd2;
localparam MEM_WRITE  = 3'd3;
localparam MEM_WRITE_WAIT = 3'd4;
localparam READ_END = 3'd5;
localparam WRITE_END = 3'd6;
localparam MEM_WRITE_FIRST_READ = 3'd7;
reg[2:0] state;
reg[9:0] rd_addr_cnt;
reg[9:0] rd_data_cnt;
reg[9:0] wr_addr_cnt;
reg[9:0] wr_data_cnt;

reg[2:0] app_cmd_r;
reg[ADDR_BITS-1:0] app_addr_r;
reg app_en_r;
reg app_wdf_end_r;
reg app_wdf_wren_r;
assign app_cmd = app_cmd_r;
assign app_addr = app_addr_r;
assign app_en = app_en_r;
assign app_wdf_end = app_wdf_end_r;
assign app_wdf_data = wr_burst_data;
assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;
assign rd_burst_finish = (state == READ_END);
assign wr_burst_finish = (state == WRITE_END);
assign burst_finish = rd_burst_finish | wr_burst_finish;

assign rd_burst_data = app_rd_data;
assign rd_burst_data_valid = app_rd_data_valid;

assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;

always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
app_wdf_wren_r <= 1'b0;
end
else if(app_wdf_rdy)
app_wdf_wren_r <= wr_burst_data_req;
end
//DDR Burst 写和读,可以参考实验教程“DDR读写测试实验.pdf”
always@(posedge mem_clk or posedge rst)
begin
if(rst)
begin
state <= IDLE;
app_cmd_r <= 3'b000;
app_addr_r <= 0;
app_en_r <= 1'b0;
rd_addr_cnt <= 0;
rd_data_cnt <= 0;
wr_addr_cnt <= 0;
wr_data_cnt <= 0;
app_wdf_end_r <= 1'b0;
end
else if(init_calib_complete ===  1'b1)
begin
case(state)
IDLE:
begin
if(rd_burst_req)//读请求
begin
state <= MEM_READ;
app_cmd_r <= 3'b001;
app_addr_r <= {rd_burst_addr,3'd0};//向IP核发送指令
app_en_r <= 1'b1;
end
else if(wr_burst_req)//写请求
begin
state <= MEM_WRITE;
app_cmd_r <= 3'b000;
app_addr_r <= {wr_burst_addr,3'd0};//向IP核发送指令
app_en_r <= 1'b1;
wr_addr_cnt <= 0;
app_wdf_end_r <= 1'b1;
wr_data_cnt <= 0;
end
end
MEM_READ:
begin
if(app_rdy)
begin
app_addr_r <= app_addr_r + 8;
if(rd_addr_cnt == rd_burst_len - 1)
begin
state <= MEM_READ_WAIT;
rd_addr_cnt <= 0;
app_en_r <= 1'b0;
end
else
rd_addr_cnt <= rd_addr_cnt + 1;
end

if(app_rd_data_valid)
begin
if(rd_data_cnt == rd_burst_len - 1)
begin
rd_data_cnt <= 0;
state <= READ_END;
end
else
begin
rd_data_cnt <= rd_data_cnt + 1;
end
end
end
MEM_READ_WAIT:
begin
if(app_rd_data_valid)
begin
if(rd_data_cnt == rd_burst_len - 1)
begin
rd_data_cnt <= 0;
state <= READ_END;
end
else
begin
rd_data_cnt <= rd_data_cnt + 1;
end
end
end
MEM_WRITE_FIRST_READ:
begin
app_en_r <= 1'b1;
state <= MEM_WRITE;
wr_addr_cnt <= 0;
end
MEM_WRITE:
begin
if(app_rdy)
begin
app_addr_r <= app_addr_r + 'b1000;
if(wr_addr_cnt == wr_burst_len - 1)
begin
app_wdf_end_r <= 1'b0;
app_en_r <= 1'b0;
end
else
begin
wr_addr_cnt <= wr_addr_cnt + 1;
end
end

if(wr_burst_data_req)
begin

if(wr_data_cnt == wr_burst_len - 1)
begin
state <= MEM_WRITE_WAIT;
end
else
begin
wr_data_cnt <= wr_data_cnt + 1;
end
end

end
READ_END:
state <= IDLE;
MEM_WRITE_WAIT:
begin
if(app_rdy)
begin
app_addr_r <= app_addr_r + 'b1000;
if(wr_addr_cnt == wr_burst_len - 1)
begin
app_wdf_end_r <= 1'b0;
app_en_r <= 1'b0;
if(app_wdf_rdy) 
state <= WRITE_END;
end
else
begin
wr_addr_cnt <= wr_addr_cnt + 1;
end
end
else if(~app_en_r & app_wdf_rdy)
state <= WRITE_END;

end
WRITE_END:
state <= IDLE;
default:
state <= IDLE;
endcase
end
end
endmodule 

4.添加ip核
在这里插入图片描述
这里我们暂时不选择axi接口,使用native
在这里插入图片描述
不需要兼容其他型号
在这里插入图片描述
我们使用的芯片位ddr3
在这里插入图片描述
如下图,1处为给ddr3芯片的时钟,我们使用800M,2处comporment表示是芯片颗粒,下拉菜单里有其他选项,表示内存条。3处是我们使用的芯片型号,我们这里使用MT41J256M16。4处表示总位宽,这里我们看下四片ddr3芯片的连接方式为16*4=64位。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
input clock period表示ip核的输入时钟,我们这里提供200M的时钟。

在这里插入图片描述
下图1处表示输入时钟的形式,有单端,差分和单端no buff,我们这里选择nobuff形式。2处表示参考时钟来源,我们参考时钟使用系统时钟就行。
在这里插入图片描述

在这里插入图片描述
然后进行管脚约束
在这里插入图片描述
添加管脚文件
请添加图片描述
验证通过
请添加图片描述
默认下一步
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
如下图,IP核生成成功。
请添加图片描述
下来我们生成ila的ip核
请添加图片描述
如下设置
请添加图片描述
请添加图片描述
请添加图片描述
两个ip核都能使用了
请添加图片描述
5.我们再添加下约束文件,这里主要约束了时钟周期和一些管脚。
请添加图片描述
接下来进行网表编译
请添加图片描述
网表编译完进行布局布线。
请添加图片描述
布局布线完成后生成下载文件
请添加图片描述
然后连接芯片
请添加图片描述
连接上芯片后就下载程序
请添加图片描述
请添加图片描述
7.下载完我们可以看到波形,我们详细看下:
请添加图片描述
可以看到在1处我们发出了wr_burst_req,当wr_burst_data_valid拉高时我们开始往芯片里面写数据,每次数据累加1,直到7f为止。
请添加图片描述
请添加图片描述
然后我们将写入的数据读出来,我们先发出读请求,当rd_burst_data_valid拉高时,表示读取数据有效。直到读取完毕,我们可以看到写入的数据与读出的数据一致。
请添加图片描述
请添加图片描述
到这里我们就初步完成了读写实验,但是代码逻辑是怎样的,我们后面再分析,进行仿真实验。


原文地址:https://blog.csdn.net/weixin_42306014/article/details/140691374

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!