DDR4读写压力测试

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标识到最终全程未拉高,说明误码测试通过。

相关推荐
not coder3 小时前
QPS 和 TPS 详解
压力测试
qq_243050793 天前
Siege:开源的 HTTP/FTP 压力测试与基准评估工具!全参数详细教程!Kali Linux教程!
linux·网络·web安全·网络安全·黑客·压力测试·kali linux
第三方软件测评3 天前
什么是软件压力测试,出压力测评报告的第三方软件检测公司推荐
软件测试·压力测试
gywl4 天前
接口性能测试-工具JMeter的学习
测试工具·jmeter·压力测试·性能测试
大春儿的试验田6 天前
Linux性能监控工具nmon
linux·压力测试
白白卡路里8 天前
【测试】功能测试和性能测试
功能测试·压力测试·安全性测试
孙克旭_13 天前
day016-系统负载压力测试-磁盘管理
linux·运维·压力测试
卑微的Coder17 天前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
乐言36118 天前
Jmeter中的BeanShell如何使用?
python·jmeter·压力测试