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標識到最終全程未拉高,說明誤碼測試通過。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/81461.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/81461.shtml
英文地址,請注明出處:http://en.pswp.cn/web/81461.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在 Windows 上使用 WSL 安裝 Ansible詳細步驟

在 Windows 上使用 WSL&#xff08;Windows Subsystem for Linux&#xff09; 安裝 Ansible 是目前最推薦的方式&#xff0c;因為 Ansible 本身是為 Linux 環境設計的&#xff0c;不支持原生 Windows 作為控制節點。 下面是一個 詳細步驟指南 &#xff0c;幫助你在 Windows 上…

編寫第一個ros程序

1.下載VScode 下載鏈接如下&#xff1a; Download Visual Studio Code - Mac, Linux, Windows 下載ARM64下的.deb文件 打開虛擬機&#xff0c;再rosnoetic下新建一個文件夾VSCODE&#xff0c;將windows下的文件導入該文件夾下如下圖。 在該文件夾下右鍵選擇在終端中打開 輸入…

代碼隨想錄算法訓練營第60期第四十九天打卡

大家好&#xff0c;今天我們還是繼續我們的動態規劃章節&#xff0c;可能有的朋友已經開始厭倦了說為什么動態規劃怎么這么多題目&#xff0c;大家可以想想我們前面其實刷過好幾種類型的動態規劃的經典題目比如說各式各樣的背包問題當然包括0-1背包&#xff0c;完全背包&#x…

centos7.9離線升級內核到4.19.12詳細教程

cenots7.9默認安裝的內核版本是:3.10.0-1160.119.1.el7.x86_64,在安裝nvidia顯卡驅動的時候,提示內核版本過低,需要升級內核版本,升級完成之后,就可以順利的安裝上nvidia顯卡驅動了,實測有效。 一、查看當前內核版本命令 uname -r二、下載離線內核的rpm包

Vue3 + TypeScript + el-input 實現人民幣金額的輸入和顯示

輸入人民幣金額的參數要求&#xff1a; 輸入要求&#xff1a; 通過鍵盤&#xff0c;只允許輸入負號、小數點、數字、退格鍵、刪除鍵、方向左鍵、方向右鍵、Home鍵、End鍵、Tab鍵&#xff1b;負號只能在開頭&#xff1b;只保留第一個小數點&#xff1b;替換全角輸入的小數點&a…

方正字庫助力華為,賦能鴻蒙電腦打造全場景字體解決方案

2025年5月19日&#xff0c;搭載華為鴻蒙操作系統的鴻蒙電腦&#xff0c;面向用戶推出集AI智能、互聯流暢、安全保障和精致體驗于一體的全新辦公系統。作為鴻蒙生態核心字體服務商&#xff0c;方正字庫為此次提供了全面的系統字體支持&#xff0c;涵蓋中文、西文及符號三大類字庫…

PHPStudy 一鍵式網站搭建工具的下載使用

目錄 一、下載 PHPStudy二、安裝步驟三、基本使用方法3.1 創建網站3.2 管理數據庫3.3 軟件管理3.4 自動啟動3.5 配置管理 四、注意事項和進階使用4.1 注意事項4.2 進階使用 背景&#xff1a; 我們在學習和工作中&#xff0c;經常會遇到各種需要自己搭建環境的場景&#xff0c;這…

java中的線程安全的集合

1.ConcurrentHashMap。 key,value結構。 jdk1.7通過分段鎖保證不同段同時操作是線程安全的&#xff0c;但并發不足&#xff0c;jdk1.8通過node節點鎖和CAS保證并發安全。不同node節點可以并發讀寫。通過它的computer,computerIfAbsent,等可以保證原子更新value。ifAbsent表示有…

MySQL問題:MySQL中使用索引一定有效嗎?如何排查索引效果

不一定有效&#xff0c;當查詢條件中不包含索引列或查詢條件復雜且不匹配索引順序 對于一些小表&#xff0c;MySQL可能選擇全表掃描而非使用索引&#xff0c;因為全表掃描的開銷可能更小 最終是否用上索引是根據MySQL成本計算決定的&#xff0c;評估CPU和I/O成本 排查索引效…

使用vscode MSVC CMake進行C++開發和Debug

使用vscode MSVC CMake進行C開發和Debug 前言軟件安裝安裝插件構建debuug方案一debug方案二其他 前言 一般情況下我都是使用visual studio來進行c開發的&#xff0c;但是由于python用的是vscode&#xff0c;所以二者如果統一的話能稍微提高一點效率。 軟件安裝 需要安裝的軟…

【后端高階面經:消息隊列篇】29、Kafka高性能探秘:零拷貝、順序寫與分區并發實戰

一、 順序寫入:磁盤性能的極致挖掘 Kafka的高性能本質上源于對磁盤順序訪問的深度優化。 傳統隨機寫入的磁盤操作需要磁頭頻繁尋道,機械硬盤的隨機寫性能通常僅為100IOPS左右,而Kafka通過追加日志(Append-Only Log)模式,將所有消息按順序寫入分區文件,使磁盤操作轉化為…

AI預測3D新模型百十個定位預測+膽碼預測+去和尾2025年5月27日第90彈

從今天開始&#xff0c;咱們還是暫時基于舊的模型進行預測&#xff0c;好了&#xff0c;廢話不多說&#xff0c;按照老辦法&#xff0c;重點8-9碼定位&#xff0c;配合三膽下1或下2&#xff0c;殺1-2個和尾&#xff0c;再殺6-8個和值&#xff0c;可以做到100-300注左右。 (1)定…

Git 初次推送遠程倉庫

Git 初次推送遠程倉庫&#xff08;完整實戰版&#xff09; —— 涵蓋重命名分支、強制合并、沖突解決等高頻場景 &#x1f525; 核心流程圖 初始化 → 關聯遠程 → 提交代碼 → 處理分支沖突 → 成功推送 1. 基礎操作&#xff08;全新倉庫&#xff09; # 初始化 cd /your/pr…

Pluto實驗報告——基于FM的音頻信號傳輸并解調恢復

目錄 一、實驗目的 ................................ ................................ ................................ .................. 3 二、實驗內容 ................................ ................................ ................................ ......…

輸出數據OutputFormat案例

輸出數據OutputFormat 案例&#xff1a; www.atguigu.com www.atguigu.com www.atguigu.com www.hao123.com www.shouhu.com www.baidu.com www.atguigu.com www.qq.com www.gaga.com www.qinghua.com www.sogou.com www.baidu.com www.alibaba.com …

STM32與ESP32的區別

STM32與ESP32都是當前電子行業中廣泛使用的微控制器芯片&#xff0c;但二者在架構、功能、應用領域以及開發生態上均存在顯著差異。需要高度實時響應和低功耗的系統通常適合STM32&#xff0c;而需要網絡連接和便捷無線通訊的物聯網應用通常更適合ESP32。 一、架構與性能 STM32…

YOLOv11改進 | Neck篇 | 雙向特征金字塔網絡BiFPN助力YOLOv11有效漲點

YOLOv11改進 | Neck篇 | 雙向特征金字塔網絡BiFPN助力YOLOv11有效漲點 引言 目標檢測領域的最新進展表明,特征金字塔網絡(FPN)的設計對模型性能具有決定性影響。本文詳細介紹如何將**雙向特征金字塔網絡(BiFPN)**集成到YOLOv11的Neck部分,通過改進的多尺度特征融合機制…

Python后端框架新星Robyn:性能與開發體驗的雙重革命

引言:Python后端框架的進化之路 在Web開發領域,Python生態長期被Flask、Django等經典框架主導。隨著異步編程需求的增長和高并發場景的普及,開發者對框架性能提出了更高要求。2023年,一款名為Robyn的新型Web框架橫空出世,以其獨特的Rust底層架構和優雅的Python API設計,…

ADS學習筆記(三) 瞬態仿真

參考書籍:見資源綁定,書籍3.4 瞬態仿真,書籍鏈接: https://pan.baidu.com/s/1pjw8p7r3-1x8qcC1-hljFQ?pwd4v79 提取碼: 4v79 本文為對實驗內容的補充 瞬態仿真放大倍數與交流仿真不一致 為什么對同一個BJT電路進行交流信號仿真和進行瞬態仿真,得出交流信號仿真的放大倍數是3.…

Flask 會話管理:從原理到實戰,深度解析 session 機制

1、Flask中session 的實現原理&#xff1a;服務器與客戶端的協作 HTTP 協議是無狀態的——服務器無法區分兩次請求是否來自同一用戶。這意味著&#xff0c;用戶登錄后跳轉到其他頁面時&#xff0c;服務器會“忘記”用戶身份。 為解決這一問題&#xff0c;Web 開發中引入了會話…