ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 前言 ` `在linux下做FPGA开发验证模块功能时我们需要仿真,但是我们使用IDE工具会拉低效率,因为无论是quartus还是xilinx的开发工具,其设计本来就比较全面,运行的时候有很多东西都在运行,如果我们只需要仿真一个简单的模块,有可能整套走下来需要几分钟,源文件更改后再次仿真又要耗时几分钟,因此会影响我们的效率。 ` `这个时候我们可以使用iverilog这块轻量级的综合工具来做好这个工作,搭建好环境后反复调试将会很节约时间。 ## 准备工作 ` `首先我们需要安装必要的工具。我的环境是deepin15.11。 * 安装iverilog ``` sudo apt-get install iverilog ``` * 安装查看波形的工具GTKwave ``` sudo apt-get install gtkwave ``` ` `linux下的verilog编辑器推荐使用**VS Code**,VS Code是一款非常好用的编辑器,基本各种语言使用中都可以得到很好的体验。一款编辑器,搞定linux下大部分开发。注意要支持verilog语法,请到VSCode的插件中搜索verilog然后选择一款进行安装。 ![](https://img.kancloud.cn/a3/f3/a3f35242e2a051b8ee61a327e56c8c60_902x528.png) ![](https://img.kancloud.cn/22/34/223400bdb79160dc5a47e9f629744773_499x428.png) ## 基本参数说明 Icarus Verilog编译器主要包含3个工具: * iverilog:用于编译verilog和vhdl文件,进行语法检查,生成可执行文件 * vvp:根据可执行文件,生成仿真波形文件 * gtkwave:用于打开仿真波形文件,图形化显示波形 在终端输入`iverilog`回车,可以看到常用参数使用方法的简单介绍: ![](https://img.kancloud.cn/91/f3/91f32daaf4d930f04f0981211aec6ddc_721x360.png) ### 参数 -o ` `这是比较常用的一个参数了,和GCC中-o的使用几乎一样,用于指定生成文件的名称。如果不指定,默认生成文件名为a.out。如:`iverilog -o test test.v`。 ### 参数 -y ` `用于指定包含文件夹,如果top.v中调用了其他的的.v模块,top.v直接编译会提示 ~~~ led_demo_tb.v:38: error: Unknown module type: led_demo 2 error(s) during elaboration. *** These modules were missing: led_demo referenced 1 times. *** ~~~ ` `找不到调用的模块,那么就需要指定调用模块所在文件夹的路径,支持相对路径和绝对路径。 ` `如:`iverilog -y D:/test/demo led_demo_tb.v` ` `如果是同一目录下:`iverilog -y ./ led_demo_tb.v`,另外,iverilog还支持Xilinx、Altera、Lattice等FPGA厂商的仿真库,需要在编译时通过-y参数指定库文件的路径,详细的使用方法可以查看官方用户指南: [https://iverilog.fandom.com/wiki/User\_Guide](https://iverilog.fandom.com/wiki/User_Guide) ### 参数-I ` `如果程序使用`include语句包含了头文件路径,可以通过-i参数指定文件路径,使用方法和-y参数一样。 ` `如:`iverilog -I D:/test/demo led_demo_tb.v` ### 参数-tvhdl ` `verilog还支持把verilog文件转换为VHDL文件,如`iverilog -tvhdl -o out_file.vhd in_file.v` ## 仿真流程 ### 综合 ` `通过`iverilog -o tb tp_counter.v counter.v`命令,对源文件和仿真文件,进行语法规则检查和编译。由于本示例比较简单,只有1个文件,如果调用了多个.v的模块,可以通过前面介绍的-y参数指定源文件的路径,否则编译报错。如果源文件都在同同一个目录,可以直接通过`./`绝对路径的方式来指定。 ` `如果编译成功,会在当前目录下生成名称为tb的文件。 ![](https://img.kancloud.cn/c6/4b/c64bc664714caf763f84dc08551b1d29_773x482.png) ### 生成波形文件 ` `使用`vvp -n tb -lxt2`命令生成lxt波形文件,运行之后,会在当前目录下生成.lxt文件。 ![](https://img.kancloud.cn/5a/fa/5afa5ad43f464194793cb8fb3d95d7db_813x84.png) ` `如果没有生成,需要检查testbench文件中是否添加了如下几行: ``` initial begin $dumpfile("tb.lxt"); $dumpvars(0, counter); end ``` ### 使用GTKwav查看波形 ` `使用命令`gtkwave tb.lxt`,可以在图形化界面中查看仿真的波形图 ![](https://wcc-blog.oss-cn-beijing.aliyuncs.com/img/iverilog/gtkwave.gif) ![](https://img.kancloud.cn/de/5c/de5cc1956e77bfc68c7ecceb87f60251_1022x635.png) ![](https://img.kancloud.cn/8a/69/8a690087655457a9e7fe2f0df9e35580_1184x497.png) verilog转换为VHDL ` `虽然VHDL和Verilog都诞生于20世纪80年代,而且都属于硬件描述语言(HDL),但是二者的语法特性却不一样。Icarus Verilog 还有一个小功能就是支持把使用Verilog语言编写的.v文件转换为VHDL语言的.vhd文件。 ` `如把led_demo.v文件转换为VHDL文件led_demo.vhd,使用命令`iverilog -tvhdl -o led_demo.vhd led_demo.v`。 ### VHDL文件编译和仿真 ` `如果你还和编译Verilog一样,使用`iverilog led_dmeo.v`来编译VHDL文件的话,那么会提示有语法错误,这是正常的,因为Verilog和VHDL是不同的语法规则,不能使用Verilog的标准来检查VHDL文件的语法。需要添加`-g2012`参数来对VHDL文件进行编译,如`iverilog -g2012 led_demo.vhd`,和Verilog一样,同样也支持Testbech文件的编译和仿真,当然需要编写对应的VHDL Testbench文件。 ## 搭建工程 ` `开始搭建工程,首先新建一个文件夹,并在VS Code中打开该文件夹。 * step1:建立verilog模块文件“counter.v” ``` `timescale 1ns/1ps module counter( input clk, input rst_n, output reg [4:0] ocnt ); always@(posedge clk or negedge rst_n) if (!rst_n) begin ocnt <= 5'd0; end else ocnt <= ocnt + 5'd1; endmodule ``` * step2:建立testbech文件"top_counter.v" ``` //`include "counter.v" `timescale 1ns/1ps module tp_counter; reg clk; reg rst_n; wire [4:0]od; counter counter ( .rst_n (rst_n), .clk (clk), .ocnt(od) ); localparam CLK_PERIOD = 2; always #(CLK_PERIOD/2) clk=~clk; initial begin $dumpfile("tb.lxt"); //生成lxt的文件名称 $dumpvars(0, counter); //tb中实例化的仿真目标实例名称 end initial begin #1 rst_n<=1'bx;clk<=1'bx; #(CLK_PERIOD*3) rst_n<=1; #(CLK_PERIOD*3) rst_n<=0;clk<=0; repeat(5) @(posedge clk); rst_n<=1; repeat(64) @(posedge clk); $dumpflush; $stop; end endmodule ``` ` `注意testbench文件中有几行iverilog编译器专用的语句,如果不加的话后面不能生成lxt文件。 ``` initial begin $dumpfile("tb.lxt"); //生成lxt的文件名称 $dumpvars(0, counter); //tb中实例化的仿真目标实例名称 end ``` * step3:编写makefile文件 ` `采用makefile的方式,可以在我么第一次搭建好工程之后,之后进行综合的时候方便一些。我们可以使用`make`命令就能进行综合仿真,一步到位。 ` `该工程的makefile文件如下: ``` tb: tp_counter.v counter.v iverilog -o tb tp_counter.v counter.v vvp -n tb -lxt2 clean: rm -rf tb tb.lxt2 ``` * step4:编写运行脚本“run.sh”,方便直接查看波形 ``` #!/bin/bash make gtkwave tb.lxt ``` ` `为脚本添加运行权限 ``` chmod +x tb.lxt ``` * step5:开始仿真 ` `上述文件都准备好后,我们可以使用`make`命令来综合工程并生成波形文件,若出现错误,在终端将会有相应的提示。 ` `没有错误后,在终端输入`./run.sh`将会再次进行综合一次,并在gtkwave中打开波形文件。然后就可以使用上面查看波形的方法查看信号。 ## $dumpfile() $ dumpvars()的用法 ` `$dumpfile和$dumpvar是verilog语言中的两个系统任务,可以调用这两个系统任务来创建和将指定信息导入VCD文件. (什么是VCD文件? 答:VCD文件是在对设计进行的仿真过程中,记录各种信号取值变化情况的信息记录文件。EDA工具通过读取VCD格式的文件,显示图形化的仿真波形,所以,可以把VCD文件简单地视为波形记录文件.)下面分别描述它们的用法并举例说明之。 ` `$dumpfile系统任务:为所要创建的VCD文件指定文件名。 ` `举例("//"符号后的内容为注释文字): ``` initial $dumpfile ("myfile.dump"); //指定VCD文件的名字为myfile.dump,仿真信息将记录到此文件 ``` ` `$dumpvar系统任务:指定需要记录到VCD文件中的信号,可以指定某一模块层次上的所有信号,也可以单独指定某一个信号。 ` `典型语法为$dumpvar(level, module\_name); 参数level为一个整数,用于指定层次数,参数module则指定要记录的模块。整句的意思就是,对于指定的模块,包括其下各个层次(层次数由level指定)的信号,都需要记录到VCD文件中去。 ```verilog 举例: initial $dumpvar (0, top); //指定层次数为0,则top模块及其下面各层次的所有信号将被记录 initial $dumpvar (1, top); //记录模块实例top以下一层的信号 //层次数为1,即记录top模块这一层次的信号 //对于top模块中调用的更深层次的模块实例,则不记录其信号变化 initial $dumpvar (2, top); //记录模块实例top以下两层的信号 //即top模块及其下一层的信号将被记录 ``` ` `假设模块top中包含有子模块module1,而我们希望记录top.module1模块以下两层的信号,则语法举例如下: ``` initial $dumpvar (2, top.module1); //模块实例top.module1及其下一层的信号将被记录 ``` ` `假设模块top包含信号signal1和signal2(注意是变量而不是子模块), 如我们希望只记录这两个信号,则语法举例如下: ``` initial $dumpvar (0, top.signal1, top.signal2); //虽然指定了层次数,但层次数是不影响单独指定的信号的 //即指定层次数和单独指定的信号无关 ``` ` `我们甚至可以在同一个$dumpvar的调用中,同时指定某些层次上的所有信号和某个单独的信号,假设模块top包含信号signal1,同时包含有子模块module1,如果我们不但希望记录signal1这个独立的信号,而且还希望记录子模块module1以下三层的所有信号,则语法举例如下: ``` initial $dumpvar (3, top.signal1, top.module1); //指定层次数和单独指定的信号无关 //所以层次数3只作用于模块top.module1, 而与信号 top.signal1无关 ``` ` `上面这个例子和下面的语句是等效的: ``` initial begin $dumpvar (0, top.signal1); $dumpvar (3, top.module1); end $dumpvar的特别用法(不带任何参数): initial $dumpvar; //无参数,表示设计中的所有信号都将被记录 ``` > 最后,我们将$dumpfile和$dumpvar这两个系统任务的使用方法在下面的例子中综合说明,假设我们有一个设计实例,名为i\_design,此设计中包含模块module1,模块module1下面还有很多层次,我们希望对这个设计进行仿真,并将仿真过程中模块module1及其以下所有层次中所有信号的变化情况,记录存储到名为mydesign.dump的VCD文件中去,则例示如下: initial begin ` `$dumpfile ("mydesign.dump"); //指定VCD文件名为mydesign.dump ` `$dumpvar (0, i_design.module1); //记录i_design.module1模块及其下面层次中所有模块的所有信号 end 最后提供一个iverilog搭建好的模板:https://gitee.com/yuan_hp/iverilog_template.git