DDR4读写压力测试

发布于:2025-05-28 ⋅ 阅读:(20) ⋅ 点赞:(0)

1.1测试环境

1.1.1整体环境介绍

板卡:    pcie-403板卡
主控芯片:    Xilinx xcvu13p-fhgb2104-2
调试软件:    Vivado 2018.3
代码环境:    Vscode utf-8
测试工程:    pcie403_user_top


1.1.2硬件介绍
UD PCIe-403使用VU13P+ZYNQ+FMC插槽架构,对外数据接口使用PCIe3.0x16和PCIe4.0x8进行数据通信或传输,支持千兆以太网、万兆以太网(100G x4),板载4组DDR4的存储器,位宽为72bit,分别容量为8G。另外PCIe-403板载有1个FMC+(兼容FMC子板)全互联的接口,满足VITA57.1和VITA57.4规范,可以适配大多数ADC/DAC FMC或FMC+子卡,且支持多板级联。

PCIe403支持全国产化替代方案,器件选择工业级和以上质量等级元器件。

产品特点:
1.PCIe接口:PCIe3.0×16(金手指)、PCIe4.0×8(MICO PCIe)
2.VU13P+ZYNQ架构
3.支持4路100G以太网
4.支持4组DDR4,每组DDR4位宽72bit
5.千兆以太网,4组100G万兆以太网
6.FPGA选型:可选配XCVU5P、XCVU7P、XCVU9P、XCVU13P、XCVU190 FPGA
7.FMC+ HPC,24路GTX,LA、HA、HB都全互联,可适配各种FMC AD/DA卡
8.远程更新升级固件
9.板载两组启动flash,可以快速切换固件程序
10.Zynq和Vu两组独立USB-JTAG调试接口
11.支持JESD204C
12.支持全国产化替换
13.单电源+6V~+12V供电,支持插入标准服务器或单独千兆、万兆网口使用
14.板载GPS/BD模块,也支持IRIG-B码时统输入,支持CAN、RS232、RS485
15.提供接口测试程序,专业团队提供技术支持
16.使用维护说明书详实,方便用户二次开发
17.质量可靠,成熟应用,批量出货,高性价比


1.2DDR4芯片手册记录

1.2.1整体介绍

PCIE403板卡搭载了4片DDR4,使用的是国产芯片CXDQ3BFAM-WG,对标镁光的芯片是MT40A512M16HA-075E,最大数据速率为2666Mbps。

从上图我们可以看到,开发板板载的这款 DDR4 芯片的行地址是 16bit 位宽,列地址是 10bit 位宽,而整个存储区域分为两个 BANK 组,每个 BANK 组又由 4 个子 BANK 组成,所以整片 DDR4 的容量就是2^16*2^10*8*16bit=512M*16bit。 DDR4 相较于 DDR3 在指令引脚上也发生了变化, DDR4 取消了我们所熟悉的使能 WE、列激活 CAS 和行激活 RAS 这三个命令引脚,而是将这三个命令引脚和地址线 A14、 A15以及 A16 复用了。除此之外在寻址的时候也不再是直接去寻址 BANK,而是先寻址 BANK 组,然后再找到这个 BANK 组中的某个子 BANK。整个数据的吞吐是 8 倍预取,因此用户端数据在读写的时候就是64bit*8=512bit 的数据量进行吞吐(注意虽然是 8 倍预取,但是每一次 IO 引脚上的数据传输依旧是 64bit,因为数据线就16根,至于为何可以达到8倍预取和DDR4内部的双沿采样,FIFO 缓冲,写数据逻辑结构有关)。

1.2.2内存计算


物理层接口:

MIG接口:

2^16 * 2^10 * 8 * 16bit = 2^29 * 16bit = 512M * 16bit  = 8G 

1.3IP配置记录

配置DDR4的MIG,截图如下:

上图所示的是 MIG IP 核的 Basic 配置界面,这里我们对几个重要的配置信息作出说明:
Component Name: MIG IP 核的命名,可以保持默认,也可以自己取一个名字。
Mode and Interface:控制器的模式和接口选项,可以选择 AXI4 接口或者普通模式,并生成对应的PHY 组件(详情请参考官方文档 pg150)。
    Memory Device Interface Speed:板载 DDR4 芯片的 IO 总线速率, KU 可以最大支持 833ps(1200MHz)。
PHY to controller clock frequency ratio:用户时钟分频系数,这里只能选择 4 比 1,因此本节实验的用户时钟频率等于 DDR4 芯片驱动时钟频率的四分之一,即是 300MHz。
Specify MMCM M and D on Advanced Clocking Page to calculate Ref Clk:特殊参考时钟选择,如果参考时钟频率在“ Reference input Clock Speed”选项列表中没有列出,可以使能这个选项,使能这个选项后Reference input Clock Speed 时钟可以通过在 Advanced Clocking 配置页面配置 M 和 D 的值,并按照公式计算出你想要的特殊参考时钟频率值。
Reference input Clock Speed:参考时钟,本节实验选择 5000ps(参考时钟频率和系统时钟频率保持一致,即 200MHz)。
Controller Options:控制器配置栏,如果使用 MIG IP 核内部默认的 DDR4 芯片,则只需要在 MemoryPart 栏选中对应的 DDR4 芯片型号或者相近的型号即可,例如我们板载的 DDR4 芯片型号为 K4A8G16 但是我们在 MIG 中实际选择的是 MT40A512M16HA。
Configuration: DDR4 的组件类型, Components 代表 DDR4 颗粒,后面几个是内存条,本节实验是对颗粒进行操作,所以选 Components。
Slot:当 DDR4 类型选择内存条时可以选择插槽数量,本节实验是对颗粒进行操作,所以只能选单槽。
IO Memory Voltage: IO 的电平,这里选择 1.2V。
Data Width:数据位宽,本节实验采用的 DDR4 颗粒位宽是 64 位的。
ECC: ECC 纠错相关的设置。
Force Read and Write commands to use AutoPrecharge when Column Address bitA3 is asserted high:当列地址 A3 被拉高强制自动预充电。
Enable AutoPrecharge Input:使能自动预充电输入端口。
Enable User Refresh and ZQCSInput:使能 ZQCS 刷新输入端口。


Advanced Options 界面的配置信息如下:
Debug Signals for controller:在 Xilinx 示例设计中,启用此功能会把状态信号连接到 ChipScope ILA 核中。
Microblaze MCS ECC option: Microblaze 的配置选项,选中它 Microblaze 的 MCS ECC 尺寸会增加。Simulation Options: 此选项仅对仿真有效。在选择 BFM 选项时,为 XiPhy 库使用行为模型,以加快模拟运行时间。选择 Unisim 则对 XiPhy 原语使用 Unisim 库。
Example Design Options:示例工程仿真文件的选择。
Advance Memory Options:提高运行性能的选项,可以选择自刷新和校准功能,并将这些信息保存在XSDB BRAM 中,也可以把 XSDB BRAM 中的信息存储在外部存储器中 。
Migration Options:引脚兼容选项,如果想兼容 UlitraScale 和 UltraScale+ fpga,就把这个选项选中。

IO Planning and Design Checklist 界面提示我们 DDR4 IO 引脚分配的方式发生改变,不再像之前 DDR3那样,需要在 MIG IP 核中就分配好管脚, DDR4 可以在 IO Planning 窗口分配管脚(或者直接编写 XDC 文件)。


1.4工程与时序记录

1.4.1编写测试工程

如上图所示:DDR4的主测试模块由以下几个模块构成
(1); ”ddr4_rw_test_top.v”主要例化了四个测试模块和一个ila用来监视DDR4读写过程中是否出错
(2); ”ddr4_test.v”是DDR4测试的主功能代码,调用MIG IP和测试逻辑底层
(3);”ddr4_rw_cntr_logic.v”是DDR4读写测试逻辑底层代码,里面的功能逻辑是生成一组伪随机数据或者累加数,然后按照DDR4的时序写入DDR4,当写入一定的字节长度时回读该数据,然后判断数据的正确性以此来判断读写是否存在问题。
(4);”datas_gen_prbs31.v”主要用来产生伪随机序列
(5);”datas_check_prbs.v”主要用来检查回读的伪随机序列的正确性。
(6);具体时序与仿真见DDR4调试问题记录章节

1.4.2添加DDR4接口的时序约束。

1.4.3写数据时序记录

1.4.4读数据时序记录


1.4.5读延迟记录
DDR的读数据会相较于读命令发出后的几个时钟周期后出现,这个延迟由MIP配置产生。


1.5仿真记录
1.5.1DDR4仿真环境搭建
1;打开XILINX的示例工程。

2;因为DDR4仿真需要DDR4的物理仿真模型,所以我们使用XILINX的仿真顶层模块,再他原本的基础上修改,但要保留他的DDR4物理模型

3;将XILINX仿真顶层模块内的 example_top 模块替换为我们编写的模块,启动Vivado Simulator。


1.5.2DDR4仿真流程
1;搭建完仿真环境后,编写仿真流程;状态机流程如下:
2;等待DDR4 IP核初始化完成后,先进行一次数据读出
3;第一次读完成后,开始写操作,向DDR4内写入累加数据或者伪随机数
4;写完成后,判断状态跳转,写一个状态到读DDR4
5;每次数据读完成后,开始进行数据比对
6;循环此操作,直到将DDR4的所有地址全部遍历一遍
7;全部遍历完成后,再复位DDR4,对每片DDR4重复读写16次
8;统计误码数

仿真测试代码:
//
// CopyRight(c) 2025, Chengdu universal_data Technology Co. Ltd.
// Module_Name        : ddr4_test_cntr.v
// Creat Time         : 2025-05-12
// Devices            : Xilinx    xcvu13p-fhgb2104-2-i
// Tool               : vivado 2018.3
// Author             : 
// Revision           : ver01
// Encode Save        : UTF-8
//
// Design : 
//                      01; 
//                      02;
//                      03;
//
`timescale 1ns / 1ps

module ddr4_test_cntr
(
    input                             user_clk            ,//  
    input                             ui_clk              ,//
    input                             rst_n               ,//           
    input                             init_calib_complete ,//DDR4 初始化 + 校准完成信号,高电平表示准备好收发数据
    //app_cmd
    input                             app_rdy             ,//MIG命令接收准备好
    input                             app_wdf_rdy         ,//MIG数据接收准备好
    output   wire       [28: 0]       app_addr            ,//读/写目标地址(按 burst 编址)
    output   wire       [ 2: 0]       app_cmd             ,//命令:读/写命令码,通常为:0=WRITE, 1=READ
    output   wire                     app_en              ,//命令有效信号
    //app_write
    output                            app_wdf_wren        ,//写数据使能(数据层)
    output             [511: 0]       app_wdf_data        ,//写数据,必须先准备好数据再发命令
    output                            app_wdf_end         ,//指示写突发结束
    output             [63: 0]        app_wdf_mask        ,//写掩码(可选)
    //app_read
    input              [511: 0]       app_rd_data         ,//读到的数据
    input                             app_rd_data_end     ,//表示一次完整读突发结束(可选)
    input                             app_rd_data_valid   ,//读数据有效   
    //alarm_flag  
    output wire                        error                        
);

// wire vio_rst_n;
// wire reset_n;

vio_0  u_vio_inst

   .clk        (user_clk),
   .probe_out0 (vio_rst_n),   // 
   .probe_out1 (vio_data_mode)//   1:prbs  0:cal_data
);

//assign reset_n = rst_n;
assign data_mode = vio_data_mode;
assign reset_n = vio_rst_n;

/
// Module    : 
// Note      : 
/

parameter                         MAX_TEST_ADDR              = 29'd536866816;
parameter                         BRUST_LENGTH               = 15'd1024;
parameter                         DUMMY_LENGTH               = 15'd1024;
localparam                        IDLE                       = 4'd0  ; //ddr初始化未完成,不进行任何操作状态
localparam                        DDR4_READ_DUMMY            = 4'd1  ; //
localparam                        DDR4_OPERATE_DONE          = 4'd2  ; //
localparam                        DDR4_WRITE                 = 4'd3  ; //写状态
localparam                        DDR4_READ                  = 4'd4  ; //读状态
localparam                        DDR4_TEST_FINISH           = 4'd15  ; //读状态
reg                [3: 0]         states                      ;
reg                [28: 0]        cal_data                    ;
reg                               cal_data_en                 ;
wire                              wfifo_wen                   ;
wire                [511: 0]      wfifo_wdata                 ;
wire                              wfifo_ren                   ;
wire                [511: 0]      wfifo_rdata                 ;
wire                              wfifo_full                  ;
wire                              wfifo_empty                 ;
wire                [10: 0]       wfifo_wcount                ;
wire                [10: 0]       wfifo_rcount                ;
wire                              rfifo_wen                   ;
wire               [511: 0]       rfifo_wdata                 ;
wire                              rfifo_ren                   ;
wire               [511: 0]       rfifo_rdata                 ;
wire                              rfifo_full                  ;
wire                              rfifo_empty                 ;
wire               [10: 0]        rfifo_wcount                ;
wire               [10: 0]        rfifo_rcount                ;
reg                [24: 0]        rd_length_cnt               ;
reg                [24: 0]        wr_length_cnt               ;
reg                [28: 0]        app_addr_wr                 ;
reg                [28: 0]        app_addr_rd                 ;
reg                [15: 0]        sector_cnt                  ;
reg                               operating_mode              ;
reg                               dummy_read_finish           ;
reg                [15: 0]        rd_check_cnt                ;
wire                              write_error_flag           ;
reg                [511: 0]        rfifo_wdata_d1              ;
reg                [511: 0]        rfifo_wdata_d2              ;
reg                               rfifo_wen_d1                ;
reg                               rfifo_wen_d2                ;
wire               [7: 0]         wdata_31prbs                ;
wire                              err_prbs31_flag             ;
wire               [30: 0]        seed                        ;
wire                              pbc_start                   ;
wire                              check_data_en               ;
wire               [ 7: 0]        check_data                  ;


wire [7:0]  debug_app_addr      ;
wire [15:0] debug_app_wdf_data  ;
wire [15:0] debug_app_rd_data   ;
wire [7:0]  debug_rd_length_cnt ;
wire [7:0]  debug_app_addr_rd   ;
wire [7:0]  debug_sector_cnt    ;
wire [7:0]  debug_wr_length_cnt ;
wire [7:0]  debug_app_addr_wr   ;
reg  [15:0] delay_cnt;
reg         cal_check_error;

assign debug_app_addr      = app_addr[7:0]     ;
assign debug_app_wdf_data  = app_wdf_data[7:0] ;
assign debug_app_rd_data   = app_rd_data[15:0]  ;
assign debug_rd_length_cnt = rd_length_cnt[7:0];
assign debug_app_addr_rd   = app_addr_rd[7:0]  ;
assign debug_wr_length_cnt = wr_length_cnt[7:0];

ila_monitor  u_ila_inst
(
    .clk                                (ui_clk                    ),
    .probe0                             (app_rdy                   ),// 1
    .probe1                             (app_wdf_rdy               ),// 1
    .probe2                             (init_calib_complete       ),// 1
    .probe3                             (debug_app_addr            ),// 8
    .probe4                             (app_cmd                   ),// 3
    .probe5                             (app_en                    ),// 1
    .probe6                             (app_wdf_wren              ),// 1
    .probe7                             (debug_app_wdf_data        ),// 16
    .probe8                             (debug_app_rd_data         ),// 16
    .probe9                             (app_rd_data_valid         ),// 1
    .probe10                            (debug_rd_length_cnt       ),// 8
    .probe11                            (debug_app_addr_rd         ),// 8
    .probe12                            (operating_mode            ),// 1
    .probe13                            (sector_cnt                ),// 16
    .probe14                            (wr_length_cnt[15:0]       ),// 16
    .probe15                            (app_addr_wr               ),// 29-
    .probe16                            (rd_check_cnt              ), //16
    .probe17                            (states                    ), //4
    .probe18                            (cal_check_error           ),  //1
    .probe19                            (rfifo_wen_d2              ),  //1
    .probe20                            (rfifo_wdata_d2            ),  //16
    .probe21                            (wdata_31prbs              ),  //8
    .probe22                            (check_data_en             ),  //1
    .probe23                            (check_data                ),  //8
    .probe24                            (err_prbs31_flag           )   //1
);

//output_cmd
assign app_addr = (states == DDR4_READ || states == DDR4_READ_DUMMY) ? app_addr_rd : app_addr_wr; 
assign app_cmd  = (states == DDR4_READ || states == DDR4_READ_DUMMY) ? 3'd1 :3'd0; 
assign app_en   = ((states == DDR4_WRITE && (app_rdy && app_wdf_rdy)) || ((states == DDR4_READ || states == DDR4_READ_DUMMY) && app_rdy)) ? 1'b1:1'b0;  

//output_wdata
assign app_wdf_wren = (states == DDR4_WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
assign app_wdf_data = data_mode ?  wdata_31prbs : wr_length_cnt;
assign app_wdf_mask = 'd0;
assign app_wdf_end  = (states == DDR4_WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
assign error        = data_mode ? err_prbs31_flag  : cal_check_error;

/
// Module    : init_delay
// Note      : 
/
    reg        init_done_d0; 
    reg        init_done_d1;
    //同步 ddr4 初始化完成信号
    always @(posedge user_clk or negedge reset_n) begin
        if(!reset_n) begin
            init_done_d0 <= 1'b0;
            init_done_d1 <= 1'b0;
        end
        else begin
            init_done_d0 <= init_calib_complete;
            init_done_d1 <= init_done_d0;
        end
    end 

/
// Module    : test_data_gen(cal_data)
// Note      : 
/

    always @(posedge user_clk or negedge reset_n)           
        begin                                        
            if(!reset_n) begin
                cal_data    <= 'd0;
            end                                                                  
            else begin
                if(init_done_d1 && app_wdf_wren)begin
                    if(cal_data  == (BRUST_LENGTH - 1'b1) )begin
                        cal_data <= 'd0;
                    end
                    else begin
                        cal_data <= cal_data + 1'b1;
                    end  
                end
                else begin
                    cal_data <= cal_data;
                end
            end    
        end

/
// Module    : test_data_gen(prbs_data)
// Note      : 
/
reg seed_load_flag;
assign seed[30:0] = 31'h5f26_b8d9;

datas_prbs_512 u_datas_gen_prbs512
(
    .clk                                (ui_clk                    ),
    .rst                                (~reset_n                  ),
    .start                              (start_flag                ),
    .prb_seeds                          (seeds                     ),
    .data_valid                         (app_wdf_wren             ),
    .data                               (prbs_data                 ) 
);

assign check_data_en = data_mode ? rfifo_wen_d2 : 1'b0;
assign check_data = data_mode ? rfifo_wdata_d2[7:0] : 8'd0;

datas_check_prbs512 u_datas_check_prbs512
(
    .clk                        (ui_clk                     ),
    .rst                        (~reset_n                   ),
    .check_start                (check_flag                 ),
    .prbs512_data               (check_data                 ),
    .data_valid                 (check_data_en              ),
    .error_flag                 (err_prbs512_flag            )
);

/
// Module    : 
// Note      : 
/

    always @(posedge ui_clk or negedge reset_n)           
        begin                                        
            if(!reset_n)  begin
                states <= IDLE;
                rd_length_cnt <= 'd0;
                wr_length_cnt <= 'd0;
                app_addr_rd   <= 'd0;
                app_addr_wr   <= 'd0;
                sector_cnt    <= 'd0;
                operating_mode <= 1'b0;
                dummy_read_finish <= 1'b0;
                delay_cnt <= 'd0;
                seed_load_flag <= 1'b0;
            end                                                                                                                             
            else begin
                case(states)
                    IDLE:begin
                        if(init_done_d1)
                            states <= DDR4_READ_DUMMY ;
                        else
                            states <= IDLE;
                    end

                    DDR4_READ_DUMMY : begin
                        if(rd_length_cnt >=  DUMMY_LENGTH - 1'b1)begin
                            states              <= DDR4_OPERATE_DONE ;
                            rd_length_cnt       <= 'd0;
                            app_addr_rd         <= 'd0;
                            operating_mode      <= 1'b1;
                            seed_load_flag      <= 1'b1;
                        end
                        else begin
                            if(app_rdy && app_wdf_rdy)begin
                                rd_length_cnt <= rd_length_cnt + 1'b1;
                                app_addr_rd   <= app_addr_rd   + 4'd8;
                            end
                        end
                    end

                    DDR4_OPERATE_DONE : begin
                        seed_load_flag <= 1'b0;
                        if(operating_mode == 1'b1)begin                                 //为1时代表刚进行完读操作转而开始进行写操作,由0转为1
                            if(app_addr_wr >= MAX_TEST_ADDR)begin
                                states        <= DDR4_TEST_FINISH ;                     //将要超过最大地址 
                            end
                            else begin  
                                delay_cnt <= delay_cnt + 1'b1;
                                if(delay_cnt >= (BRUST_LENGTH - 2'd2))begin
                                    states        <= DDR4_WRITE ;                       
                                    sector_cnt    <= sector_cnt + 1'b1;
                                    wr_length_cnt <= 'd0;
                                    dummy_read_finish   <= 1'b1;
                                end
                            end
                        end
                        else begin
                            delay_cnt <=  delay_cnt + 1'b1;
                            if((delay_cnt >= BRUST_LENGTH - 2'd2) )begin
                                states        <= DDR4_READ ;
                                rd_length_cnt <= 'd0;
                            end
                        end
                    end

                    DDR4_WRITE : begin
                        if(wr_length_cnt >= (BRUST_LENGTH - 1'b1) && (app_rdy && app_wdf_rdy))begin
                            states <= DDR4_OPERATE_DONE ;
                            app_addr_wr    <= app_addr_wr ;
                            operating_mode <= 1'b0;
                            delay_cnt <= 'd0;
                        end
                        else if(app_rdy && app_wdf_rdy)begin
                            wr_length_cnt <= wr_length_cnt + 1'b1;
                            app_addr_wr <= app_addr_wr + 4'd8;
                        end
                        else begin
                            wr_length_cnt <= wr_length_cnt ;
                            app_addr_wr   <= app_addr_wr;
                        end
                    end

                    DDR4_READ : begin
                        if(rd_length_cnt >= (BRUST_LENGTH - 1'b1) && (app_rdy && app_wdf_rdy))begin
                            states <= DDR4_OPERATE_DONE ;
                            app_addr_rd    <= app_addr_rd ;
                            operating_mode <= 1'b1;
                            delay_cnt <= 'd0;
                        end
                        else if(app_rdy && app_wdf_rdy)begin
                            rd_length_cnt <= rd_length_cnt + 1'b1;
                            app_addr_rd <= app_addr_rd + 4'd8;
                        end
                        else begin
                            rd_length_cnt <= rd_length_cnt ;
                            app_addr_rd   <= app_addr_rd;
                        end
                    end

                    DDR4_TEST_FINISH : begin
                        states <= DDR4_TEST_FINISH;
                        rd_length_cnt <= 'd0;
                        wr_length_cnt <= 'd0;
                        app_addr_rd   <= 'd0;
                        app_addr_wr   <= 'd0;
                        sector_cnt    <= 'd0;
                        operating_mode <= 1'b0;
                    end


 
                    default:begin
                        states <= IDLE;
                        rd_length_cnt <= 'd0;
                        wr_length_cnt <= 'd0;
                        app_addr_rd   <= 'd0;
                        app_addr_wr   <= 'd0;
                        sector_cnt    <= 'd0;
                        operating_mode <= 1'b0;
                    end
                endcase
            end
        end                                             

    /
    // Module    : readback_data_check
    // Note      : 
    /
        assign rfifo_wen   = (dummy_read_finish) ? app_rd_data_valid : 1'b0 ;
        assign rfifo_wdata = app_rd_data;

        always @(posedge ui_clk or negedge reset_n)           
        begin 
            if(!reset_n)  begin
                rd_check_cnt        <= 'd0;
                cal_check_error     <= 1'b0;
                rfifo_wen_d1        <= 1'b0;
                rfifo_wen_d2        <= 1'b0;
                rfifo_wdata_d1      <= 'd0;
                rfifo_wdata_d2      <= 'd0;
            end 
            else begin
                rfifo_wen_d1   <= rfifo_wen;
                rfifo_wen_d2   <= rfifo_wen_d1;
                rfifo_wdata_d1 <= rfifo_wdata;
                rfifo_wdata_d2 <= rfifo_wdata_d1;
                if(rd_check_cnt == BRUST_LENGTH ) begin
                    rd_check_cnt <= 'd0;
                    cal_check_error  <= 1'b0;         
                end
                else begin
                    if(rfifo_wen_d2) begin
                        rd_check_cnt <= rd_check_cnt + 1'b1;  
                        if(rd_check_cnt == rfifo_wdata_d2[15:0])begin
                            cal_check_error <= 1'b0;
                        end
                        else begin
                            cal_check_error <= 1'b1;
                        end 
                    end 
                    else begin
                        rd_check_cnt <= rd_check_cnt;
                    end
                end
            end
        end                               
                                                                   
endmodule


1.5.3DDR4示例仿真时序(正确版)
1.5.3.1APP_CMD时序


(1);如上图接口注释:小标1代表仿真的DDR4 IP核初始化完成,只有此信号拉高才正式开始工作
(2);小标2里的app_rdy标识写/读命令是否可以发送
(3);小标2里的app_wdf_rdy标识写数据是否可以发送
(4);小标3里的app_en为命令有效标识
(5);小标3里的app_cmd为读写标识,为0代表写数据到DDR4,为1代表从DDR4内读数据。
1.5.3.2APP_WRITE时序

(1);app_wdf_data[511:0]:代表向DDR4内写入的数据
(2);app_wdf_end:数据结束标识,当前一直为1是因为按照512bit连续写入。
(3);app_wdf_mask:数据掩码,为1数据对应的字节无效,功能与tkeep类似,但是使能相反,为0时数据有效。
(4);app_wdf_wren:写数据使能
(5);
1.5.3.3APP_READ时序

1.6问题记录
1.6.1问题记录1
在数据写入DDR4后再将数据按地址读出,发现某些地址的数据未写入,出错的地址随机无规律,vio复位整体模块,仍会出错,出错地址随机。分析后感觉像是那个未写入数据的地址命令未成功使能,怀疑是否是因为MIG_IP刚初始化完成后的这一阶段的app_rdy不稳定导致的
修改代码,再MIG_IP初始化完成后先进行一段时间的数据读取操作,再进行读写

1.6.1.1先进行一段时间的数据读取操作再开始写
                DUMMY_READ : begin    
                    if(app_rdy)begin
                        if(rd_brust_length_cnt >=  DUMMY_LENGTH)begin
                            states              <= DDR4_WRITE_TEST ;
                            app_addr_rd         <= 'd0;
                            app_cmd             <= 3'b1;
                            app_en              <= 1'b0;
                            rd_brust_length_cnt <= 29'b0;
                        end
                        else begin
                            if(app_wdf_rdy)begin
                                states      <= DUMMY_READ;
                                app_addr_rd <= app_addr_rd + 4'd8;
                                app_cmd     <= 3'b1;
                                app_en      <= 1'b1;
                                rd_brust_length_cnt <= rd_brust_length_cnt + 1'b1;
                            end
                            else begin
                                app_en      <= 1'b0;
                            end 
                        end
                    end
                    else begin
                        app_en      <= 1'b0;
                    end 
                end

先进行一段时间的数据读取操作,再进行读写,结果情况一样


1.6.2使用网上测试代码读写程序的时序
在网上查找DDR读写出错时的问题介绍,使用网上的测试代码,发现读写正常。
问题分析:查看网上的代码,发现他的测试程序仅仅只是写入了1024字节的数据到DDR中,然后一直去读取这个数据,并且写入DDR的数据速率仅为50M,速率较低。不具备有效的测试效果。
分析可能是满速写入时,某个地址写出错。尝试降低代码的写入速率。

下面记录网上测试代码的时序:
1.6.2.1写数据

1.6.2.2读时序


1.6.2.3读延迟

1.6.3读写(200M)
当前模式:当MIG_IP初始化完成后,修改代码逻辑,使用200M时钟直接产生128个512比特的累加数写入DDR4,写完后将数据读出来。

DDR4_WRITE_TEST : begin                                             
                    if(app_rdy)begin
                        if(wr_brust_length_cnt >=  BRUST_LENGTH)begin
                            states              <= DDR4_READ_TEST ;
                            app_addr_wr         <= app_addr_wr;
                            app_cmd             <= 3'b0;
                            app_en              <= 1'b0;
                            wfifo_wen           <= 1'b0;
                            app_wdf_end         <= 1'b0; 
                            wr_brust_length_cnt <= 29'b0;
                        end
                        else begin
                            if(app_wdf_rdy)begin
                                states      <= DDR4_WRITE_TEST;
                                wfifo_wen   <= 1'b1;
                                app_wdf_end <= 1'b1;
                                app_addr_wr <= app_addr_wr + 4'd8;
                                app_cmd     <= 3'b0;
                                app_en      <= 1'b1;
                                wr_brust_length_cnt <= wr_brust_length_cnt + 1'b1;
                            end
                            else begin
                                wfifo_wen   <= 1'b0;
                                wfifo_wen   <= 1'b0;
                                app_en      <= 1'b0;
                            end 
                        end
                    end
                    else begin
                        app_en      <= 1'b0;
                        wfifo_wen   <= 1'b0;
                    end 
                end
                DDR4_READ_TEST : begin    
                    if(app_rdy)begin
                        if(rd_brust_length_cnt >=  BRUST_LENGTH)begin
                            states      <= DDR4_READ_AGAIN ;
                            //app_addr_rd <= app_addr_rd;
                            app_addr_rd <= 'd0;
                            app_cmd     <= 3'b1;
                            app_en      <= 1'b0;
                            rd_brust_length_cnt <= 29'b0;
                        end
                        else begin
                            if(app_wdf_rdy)begin
                                states      <= DDR4_READ_TEST;
                                app_addr_rd <= app_addr_rd + 4'd8;
                                app_cmd     <= 3'b1;
                                app_en      <= 1'b1;
                                rd_brust_length_cnt <= rd_brust_length_cnt + 1'b1;
                            end
                            else begin
                                app_en      <= 1'b0;
                            end 
                        end
                    end
                    else begin
                        app_en      <= 1'b0;
                    end 
                end
                DDR4_READ_AGAIN : begin    
                    if(app_rdy)begin
                        if(rd_brust_length_cnt >=  BRUST_LENGTH)begin
                            states      <= DDR4_TEST_DONE ;
                            app_addr_rd <= app_addr_rd;
                            app_cmd     <= 3'b1;
                            app_en      <= 1'b0;
                            rd_brust_length_cnt <= 29'b0;
                        end
                        else begin
                            if(app_wdf_rdy)begin
                                states      <= DDR4_READ_AGAIN;
                                app_addr_rd <= app_addr_rd + 4'd8;
                                app_cmd     <= 3'b1;
                                app_en      <= 1'b1;
                                rd_brust_length_cnt <= rd_brust_length_cnt + 1'b1;
                            end
                            else begin
                                app_en      <= 1'b0;
                            end 
                        end
                    end
                    else begin
                        app_en      <= 1'b0;
                    end 
                end

1.7最终上板ILA结果记录
1.7.1环境记录

1.7.2Dummy_READ

1.7.3DDR4_WRITE

1.7.4DDR4_DONE

1.7.5DDR4_READ


1.7.6DATA_CHECK


1.7.7伪随机序列的产生与校验

1.7.8MATLAB分析一组回读的DDR4数据
1;使用ILA抓取一组读写时序,导入到matlab内将读出的数据与写入的数据比较,查看是否一致
2;分析回读的DDR4数据,查看是否有误码(伪随机多项式31-18-0)


1.7.9误码记录

在小标1处,DDR4读写测试完成,查看2处的prbs_error_flag标识到最终全程未拉高,说明误码测试通过。


网站公告

今日签到

点亮在社区的每一天
去签到