modelsim 獨立模擬vivado的IP核及模擬指令碼

南方菜鳥 發表於 2021-07-21

Modelsim獨立模擬vivado的IP

       最近一直在做local dimming專案的FPGA硬體實現,演算法的其中一步就是直方圖統計,即數字影像的某一灰度級的畫素數,這個直方圖的原始碼找了半天才搞到,就在<<牟新剛周曉鄭曉亮著: 基千FPGA的數字影像處理原理及應用>>這一本書有詳細的描述。但有了這個程式碼,還得檢視直方圖處理的效果,那我只有搭建模擬檢視,但modelsim一直出錯,提示直方圖模組呼叫的雙口ram不存在,於是下面介紹modelsim獨立模擬帶有vivado的IP的解決辦法。

後面還會附上我一直在用的模擬指令碼,十分方便!

一:實現步驟

      第一步在vivado中編譯模擬庫,將編譯後的模擬庫放在自己新建的資料夾,如D:/xilinx/xlib,我已經編譯好了,如下圖

modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 第二步,找到編譯庫路徑下的modelsim.ini檔案,即下面右圖中的紅框檔案,去掉只讀屬性,開啟後選擇包含編譯庫的程式碼,圖2中的63-72即vivado中包含的編譯庫,複製後貼上到modelsim10.5安裝根目錄下的modelsim.ini檔案中,如第三張圖中的83-94行,即為貼上註釋的,為了以後避免和alter及ISE14.7等編譯庫混合,用分號註釋加上分割線,下次用到其他的編譯庫則註釋就行。儲存後勾選只讀屬性,如下圖所示

 

modelsim 獨立模擬vivado的IP核及模擬指令碼modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 第三步,模擬帶IP核的檔案前提是你在vivado生成了IP核,如下圖所示,找到下面紅框中的兩個檔案路徑,複製後這兩個檔案加入到模擬工程路徑D:\3FPGA_project\02LCD_project\histogram_sim\rtl,如下面的第二張圖所示

modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 

第四步:在modelsim中獨立模擬,我一般是用指令碼,即do檔案的形式,通過編譯do檔案tb_top.do和波形新增do檔案tb_top_wave.do實現自動模擬,這樣相對於手工的形式可以避免很多體力活,先開啟modelsim切換路徑到sim下,直接在modelsim中輸入do tb_top.do就可載入波形,如下圖所示:

modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 第五步,波形顯示

modelsim 獨立模擬vivado的IP核及模擬指令碼

 

 

二:原始碼

1.設計模組原始碼histogram_2d的原始碼,已經將其中的雙口ram的IP核呼叫和其中一些程式碼註釋(報錯的),因為書中的vivado版本比較老,故不能模擬執行,會報錯。

modelsim 獨立模擬vivado的IP核及模擬指令碼
  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
histogram_2d

2.原創的指令碼檔案

A新增訊號和顯示波形的tb_top_wave.do

modelsim 獨立模擬vivado的IP核及模擬指令碼
 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}
tb_top_wave.do

B新建work庫,編譯.v檔案和啟動頂層模擬檔案,及執行新增訊號和顯示波形的tb_top_wave.do的編譯do檔案tb_top.do

modelsim 獨立模擬vivado的IP核及模擬指令碼
 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
tb_top.do

 

三,總結

本文通過實踐得出,不同於下面的部落格。書中的設計思路及網上資料很有幫助,但具體細節實現上會碰到問題。故多嘗試自己動手編寫程式碼實現,多借鑑別人的演算法框架和思路。

參考部落格:

https://www.cnblogs.com/ninghechuan/p/8305925.html

Modelsim獨立模擬Vivado Clocking Wizard IP Core

https://cloud.tencent.com/developer/article/1529571