首页 > 代码库 > ModelSim仿真入门 精讲

ModelSim仿真入门 精讲

ModelSim仿真入门之一:软件介绍

编写这个教程之前,为了让不同水平阶段的人都能阅读,我尽量做到了零基础入门这个目标,所有的操作步骤都经过缜密的思考,做到了详细再详细的程度。

如果您是FPGA开发方面的初学者,那么这个教程一定能够帮助你在仿真技术上越过新人的台阶;如果您是FPGA开发的老手,这篇文档也并非对您没有帮助,您可以把教程发给其他刚入门的同事,免去您亲自上阵指导的麻烦,把主要的精力放在更有价值的地方。

   

一、FPGA设计仿真验证简介

    严格来讲,FPGA设计验证包括功能仿真、时序仿真和电路验证,它们分别对应整个开发流程的每一个步骤。仿真是指使用设计软件包对已实现的设计进行完整的测试,并模拟实际物理环境下的工作情况。

功能仿真是指仅对逻辑功能进行模拟测试,以了解其实现的功能是否满足原设计的要求,仿真过程没有加入时序信息,不涉及具体器件的硬件特性,如延时特性等,因此又叫前仿真,它是对HDL硬件描述语言的功能实现情况进行仿真,以确保HDL语言描述能够满足设计者的最初意图。

时序仿真则是在HDL可以满足设计者功能要求的基础上,在布局布线后,提取有关的器件延迟、连线延时等时序参数信息,并在此基础上进行的仿真,也成为后仿真,它是接近于器件真实运行状态的一种仿真。

   

二、仿真软件ModelSim及其应用

HDL的仿真软件有很多种,如VCSVSSNC-VerilogNC-VHDLModelSim等,对于开发FPGA来说,一般是使用FPGA厂家提供的集成开发环境,他们都有自己的仿真器,如Xilinx公司的ISEAltera公司的Quartus II等,但是这些厂家开发的仿真器的仿真功能往往比不上专业的EDA公司的仿真工具,如ModelSim AEAltera Edition)、ModelSim XEXilinx Edition)等。Quartus II设有第三方仿真工具的接口,可以直接调用其他EDA公司的仿真工具,这极大地提高了EDA设计的水平和质量。

ModelSimModel TechnologyMentor Graphics的子公司)的HDL硬件描述语言的仿真软件,该软件可以用来实现对设计的VHDLVerilog HDL 或是两种语言混合的程序进行仿真,同时也支持IEEE常见的各种硬件描述语言标准。

无论是从使用界面和调试环境,还是从仿真速度和效果上看,ModelSim都可以算的上是业界比较优秀的HDL语言仿真软件。它是唯一的单内核支持VHDLVerilog HDL混合仿真的仿真器,是做FPGA/ASIC设计的RTL级和门级电路仿真的好选择,它采用直接优化的编译技术,Tcl/Tk技术和单一内核仿真技术,具有仿真速度快,编译的代码与仿真平台无关,便于IP核的保护和加快错误程序定位等优点。

ModelSim分几种不同的版本:ModelSim SEModelSim PEModelSim LEModelSim OEM,其中的SEPELE是其最高版本,编译速度是所有版本中最快的,而OEM版本就是集成在FPGA厂家设计工具中的版本,它们专门和某个厂家的FPGA配套来使用,如后面使用到的ModelSim AE就是专门针对Altera公司QuartusII的配套的OEM产品。 

三、ModelSim的仿真流程

ModelSim不仅可以用于数字电路系统设计的功能仿真,还可以应用于数字电路系统设计的时序仿真。 ModelSim的使用中,最基本的步骤包括创建工程、编写源代码、编译、启动仿真器和运行仿真五个步骤,仿真流程如图1所示:

1 ModelSim仿真的基本流程(基于工程的)

这个是基于工程的流程,还有一种是基于库文件的,和基于工程的相比,它需要自己创建工作库,另外关闭ModelSim软件后,下次还得自己手动打开设计文件,而基于工程的就不会这样,工程是一直保持的状态,不用每次启动软件后再手工加载,除非我们自己关掉这个工程。还有另外两个流程,这里不提了,详细内容可参看ModelSim AlteraTutorial.PDF 。在软件的安装目录的DOCS文件夹内是全部的参考文档,包括使用手册等。

   

四、仿真测试文件(Test Bench)程序的设计方法

随着设计量和复杂度的不断增加,数字验证变得越来越难,所消耗的成本也越来越高,面对这种挑战,验证工程师必须依靠相应的验证工具和方法。对于大型的设计,比如上百万门的设计验证,工程师必须使用一整套规范的验证工具,而对于较小的设计,使用具有HDL Test Bench的仿真器是一个不错的选择。

一般来说,Test Bench使用工业标准VHDL或者Verilog HDL语言来描述,简单的Test Bench通过调用用户设计的功能模块,然后进行仿真,较为复杂的Test Bench还包括一些其他的功能,比如包含特定的激励向量或者进行实际输出与期望的比较等。

在开始写Test Bench之前,很重要的一点就是要设计实例化DUTDesign Under Test,即就是被测元件),还要详细了解整个的测试计划和测试案例。整个的测试Test Bench环境如图2所示:

2 Test Bench的测试环境

从图中可以看见,Test Bench和被测对象Counter构成了一个封闭的循环,Test Bench负责向被测元器件的输入端口提供激励(时钟)和一些控制信号(复位和置位信号),另外Test Bench还监测被测元器件的输出端口所输出的信号值是否和我们的设计预期相符,并把监测的情况显示给我们。

    由于Test Bench程序和被测对象构成了一个封闭的循环,因此Test Bench的输入端口需要与被测对象的输出端口连接,Test Bench的输出端口则要与被测对象的输入端口相连接。所以在端口的定义上,Test Bench程序需要和被测对象相对应。

被测元器件是一个已经设计好的电路或系统,Test Bench是用元件例化语句将其嵌入程序中。VerilogHDL测试平台是一个设有输入输出端口的设计模块,被测元器件的输入端定义为reg(寄存器)型变量,在always块或initial块中赋值(产生测试条件),被测元器件的输出端定义为wire(线网)型变量,产生相应输入变化的输出结果(波形)。

   

4.1 组合逻辑电路Test Bench的设计

组合逻辑的设计验证,主要就是检查设计结果是不是符合该电路的真值表功能,因此在编写组合逻辑Test Bench时,用initial块把被测电路的输入按照真值表提供的数据变化作为测试条件,就能实现Test Bench的设计。

1. 编写一位全加器的Test Bench程序

全加器的AB两个是1位二进制加数的输入端,CI是低位来的进位输入端,CO是向高位进位的输出端,SO是全加器的本位和值。

Verilog HDL语言编写的全加器程序adder.v如下:

//-----------------------------------------------------

// DesignName : adder1

// FileName   : adder1.v

//Function    : 1 bit full adder

//Coder       : Cheng xu

//-----------------------------------------------------

moduleadder1(

a                                       ,

b                                       ,

ci                                      ,

so                                     ,

co

);

// Portdeclarations

input a                           ;

input b                           ;

input ci                ;

   

output so           ;

output co           ;

   

//InternalVariables

wire                      a                             ;

wire                      b                       ;

wire                      ci                       ;

wire                      so                          ;

wire                      co                          ;

//CodeStarts Here

assign {co, so} = a + b + ci;

endmodule

   

根据全加器的真值表写的全加器的Test Bench程序test_adder1.v如下:

//test_adder1.v

`timescale1ns/1ns

moduletest_adder1;

wire so                           ;

wire co                           ;

reg a                               ;

reg b                               ;

reg ci                               ;

   

adder1 U(

          .a(a),

          .b(b),

          .ci(ci),

          .so(so),

          .co(co)

          );

   

initial

begin

          #20 a = 0; b = 0; c = 0;

          #20 a = 0; b = 0; c = 1;

          #20 a = 0; b = 1; c = 0;

          #20 a = 0; b = 1; c = 1;

          #20 a = 1; b = 0; c = 0;

          #20 a = 1; b = 0; c = 1;

          #20 a = 1; b = 1; c = 0;

          #20 a = 1; b = 1; c = 1;

          #200 $stop;

end

endmodule

    下面我们就以ModelSimEDA平台,仿真上面的程序。这一讲先暂时不讲仿真测试的方法,留到下一讲再来详述,这一讲主要讲述的内容是Test Bench程序的编写方法,故现在仅仅给出仿真的波形图,全加器的仿真波形如图3所示:

一位全加器的仿真波形图

现在对着这个图,我们返回来再来看看我们编写的test_adder1.v这个Test Bench程序究竟完成了哪些工作,是不是按照我们的要求来工作的:

①首先看程序第二行的`timescale1ns/1ns这句代码,这个是时间尺度指令,它是用来定义模块的仿真时间单位和时间精度的,其使用格式为:`timescale 仿真时间单位/时间精度,用于说明仿真时间单位和时间精度的数字只能是110100,不能为其它的数字,单位可以是smsusnspsfs。仿真时间单位是指的模块仿真时间和延时的基准单位,也就是说只有定义了仿真时间单位,程序中的延时符号"#"才有意义,如程序中的一行 #20 a = 0; b = 0; c = 0; 前面的 #20 就是延时20个时间基准单位,按照程序中的1ns这个基准,就延时了20ns。需要说明的是该行程序的下一行 #20 a =0; b = 0; c = 1; 前面的延时20ns是相对于前一个的延时来说的,也就是第二行在第一行完了之后延时20ns执行。这时候再看看仿真的波形图就不难理解最开始的线为什么是红色而不是正常的绿色的原因了,因为我们在程序中begin的下一行就是 #20 a =0; b = 0; c = 0; 它前面的延时20ns是相对于begin的延时,也就是说程序开始的时候是什么都不做的,输出为不确定的值,过了20ns才将全0赋给了abci,这个时候才是最开始的绿线的部分。

②在Test Bench程序中,把全加器的输入abci定义为了reg型变量,把输出soco定义为了wire型变量,这个和被测元件的定义情况刚好是反的,这样也说明了TestBench程序和被测元件是封闭的一个循环。用元件例化语句adder1U( .a(a), .b(b), .ci(ci), .so(so), .co(co) ) ; 把全加器设计电路嵌入到Test Bench程序中。

③程序的后面有一句 #200 $stop; 这个是一个系统任务,用来暂停仿真过程的,将控制权交还给用户,用户在取得控制权以后可以输入其它的控制命令或者查看仿真结果等,之后可以从暂停的地方恢复仿真过程。$stop有两种表达形式,带参数的和不带参数的:

$stop

$stopn);  //n可以取012

不带参数的$stop等同于$stop0),在暂停时不输出任何信息;$stop1)在暂停时输出当前仿真时刻和暂停处在程序中的位置;$stop2)不仅有$stop1)的作用,还能输出仿真时占用内存大小和CPU时间。

而用于退出仿真过程的系统任务是 $finish,我们在点击Run(开始运行)的时候,系统会询问我们是否要结束仿真,假如我们选"是",这个系统任务会把ModelSim软件在完成仿真后关闭,假如我们选"否",则可以继续留在仿真界面。

和一位全加器的真值表进行全部的对比后发现和该仿真波形完全一致,仿真结束。

4.2 时序逻辑电路Test Bench的设计

时序逻辑电路Test Bench的设计要求和组合逻辑电路基本相同,主要区别在于时序逻辑电路Test Bench软件中,需要用always块语句生成时钟信号。

2所编写的程序,就是在下一讲当中的实例,利用这个实例来讲解软件的全部操作流程和使用方法,这一讲先来分析这个程序以及和它相配套的Test Bench程序,看看它们是否能够按照我们设计期望的那样输出仿真结果。

2. 编写8位加法器的Test Bench程序

第一个文件,源程序:

//-----------------------------------------------------

// DesignName : counter8

// FileName   : counter8.v

//Function    : 8 bits counter with asyncclear and sync load

//Coder       : Cheng xu

//-----------------------------------------------------

modulecounter8(

clk                                    ,

aclr                        ,

load                      ,

load_din             ,

dout

);

   

// Portdeclarations

input                               clk                          ;

input                               aclr              ;

input                               load            ;

input          [7:0] load_din   ;

   

output       [7:0] dout           ;

//InternalVariables

wire                      clk                          ;

wire                      aclr              ;

wire                      load            ;

wire [7:0] load_din   ;

wire [7:0] dout           ;

   

reg              [7:0] counter = 0        ;

   

//CodeStarts Here

always @(posedge clk or negedge aclr)

if(!aclr)

          counter <= 0;

else if(load == 1)

          counter <= load_din;

else

          counter <= counter + 1;

   

assigndout = counter;

   

endmodule

   

第二个文件,Test Bench仿真测试程序:

//test_counter8.v

`timescale1ns/1ns    //注意最前面的符号是数字键"1"左边的那个符号,不是单引号

moduletest_counter8;

reg                                   clk                          ;

reg                                   aclr              ;

reg                                   load            ;

reg              [7:0] load_din   ;

wire [7:0] dout           ;

initial

begin

          clk = 0;

          aclr = 1;

          load = 0;

          load_din = 0;

#120 aclr = 0;

#40             aclr = 1;

#20             load = 1;

          load_din = 100;

#20             load = 0;

#100 $stop;

end

   

always#10 clk = ~clk;

counter8U(

          .clk(clk),

          .aclr(aclr),

          .load(load),

          .load_din(load_din),

          .dout(dout)

          );

   

endmodule

八位加法器仿真波形图

    现在就对着这个图,来看看我们编写的test_counter8.v文件是不是按照我们的设计要求的那样来工作的:

①和组合逻辑的设计一样,我们要在test_counter8.v中例化被测元件counter8,把八位加法器元件嵌入到test_counter8.v这个Test Bench中来。

②和组合逻辑不同的是,我们要利用always #10 clk = ~clk; 这个语句来产生周期为20个时间基准单位(1ns)的时钟(方波),即就是20ns的时钟信号。注意:时钟只能用always块才能生成,但要在initial块中赋给时钟的初始值(如clk=0clk=1),如果不设置时钟初始值,则在仿真的时钟输出端是一个未知x(不变,就是例1中的那段红线了)。

③在initial块中生成复位信号和加载信号,注意:一定要给复位信号和加载信号赋给初始值,否则和不设置时钟初始值一样会出现问题的。

④在initial块的begin语句一开始就设置相关的初始值是一个好习惯。

test_counter8.v进行全部的对比后发现和该仿真波形完全一致,仿真结束。

至此,第一讲全部内容结束,主要是讲了Test Bench程序的编写方法,下一讲我们将介绍ModelSim软件的使用方法。

 

ModelSim仿真入门之二:功能仿真

本实验的目的就是在ModelSim环境下学习掌握该软件的一般仿真测试流程和仿真测试方法,另外学习编写简单的Test Bench程序并在ModelSim下进行调试。

实验步骤如下:

1.       打开ModelSim软件,如图1所示:

打开软件

2. 软件的启动画面如图2所示,进入界面后如图3所示:

软件的启动画面

软件进入后的画面

注意:如果是第一次使用软件,进入后会有一些诸如软件的欢迎画面等不相关的对话框,无须担心,直接关闭即可,亦可选择下次登陆时不显示。

3. 进入ModelSim主窗口后,选择File菜单下的"New→Project",新建一个工程,在弹出的对话框中,给该工程命名并指定一个存放的路径,如图4所示:

新建工程

在这里,工程名和你的顶层文件名保持一致是推荐的做法。路径的注意事项已经说过,这里不再提及。默认的库名就是"work",这个无需更改,点击"OK"即可。

4. 之后会弹出如图5的对话框,选择是新建一个文件还是添加已存在的文件,这两个都可以选择,假如事先编好了文件,就选择添加进来,假如没有就新建。在这里使用添加已有文件,在软件开始之前就编好所用的程序,这样比较方便些。软件自带的编辑环境不是很好,使用第三方的编辑工具是推荐的方法。建议使用UltraEditNotepad++这些专业的代码编辑软件。

UltraEdit偏重于功能的强大和丰富的用户可定制化特性,而Notepad++更加注重易用性。两者在普通功能上差异不是特别大,根据自己的喜好选择一款即可。

给工程中添加文件

在路径G:\FPGA_Project\ModelSim\counter8下新建两个文件,一个是counter8.v,一个是test_counter8.v,前者是我们的原始的设计文件,后者是其相应的仿真测试文件。在这个路径的Windows目录下,在空白处右键选择新建一个文本文档.TXT格式,然后在这个文件上右键选择UltraEditEdit with Notepad++就可以启动相应的代码编辑工具进行编辑了,保存的时候注意存成".v"".vhd"格式即可。

以下给出两个文件的代码:

第一个文件:

//-----------------------------------------------------

// DesignName : counter8

// FileName   : counter8.v

//Function    : 8 bits counter with asyncclear and sync load

//Coder       : Cheng xu

//-----------------------------------------------------

modulecounter8(

clk                                    ,

aclr                        ,

load                      ,

load_din             ,

dout

);

   

// Portdeclarations

input                               clk                          ;

input                               aclr              ;

input                               load            ;

input          [7:0] load_din   ;

   

output       [7:0] dout           ;

   

//InternalVariables

wire                      clk                          ;

wire                      aclr              ;

wire                      load            ;

wire [7:0] load_din   ;

wire [7:0] dout           ;

   

reg              [7:0] counter = 0        ;

   

//CodeStarts Here

always @(posedge clk or negedge aclr)

if(!aclr)

          counter <= 0;

else if(load == 1)

          counter <= load_din;

else

          counter <= counter + 1;

   

assigndout = counter;

   

endmodule

第二个文件:

//test_counter8.v

`timescale1ns/1ns                                               //注意最前面的符号是数字键"1"左边的//那个符号,不是单引号

moduletest_counter8;

reg                                   clk                          ;

reg                                   aclr              ;

reg                                   load            ;

reg              [7:0] load_din   ;

wire     [7:0]  dout           ;

   

initial

begin

          clk = 0;

          aclr = 1;

          load = 0;

          load_din = 0;

#120 aclr = 0;

#40             aclr = 1;

#20             load = 1;

          load_din = 100;

#20             load = 0;

#100 $stop;                      //可以不添加这个仿真结束的系统任务

end

always#10 clk = ~clk;

   

counter8U(

          .clk(clk),

          .aclr(aclr),

          .load(load),

          .load_din(load_din),

          .dout(dout)

          );

   

endmodule

这样,我们就在该工程路径下建立好了这两个文件。当然新建这两个文件的的工作可以是放在我们这个全部的工作开始之前进行的,无需等到第4个步骤开始的时候再进行。

5. 把刚才新建的文件添加到工程中去,点击"AddExisting Flie"后出现如下画面,如图6所示:

 

添加原始的待测试程序文件

   

点击"OK"后,继续添加另外一个测试文件,如图7所示:

添加仿真测试文件

之后点"OK",再关闭"Add items to the Project"这个对话框。最简单的办法是一次同时添加两个文件,点击"Browse"之后,鼠标直接框选这两个文件,这样可以一次添加多个文件到ModelSim工程中。

6. 我们在软件的Project区域已经能看到我们添加的这两个文件了,如图8所示:

 

8 Project区域状态

   

我们下面就可以编译这两个文件了,这时候因为还没有编译文件,所以Status一栏显示的是两个问号。接着在这个Project区域单击鼠标右键,选择"Compile→Compile All",把HDL源文件编译到当前工程的工作库当中去。如图9所示:

编译源文件和仿真测试文件

我们在软件下方的Transcript区域中假如看到如图10的字样,就说明编译通过了:

10 编译成功画面

注意中间的两个successful说明成功了。另外,我们在Project区域中的Status一栏中能够看见两个绿色的勾,这也是一种编译成功的提示。

7. 编译通过之后,在Project区域鼠标右键点击"Add to Project → Simulation Configuration",如图11所示:

11 添加Simulation Configuration

在出现的Add SimulationConfiguration对话框的右下角打开OptimizationOptions,打开后切换到Options选项卡页面,在Optimization Level中选择Disable Optimizations,如图12所示:

12 关闭优化选项

点击OK确定之后返回Add Simulation Configuration对话框,在Optimization栏中关闭Enable Optimization,再展开work目录,选中Test Bench文件test_counter8,之后save保存。如图13所示:

13 关闭优化选项

此时会在Project区域出现一个仿真配置文件:Simulation 1,双击它就能进入仿真了,在重启ModelSim之后,还可以双击它进入仿真,比较方便。

注意:如果不关闭优化选项的话,有时候ModelSim软件会报错导致不能正常进行仿真。

8. 双击"Simulation 1"后进入仿真波形界面,在Object区域鼠标右键选择"Add → To Wave → Signals inRegion",把待仿真的信号添加入Wave窗口。如图14所示:

14 待仿真的信号添加入Wave窗口

9. 接着我们把wave窗口中的两个信号量改成无符号数显示,方便我们观察,在load_dindout上依次单击鼠标右键,按照图15的方法修改即可:

 

ModelSim仿真入门之三:时序仿真

正如前面第二讲所述,时序仿真在实际应用中使用的并不多,但是为了保持仿真系列文档的完整性,我们还是把仿真的方法写出来。

时序仿真就要比第二讲的功能仿真步骤上要多一些,本讲以目前的QuartusII12.0SP2版本和Cyclone IVEP4CE6F17C8为例,讲解下时序仿真的方法和步骤。

时序仿真需要的文件总共有以下几种:

①综合后生成的网表文件" * .vo "(假如在Setting里面设置里输出语言为VHDL的话,则生成的网表文件为" * .vho")

②综合后生成的具有工程延时信息的文件" * .sdo "(VHDL语言亦为此)

 Test Bench程序文件

 Altera的元器件库

大致的过程就是先在Quartus II中生成网表文件和时延文件,然后调用ModelSim进行仿真,具体的时序仿真步骤如下:

1. 打开Quartus II软件,新建工程,再新建文件counter8.v,把上一讲中的counter8.v这个源文件复制到Quartus II的工程目录中,并添加该文件到工程中。接着,选择"Settings"→"EDA Tool Settings",选择左栏的"Simulation",设置情况如图1所示。

第一栏的"Tool name"选择ModelSim-Altera

第二栏的"Format for output netlist"选择自己熟悉的语言,VHDLVerilog都可以,后面的"output directory"是选择输出的网表文件和延时信息文件的存放路径,一般选择默认即可,这样的话,将来编译成功后,会在Quartus II的工程文件夹(本例为counter8这个文件夹)下面生成一个simulation/modelsim的文件夹,里面存有将来要用到的.vo.sdo这两个文件。

再往下,看到有"More EDA Netlist WritterSettings…"按钮,点击后进入设置画面,设置情况如图2所示。注意的地方就是Generatenetlist for functional simulation这一项后面是处于OFF的关闭状态,这样才能生成我们所要的时序仿真文件。

1 simulation的设置

2 More EDA Netlist WritterSettings的设置

都设置好了以后,全部点击"OK"后退出设置,在QII的编译环境下执行全编译。编译中的情况如图3所示:

编译中的情况

注意:下面比我们平时进行的全编译时多了一项"EDANetlist Writer",图3的红色箭头指向的位置。

2. 找到新建工程目录所在的文件夹,在里面找到simulation/modelsim这个文件夹,会发现文件夹内有10个文件,如图4所示:

生成的10个文件

注意:counter8.vocounter8_v.sdo就是时序仿真需要的两个重要的网表文件,它们与counter8_8_1200mv_85c_slow.vocounter8_8_1200mv_85c_v_slow.sdo只是命名不同而已,文件的内容其实是一样的。后两个是QuartusII目前新的网表文件的命名方法,文件名标示出了速度等级(-8)、内核电压(1200mv)、温度条件(85℃)以及时序模型(slow)。

之所以Altera还没有取消旧的命名文件方法并让QuartusII继续生成这两个网表文件,是因为有TclScript文件是按照旧的命名方法写的,需要兼容它们。

以下时序仿真以counter8.vocounter8_v.sdo为例,如果需要用fast时序模型做仿真,也是按照下面的方法进行,只是把vosdo文件换为fast

另外".xrf"和".sft"这两个文件,是QuartusII编译生成的一些相关的信息文件,时序仿真用不到。

3. 打开ModelSim软件,新建一个工程,如图5所示:

新建工程并指定路径

①接着把刚才生成的counter8.vocounter8_v.sdo两个文件拷贝到现在个仿真工程的目录下面。

②之后还要拷贝一个很重要的文件,到QuartusII的安装目录下: \quartus\eda\sim_lib,找到cycloneive_atoms.v这个文件,这个是Altera器件库的库文件,进行时序仿真就是基于这个库文件的,把它也拷贝到仿真工程目录。

注意:我们是以Cyclone IVEP4CE6F17C8为例的,所以这里需要复制的就是cycloneive这个库文件,如果是其它器件的话,需要再对应选择。

③把test_counter8.v文件拷贝到这个仿真工程目录下面。

④在QII安装目录的..altera\12.0\quartus\eda\fv_lib\verilog,把这里面的dffeas.vdffep.v文件拷贝到这个仿真工程目录下面。

4. 进行完上面的步骤后,返回到ModelSim这个软件界面,会发现软件还停留在刚才新建工程,需要我们为其工程添加文件的对话框,那我们就添加文件,把"counter8.vo"、"cycloneive_atoms.v"、"test_counter8.v"、"dffeas.v"和"dffep.v"这5个文件添加进去,如图6所示:

添加的5个文件

注意:此时不需要添加counter8.v这个文件了,.vo文件可以替代它。

5. 之后关闭添加文件对话框,可以看见Project区域有了我们添加的5个文件了,在该区域点右键,"Compile"→"Compile All"执行全部编译。

6. Project区域点右键,"Add to Project"→"Simulation Configuration"添加一个仿真配置的设置,这时会直接弹出添加仿真配置对话框,这里,我们要进行如下的设置:

①在"Design"选项卡下展开work前面的"+"号后点选test_counter8,这个就是Test Bench文件。如图7所示:

7 Design选项卡的设置

②再切换到"SDF"选项卡,点击"Add"添加".sdo"文件,点击浏览后会直接出现这个".sdo"文件的,选择即可,在下面的"Apply to Region"内输入"U",这个就是我们的Test Bench程序中例化顶层文件的例化名字。如图8所示:

8 SDF选项卡的设置

接着把下面的两个SDF选项的复选框都选中。如图9所示:

选中SDF选项的两个复选框

点击"OK"退出配置设置界面。配置好了以后的Project区域的内容如图10所示:

10 Project区域的内容

7. 双击Simulation执行仿真,后面的步骤和功能仿真一样的了,不再赘述。仿真的波形图如图11所示:

11 时序仿真的波形图

从图11中可以看到dout相对于主时钟clk有明显的延时,这个延时大小与当前使用的器件的时序模型有关。

在具体实践过程中,可能还会遇到各种各样的问题,ModelSim正常运行也依赖于仿真库文件的齐备,所以碰到某些工程在仿真中遇到报错的情况时,不妨检查下ModelSim的提示信息,看看是否有仿真所必需的库文件没有添加进来。

ModelSim仿真入门 精讲