使用MIG IP 核实现DDR3读写测试学习

1、简介

DDR3 SDRAM(Double-Data-Rate Three Synchronous Dynamic Random Access Memory)是 DDR SDRAM 的第三代产品,相较于 DDR2,DDR3 有更高的运行性能与更低的电压。DDR SDRAM 是在 SDRAM 技术的基础上发展改进而来的,同 SDRAM 相比,DDR SDRAM 的最大特点是双沿触发,即在时钟的上升沿和下降沿都能进行数据采集和发送,同样的工作时钟,DDR SDRAM 的读写速度可以比传统的 SDRAM 快一倍。本次实验使用的 DDR3 芯片是MT41K256M16JT,容量大小为 256M*16bit,也就是 512MByte,使用位于ZYNQ的PL侧的DDR3 芯片。

由于 DDR3 的时序非常复杂,如果直接编写 DDR3 的控制器代码,那么工作量是非常大的,且性能难以得到保证。值得一提的是,Artix7 系列 FPGA 自带了 DDR3 控制器的硬核,用户可以直接借助 IP 核来实现对 DDR3 的读写操作,从而大大降低了 DDR3 的开发难度。本次实验将使用 Xilinx 公司 MIG(Memory Interface Generators) IP 核来实现 DDR3 读写测试。

MIG IP 核是 Xilinx 公司针对 DDR 存储器开发的 IP,里面集成存储器控制模块,实现 DDR 读写操作的控制流程,下图是 7 系列的 MIG IP 核结构框图。MIG IP 核对外分出了两组接口。左侧是用户接口,就是用户(FPGA)同 MIG 交互的接口,用户只有充分掌握了这些接口才能操作 MIG。右侧为 DDR 物理芯片接口,负责产生具体的操作时序,并直接操作芯片管脚。这一侧用户只负责分配正确的管脚,其他不用关心。

使用这个 IP 核,用户将可以进行 DDR3 的读写操作而不必熟悉 DDR3 具体的读写控制时序,当然用户必须掌握用户接口侧的操作时序,并严格遵照时序来编写代码,这样才能正确实现对 DDR3 的读写操作。在了解具体时序之前,大家有必要先了解相关的信号定义。下图给出了MIG IP 核用户接口的信号及其说明。

MIG IP 核用户侧端口数量共 26 个,当然用户并不用去关心所有的信号,只需要了解本实验要用到几组重要信号。下面将对这些信号逐一讲解并以表格的形式呈现给大家。为了与官方的文档保持一致,表中标明的信号的方向是以MIG IP核作为参照的,例如表格中的信号方向定义为输出,那么相对于用户端(FPGA)来说实际上是输入。

表1 MIG IP 核用户接口部分信号定义

以上是用户需要用到的信号。

DDR3 的读或者写都包含写命令操作,其中写操作命令(app_cmd)的值等于 0,读操作 app_cmd 的值等于 1。首先来看写命令时序,如下图所示。首先检查 app_rdy,为高则表明此时 IP 核命令接收处于准备好状态,可以接收用户命令,在当前时钟拉高 app_en,同时发送命令(app_cmd)和地址(app_addr),此时命令和地址被写入。

写命令时序

写数据的时序如下图所示。

非背靠背写时序

如上图所示,写数据有三种情形均可以正确写入:

(1)写数据时序和写命令时序发生在同一拍;

(2)写数据时序比写命令时序提前一拍;

(3)写数据时序比写命令时序至多延迟晚两拍;

结合上图,写时序总结如下:首先需要检查 app_wdf_rdy,该信号为高表明此时 IP 核数据接收处于准备完成状态,可以接收用户发过来的数据,在当前时钟拉高写使能(app_wdf_wren),给出写数据(app_wdf_data)。这样加上发起的写命令操作就可以成功向 IP 核写数据。这里有一个信号 app_wdf_mask,它是用来屏蔽写入数据的,该信号为高则屏蔽相应的字节,该信号为 0 默认不屏蔽任何字节。

这里需要指出的是 DDR3 的读或者写操作都可以分为背靠背和非背靠背两种情形。背靠背,即读或者写每个时钟都连续进行,中间没有间隙。非背靠背写则是非连续的读写。

对于背靠背写,其实也有三种情形,唯一点不同的是,它没有最大延迟限制,如下图所示。

背靠背写时序

接着来看读数据,如下图所示:

读时序

读时序比较简单,发出读命令后,用户只需等待数据有效信号(app_rd_data_valid)拉高,为高表明此时数据总线上的数据是有效的返回数据。需要注意的是,在发出读命令后,有效读数据要晚若干周期才出现在数据总线上。下面是背靠背读的情况,如下图所示。

背靠背读时序图

在连续读的时候,读到的数据顺序跟请求的命令/地址是相对应的。通常使用 DDR3 的时候,为了最大限度地提高 DDR3 效能,充分利用突发写的特点,非背靠背很少用,而更多地采用背靠背操作。本章实验的读写操作就是基于背靠背模式进行的。

2、程序设计

首先 FPGA 通过调用 MIG IP 核向 DDR3 芯片写入数据,写完之后通过 MIG IP 核从 DDR3 芯片读出所写入的数据,并判断读出的数据与写入的数据是否相同,如果相同则 LED 灯常亮,否则 LED 灯闪烁。由此画出系统的功能框图如下图所示:

由系统总体框图可知,FPGA 顶层模块例化了以下三个模块,分别是读写模块(ddr3_rw)、MIG IP 核模块(mig_7series_0)和时钟模块(clk_wiz_0)。

ddr3_rw 模块产生读写 MIG IP 核用户接口的时序,实现与 MIG IP 核的数据及信号交互,此外,也在该模块实现读写测试结果的判断,并送出判断结果的指示信号 LED。clk_wiz_0 是本实验的时钟模块,负责产生 MIG IP 核所需的系统时钟 sys_clk_i 和参考时钟 clk_ref_i,本次实验这两个时钟都是 200M。mig_7series_0模块就是在简介部分中提到的 MIG IP 核,该模块一方面负责与用户(FPGA)进行数据交互,另一方面还产生控制 DDR3 读写的各种时序,并实现对 DDR 芯片的读写操作。用户可以把 mig_7series_0 看作是 FPGA与 DDR3 芯片交流信息的桥梁,当然这个"桥梁"的实现代码不需要用户自己写,Xilinx 官方已帮大家写好,并封装成了一个 IP 供用户调用,用户只需要根据实际应用配置该 IP 即可。

首先在 Vivado 环境里新建一个工程,本实验取名为 ddr3_rw_top。再点击 Project Manager 界面下的 IP Catalog,打开 IP Catalog 界面。如下图所示。

在搜索栏中输入 MIG,此时出现 MIG IP 核,直接双击打开。如下图所示。

下面让确认工程的信息,主要是芯片信息和编译环境的信息,如果没什么问题,直接点击"Next"。 如下图所示。

如下图所示,这一页选择"Create Design",在"Component Name"一栏设置该 IP 元件的名称,这里取默认软件的名称,再往下选择控制器数量,默认为"1"即可。最后关于 AXI4 接口,因为本工程不去使用,所以不勾选。配置完成点击"Next"。

如下图所示,这一页主要是让用户选择可以兼容的芯片,本工程默认不勾选,即不需要兼容其他的 FPGA芯片。配置完成点击"Next"。

如下图所示,这一页选择第一个选项"DDR3 SDRAM",因为本实验用的就是 DDR3 芯片。配置完成点击"Next"。

如下图所示,从这页开始,下面来讲解如何配置 MIG IP 核,大家可以对照图片和文字来详细了解各个选项和本次实验的配置参数。

Clock Period: DDR3 芯片运行时钟周期,这个参数的范围和 FPGA 的芯片类型以及具体类型的速度等级有关。本实验选择 2500ps,对应 400M,这是本次实验所采用芯片可选的最大频率。注意这个时钟是 MIG IP 核产生,并输出给 DDR3 物理芯片使用的,它关系到 DDR3 芯片具体的运行带宽。比如本次实验的开发板板载了一颗 DDR3 芯片,数据位宽总共 16 位,因为是双沿触发,这里带宽达到了 12.8Gb/s(800M*16bit)。
PHY to Controller Clock Ratio: DDR3 物理芯片运行时钟和 MIG IP 核的用户端(FPGA)的时钟之比,一般有 4:1 和 2:1 两个选项,本次实验选 4:1。由于 DDR 芯片的运行时钟是 400Mhz,因此 MIG IP 核的用户时钟(ui_clk)就是 100Mhz。一般来说高速传输的场合选择 4:1,要求低延时的场合选择 2:1。这里还要指出,当 DDR3 时钟选择选择了 350M 到最高的 400M,比例默认只为 4:1,低于 350M 才有 4:1 和 2:1 两个选项。

VCCAUX_IO: 这是 FPGA 高性能 bank(High Performance bank)的供电电压。它的设置取决于 MIG 控制器运行的周期/频率。当用户让控制器工作在最快频率的时候,系统会默认为 1.8V,当然在 1.8V 下用户可以运行低一点的频率。本实验默认 1.8V。

Memory Type: DDR3 储存器类型选择。本实验选择 Component。

Memory Part: DDR3 芯片的具体型号。本实验选择 MT41J128M16XX-125,这个型号其实和实际硬件原理图上的型号 NT5CB128M16CP-DI 是不同的,这个没关系,只要用户的 DDR3 芯片容量和位宽一致大部分是可以兼容的,其他的型号也是可以的,大家有兴趣可以去尝试。

Memory Voltage: 是 DDR3 芯片的电压选择,本实验选 1.5v。

Data Width: 数据位宽选择,这里选择 16。

ECC: ECC 校验使能,数据位宽为 72 位的时候才能使用。本实验不使用它。

Data Mask: 数据屏蔽管脚使能。勾选它才会产生屏蔽信号,本实验没用到数据屏蔽,但是在这里还是把它勾选上。

Number of Bank Machines: Bank Machine 的数量是用来对具体的每个或某几个来单独控制的,选择多了控制效率就会高,相应的占用的资源也多,本实验选择 4 个,平均一个 Bank Machine 控制两个 BANK(本次实验的 DDR3 芯片是八个 bank)。

ORDERING: 该信号用来决定 MIG 控制器是否可以对它收到的指令进行重新排序,选择 Normal 则允许,Strict 则禁止。本实验选择 Normal,从而获得更高效率。

点击"NEXT"按钮,界面如下图所示。

Input Clock Period: MIG IP 核的系统输入时钟周期,该输入时钟是由 FPGA 内部产生的,本次实验选择的时钟频率为 200MHz(5000ps)。

Read Burst Type and Length: 突发类型选择,突发类型有顺序突发和交叉突发两种,本实验选择顺序突发(Sequential),其突发长度固定为 8。

Output Driver Impdance Control: 输出阻抗控制。本实验选择 RZQ/7。

RTT: 终结电阻,可进行动态控制。本次实验选择 RZQ/4。

Controller Chip Select Pin: 片选管脚引出使能。本实验选择 enable,表示把片选信号 cs#引出来,由外部控制。

BANK_ROW_COLUMN: 寻址方式选择。本实验选择第二种,即 BANK-ROW-COLUMN 的形式,这是一种最常规的 DDR3 寻址方式,即要指定某个地址,先指定 bank,再指定行,最后指定列,这样就确定了一个具体地址。一般来说这样寻址方式有利于降低功耗,但是读写性能(效率)上不如 "ROW_BANK_COLUMN"。配置完成点击"Next"。

如下图所示这是对 MIG IP 系统时钟的属性设置。

System Clock: MIG IP核输入时钟。本实验选择"No Buffer", 因为IP核的输入系统时钟是单端时钟,是由内部的MMCM产生的,MMCM所产生的时钟默认添加了buffer。

Reference Clock: MIG IP 核参考时钟。同样选择"No Buffer",将由时钟模块生成。感兴趣的用户也可以选择"Use System Clock"这个选项,这时候的 MIG IP 系统时钟同时作为了参考时钟,IP 核参考时钟要求是 200Mhz,而 MIG IP 核的系统时钟刚好也使用了 200Mhz 的系统时钟。

System Reset Polarity: 复位有效电平选择。本实验选择"ACTIVE LOW"低电平有效。

Debug Signals Control: 该选项用于控制 MIG IP 核是否把一些调试信号引出来,它会自动添加到 ILA,这些信号包括一些 DDR3 芯片的校准状态信息。本实验选择选择"OFF",不需要让 IP 核生产各种调试信号。

Sample Data Depth: 采样深度选择。当"Debug Signals Control"选择"OFF"时,所有采样深度是不可选的。

Internal Vref: 内部参考管脚,表示将某些参考管脚当成普通的输入管脚来用。由于开发板的 IO 资源较为紧张,因此这里需要选择"ON",把参考管脚当做普通的输入管脚来用。

IO Power Reduction: IO 管脚节省功耗设置。本实验选择"ON",即开启。

XADC Instantiation: XADC 模块例化。使用 MIG IP 核运行的时候需要进行温度补偿,可以直接选择XADC 模块的温度数据引到 MIG IP 核来使用,否则需要额外提供温度数据,所以本实验选择"Enable"。

继续点击"NEXT"按钮,界面如下图所示。

上图界面是内部高性能 bank 端接匹配阻抗的设置,这里不去改它,默认 50 欧姆即可。接下来点击"NEXT"按钮,界面如下图所示。

Pin/Bank Selection Mode: 管脚模式选择。本次实验选择第二种。

继续点击"Next"按钮,界面如下图所示。

这里管脚分配是对照原理图一个一个选择的。

如下图所示,这里软件默认设置,直接点击"Next"。

如下图所示,这里显示 DDR3 IP 配置的概况,查看一下,没有问题就点击"Next"。

如下图所示,选择"Accept",然后点击"Next"。

如下图,这是 PCB 信息选项卡,直接点击"Next"。

如下图,这是整个 MIG IP 核设计的汇总,直接点击"Generate"。

如下图所示,选择"Global",然后点击"Generate"。到这里完成了 MIG IP 核的所有配置。并最终生成 IP 核。

下面来看具体代码

module ddr3_rw_top(
   input              sys_clk,         //系统时钟
   //input              sys_rst_n,       //复位,低有效
    // DDR3
   inout [15:0]       ddr3_dq,         //DDR3 数据
   inout [1:0]        ddr3_dqs_n,      //DDR3 dqs负
   inout [1:0]        ddr3_dqs_p,      //DDR3 dqs正       
   output [13:0]      ddr3_addr,       //DDR3 地址
   output [2:0]       ddr3_ba,         //DDR3 banck 选择
   output             ddr3_ras_n,      //DDR3 行选择
   output             ddr3_cas_n,      //DDR3 列选择
   output             ddr3_we_n,       //DDR3 读写选择
   output             ddr3_reset_n,    //DDR3 复位
   output [0:0]       ddr3_ck_p,       //DDR3 时钟正
   output [0:0]       ddr3_ck_n,       //DDR3 时钟负
   output [0:0]       ddr3_cke,        //DDR3 时钟使能
   output [0:0]       ddr3_cs_n,       //DDR3 片选
   output [1:0]       ddr3_dm,         //DDR3_dm
   output [0:0]       ddr3_odt,        //DDR3_odt
   //用户
   output             led              //错误指示信号
    );                
                      
 //wire define  
wire                 clk_330;
wire                 error_flag;
wire                  ui_clk ;             //用户时钟
wire [27:0]           app_addr;            //DDR3 地址
wire [2:0]            app_cmd;             //用户读写命令
wire                  app_en;              //MIG IP核使能
wire                  app_rdy;             //MIG IP核空闲
wire [127:0]          app_rd_data;         //用户读数据
wire                  app_rd_data_end;     //突发读当前时钟最后一个数据 
wire                  app_rd_data_valid;   //读数据有效
wire [127:0]          app_wdf_data;        //用户写数据 
wire                  app_wdf_end;         //突发写当前时钟最后一个数据 
wire [15:0]           app_wdf_mask;        //写数据屏蔽
wire                  app_wdf_rdy;         //写空闲
wire                  app_sr_active;       //保留
wire                  app_ref_ack;         //刷新请求
wire                  app_zq_ack;          //ZQ 校准请求
wire                  app_wdf_wren;        //DDR3 写使能                  
wire                  locked;              //锁相环频率稳定标志
wire                  clk_ref_i;           //DDR3参考时钟
wire                  sys_clk_i;           //MIG IP核输入时钟
wire                  clk_200;             //200M时钟
wire                  ui_clk_sync_rst;     //用户复位信号
wire                  init_calib_complete; //校准完成信号
wire [20:0]           rd_cnt;              //实际读地址计数
wire [1 :0]           state;                //状态计数器
wire [23:0]           rd_addr_cnt;         //用户读地址计数器
wire [23:0]           wr_addr_cnt;         //用户写地址计数器

reg [15:0] iReset_cnt;
reg sys_rst_n;
//reset
always @(posedge clk_out2) begin
   if(iReset_cnt<=100)begin
        sys_rst_n<=1'b0;
        iReset_cnt<=iReset_cnt+1'b1;
   end
   else if(iReset_cnt>100)begin
        sys_rst_n<=1'b1;
   end
   else begin
        iReset_cnt<=1'b0;
   end
end




//*****************************************************
//**                    main code
//*****************************************************

//读写模块
 ddr3_rw u_ddr3_rw(
    .ui_clk               (ui_clk),                
    .ui_clk_sync_rst      (ui_clk_sync_rst),       
    .init_calib_complete  (init_calib_complete),
    .app_rdy              (app_rdy),
    .app_wdf_rdy          (app_wdf_rdy),
    .app_rd_data_valid    (app_rd_data_valid),
    .app_rd_data          (app_rd_data),
    
    .app_addr             (app_addr),
    .app_en               (app_en),
    .app_wdf_wren         (app_wdf_wren),
    .app_wdf_end          (app_wdf_end),
    .app_cmd              (app_cmd),
    .app_wdf_data         (app_wdf_data),
    .state                (state),
    .rd_addr_cnt          (rd_addr_cnt),
    .wr_addr_cnt          (wr_addr_cnt),
    .rd_cnt               (rd_cnt),
    
    .error_flag           (error_flag),
    .led                  (led)
    );
    
//MIG IP核模块
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), 
                                                   // 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                     (),            // input	        app_sr_req
    .app_ref_req                    (),            // input	        app_ref_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                   (31'b0),        // input [31:0]	app_wdf_mask
    // System Clock Ports
    .sys_clk_i                      (clk_200),
    // Reference Clock Ports
    .clk_ref_i                      (clk_200),
    .sys_rst                        (sys_rst_n)     // input         sys_rst
    );
    
//PLL模块 
clk_wiz_0 u_clk_wiz_0
   (
    // Clock out ports
    .clk_out1(clk_200),     // output clk_out1
    .clk_out2(clk_out2),     // output clk_out2
    // Status and control signals
    .reset(1'b0),           // input resetn
    .locked(locked),        // output locked
   // Clock in ports
    .clk_in1(sys_clk)
    );                      // input clk_in1
    
 ila_0 your_instance_name (
	.clk(clk_200), // input wire clk


	.probe0(app_rd_data), // input wire [127:0]  probe0  
	.probe1(app_cmd), // input wire [2:0]  probe1 
	.probe2(app_addr), // input wire [28:0]  probe2 
	.probe3(app_rdy), // input wire [0:0]  probe3 
	.probe4(app_wdf_rdy), // input wire [0:0]  probe4 
	.probe5(app_rd_data_valid), // input wire [0:0]  probe5 
	.probe6(app_en), // input wire [0:0]  probe6 
	.probe7(app_wdf_data), // input wire [127:0]  probe7 
	.probe8(state), // input wire [1:0]  probe8 
	.probe9(error_flag), // input wire [0:0]  probe9 
	.probe10(rd_addr_cnt), // input wire [23:0]  probe10 
	.probe11(wr_addr_cnt), // input wire [23:0]  probe11 
	.probe12(rd_cnt), // input wire [20:0]  probe12 
	.probe13(ui_clk_sync_rst), // input wire [0:0]  probe13 
	.probe14(init_calib_complete) // input wire [0:0]  probe14
);
    
    
    
endmodule

这是顶层模块的代码,大家可以看到顶层模块一共例化了三个子模块,分别是 DDR3 读写模块(ddr3_rw_top)、MIG IP 核模块(mig_7series_0)和时钟模块(clk_wiz_0)。每个模块的功能在上文已经详细提及,这里给出了源代码以及详细的注释,请大家自行查看。我所用的板卡没有sys_rst_n信号,这里单独写了一个系统复位。

//reset
always @(posedge clk_out2) begin
   if(iReset_cnt<=100)begin
        sys_rst_n<=1'b0;
        iReset_cnt<=iReset_cnt+1'b1;
   end
   else if(iReset_cnt>100)begin
        sys_rst_n<=1'b1;
   end
   else begin
        iReset_cnt<=1'b0;
   end
end

重点讲解 DDR3 读写模块。

 module ddr3_rw (          
     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,     //读数据有效
     input          [127:0]   app_rd_data,           //用户读数据
     output reg     [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     [127:0]   app_wdf_data,          //用户写数据
     output reg     [1 :0]    state,                 //读写状态
     output reg     [23:0]    rd_addr_cnt,           //用户读地址计数
     output reg     [23:0]    wr_addr_cnt,           //用户写地址计数
     output reg     [20:0]    rd_cnt,                //实际读地址标记
     output reg               error_flag,            //读写错误标志
     output reg               led                    //读写测试结果指示灯
     );
 
 //parameter define
 parameter  TEST_LENGTH = 1000;
 parameter  L_TIME = 25'd25_000_000;
 parameter  IDLE        = 2'd0;            //空闲状态
 parameter  WRITE       = 2'd1;            //写状态
 parameter  WAIT        = 2'd2;            //读到写过度等待
 parameter  READ        = 2'd3;            //读状态
 
 //reg define
 reg  [24:0]  led_cnt;    //led计数
 
 //wire define
 wire         error;     //读写错误标记
 wire         rst_n;     //复位,低有效
 
  //*****************************************************
 //**                    main code
 //***************************************************** 
 
 assign rst_n = ~ui_clk_sync_rst;
 //读信号有效,且读出的数不是写入的数时,将错误标志位拉高
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));
 
 //在写状态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;  
     
 //DDR3读写逻辑实现
 always @(posedge ui_clk or negedge rst_n) begin
     if((~rst_n)||(error_flag)) begin 
         state    <= IDLE;          
         app_wdf_data <= 128'd0;     
         wr_addr_cnt  <= 24'd0;      
         rd_addr_cnt  <= 24'd0;       
         app_addr     <= 28'd0;          
     end
     else if(init_calib_complete)begin               //MIG IP核初始化完成
         case(state)
             IDLE:begin
                 state    <= WRITE;
                 app_wdf_data <= 128'd0;   
                 wr_addr_cnt  <= 24'd0;     
                 rd_addr_cnt  <= 24'd0;       
                 app_addr     <= 28'd0;        
              end
             WRITE:begin
                 if(wr_addr_cnt == TEST_LENGTH - 1 &&(app_rdy && app_wdf_rdy))
                     state    <= WAIT;                  //写到设定的长度跳到等待状态
                 else if(app_rdy && app_wdf_rdy)begin   //写条件满足
                     app_wdf_data <= app_wdf_data + 1;  //写数据自加
                     wr_addr_cnt  <= wr_addr_cnt + 1;   //写地址自加
                     app_addr     <= app_addr + 8;      //DDR3 地址加8
                 end
                 else begin                             //写条件不满足,保持当前值
                     app_wdf_data <= app_wdf_data;      
                     wr_addr_cnt  <= wr_addr_cnt;
                     app_addr     <= app_addr; 
                 end
               end
             WAIT:begin                                                  
                 state   <= READ;                     //下一个时钟,跳到读状态
                 rd_addr_cnt <= 24'd0;                //读地址复位
                 app_addr    <= 28'd0;                //DDR3读从地址0开始
               end
             READ:begin                               //读到设定的地址长度    
                 if(rd_addr_cnt == TEST_LENGTH - 1 && app_rdy)
                     state   <= IDLE;                   //则跳到空闲状态 
                 else if(app_rdy)begin                  //若MIG已经准备好,则开始读
                     rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
                     app_addr    <= app_addr + 8;       //DDR3地址加8
                 end
                 else begin                             //若MIG没准备好,则保持原值
                     rd_addr_cnt <= rd_addr_cnt;
                     app_addr    <= app_addr; 
                 end
               end
             default:begin
                 state    <= IDLE;
                 app_wdf_data <= 128'd0;
                 wr_addr_cnt  <= 24'd0;
                 rd_addr_cnt  <= 24'd0;
                 app_addr     <= 28'd0;
             end
         endcase
     end
 end   
                         
 //对DDR3实际读数据个数编号计数
 always @(posedge ui_clk or negedge rst_n) begin
     if(~rst_n) 
         rd_cnt  <= 0;              //若计数到读写长度,且读有效,地址计数器则置0                                    
     else if(app_rd_data_valid && rd_cnt == TEST_LENGTH - 1)
          rd_cnt <= 0;              //其他条件只要读有效,每个时钟自增1
     else if (app_rd_data_valid )
         rd_cnt <= rd_cnt + 1;
 end
 
 //寄存状态标志位
 always @(posedge ui_clk or negedge rst_n) begin
     if(~rst_n) 
         error_flag <= 0;
     else if(error)
         error_flag <= 1;
  end
  
 //led指示效果控制
 always @(posedge ui_clk or negedge rst_n) begin
      if((~rst_n) || (~init_calib_complete )) begin
         led_cnt <= 25'd0;
         led <= 1'b0;
     end
     else begin
         if(~error_flag)                        //读写测试正确         
             led <= 1'b1;                       //led灯常亮
          else begin                            //读写测试错误
             led_cnt <= led_cnt + 25'd1;
             if(led_cnt == L_TIME - 1'b1) begin
             led_cnt <= 25'd0;
             led <= ~led;                      //led灯闪烁
             end                    
          end
       end
 end
 
 endmodule

代码第 2 行到第 20 行是模块的接口定义,其实大部分是 MIG IP 核中要操作的用户接口信号,这些信号在上文已经以表格的形式详细呈现给大家,有不清楚的地方请回去查看。

代码第 24 行和第 29 行是参数定义,首先定义了 L_TIME(闪烁间隔),该参数用来控制 LED 闪烁频率。接着,定义了 TEST_LENGTH(测试长度),注意这里的测试长度是相对于用户端来说的,一个测试长度对应到 DDR 芯片需要访问是 8 个地址。最后定义了读写测试的四个状态,IDLE(空闲)、WRITE(写)、 WAIT(等待)、READ(读)。

代码第 42 行,信号 ui_clk_sync_rst 是 MIG IP 核产生,用于复位用户逻辑,其高电平有效。在代码中将 ui_clk_sync_rst 信号取反,是为了转换成代码中习惯使用的复位低电平有效。

代码第 44 行,是错误标志的逻辑判断。读数据有效信号(app_rd_data_valid)为高时去比较 app_rd_data和 rd_cnt 的值,不相等则读写错误。关于 rd_cnt 的含义将会在下文详细介绍。

代码第 47 行到 48 行,首先产生了 MIG IP 核命令发送使能信号,这里可以看出,读或者写状态都要拉高该信号,需要特别指出的是,在写的时候,app_en 拉高的条件是:app_rdy(MIG 命令接收准备好标志)和 app_wdf_rdy(MIG 数据接收准备好标致)同时为高,根据数据手册,其实用户可以只判断 app_rdy 信号,当 app_rdy 信号为高,就可以拉高使能 app_en,之所以同时判断 app_rdy 和 app_wdf_rdy 是因为本实验只考虑 DDR3 写状态命令发送和数据发送同拍的情形,这样的做法基本上不会对 DDR3 读写操作效率造成很大影响,但是却能大大简化代码编写难度。

代码第 51 行,产生写使能,如上所述,这里只考虑写命令和写数据同时发起的情形,当检测到命令接收和数据接收标志同时拉高的时候,写操作也在同一时间被发起,即拉高app_wdf_wren(写数据使能)。这里要解释一下代码第 54 行。上文说过 app_wdf_end 表明当前时钟是突发写过程的最后一个时钟周期,由于本实验采用的是 4:1 模式,每一个用户时钟周期实际上都完成了一个写突发操作,所以在突发写有效时,即 app_wdf_wren 拉高,每个时钟,app_wdf_end 也跟着拉高,所以在 4:1 模式信号 app_wdf_end 和 app_wdf_wren 是同步变化的。

代码 60 行起的 always 模块是 DDR3 读写逻辑的实现。采用了一段式状态机来编写整个读写测试逻辑,如下图所示,一共分为四个状态。描述如下:上电一开始处于 IDLE(空闲)状态,初始化完成后跳到 WRITE(DDR3 写),写到测试长度跳到 WAIT(等待)状态,在此消耗一个实在周期后无条件跳到 READ(DDR3读),读到测试长度跳回空闲状态。

下面来讲解具体的各状态,主要是写和读状态。

首先来看看写过程,为了便于理解,下面给出了 ILA 中采集到的 DDR3 写的波形,同时为了方便观察完整的写过程,把本次实验的一次读写长度设为 10。如下图所示:

写状态首先判断 MIG IP 核发送过来的信号 app_rdy 和 app_wdf_rdy,当这两个信号同时为高时,拉高 app_en 和 app_wdf_wren,同时时给写出命令,即 app_cmd 为 0,此刻正式进行 DDR3 写过程。写的过程中,写数据 app_wdf_data 和地址计数 wr_addr_cnt 在每个时钟自加 1。另外大家注意到 app_addr 每次自加 8,前面其实提到,用户端在每一个用户时钟进行一个 128bit 的数据的传输,在 DDR3 物理芯片端需要分 8 次传输,每次传输一个地址位宽 16bit,8 次就需要 8 个地址。通过写时序图和 ILA 图进行对比,可以发现两者是一致的,即说明本次实验是成功的。

代码第 96 到 107 行是 DDR3 读过程,在读状态,判断 MIG IP 核发送过来的信号 app_rdy,当这个信号为高时,拉高 app_en,同时给出读命令,即 app_cmd 为 1,此时开始进行读操作。在进行读操作的时候,app_addr 同样每次自加 8。下面还是给出 ILA 上观察到的读信号波形,方便大家理解查看。

读操作结束又跳回空闲状态,如代码第 98 行所示。

代码第 120 行到 127 行的 always 模块,产生 rd_cnt,实现了对读出的地址数据的编号。为什么会有这样的处理,其实根本原因就是读数据的返回和读操作并不跟写那样是在同一拍出现的,会晚若干周期出现。这样意味着不能用读数据直接和当前的读地址去比较,所以本次实验单独构造了 rd_cnt 这个信号,它代表一次读操作真正返回的有效的读数据个数。

代码第 130 行到 135 行的 always 模块,实现了错误标识的寄存。代码第 138 行到 154 行的 always 模块实现读写测试 LED 灯的显示逻辑。错误则闪烁,正确则常亮。

最后通过采集到的波形来看读写测试结果,在读数据有效信号 app_rd_data_valid 为高时,数据总线上的 app_rd_data 分别和地址编号相等 rd_cnt 相等。按照本次实验的设计,写进 DDR3 的数值其实就是它的地址编号,也即等于数据编号,这就是正确的读写结果。当然大家可以看到错误指示信号 error_flag 始终为 0,即没有出现读写错误。
DDR3 板子测试结果:

约束文件

mig_7series_0.xdc

##################################################################################################
## 
##  Xilinx, Inc. 2010            www.xilinx.com 
##  Sat Jan  6 20:58:27 2024

##  Generated by MIG Version 4.2
##  
##################################################################################################
##  File name :       mig_7series_0.xdc
##  Details :     Constraints file
##                    FPGA Family:       ZYNQ
##                    FPGA Part:         XC7Z035-FBG676
##                    Speedgrade:        -1
##                    Design Entry:      VERILOG
##                    Frequency:         400 MHz
##                    Time Period:       2500 ps
##################################################################################################

##################################################################################################
## Controller 0
## Memory Device: DDR3_SDRAM->Components->MT41K256M16XX-15E
## Data Width: 16
## Time Period: 2500
## Data Mask: 1
##################################################################################################

set_property IO_BUFFER_TYPE NONE [get_ports {ddr3_ck_n[*]} ]
set_property IO_BUFFER_TYPE NONE [get_ports {ddr3_ck_p[*]} ]
          
#create_clock -period 5 [get_ports sys_clk_i]
          
#create_clock -period 5 [get_ports clk_ref_i]
          
############## NET - IOSTANDARD ##################


# PadFunction: IO_L1N_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[0]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[0]}]
set_property PACKAGE_PIN F4 [get_ports {ddr3_dq[0]}]

# PadFunction: IO_L2P_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[1]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[1]}]
set_property PACKAGE_PIN D4 [get_ports {ddr3_dq[1]}]

# PadFunction: IO_L2N_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[2]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[2]}]
set_property PACKAGE_PIN D3 [get_ports {ddr3_dq[2]}]

# PadFunction: IO_L4P_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[3]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[3]}]
set_property PACKAGE_PIN D1 [get_ports {ddr3_dq[3]}]

# PadFunction: IO_L4N_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[4]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[4]}]
set_property PACKAGE_PIN C1 [get_ports {ddr3_dq[4]}]

# PadFunction: IO_L5P_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[5]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[5]}]
set_property PACKAGE_PIN E2 [get_ports {ddr3_dq[5]}]

# PadFunction: IO_L5N_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[6]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[6]}]
set_property PACKAGE_PIN E1 [get_ports {ddr3_dq[6]}]

# PadFunction: IO_L6P_T0_33 
set_property SLEW FAST [get_ports {ddr3_dq[7]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[7]}]
set_property PACKAGE_PIN F3 [get_ports {ddr3_dq[7]}]

# PadFunction: IO_L7N_T1_33 
set_property SLEW FAST [get_ports {ddr3_dq[8]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[8]}]
set_property PACKAGE_PIN H1 [get_ports {ddr3_dq[8]}]

# PadFunction: IO_L8P_T1_33 
set_property SLEW FAST [get_ports {ddr3_dq[9]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[9]}]
set_property PACKAGE_PIN H4 [get_ports {ddr3_dq[9]}]

# PadFunction: IO_L8N_T1_33 
set_property SLEW FAST [get_ports {ddr3_dq[10]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[10]}]
set_property PACKAGE_PIN H3 [get_ports {ddr3_dq[10]}]

# PadFunction: IO_L10P_T1_33 
set_property SLEW FAST [get_ports {ddr3_dq[11]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[11]}]
set_property PACKAGE_PIN H2 [get_ports {ddr3_dq[11]}]

# PadFunction: IO_L10N_T1_33 
set_property SLEW FAST [get_ports {ddr3_dq[12]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[12]}]
set_property PACKAGE_PIN G1 [get_ports {ddr3_dq[12]}]

# PadFunction: IO_L11P_T1_SRCC_33 
set_property SLEW FAST [get_ports {ddr3_dq[13]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[13]}]
set_property PACKAGE_PIN L3 [get_ports {ddr3_dq[13]}]

# PadFunction: IO_L11N_T1_SRCC_33 
set_property SLEW FAST [get_ports {ddr3_dq[14]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[14]}]
set_property PACKAGE_PIN K3 [get_ports {ddr3_dq[14]}]

# PadFunction: IO_L12P_T1_MRCC_33 
set_property SLEW FAST [get_ports {ddr3_dq[15]}]
set_property IOSTANDARD SSTL15_T_DCI [get_ports {ddr3_dq[15]}]
set_property PACKAGE_PIN J4 [get_ports {ddr3_dq[15]}]

# PadFunction: IO_L7P_T1_34 
set_property SLEW FAST [get_ports {ddr3_addr[14]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[14]}]
set_property PACKAGE_PIN F5 [get_ports {ddr3_addr[14]}]

# PadFunction: IO_L7N_T1_34 
set_property SLEW FAST [get_ports {ddr3_addr[13]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[13]}]
set_property PACKAGE_PIN E5 [get_ports {ddr3_addr[13]}]

# PadFunction: IO_L8P_T1_34 
set_property SLEW FAST [get_ports {ddr3_addr[12]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[12]}]
set_property PACKAGE_PIN D9 [get_ports {ddr3_addr[12]}]

# PadFunction: IO_L8N_T1_34 
set_property SLEW FAST [get_ports {ddr3_addr[11]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[11]}]
set_property PACKAGE_PIN D8 [get_ports {ddr3_addr[11]}]

# PadFunction: IO_L9P_T1_DQS_34 
set_property SLEW FAST [get_ports {ddr3_addr[10]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[10]}]
set_property PACKAGE_PIN F9 [get_ports {ddr3_addr[10]}]

# PadFunction: IO_L9N_T1_DQS_34 
set_property SLEW FAST [get_ports {ddr3_addr[9]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[9]}]
set_property PACKAGE_PIN E8 [get_ports {ddr3_addr[9]}]

# PadFunction: IO_L10P_T1_34 
set_property SLEW FAST [get_ports {ddr3_addr[8]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[8]}]
set_property PACKAGE_PIN E6 [get_ports {ddr3_addr[8]}]

# PadFunction: IO_L10N_T1_34 
set_property SLEW FAST [get_ports {ddr3_addr[7]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[7]}]
set_property PACKAGE_PIN D5 [get_ports {ddr3_addr[7]}]

# PadFunction: IO_L11P_T1_SRCC_34 
set_property SLEW FAST [get_ports {ddr3_addr[6]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[6]}]
set_property PACKAGE_PIN F8 [get_ports {ddr3_addr[6]}]

# PadFunction: IO_L11N_T1_SRCC_34 
set_property SLEW FAST [get_ports {ddr3_addr[5]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[5]}]
set_property PACKAGE_PIN E7 [get_ports {ddr3_addr[5]}]

# PadFunction: IO_L12P_T1_MRCC_34 
set_property SLEW FAST [get_ports {ddr3_addr[4]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[4]}]
set_property PACKAGE_PIN G7 [get_ports {ddr3_addr[4]}]

# PadFunction: IO_L12N_T1_MRCC_34 
set_property SLEW FAST [get_ports {ddr3_addr[3]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[3]}]
set_property PACKAGE_PIN F7 [get_ports {ddr3_addr[3]}]

# PadFunction: IO_L19P_T3_34 
set_property SLEW FAST [get_ports {ddr3_addr[2]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[2]}]
set_property PACKAGE_PIN C4 [get_ports {ddr3_addr[2]}]

# PadFunction: IO_L19N_T3_VREF_34 
set_property SLEW FAST [get_ports {ddr3_addr[1]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[1]}]
set_property PACKAGE_PIN C3 [get_ports {ddr3_addr[1]}]

# PadFunction: IO_L20P_T3_34 
set_property SLEW FAST [get_ports {ddr3_addr[0]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_addr[0]}]
set_property PACKAGE_PIN B5 [get_ports {ddr3_addr[0]}]

# PadFunction: IO_L20N_T3_34 
set_property SLEW FAST [get_ports {ddr3_ba[2]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_ba[2]}]
set_property PACKAGE_PIN B4 [get_ports {ddr3_ba[2]}]

# PadFunction: IO_L22P_T3_34 
set_property SLEW FAST [get_ports {ddr3_ba[1]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_ba[1]}]
set_property PACKAGE_PIN A4 [get_ports {ddr3_ba[1]}]

# PadFunction: IO_L22N_T3_34 
set_property SLEW FAST [get_ports {ddr3_ba[0]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_ba[0]}]
set_property PACKAGE_PIN A3 [get_ports {ddr3_ba[0]}]

# PadFunction: IO_L23P_T3_34 
set_property SLEW FAST [get_ports {ddr3_ras_n}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_ras_n}]
set_property PACKAGE_PIN C2 [get_ports {ddr3_ras_n}]

# PadFunction: IO_L23N_T3_34 
set_property SLEW FAST [get_ports {ddr3_cas_n}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_cas_n}]
set_property PACKAGE_PIN B1 [get_ports {ddr3_cas_n}]

# PadFunction: IO_L24P_T3_34 
set_property SLEW FAST [get_ports {ddr3_we_n}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_we_n}]
set_property PACKAGE_PIN B2 [get_ports {ddr3_we_n}]

# PadFunction: IO_L12N_T1_MRCC_33 
set_property SLEW FAST [get_ports {ddr3_reset_n}]
set_property IOSTANDARD LVCMOS15 [get_ports {ddr3_reset_n}]
set_property PACKAGE_PIN J3 [get_ports {ddr3_reset_n}]

# PadFunction: IO_L15P_T2_DQS_34 
set_property SLEW FAST [get_ports {ddr3_cke[0]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_cke[0]}]
set_property PACKAGE_PIN C9 [get_ports {ddr3_cke[0]}]

# PadFunction: IO_L15N_T2_DQS_34 
set_property SLEW FAST [get_ports {ddr3_odt[0]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_odt[0]}]
set_property PACKAGE_PIN B9 [get_ports {ddr3_odt[0]}]

# PadFunction: IO_L24N_T3_34 
set_property SLEW FAST [get_ports {ddr3_cs_n[0]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_cs_n[0]}]
set_property PACKAGE_PIN A2 [get_ports {ddr3_cs_n[0]}]

# PadFunction: IO_L1P_T0_33 
set_property SLEW FAST [get_ports {ddr3_dm[0]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_dm[0]}]
set_property PACKAGE_PIN G4 [get_ports {ddr3_dm[0]}]

# PadFunction: IO_L7P_T1_33 
set_property SLEW FAST [get_ports {ddr3_dm[1]}]
set_property IOSTANDARD SSTL15 [get_ports {ddr3_dm[1]}]
set_property PACKAGE_PIN J1 [get_ports {ddr3_dm[1]}]

# PadFunction: IO_L3P_T0_DQS_33 
set_property SLEW FAST [get_ports {ddr3_dqs_p[0]}]
set_property IOSTANDARD DIFF_SSTL15_T_DCI [get_ports {ddr3_dqs_p[0]}]
set_property PACKAGE_PIN G2 [get_ports {ddr3_dqs_p[0]}]

# PadFunction: IO_L3N_T0_DQS_33 
set_property SLEW FAST [get_ports {ddr3_dqs_n[0]}]
set_property IOSTANDARD DIFF_SSTL15_T_DCI [get_ports {ddr3_dqs_n[0]}]
set_property PACKAGE_PIN F2 [get_ports {ddr3_dqs_n[0]}]

# PadFunction: IO_L9P_T1_DQS_33 
set_property SLEW FAST [get_ports {ddr3_dqs_p[1]}]
set_property IOSTANDARD DIFF_SSTL15_T_DCI [get_ports {ddr3_dqs_p[1]}]
set_property PACKAGE_PIN K2 [get_ports {ddr3_dqs_p[1]}]

# PadFunction: IO_L9N_T1_DQS_33 
set_property SLEW FAST [get_ports {ddr3_dqs_n[1]}]
set_property IOSTANDARD DIFF_SSTL15_T_DCI [get_ports {ddr3_dqs_n[1]}]
set_property PACKAGE_PIN K1 [get_ports {ddr3_dqs_n[1]}]

# PadFunction: IO_L21P_T3_DQS_34 
set_property SLEW FAST [get_ports {ddr3_ck_p[0]}]
set_property IOSTANDARD DIFF_SSTL15 [get_ports {ddr3_ck_p[0]}]
set_property PACKAGE_PIN B6 [get_ports {ddr3_ck_p[0]}]

# PadFunction: IO_L21N_T3_DQS_34 
set_property SLEW FAST [get_ports {ddr3_ck_n[0]}]
set_property IOSTANDARD DIFF_SSTL15 [get_ports {ddr3_ck_n[0]}]
set_property PACKAGE_PIN A5 [get_ports {ddr3_ck_n[0]}]


set_property INTERNAL_VREF  0.750 [get_iobanks 33]


set_property LOC PHASER_OUT_PHY_X1Y19 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_D.ddr_byte_lane_D/phaser_out}]
set_property LOC PHASER_OUT_PHY_X1Y18 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/phaser_out}]
set_property LOC PHASER_OUT_PHY_X1Y22 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/phaser_out}]
set_property LOC PHASER_OUT_PHY_X1Y21 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_B.ddr_byte_lane_B/phaser_out}]
set_property LOC PHASER_OUT_PHY_X1Y20 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_A.ddr_byte_lane_A/phaser_out}]


set_property LOC PHASER_IN_PHY_X1Y19 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_D.ddr_byte_lane_D/phaser_in_gen.phaser_in}]
set_property LOC PHASER_IN_PHY_X1Y18 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/phaser_in_gen.phaser_in}]
## set_property LOC PHASER_IN_PHY_X1Y22 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/phaser_in_gen.phaser_in}]
## set_property LOC PHASER_IN_PHY_X1Y21 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_B.ddr_byte_lane_B/phaser_in_gen.phaser_in}]
## set_property LOC PHASER_IN_PHY_X1Y20 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_A.ddr_byte_lane_A/phaser_in_gen.phaser_in}]





set_property LOC OUT_FIFO_X1Y19 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_D.ddr_byte_lane_D/out_fifo}]
set_property LOC OUT_FIFO_X1Y18 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/out_fifo}]
set_property LOC OUT_FIFO_X1Y22 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/out_fifo}]
set_property LOC OUT_FIFO_X1Y21 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_B.ddr_byte_lane_B/out_fifo}]
set_property LOC OUT_FIFO_X1Y20 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_A.ddr_byte_lane_A/out_fifo}]


set_property LOC IN_FIFO_X1Y19 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_D.ddr_byte_lane_D/in_fifo_gen.in_fifo}]
set_property LOC IN_FIFO_X1Y18 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/in_fifo_gen.in_fifo}]


set_property LOC PHY_CONTROL_X1Y4 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/phy_control_i}]
set_property LOC PHY_CONTROL_X1Y5 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/phy_control_i}]


set_property LOC PHASER_REF_X1Y4 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/phaser_ref_i}]
set_property LOC PHASER_REF_X1Y5 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_0.u_ddr_phy_4lanes/phaser_ref_i}]


set_property LOC OLOGIC_X1Y243 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_D.ddr_byte_lane_D/ddr_byte_group_io/*slave_ts}]
set_property LOC OLOGIC_X1Y231 [get_cells  -hier -filter {NAME =~ */ddr_phy_4lanes_1.u_ddr_phy_4lanes/ddr_byte_lane_C.ddr_byte_lane_C/ddr_byte_group_io/*slave_ts}]



set_property LOC PLLE2_ADV_X1Y5 [get_cells -hier -filter {NAME =~ */u_ddr3_infrastructure/plle2_i}]
set_property LOC MMCME2_ADV_X1Y5 [get_cells -hier -filter {NAME =~ */u_ddr3_infrastructure/gen_mmcm.mmcm_i}]
          


set_multicycle_path -from [get_cells -hier -filter {NAME =~ */mc0/mc_read_idle_r_reg}] \
                    -to   [get_cells -hier -filter {NAME =~ */input_[?].iserdes_dq_.iserdesdq}] \
                    -setup 6

set_multicycle_path -from [get_cells -hier -filter {NAME =~ */mc0/mc_read_idle_r_reg}] \
                    -to   [get_cells -hier -filter {NAME =~ */input_[?].iserdes_dq_.iserdesdq}] \
                    -hold 5

set_false_path -through [get_pins -filter {NAME =~ */DQSFOUND} -of [get_cells -hier -filter {REF_NAME == PHASER_IN_PHY}]]

set_multicycle_path -through [get_pins -filter {NAME =~ */OSERDESRST} -of [get_cells -hier -filter {REF_NAME == PHASER_OUT_PHY}]] -setup 2 -start
set_multicycle_path -through [get_pins -filter {NAME =~ */OSERDESRST} -of [get_cells -hier -filter {REF_NAME == PHASER_OUT_PHY}]] -hold 1 -start

#set_max_delay -datapath_only -from [get_cells -hier -filter {NAME =~ *temp_mon_enabled.u_tempmon/* && IS_SEQUENTIAL}] -to [get_cells -hier -filter {NAME =~ *temp_mon_enabled.u_tempmon/device_temp_sync_r1*}] 20
set_max_delay -to [get_pins -hier -include_replicated_objects -filter {NAME =~ *temp_mon_enabled.u_tempmon/device_temp_sync_r1_reg[*]/D}] 20
set_max_delay -from [get_cells -hier *rstdiv0_sync_r1_reg*] -to [get_pins -filter {NAME =~ */RESET} -of [get_cells -hier -filter {REF_NAME == PHY_CONTROL}]] -datapath_only 5
#set_false_path -through [get_pins -hier -filter {NAME =~ */u_iodelay_ctrl/sys_rst}]
set_false_path -through [get_nets -hier -filter {NAME =~ */u_iodelay_ctrl/sys_rst_i}]
          
set_max_delay -datapath_only -from [get_cells -hier -filter {NAME =~ *ddr3_infrastructure/rstdiv0_sync_r1_reg*}] -to [get_cells -hier -filter {NAME =~ *temp_mon_enabled.u_tempmon/xadc_supplied_temperature.rst_r1*}] 20

ddr.xdc

#############clock and reset Setting##################
create_clock -period 20 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS18 [get_ports {sys_clk}]
set_property PACKAGE_PIN D15 [get_ports {sys_clk}]
#############led Setting##############################
set_property IOSTANDARD LVCMOS15 [get_ports {led}]
set_property PACKAGE_PIN J10 [get_ports {led}]
相关推荐
szxinmai主板定制专家43 分钟前
【国产NI替代】基于FPGA的32通道(24bits)高精度终端采集核心板卡
大数据·人工智能·fpga开发
HIZYUAN6 小时前
AGM FPGA如何配置上拉或者下拉电阻
fpga开发
∑狸猫不是猫6 小时前
(13)CT137A- 简易音乐盒设计
fpga开发
ThreeYear_s12 小时前
基于FPGA 的4位密码锁 矩阵键盘 数码管显示 报警仿真
fpga开发·矩阵·计算机外设
Anin蓝天(北京太速科技-陈)18 小时前
252-8路SATAII 6U VPX高速存储模块
fpga开发
如何学会学习?21 小时前
2. FPGA基础了解--全局网络
fpga开发
Anin蓝天(北京太速科技-陈)21 小时前
271-基于XC7V690T的12路光纤PCIe接口卡
嵌入式硬件·fpga开发
碎碎思1 天前
FPGA新闻速览-WiMi开发基于FPGA的数字量子计算机验证技术
fpga开发·量子计算
hi942 天前
Vivado - 远程调试 + 远程综合实现 + vmWare网络配置 + NFS 文件共享 + 使用 VIO 核
嵌入式硬件·fpga开发·vivado 远程开发·vmware网络配置
乘风~&2 天前
基于发FPGA 练手智能小车顶层文件
fpga开发