PCIE_DMA實例四:xapp1052在Xilinx 7系列(KC705/VC709)FPGA上的移植
一:前言
這段時間有個朋友加微信請求幫忙調試一塊PCIe採集卡。該採集卡使用xilinx xc7k410t做控制器,上位機爲XP系統,原有的驅動和測試軟件都是基於xapp1052寫的。衆所周知,Xilinx升級到7系列後,原來的pcie ip核trn接口統統轉換成了axis接口,這可愁壞了之前用xapp1052的朋友,一下子不好用了,如何把xapp1052移植到K7系列FPGA上,貌似很有市場。博主本着利人利己的原則,翻出了三五年前玩剩的xapp1052,稍加改動,完成了一版能在K7上使用的BMD工程。另外針對專門做採集卡的朋友,博主提供了一個由FIFO作爲用戶接口的BMD工程。
二:前期準備
1、pcie基礎還是要有,尤其是協議部分。推薦一本電子書,很經典,請耐心讀它(Addison.Wesley.PCI.Express.System.Architecture.eBook-LiB.chm)下載地址:http://download.csdn.net/download/yuzeren48/7723815
2、pg054
3、Vivado2018.2套件
4、Windriver
5、Visual studio 2010
三:移植步驟
1、在vivado中創建一個K7 pcie ipcore的example工程。
2、在xilinx官網下載xapp1052.pdf與xapp1052.zip
3、將1和2兩個工程的代碼融合並稍作修改,形成BMD工程,可選擇64位寬(4x 2.5G )或128位寬(4x 5G),代碼層級如下:
4,關鍵代碼分析
1)、BMD_EP_MEM-Control/Status Registers 所有的用戶狀態寄存器在模塊BMD_EP_MEM中。我們一切的DMA傳輸首先是要控制這 些寄存器,然後進行DMA傳輸,傳輸完成後在讀取狀態寄存器的值獲取傳輸狀態。
2)、EP_RX_ENGINE-Target 這個模塊的功能在EP_RX_ENGINE裏面實現,負責接收讀寫TLP命令,並且提交完成讀 寫內存的完成響應。EP_RX_ENGINE裏面,Target接收PC發過來的32bit不帶數據的存儲 器讀請求和帶1個DW字的32bit 存儲器寫請求。控制狀態寄存器就是通過Target讀寫。
3)、EP_RX_ENGINE-Rx引擎 Rx引擎除了負責接PC讀寫存儲器請求,也要完成開發板發出的讀內存請求的完成響應 (DMA傳輸)。
4)、BMD_EP_MEM-Tx引擎 Tx引擎除了負責接發送DMA數據到PC,也要發送PC發送的讀寫TLP包的完成響應。
5)、BMD_EP_MEM-Control/Status Registers 所有的用戶狀態寄存器在模塊BMD_EP_MEM中。我們一切的DMA傳輸首先是要控制這 些寄存器,然後進行DMA傳輸,傳輸完成後在讀取狀態寄存器的值獲取傳輸狀態。
6)、Interface 總線接口,pcie-app_7x就是包含了BMD所有的總線接口。
7)、AXI4-Stream轉BMD協議接口 axi_trn_rx模塊和axi_trn_tx實現了最新的AXI4-Stream協議轉BMD協議。有讀者可能 會懷疑轉換的協議可能影響傳輸效率,實際上不會有任何效率的犧牲,因爲這裏是FPGA直 接完成了協議的轉換,沒有任何的延遲。
筆者對關鍵邏輯部分代碼做了非常詳細的註釋,讀者可在文末根據需求自行購買。
5,上位機軟件代碼分析
裝好windriver後,我們可以在windriver安裝目錄下找到BMD工程對應的驅動文件
使用VS2010打開後,文件目錄如下,如需獲取每個C文件中函數的功能定義,請在文末購買相應資料。
運行該測試程序,並打開VIVADO工程抓包,看上去是V5的測試代碼,但可以通過輸入VendorID和DeviceID來找到我們自己的板卡。
在4x GEN1(2.5G)的情況下,做連續讀寫測試,實測PCIe寫帶寬約爲840MB/s,PCIe讀帶寬約爲761MB/s,基本上接近滿帶寬了。
四、工程化範例
以上工程就是xapp1052在K7上的移植測試,但對於做工程應用的朋友來說,這個工程並不實用,所有DMA讀寫的數據都是根據我們用戶自己配置的一個patten寄存器固定死的,如果要把FIFO中的數據通過xapp1052 DMA傳輸到系統內存,則需要修改部分源代碼。這裏,博主有償爲大家提供了一個FIFO接口的BMD工程。
用戶接口如下:
module pcie_app_7x#( parameter C_DATA_WIDTH = 64, // RX/TX interface data width // Do not override parameters below this line parameter KEEP_WIDTH = C_DATA_WIDTH / 8 , // TKEEP width parameter REM_WIDTH = (C_DATA_WIDTH == 128) ? 2 : 1 // trem/rrem width )( input user_clk, input user_reset, input user_lnk_up, // Tx input [5:0] tx_buf_av, input tx_cfg_req, input tx_err_drop, output tx_cfg_gnt, input s_axis_tx_tready, output [C_DATA_WIDTH-1:0] s_axis_tx_tdata, output [KEEP_WIDTH-1:0] s_axis_tx_tkeep, output [3:0] s_axis_tx_tuser, output s_axis_tx_tlast, output s_axis_tx_tvalid, // Rx output rx_np_ok, output rx_np_req, input [C_DATA_WIDTH-1:0] m_axis_rx_tdata, input [KEEP_WIDTH-1:0] m_axis_rx_tkeep, input m_axis_rx_tlast, input m_axis_rx_tvalid, output m_axis_rx_tready, input [21:0] m_axis_rx_tuser, // Flow Control input [11:0] fc_cpld, input [7:0] fc_cplh, input [11:0] fc_npd, input [7:0] fc_nph, input [11:0] fc_pd, input [7:0] fc_ph, output [2:0] fc_sel, // CFG input [31:0] cfg_do, input cfg_rd_wr_done, output [31:0] cfg_di, output [3:0] cfg_byte_en, output [9:0] cfg_dwaddr, output cfg_wr_en, output cfg_rd_en, output cfg_err_cor, output cfg_err_ur, output cfg_err_ecrc, output cfg_err_cpl_timeout, output cfg_err_cpl_abort, output cfg_err_cpl_unexpect, output cfg_err_posted, output cfg_err_locked, output [47:0] cfg_err_tlp_cpl_header, input cfg_err_cpl_rdy, output cfg_interrupt, input cfg_interrupt_rdy, output cfg_interrupt_assert, output [7:0] cfg_interrupt_di, input [7:0] cfg_interrupt_do, input [2:0] cfg_interrupt_mmenable, input cfg_interrupt_msienable, input cfg_interrupt_msixenable, input cfg_interrupt_msixfm, output cfg_turnoff_ok, input cfg_to_turnoff, output cfg_trn_pending, output cfg_pm_wake, input [7:0] cfg_bus_number, input [4:0] cfg_device_number, input [2:0] cfg_function_number, input [15:0] cfg_status, input [15:0] cfg_command, input [15:0] cfg_dstatus, input [15:0] cfg_dcommand, input [15:0] cfg_lstatus, input [15:0] cfg_lcommand, input [15:0] cfg_dcommand2, input [2:0] cfg_pcie_link_state, output [1:0] pl_directed_link_change, input [5:0] pl_ltssm_state, output [1:0] pl_directed_link_width, output pl_directed_link_speed, output pl_directed_link_auton, output pl_upstream_prefer_deemph, input [1:0] pl_sel_link_width, input pl_sel_link_rate, input pl_link_gen2_capable, input pl_link_partner_gen2_supported, input [2:0] pl_initial_link_width, input pl_link_upcfg_capable, input [1:0] pl_lane_reversal_mode, input pl_received_hot_rst, output [63:0] cfg_dsn, //user port output [63:0] RX_FIFO_DATA_o, output RX_FIFO_WR_o, input [63:0] TX_FIFO_DATA_i, output TX_FIFO_RD_o, output [7:0] pcie_tap )
這個接口可以直接連在xilinx的PCIe IP核接口上,用戶接口對於做數據採集卡的朋友非常友好。這裏做一個利用XAPP1052 通過FIFO採集數據的例子。爲了驗證數據的方便,我們在開發板上通過一個計數器計數,把計數的值發送到上位機上。測試結果如下:
另外需要補充一點,這個工程在DMA寫數據,也就是FPGA寫數據到PC的時候,沒有任何問題。但是在DMA讀數據的時候,也就是FPGA從PC端讀大量數據的時候,會出現返回的數據包亂序的現象。這是xapp1052的通病,對於做採集卡的朋友,這個問題並無影響,因爲不需要從PC端讀數據。如果有朋友需要讀寫都正確無誤的PCIe DMA工程,可單獨聯繫我,本人有全套PCIe DMA源代碼,可用於各種系列的FPGA,但源碼價格不菲哦。
五、附件
1、xapp1052 K7移植工程,附硬件代碼註釋和windows驅動說明(200元一份)
2、xapp1052 K7移植工程(FIFO接口),附硬件代碼註釋和windows驅動說明以及測試程序(500元一份)
3、支持XILINX全系列的多通道PCIe DMA IP核(價格詳談)
有需要請微信(330853172)聯繫