Modelsim獨立模擬vivado的IP
最近一直在做local dimming專案的FPGA硬體實現,演算法的其中一步就是直方圖統計,即數字影像的某一灰度級的畫素數,這個直方圖的原始碼找了半天才搞到,就在<<牟新剛周曉鄭曉亮著: 基千FPGA的數字影像處理原理及應用>>這一本書有詳細的描述。但有了這個程式碼,還得檢視直方圖處理的效果,那我只有搭建模擬檢視,但modelsim一直出錯,提示直方圖模組呼叫的雙口ram不存在,於是下面介紹modelsim獨立模擬帶有vivado的IP的解決辦法。
後面還會附上我一直在用的模擬指令碼,十分方便!
一:實現步驟
第一步在vivado中編譯模擬庫,將編譯後的模擬庫放在自己新建的資料夾,如D:/xilinx/xlib,我已經編譯好了,如下圖
第二步,找到編譯庫路徑下的modelsim.ini檔案,即下面右圖中的紅框檔案,去掉只讀屬性,開啟後選擇包含編譯庫的程式碼,圖2中的63-72即vivado中包含的編譯庫,複製後貼上到modelsim10.5安裝根目錄下的modelsim.ini檔案中,如第三張圖中的83-94行,即為貼上註釋的,為了以後避免和alter及ISE14.7等編譯庫混合,用分號註釋加上分割線,下次用到其他的編譯庫則註釋就行。儲存後勾選只讀屬性,如下圖所示
第三步,模擬帶IP核的檔案前提是你在vivado生成了IP核,如下圖所示,找到下面紅框中的兩個檔案路徑,複製後這兩個檔案加入到模擬工程路徑D:\3FPGA_project\02LCD_project\histogram_sim\rtl,如下面的第二張圖所示
第四步:在modelsim中獨立模擬,我一般是用指令碼,即do檔案的形式,通過編譯do檔案tb_top.do和波形新增do檔案tb_top_wave.do實現自動模擬,這樣相對於手工的形式可以避免很多體力活,先開啟modelsim切換路徑到sim下,直接在modelsim中輸入do tb_top.do就可載入波形,如下圖所示:
第五步,波形顯示
二:原始碼
1.設計模組原始碼histogram_2d的原始碼,已經將其中的雙口ram的IP核呼叫和其中一些程式碼註釋(報錯的),因為書中的vivado版本比較老,故不能模擬執行,會報錯。
1 `timescale 1ns/1ns 2 3 module histogram_2d( 4 rst_n, 5 clk, 6 din_valid, 7 din, 8 dout, 9 vsync, 10 dout_valid, 11 rdyOutput, 12 //`ifdef Equalize 13 hist_cnt_addr, 14 hist_cnt_out, 15 //`endif 16 //`ifdef LinearTransfer 17 lowCnt, 18 highCnt, 19 lowIndex, 20 highIndex, 21 //`endif 22 int_flag 23 ); 24 25 parameter DW = 14; 26 parameter IH = 512; 27 parameter IW = 640; 28 parameter TW = 32; 29 30 localparam TOTAL_CNT = IW * IH; 31 localparam HALF_WIDTH = (TW>>1); 32 33 input rst_n; 34 input clk; 35 input din_valid; 36 input [DW-1:0]din; 37 input rdyOutput; 38 39 output reg [HALF_WIDTH:0]dout; 40 input vsync; 41 output reg dout_valid; 42 output reg int_flag; 43 44 //`ifdef LinearTransfer 45 input [TW-1:0]lowCnt; 46 input [TW-1:0]highCnt; 47 output reg[DW-1:0]lowIndex; 48 output reg[DW-1:0]highIndex; 49 //`endif 50 51 //`ifdef Equalize 52 input [DW-1:0]hist_cnt_addr; 53 output reg [TW-1:0]hist_cnt_out; 54 //`endif 55 56 reg vsync_r; 57 reg dvalid_r; 58 reg dvalid_r2; 59 reg [DW-1:0]din_r; 60 reg [DW-1:0]din_r2; 61 wire hsync_fall; 62 wire hsync_rise; 63 reg [9:0]hsync_count; 64 reg count_en; 65 wire [DW-1:0]mux_addr_b; 66 wire [DW-1:0]mux_addr_b2; 67 wire [TW-1:0]q_a; 68 wire [TW-1:0]q_b; 69 reg [TW-1:0]counter; 70 wire [TW-1:0]count_value; 71 wire rst_cnt; 72 wire inc_en; 73 wire we_a; 74 wire we_b; 75 wire we_b_l; 76 reg we_b_h; 77 78 reg int_r; 79 80 wire [DW-1:0]addr_a; 81 wire [DW-1:0]clr_addr; 82 reg [DW-1:0]clr_addr_r; 83 reg [DW:0]out_pixel; 84 85 reg count_all; 86 //reg count_all_r; 87 reg count_en_r; 88 89 reg [TW-1:0]hist_cnt; 90 wire rstOutput; 91 92 93 wire [TW-1:0]dataTmp2; 94 wire clr_flag; 95 96 assign #1 hsync_fall = dvalid_r & (~(din_valid)); 97 assign #1 hsync_rise = (~(dvalid_r)) & din_valid; 98 99 always @(posedge clk or negedge rst_n) 100 if (((~(rst_n))) == 1'b1) 101 hsync_count <= #1 {10{1'b0}}; 102 else 103 begin 104 if (vsync_r == 1'b1) 105 hsync_count <= #1 {10{1'b0}}; 106 else if (hsync_fall == 1'b1) 107 hsync_count <= hsync_count + 10'b1; 108 end 109 110 always @(posedge clk or negedge rst_n) 111 if (((~(rst_n))) == 1'b1) 112 count_en <= #1 1'b0; 113 else 114 begin 115 if (hsync_count >= IH) 116 count_en <= #1 1'b0; 117 else if (hsync_rise == 1'b1) 118 count_en <= #1 1'b1; 119 else 120 count_en <= #1 count_en; 121 end 122 123 assign mux_addr_b = ((count_en == 1'b1)) ? din_r : 124 clr_addr; 125 assign mux_addr_b2 = ((count_en == 1'b1)) ? din_r : 126 clr_addr_r; 127 128 129 always @(posedge clk) 130 begin 131 din_r2 <= #1 din_r; 132 dvalid_r2 <= #1 dvalid_r; 133 end 134 135 always @(posedge clk) 136 begin 137 if (rst_cnt == 1'b1) 138 counter <= #1 {{TW-1{1'b0}},1'b1}; 139 else if (inc_en == 1'b1) 140 counter <= #1 counter + {{TW-1{1'b0}},1'b1}; 141 else 142 counter <= #1 counter; 143 end 144 145 assign #1 rst_cnt = (((din_r != din_r2) | ((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0)))) ? 1'b1 : 146 1'b0; 147 assign #1 inc_en = (((din_r == din_r2) & (dvalid_r2 == 1'b1))) ? 1'b1 : 148 1'b0; 149 150 assign #1 we_a = ((((din_r != din_r2) & (dvalid_r2 == 1'b1)) | ((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0)))) ? 1'b1 : 151 1'b0; 152 assign #1 count_value = ((count_en == 1'b1)) ? counter + q_b : 153 {TW{1'b0}}; 154 155 assign #1 addr_a = din_r2; 156 157 assign dataTmp2 = {TW{1'b0}}; 158 159 // hist_buffer dpram_bin_l( 160 // .address_a(addr_a), //addra 161 // .address_b(mux_addr_b), //addrb 162 // .clock(clk), 163 // .data_a(count_value[HALF_WIDTH - 1:0]), //dina 164 // .data_b(dataTmp2[HALF_WIDTH - 1:0]), 165 // .wren_a(we_a), 166 // .wren_b(we_b_l), 167 // .q_a(q_a[HALF_WIDTH - 1:0]), //douta 168 // .q_b(q_b[HALF_WIDTH - 1:0]) //doutb 169 // ); 170 // 171 hist_buffer dpram_bin_l ( 172 .clka(clk), // input wire clka 173 .ena(1), // input wire ena 174 .wea(we_a), // input wire [0 : 0] wea 175 .addra(addr_a[9 : 0]), // input wire [9 : 0] addra 176 .dina(count_value[HALF_WIDTH - 1:0]), // input wire [31 : 0] dina 177 .douta(q_a[HALF_WIDTH - 1:0]), // output wire [31 : 0] douta 178 .clkb(clk), // input wire clkb 179 .enb(1), // input wire enb 180 .web(we_b_l), // input wire [0 : 0] web 181 .addrb(mux_addr_b[9 : 0]), // input wire [9 : 0] addrb 182 .dinb(0), // input wire [31 : 0] dinb 183 .doutb(q_b[HALF_WIDTH - 1:0]) // output wire [31 : 0] doutb 184 ); 185 // 186 // defparam dpram_bin_l.AW = DW; 187 // defparam dpram_bin_l.DW = HALF_WIDTH; 188 189 // hist_buffer dpram_bin_h( 190 // .address_a(addr_a), 191 // .address_b(mux_addr_b2), 192 // .clock(clk), 193 // .data_a(count_value[TW - 1:HALF_WIDTH]), 194 // .data_b(dataTmp2[TW - 1:HALF_WIDTH]), 195 // .wren_a(we_a), 196 // .wren_b(we_b_h), 197 // .q_a(q_a[TW - 1:HALF_WIDTH]), 198 // .q_b(q_b[TW - 1:HALF_WIDTH]) 199 // ); 200 201 hist_buffer dpram_bin_h ( 202 .clka(clk), // input wire clka 203 .ena(1), // input wire ena 204 .wea(we_a), // input wire [0 : 0] wea 205 .addra(addr_a[9 : 0]), // input wire [9 : 0] addra 206 .dina(count_value[TW - 1:HALF_WIDTH]), // input wire [31 : 0] dina 207 .douta(q_a[TW - 1:HALF_WIDTH]), // output wire [31 : 0] douta 208 .clkb(clk), // input wire clkb 209 .enb(1), // input wire enb 210 .web(we_b_h), // input wire [0 : 0] web 211 .addrb(mux_addr_b2[9 : 0]), // input wire [9 : 0] addrb 212 .dinb(0), // input wire [31 : 0] dinb 213 .doutb(q_b[TW - 1:HALF_WIDTH]) // output wire [31 : 0] doutb 214 ); 215 216 // defparam dpram_bin_h.AW = DW; 217 // defparam dpram_bin_h.DW = HALF_WIDTH; 218 219 always @(posedge clk or negedge rst_n) 220 if (((~(rst_n))) == 1'b1) 221 count_en_r <= #1 1'b0; 222 else 223 count_en_r <= #1 count_en; 224 225 assign rstOutput = count_en_r | (~(rdyOutput)); 226 227 reg [DW-1:0]lowIndex_tmp; 228 reg [DW-1:0]highIndex_tmp; 229 reg [DW-1:0]highIndex_tmp2; 230 reg bFindMax; 231 reg bFindMin; 232 233 always @(posedge clk or negedge rst_n) 234 if ((~(rst_n)) == 1'b1) 235 begin 236 lowIndex_tmp <= {DW{1'b0}}; 237 highIndex_tmp <= {DW{1'b1}}; 238 bFindMin <= 1'b0; 239 bFindMax <= 1'b0; 240 highIndex_tmp2 <= {DW{1'b0}}; 241 end 242 else 243 begin 244 if (vsync_r == 1'b0 & vsync == 1'b1) 245 begin 246 lowIndex_tmp <= {DW{1'b0}}; 247 highIndex_tmp <= {DW{1'b1}}; 248 highIndex_tmp2 <= {DW{1'b0}}; 249 lowIndex <= lowIndex_tmp; 250 if (bFindMax == 1'b1) 251 highIndex <= highIndex_tmp; 252 else 253 highIndex <= highIndex_tmp2; 254 bFindMin <= 1'b0; 255 bFindMax <= 1'b0; 256 end 257 else 258 begin 259 if (out_pixel[0] == 1'b1) 260 begin 261 if ((~(q_b == {HALF_WIDTH{1'b0}}))) 262 highIndex_tmp2 <= clr_addr - 4'h1; 263 if ((hist_cnt >= lowCnt) & bFindMin == 1'b0) 264 begin 265 lowIndex_tmp <= clr_addr - 4'h1; 266 bFindMin <= 1'b1; 267 end 268 if (hist_cnt >= (TOTAL_CNT - highCnt) & bFindMax == 1'b0) 269 begin 270 highIndex_tmp <= clr_addr - 4'h1; 271 bFindMax <= 1'b1; 272 end 273 end 274 end 275 end 276 277 // hist_buffer hist_cnt_buf( 278 // .address_a(out_pixel_r2), 279 // .address_b(hist_cnt_addr), 280 // .clock(clk), 281 // .data_a(hist_cnt), 282 // .data_b(), 283 // .wren_a(dout_valid), 284 // .wren_b(1'b0), 285 // .q_a(), 286 // .q_b(hist_cnt_temp) 287 // ); 288 // defparam hist_cnt_buf.AW = DW; 289 // defparam hist_cnt_buf.DW = TW; 290 291 hist_buffer hist_cnt_buf ( 292 .clka(clk), // input wire clka 293 .ena(1), // input wire ena 294 .wea(dout_valid), // input wire [0 : 0] wea 295 .addra(out_pixel[9:0]), // input wire [9 : 0] addra 296 .dina(hist_cnt), // input wire [31 : 0] dina 297 .douta(), // output wire [31 : 0] douta 298 .clkb(clk), // input wire clkb 299 .enb(1), // input wire enb 300 .web(0), // input wire [0 : 0] web 301 .addrb(hist_cnt_addr[9:0]), // input wire [9 : 0] addrb 302 .dinb(0), // input wire [31 : 0] dinb //data_b 303 .doutb(hist_cnt_temp) // output wire [31 : 0] doutb 304 ); 305 306 endmodule
2.原創的指令碼檔案
A新增訊號和顯示波形的tb_top_wave.do
1 #新增訊號和顯示其波形 2 onerror {resume} 3 quietly WaveActivateNextPane {} 0 4 add wave -noupdate -divider {input paramters} 5 add wave -noupdate -radix unsigned /tb_top/CLK_FREQ 6 add wave -noupdate -radix unsigned /tb_top/CLK_PERIOD 7 8 add wave -noupdate -divider {histogram_2d input} 9 add wave -noupdate /tb_top/inst_hist/clk 10 add wave -noupdate /tb_top/inst_hist/rst_n 11 add wave -noupdate /tb_top/inst_hist/din_valid 12 add wave -noupdate /tb_top/inst_hist/din 13 14 add wave -noupdate -divider {histogram_2d output} 15 add wave -noupdate /tb_top/inst_hist/dout 16 add wave -noupdate /tb_top/inst_hist/vsync 17 add wave -noupdate /tb_top/inst_hist/dout_valid 18 19 add wave -noupdate -divider {end signal} 20 21 TreeUpdate [SetDefaultTree] 22 WaveRestoreCursors {{Cursor 1} {912366093 ps} 0} 23 configure wave -namecolwidth 150 24 configure wave -valuecolwidth 100 25 configure wave -justifyvalue left 26 configure wave -signalnamewidth 0 27 configure wave -snapdistance 10 28 configure wave -datasetprefix 0 29 configure wave -rowmargin 4 30 configure wave -childrowmargin 2 31 configure wave -gridoffset 0 32 configure wave -gridperiod 1 33 configure wave -griddelta 40 34 configure wave -timeline 0 35 configure wave -timelineunits ns 36 update 37 WaveRestoreZoom {891247063 ps} {925431255 ps}
B新建work庫,編譯.v檔案和啟動頂層模擬檔案,及執行新增訊號和顯示波形的tb_top_wave.do的編譯do檔案tb_top.do
1 #不需要新建modelsim工程,直接執行.do檔案就可以模擬 2 quit -sim 3 #新建work庫 4 vlib work 5 6 #將work庫對映到當前工作目錄 7 #vmap [-help] [-c] [-del] [<logical_name>] [<path>] 8 vmap work 9 10 #編譯所有.v檔案到work工作庫 11 #-work <path> Specify library WORK 12 #-vlog01compat Ensure compatibility with Std 1364-2001 13 #-incr Enable incremental compilation 14 #"rtl/*.v" 當前工作目錄下的rtl資料夾中的所有.v檔案,支援相對路徑,但是要加雙引號“” 15 #vlog 16 17 vlog -work work -vlog01compat -incr "../testbench/prim_sim.v" 18 vlog -work work -vlog01compat -incr "../testbench/tb_top.v" 19 20 vlog -work work -vlog01compat -incr "../rtl/histogram_2d.v" 21 vlog -work work -vlog01compat -incr "../rtl/*.v" 22 #vlog -work work -vlog01compat -incr "../rtl/uart_master_src/*.v" 23 24 25 #編譯所有.vhd檔案 26 #-work <path> Specify library WORK 27 #-93 Enable support for VHDL 1076-1993 28 #-2002 Enable support for VHDL 1076-2002 29 #vcom 30 31 #啟動模擬頂層檔案 32 #-L <libname> Search library for design units instantiated from Verilog and for VHDL default component binding 33 #+nowarn<CODE | Number> Disable specified warning message (Example: +nowarnTFMPC) 34 #-t [1|10|100]fs|ps|ns|us|ms|sec Time resolution limit VHDL default: resolution setting from .ini file) 35 # (Verilog default: minimum time_precision in the design) 36 #-novopt Force incremental mode (pre-6.0 behavior) 37 38 vsim +nowarnTFMPC -L work -novopt -l tb_top.log work.tb_top 39 40 #產生一個wave log format(WLF)...... 41 log -r /* 42 43 #開啟wave視窗 44 view wave 45 46 #新增模擬訊號 47 #在已經新增好訊號和設定好格式的wave視窗,點選【File】->【Save Fomat】 48 #存為任意名字的.do檔案,該檔案包含了載入哪些訊號及其顯示格式的命令 49 do tb_top_wave.do 50 51 #設定執行時間 52 run -all 53 54 #dataflow除錯 55 #具體方法是在模擬後執行命令 view dataflow 就可以開啟dataflow檔案, 56 #在dataflow的視窗選單中點選add中的view all nets就可以觀察到各個模組之間的邏輯聯絡, 57 #模組一般都為initial模組、always模組、assign模組等等。點選中一個模組,則這個模組變為紅色。 58 #這時候在view選單下點選show wave就可以在視窗下方彈出wave視窗, 59 #不同的是這個wave視窗所顯示的訊號變數僅為點選中的模組所包括的訊號變數, 60 #這時候也可以點選模擬run –all小圖示來模擬有關這個模組的輸入輸出關係。 61 #view dataflow
三,總結
本文通過實踐得出,不同於下面的部落格。書中的設計思路及網上資料很有幫助,但具體細節實現上會碰到問題。故多嘗試自己動手編寫程式碼實現,多借鑑別人的演算法框架和思路。
參考部落格:
https://www.cnblogs.com/ninghechuan/p/8305925.html
(Modelsim獨立模擬Vivado Clocking Wizard IP Core)
https://cloud.tencent.com/developer/article/1529571