一、前言
利用FPGA設計算法一直以來都是熱點,同樣也是難點。將復雜的數學公式 模型通過硬件系統來搭建,在低延時 高並行性等優勢背后極大提高了設計難度和開發周期。Xilinx公司的sysGen(system generator)工具擴展了MATLAB的simulink,提供很多IP Catalog中沒有的基礎模塊和針對DSP應用的硬件模型。工程師利用豐富的模塊和MATLAB強大的數據處理及可視化能力能夠更快速完成設計與仿真驗證工作。
二、sysGen算法系統設計
本文以個最簡單的例子講述利用sysGen搭建算法IP核,並集成到IP Integrator中作為ZYNQ PS端CPU的“定制外設”。僅用於測試目的。設計需求:在sysGen中搭建系統,將輸入定點整數數據*2后輸出,輸入位寬為8bit。
在System Generator token中設定仿真步長為1sec。點擊需要觀測的信號連線,右擊選擇Xilinx add to viewer。啟動仿真並啟動Xilinx waveform viewer:
本質上就是調用Vivado的XSim工具進行行為仿真。仿真結果可見完成預期目標,現雙擊System Generator token ,選擇Compiliation類型為IP Catalog並勾選Create testbench,按下Generate生成IP核。
三、仿真測試
根據User Guide介紹sysGen是“周期和比特精准的”,我們還是在Vivado環境下再次驗證下。netlist文件夾內子文件夾ip_catalog中為IP核示例工程,由於自動生成了testbench,打開后直接進行行為仿真。sysGen在創建testbench時會將經過gatein和gateout的數據儲存到文件中,testbench進行的工作為:將gatein數據作為測試激勵送入到相應設計輸入端口,之后把設計輸出得到結果與gateout文件數據進行逐一比較從而驗證設計是否與sysGen環境下仿真結果一致。
發現個比較有意思的現象,自動生成的testbench中clock生成並約束的50MHz,而是認為進行了拓展。
仿真波形如圖:
將clock處改動為50MHz后,經過測試發現如果系統一開始就輸入數據,前幾個數據沒有被真正處理,輸出錯誤。可能是軟件BUG吧,不過這種情況也非常少見,實際系統中輸入數據大多情況會啟動一段時間后才輸入。這里等待100ns后再啟動clock翻轉:
改動后仿真波形:
四、AXI-Stream總線形式IP
到此算法IP的設計與驗證結束。如果想將這個IP核導入到IP Integrator中作為CPU的外設,其接口必須滿足AXI總線標准,因此回到sysGen中更改端口名稱和位寬。端口要符合AXI-Stream標准信號名稱,位寬為8bit整數倍。
生成IP核后,打開新的工程,導入該IP核到repository。
五、Block Design系統搭建
系統結構與上一篇該系列博文類似,均是以AXI DMA為核心的Loop系統,只是將AXI-Stream Data FIFO改成了自定義IP核。由於IP核slave和master接口只包含tdata和tvalid信號,因此需要添加接口銜接的一些簡單邏輯。tready信號和tkeep信號直接連接constant使用常數驅動,DMA的s_axis_s2mm接口的tlast由wrapper內計數器邏輯驅動,將system中FCLK_CLK0 peripheral_aresetn m_axis_tvalid和s_axis_s2mm_tlast信號引出到wrapper中。
有一點比較坑:自定義IP通過AXI總線與DMA互聯時,總線下相應的接口不一定會正確對應,所以需要分別將兩端的每個接口相連。可以通過打開綜合后的設計來確認連線無誤。
自動生成wrapper后改動添加代碼如下:

1 `timescale 1 ps / 1 ps 2 3 module user_wrapper 4 (DC, 5 DDR_addr, 6 DDR_ba, 7 DDR_cas_n, 8 DDR_ck_n, 9 DDR_ck_p, 10 DDR_cke, 11 DDR_cs_n, 12 DDR_dm, 13 DDR_dq, 14 DDR_dqs_n, 15 DDR_dqs_p, 16 DDR_odt, 17 DDR_ras_n, 18 DDR_reset_n, 19 DDR_we_n, 20 //FCLK_CLK0, 21 FIXED_IO_ddr_vrn, 22 FIXED_IO_ddr_vrp, 23 FIXED_IO_mio, 24 FIXED_IO_ps_clk, 25 FIXED_IO_ps_porb, 26 FIXED_IO_ps_srstb, 27 RES, 28 SCLK, 29 SDIN, 30 VBAT, 31 VDD 32 //m_axis_tvalid, 33 //peripheral_aresetn, 34 //s_axis_s2mm_tlast 35 ); 36 output DC; 37 inout [14:0]DDR_addr; 38 inout [2:0]DDR_ba; 39 inout DDR_cas_n; 40 inout DDR_ck_n; 41 inout DDR_ck_p; 42 inout DDR_cke; 43 inout DDR_cs_n; 44 inout [3:0]DDR_dm; 45 inout [31:0]DDR_dq; 46 inout [3:0]DDR_dqs_n; 47 inout [3:0]DDR_dqs_p; 48 inout DDR_odt; 49 inout DDR_ras_n; 50 inout DDR_reset_n; 51 inout DDR_we_n; 52 //output FCLK_CLK0; 53 inout FIXED_IO_ddr_vrn; 54 inout FIXED_IO_ddr_vrp; 55 inout [53:0]FIXED_IO_mio; 56 inout FIXED_IO_ps_clk; 57 inout FIXED_IO_ps_porb; 58 inout FIXED_IO_ps_srstb; 59 output RES; 60 output SCLK; 61 output SDIN; 62 output VBAT; 63 output VDD; 64 //output [0:0]m_axis_tvalid; 65 //output [0:0]peripheral_aresetn; 66 //input s_axis_s2mm_tlast; 67 68 69 localparam DATA_NUM = 256; 70 71 wire DC; 72 wire [14:0]DDR_addr; 73 wire [2:0]DDR_ba; 74 wire DDR_cas_n; 75 wire DDR_ck_n; 76 wire DDR_ck_p; 77 wire DDR_cke; 78 wire DDR_cs_n; 79 wire [3:0]DDR_dm; 80 wire [31:0]DDR_dq; 81 wire [3:0]DDR_dqs_n; 82 wire [3:0]DDR_dqs_p; 83 wire DDR_odt; 84 wire DDR_ras_n; 85 wire DDR_reset_n; 86 wire DDR_we_n; 87 wire FCLK_CLK0; 88 wire FIXED_IO_ddr_vrn; 89 wire FIXED_IO_ddr_vrp; 90 wire [53:0]FIXED_IO_mio; 91 wire FIXED_IO_ps_clk; 92 wire FIXED_IO_ps_porb; 93 wire FIXED_IO_ps_srstb; 94 wire RES; 95 wire SCLK; 96 wire SDIN; 97 wire VBAT; 98 wire VDD; 99 wire [0:0]m_axis_tvalid; 100 wire [0:0]peripheral_aresetn; 101 wire s_axis_s2mm_tlast; 102 103 reg [8-1:0] cnt; 104 wire add_cnt; 105 wire end_cnt; 106 107 system system_i 108 (.DC(DC), 109 .DDR_addr(DDR_addr), 110 .DDR_ba(DDR_ba), 111 .DDR_cas_n(DDR_cas_n), 112 .DDR_ck_n(DDR_ck_n), 113 .DDR_ck_p(DDR_ck_p), 114 .DDR_cke(DDR_cke), 115 .DDR_cs_n(DDR_cs_n), 116 .DDR_dm(DDR_dm), 117 .DDR_dq(DDR_dq), 118 .DDR_dqs_n(DDR_dqs_n), 119 .DDR_dqs_p(DDR_dqs_p), 120 .DDR_odt(DDR_odt), 121 .DDR_ras_n(DDR_ras_n), 122 .DDR_reset_n(DDR_reset_n), 123 .DDR_we_n(DDR_we_n), 124 .FCLK_CLK0(FCLK_CLK0), 125 .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn), 126 .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp), 127 .FIXED_IO_mio(FIXED_IO_mio), 128 .FIXED_IO_ps_clk(FIXED_IO_ps_clk), 129 .FIXED_IO_ps_porb(FIXED_IO_ps_porb), 130 .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb), 131 .RES(RES), 132 .SCLK(SCLK), 133 .SDIN(SDIN), 134 .VBAT(VBAT), 135 .VDD(VDD), 136 .m_axis_tvalid(m_axis_tvalid), 137 .peripheral_aresetn(peripheral_aresetn), 138 .s_axis_s2mm_tlast(s_axis_s2mm_tlast)); 139 140 always @(posedge FCLK_CLK0)begin 141 if(!peripheral_aresetn)begin 142 cnt <= 0; 143 end 144 else if(add_cnt)begin 145 if(end_cnt) 146 cnt <= 0; 147 else 148 cnt <= cnt + 1; 149 end 150 end 151 152 assign add_cnt = m_axis_tvalid; 153 assign end_cnt = add_cnt && cnt== DATA_NUM-1; 154 155 assign s_axis_s2mm_tlast = end_cnt; 156 157 endmodule
當自定義IP核輸出256個數據時,拉高tlast信號結束傳輸。打開綜合后的設計,添加調試探針,抓取DMA與自定義IP之間的接口信號,set up debug后完成接下來的流程。
六、軟硬件聯調
在硬件系統中定義數據幀長度為256個,數據位寬為16bit,因此C代碼中DMA啟動傳輸函數中數據長度參數為512byte。測試數據生成與檢測代碼非常簡單:
我們直接查看ILA抓取AXI S總線波形:
看到CPU產生數據從1到4重復遞增,IP核輸出結果從2到8重復遞增,輸出為輸入的2倍。
傳輸完成后進入DMA發送和接收中斷,軟件檢測結果正確。在Memory窗口能夠直接查看內存絕對地址里的數據,選定DDR接收緩存區起始地址,其中的數據與AXI總線傳回數據一致,證明系統聯調成功。之后任意算法模塊均可采用本文方式進行設計和集成,可以說一勞永逸!