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

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…...
uniapp 开发企业微信小程序时,如何在当前页面真正销毁前或者关闭小程序前调用一个api接口
在 UniApp 开发企业微信小程序时,若需在页面销毁或小程序关闭前调用 API 接口,需结合页面生命周期和应用生命周期实现。以下是具体实现方案及注意事项: 一、在页面销毁前调用 API(页面级) 通过页面生命周期钩子 onUnl…...
WPF 按钮点击音效实现
WPF 按钮点击音效实现 下面我将为您提供一个完整的 WPF 按钮点击音效实现方案,包含多种实现方式和高级功能: 完整实现方案 MainWindow.xaml <Window x:Class"ButtonClickSound.MainWindow"xmlns"http://schemas.microsoft.com/win…...

编写测试用例
测试用例(Test Case)是用于测试系统的要素集合 目录 编写测试用例作用 编写测试用例要包含七大元素 测试用例的设计方法 1、等价类法 2、边界值法 3、正交表法 4、判定表法 5、错误推测法 6、场景法 编写测试用例作用 1、确保功能全面覆盖…...
解释程序(Python)不需要生成机器码 逐行解析 逐行执行
在计算机组成原理中,解释程序(Interpreter)通常不会生成独立的机器码,但具体情况取决于解释器的实现方式。以下是详细分析: 1. 传统解释程序:不生成机器码 直接逐行执行: 经典的解释器ÿ…...

每日Prompt:隐形人
提示词 黑色棒球帽,白色抹胸、粉色低腰短裙、白色襪子,黑色鞋子,粉紅色背包,衣服悬浮在空中呈现动态姿势,虚幻引擎渲染风格,高清晰游戏CG质感,户外山林背景,画面聚焦在漂浮的衣服上…...

TensorFlow深度学习实战(19)——受限玻尔兹曼机
TensorFlow深度学习实战(19)——受限玻尔兹曼机 0. 前言1. 受限玻尔兹曼机1.1 受限玻尔兹曼机架构1.2 受限玻尔兹曼机的数学原理 2. 使用受限玻尔兹曼机重建图像3. 深度信念网络小结系列链接 0. 前言 受限玻尔兹曼机 (Restricted Boltzmann Machine, RB…...

告别手动绘图!基于AI的Smart Mermaid自动可视化图表工具搭建与使用指南
以下是对Smart Mermaid的简单介绍: 一款基于 AI 技术的 Web 应用程序,可将文本内容智能转换为 Mermaid 格式的代码,并将其渲染成可视化图表可以智能制作流程图、序列图、甘特图、状态图等等,并且支持在线调整、图片导出可以Docke…...

【Oracle】安装单实例
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 安装前的准备工作1.1 硬件和系统要求1.2 检查系统环境1.3 下载Oracle软件 2. 系统配置2.1 创建Oracle用户和组2.2 配置内核参数2.3 配置用户资源限制2.4 安装必要的软件包 3. 目录结构和环境变量3.1 创建Ora…...
C++测开,自动化测试,业务(第一段实习)
目录 🌼前言 一,实习经历怎么写简历 🌹业务理解 🎂结构化表达 二,实习 🦂技术和流程卡点 🔑实习收获 / 代码风格 三,测试理论,用例设计,工具链 &…...

QT中更新或添加组件时出现“”qt操作至少需要一个处于启用状态的有效资料档案库“解决方法”
在MaintenanceTool.exe中点击下一步 第一个: 第二个: 第三个: 以上任意一个放入资料库中...

论文速读《UAV-Flow Colosseo: 自然语言控制无人机系统》
论文链接:https://arxiv.org/abs/2505.15725项目主页:https://prince687028.github.io/UAV-Flow/ 0. 简介 近年来,无人机技术蓬勃发展,但如何让无人机像智能助手一样理解并执行人类语言指令,仍是一个前沿挑战。现有研…...

ES6+中Promise 中错误捕捉详解——链式调用catch()或者async/await+try/catch
通过 unhandledrejection 捕捉未处理的 Promise 异常,手动将其抛出,最终让 window.onerror 捕捉,从而统一所有异常的处理逻辑 规范代码:catch(onRejected)、async...awaittry...catch 在 JavaScript 的 Pro…...
CDN安全加速:HTTPS加密最佳配置方案
CDN安全加速的HTTPS加密最佳配置方案需从证书管理、协议优化、安全策略到性能调优进行全链路设计,以下是核心实施步骤与注意事项: 一、证书配置与管理 证书选择与格式 证书类型:优先使用受信任CA机构颁发的DV/OV/EV证…...

解常微分方程组
Euler法 function euler_method % 参数设置 v_missile 450; % 导弹速度 km/h v_enemy 90; % 敌艇速度 km/h % 初始条件 x0 0; % 导弹初始位置 x y0 0; % 导弹初始位置 y xe0 120; % 敌艇初始位置 y t0 0; % 初始时间 % 时间步长和总时间 dt 0.01; % 时间步长 t_final …...

C++实现汉诺塔游戏自动完成
目录 一、汉诺塔的规则二、数学递归推导式三、步骤实现(一)汉诺塔模型(二)递归实现(三)显示1.命令行显示2.SDL图形显示 四、处理用户输入及SDL环境配置五、总结六、源码下载 一、汉诺塔的规则 游戏由3根柱子和若干大小不一的圆盘组成,初始状态下,所有的…...
在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统
🚀 在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统 📚 目录 🚀 在 ABP VNext 中集成 Serilog:打造可观测、结构化日志系统1. 为什么要使用结构化日志? 🤔2. 核心集成步骤 Ὦ…...

pikachu靶场通关笔记07 XSS关卡03-存储型XSS
目录 一、XSS 二、存储型XSS 三、源码分析 四、渗透实战 1、输入mooyuan试一试 2、注入Payload 3、查看数据库 4、再次进入留言板页面 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关)渗透集合,通过对XSS关卡源码的代码审计找到XSS风险的…...
GitLab CI、GitHub Actions和Jenkins进行比较
特性/工具JenkinsGitLab CIGitHub Actions架构设计哲学Master/Agent分布式架构,通过插件扩展功能代码与CI/CD强耦合,内置Git仓库,基于Runner注册机制事件驱动,与GitHub深度集成,基于虚拟机的Job执行单元核心运行机制支…...
strcat及其模拟实现
#define _CRT_SECURE_NO_WARNINGS strcat 追加字符串 str "string"(字符串) cat "concatenate"(连接 / 追加) char* strcat(char* destination, const char* source); strcat的应用 方法一ÿ…...

OpenCV CUDA模块直方图计算------用于在 GPU 上执行对比度受限的自适应直方图均衡类cv::cuda::CLAHE
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模块中提供的一个类,用于在 GPU 上执行对比度受限的自适应直方图均衡(Contrast Limi…...

华为OD机试真题——矩形绘制(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…...
通义开源视觉感知多模态 RAG 推理框架 VRAG-RL:开启多模态推理新时代
通义实验室的自然语言智能团队,凭借深厚的技术积累与创新精神,成功研发并开源了视觉感知多模态 RAG 推理框架 VRAG-RL,为 AI 在复杂视觉信息处理领域带来了重大突破。 传统 RAG 方法的局限 传统的检索增强型生成(RAG࿰…...
爬虫入门:从基础到实战全攻略
🧠 一、爬虫基础概念 1.1 爬虫定义 爬虫(Web Crawler)是模拟浏览器行为,自动向服务器发送请求并获取响应数据的一种程序。主要用于从网页中提取结构化数据,供后续分析、展示或存储使用。 1.2 爬虫特点 数据碎片化&…...
qemu安装risc-V 64
参考这篇文章https://developer.aliyun.com/article/1323996,其中在wsl下面安装可能会报错环境变量中有空格。 # clean_path.sh#!/bin/bash# 备份旧 PATH OLD_PATH"$PATH"# 过滤掉包含空格、制表符、换行的路径 CLEAN_PATH"" IFS: read -ra PA…...

JDBC连不上mysql:Unable to load authentication plugin ‘caching_sha2_password‘.
最近为一个spring-boot项目下了mysql-9.3.0,结果因为mysql版本太新一直报错连不上。 错误如下: 2025-06-01 16:19:43.516 ERROR 22088 --- [http-nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispat…...
AsyncIOScheduler与BackgroundScheduler的线程模型对比
1. BackgroundScheduler的线程机制 多线程模型:BackgroundScheduler基于线程池执行任务,默认通过ThreadPoolExecutor创建独立线程处理任务,每个任务运行在单独的线程中,主线程不会被阻塞。适用场景:适合同步…...
Python+MongoDb使用手册(精简)
这里是学了下面链接的内容,加上一些自己学习的内容综合的,大家也可以去看看这篇文章,写的特别好 【python】在Python中操作MongoDB的详细用法教程与实战案例分享_python轻松入门,基础语法到高阶实战教学-CSDN专栏 1 库࿱…...
前端面经 协商缓存和强缓存
HHTTPTTP缓存 协商缓存和强缓存 核心区别是否向服务器发起请求验证资源过期 强缓存 浏览器直接读取本地缓存,不发请求 HTTP响应头 Cache-Control:max-age3600资源有效期 Expires优先级低 如果有效浏览器返回200(浏览器换伪造的200) 应用静态资源 协商缓存 OK如果 1强缓…...

MacOS安装Docker Desktop并汉化
1. 安装Docker Desktop 到Docker Desktop For Mac下载对应系统的Docker Desktop 安装包,下载后安装,没有账号需要注册,然后登陆即可。 2. 汉化 前往汉化包下载链接下载对应系统的.asar文件 然后将安装好的文件覆盖原先的文件app.asar文件…...