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