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

histogram_2d

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}

tb_top_wave.do

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

tb_top.do

三,总结

本文通过实践得出,不同于下面的博客。书中的设计思路及网上资料很有帮助,但具体细节实现上会碰到问题。故多尝试自己动手编写代码实现,多借鉴别人的算法框架和思路。

参考博客:

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

Modelsim独立仿真Vivado Clocking Wizard IP Core

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

modelsim 独立仿真vivado的IP核及仿真脚本的更多相关文章

  1. 调用altera IP核的仿真流程—下

    调用altera IP核的仿真流程—下 编译 在 WorkSpace 窗口的 counter_tst.v上点击右键,如果选择Compile selected 则编译选中的文件,Compile All是 ...

  2. 调用altera IP核的仿真流程—上

    调用altera IP核的仿真流程—上 在学习本节内容之后,请详细阅读<基于modelsim-SE的简单仿真流程>,因为本节是基于<基于modelsim-SE的简单仿真流程>的 ...

  3. Altera三速以太网IP核快速仿真与使用(上篇)

    对于比较高级的ip核,altera一般都会提供仿真案例,网上有关于这个IP核的各种仿真方法,但都比较繁琐,前几日,朋友跟我分享了一个比较快速高效的仿真方法,这个方法也是他摸索折腾了一段时间才总结出来的 ...

  4. Mdoelsim10.4怎么脚本单独仿真ISE14.7 IP核

    软件版本: Modelsim10.4SE ISE14.7 仿真IP:时钟管理IP(clock wizard)   流程: 1.对于Modelsim10.4SE,并不自带Xilinx家的仿真库,因此首先 ...

  5. altera DDR2 IP核之仿真

    在生成的IP核文件夹下,有一个testbench文件夹,里面包含了一个example测试激励和DDR2仿真模型. 如下 20 -rw-r--r-- 1 Administrator 197121 171 ...

  6. 用Modelsim SE 直接仿真 Altera(Intel PSG) IP核 需要注意的问题

    如果我们直接用Modelsim SE仿真 Altera IP核,首先会进入Quartus II目录下找到IP核对应的仿真库源文件,然后在Modelsim SE中进行编译,添加到Modelsim SE的 ...

  7. Lattice 的 DDR IP核使用调试笔记之DDR 的 仿真

    —— 远航路上ing 整理于 博客园.转载请标明出处. 在上节建立完工程之后,要想明确DDR IP的使用细节,最好是做仿真.然后参考仿真来控制IP 核. 仿真的建立: 1.在IP核内的以下路径找到以下 ...

  8. System Generator 生成IP核在Vivado中进行调用

    System Generator 生成IP核在Vivado中进行调用 1.首先在Simulink中搭建硬件模型 2.查看仿真结果 3.资源分析与时序分析 4.启动vivado,关联生成的IP核 5.调 ...

  9. 如何用ModelsimSE仿真IP核-以PLL为例

    我们之前介绍了如何使用Modelsim SE进行仿真和利用do文件的仿真方法,但是其中待仿真的模块是我们自己编写的Verilog模块,但是在实际工作中,我们的设计中会经常用到FPGA厂商给我们提供的现 ...

随机推荐

  1. ARM NEON指令集优化理论与实践

    ARM NEON指令集优化理论与实践 一.简介 NEON就是一种基于SIMD思想的ARM技术,相比于ARMv6或之前的架构,NEON结合了64-bit和128-bit的SIMD指令集,提供128-bi ...

  2. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

  3. python 日期与字符串之间的转换

    1.str转换为datetime >>> from datetime import datetime >>> cday = datetime.strptime('2 ...

  4. 我的物联网大学【第二章】:Luat的出世

    壹 启动火种 有一位软件行业的大神,名字叫做许小刚. 小刚是一位憨厚的年轻的码农,嵌入式.后端.前端,无所不能,是一个很牛的物联网全栈工程师,也是一家物联网软件公司的创始人兼CEO. 有次跟我.老陆. ...

  5. Pytest学习笔记8-参数化

    前言 我们在实际自动化测试中,某些测试用例是无法通过一组测试数据来达到验证效果的,所以需要通过参数化来传递多组数据 在unittest中,我们可以使用第三方库parameterized来对数据进行参数 ...

  6. Unity 按空格一直触发Button点击事件的问题

    #解决 这是由于Button中Navigation(导航)功能导致的. 将导航设置为None即可. 真是气死我了,我说为什么点击完按钮界面,按空格就一直触发界面,难搞

  7. 温故知新,基于Nexus3和Docker搭建私有Docker Mirrors镜像库

    前言 接着上一篇文章关于基于Nexus3和Docker搭建私有Nuget服务的探索,我们可以进一步利用Nexus3来创建一个私有的Docker镜像库满足内部需求. 仓库类型 hosted: 本地存储, ...

  8. 互联网巨头们的 SRE 运维实践「GitHub 热点速览 v.21.27」

    作者:HelloGitHub-小鱼干 本周大热点无疑是前几天 GitHub 发布的 Copilot,帮你补全代码,给你的注释提出建议,预测你即将使用的代码组件-如此神奇的 AI 技术,恰巧本周微软也开 ...

  9. 24、dhcp服务搭建

    1.dhcp介绍: DHCP(Dynamic Host Configuration Protocol),动态主机配置协议,DHCP 协议主要是用来自动为局域网中的客户机分配 TCP/IP 信息的网络协 ...

  10. P2P技术(2)——NAT穿透

    P2P可以是一种通信模式.一种逻辑网络模型.一种技术.甚至一种理念.在P2P网络中,所有通信节点的地位都是对等的,每个节点都扮演着客户机和服务器双重角色,节点之间通过直接通信实现文件信息.处理器运算能 ...