Bootstrap

简易CPU设计入门:ModelSim的使用方法介绍(一)

在这里,我所讲解的ModelSim的使用知识,是指,你在首先进行了ModelSim与Quartus的关联以后,通过Quartus来启动ModelSim以后的基本使用方法。

也就是,使用之前,你需要首先已经准备好了一个Quartus项目,代码都编译好了,Test Bench文件你也写好了,并且仿真平台的设置,你也都进行好了。在这些个工作都准备好了以后,你在Quartus里面启动了rtl仿真,启动了ModelSim软件。在这种情况下,我们来谈ModelSim的基本使用方法。

当然了,其实我们也可以不借助于Quartus来编译项目代码,直接在ModelSim来编译代码也可以。不过,在这里,我还是只讲解借助于Quartus来编译好代码之后,来使用ModelSim的情况。

对于本部分知识的讲解,主要地,还是考虑到初学者的情况。如果你是一个初学者,那么,有可能,你对于许多的软件的使用,还并不熟悉。假如你不是初学者,你有过FPGA的学习经验,本部分,可以作为一点复习材料,你可以粗略地看一看。

接下来,我们开始讲解内容了。

一.   准备工作

所谓的准备工作,它指的是,你首先要准备好一个现有的工程。你当然可以自己写一个工程。不过,作为一篇教程,我还是要给出一个统一的,编写好了的工程,让大家在统一的项目代码里面,看我的讲解。

我在上一节,讲解了如何来打开一个现有的项目代码,也讲解了本专栏CPU代码的基本的框架结构。那么,我们就使用上一节的项目代码就可以了。

如何来下载项目代码,如何来打开现有的项目,我在上一节已经是作出了讲解。

在这里,我将上一节的链接贴在下面,大家直接点过去就好了。

简易CPU设计入门:打开一个现有的项目

成功地打开了一个项目以后,我们开始接下来的内容的学习。

二.   点击仿真命令,开启仿真

在Quartus软件里面,依次点击【Tools】,【Run Simulation Tool】,【RTL Simulation】命令,过程如下图所示。

图1
图2
图3

这样一来呢,我们就通过Quartus软件,开启了ModelSim仿真。想要在点击了相应的菜单命令以后,启动ModelSim的仿真,你需要首先设置好了Quartus与ModelSim的关联。如何来设置这个关联,你可以参考着我的下面的链接所示的博客。

设置Quartus与ModelSIm的关联

想要运行一个仿真,除了需要设置好Quartus与ModelSim的关联之外,我们也需要准备好Test Bench文件与设置好仿真参数。

接下来,我们来看一看我们的Test Bench文件。

Test Bench文件,我将其放在了【cpu_me01/test_bench】路径里面,测试块的文件名为【tb_cpu_me01.v】。我们用notepad++来打开它,对于这里的代码,如果你还只是初学者,简易你暂时地只是粗略地浏览一下子就好,先不要细究。里面的内容,以后我会慢慢地来讲。代码如下所示。

`timescale 1ns/1ns
module tb_cpu_top();
reg sys_clk;
reg sys_rst_n;
wire [15:0] test_ip;
wire [15:0] test_instruct;

wire [15:0] inner_reg0;
wire [15:0] reg_da;
wire [15:0] test_data_bus;
wire [15:0] test_data_inner;
wire [9:0] cnt;
wire busy_flag;
wire work_ok;
wire [5:0] state;
wire [15:0] ram_data_buf;

assign inner_reg0 = cpu_top_inst.ctrl_center_inst.inner_reg[0];
assign reg_da = cpu_top_inst.ctrl_center_inst.rw_reg_inst.reg_00.data_reg;
assign test_data_bus = cpu_top_inst.ctrl_center_inst.data_bus;
assign test_data_inner = cpu_top_inst.ctrl_center_inst.rw_reg_inst.reg_00.data_sig_inner;
assign cnt = cpu_top_inst.ctrl_center_inst.instruct_unit_inst.sdal_inst.cnt;
assign busy_flag = cpu_top_inst.ctrl_center_inst.instruct_unit_inst.sdal_inst.busy_flag;
assign work_ok = cpu_top_inst.ctrl_center_inst.work_ok;
assign state = cpu_top_inst.ctrl_center_inst.instruct_unit_inst.sdal_inst.state;
assign ram_data_buf = cpu_top_inst.ctrl_center_inst.rw_ram_inst.data_buf;

initial
begin
	sys_clk <= 1'b1;
	sys_rst_n <= 1'b0;
#60
	sys_rst_n <= 1'b1;
end

always #10 sys_clk = ~sys_clk;

cpu_top cpu_top_inst
(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.test_ip(test_ip),
	.test_instruct(test_instruct)
);

endmodule

我们先来看代码的前几行和最下面的几行。

图4

图5

图5的38到44行 ,是对CPU顶层模块的实例化代码。本CPU项目代码的顶层模块名,为【cpu_top】,在测试块里面,将这一顶层模块的实例名字,命名为【cpu_top_inst】。

在cpu顶层模块中,我设置了端口。在图5的40行到43行里面,可以大致地看到这四个端口的名字,它们分别为【sys_clk】,【sys_rst_n】,【test_ip】和【test_instruct】。

在图4的3到6行,我们可以看到这四个端口的位宽与类型信息。sys_clk和sys_rst_n为输入端口。这是因为,一般来讲,设计块里面的输入端口,到了激励块里面,它会是reg型变量。惯例就是这样的。

而对于【test_ip】和【test_instruct】,在激励块里面,它成为了wire型,且位宽都是16位的。一般地,激励块里面的wire型变量,对应到设计块里面,它会是一个输出端口。

这样一来,我们一般的处理方式为,将设计块里面输入端口拷贝到激励块的时候,将input类型修改为reg类型。而设计块里面的output类型,我们将其拷贝到激励块的时候,需要将其设置为wire类型。

如果设计块里面的某一个端口为inout类型的,也就是输入输出两用的,我们将其拷贝到了激励块里面,要如何来做呢?我认为,可以在激励块里面将其设置为wire类型的。

图4的3行到6行的代码,分别是cpu_top模块的输入激励信号与输出观测信号。本测试块的【sys_clk】和【sys_rst_n】变量,用于给cpu_top模块提供输入时钟信号与输入复位信号。复位信号的名字为【sys_rst_n】,之所以还有一个n,是因为,我们将复位信号设置为低电平有效。低电平才会复位,高电平不复位。

除了图4与图5所示的代码之外,还有其他的很多行代码。那些个代码呢,我这里就先不展开了,大家可以只粗略地浏览一下就可以了。暂时地,我们先不去深究这些个代码。

接下来,我们再来看一看cpu_top模块的端口声明的语句。

cpu_top模块,位于【cpu_me01/code】里面的【cpu_top.v】文件里面。我们来看一看。

图6

在图6的3到7行,是cpu_top模块的端口声明部分。大家可以将其与图4的3到6行作一下对比。

接下来呢,我们再来看一看,我是如何来设置仿真的参数的。

在Quartus软件里面,我们依次点击【Assignments】,【Settings】菜单命令,打开【Settings】对话框,过程如下图所示。

图7
图8

打开了【Settings】对话框以后,你的界面,有可能像我这样子,已经是停在了Simulation选项里面。如果,不是停留在【Simulation】选项上,我们需要再【Settings】对话框里面,点击左侧的【Simulation】按钮,切换到【Simulation】选项的界面里。

在【Simulation】选项的界面里,有一个【Tool name】一栏,我们将其设置为如下所示的结果。

图9

然后呢,请你找到下方的图10所示的区域。

图10

找到了以后,首先点击选择左侧的红色框线所示的单选按钮。然后呢,你需要再去点击右侧的红色框线所示的【Test Benches】按钮,打开【Test Benches】对话框,如下图所示。

图11

从图11来看,下方的表格里面,【Name】一列下面有两个,分别是【tb_ctrl_center】和【tb_cpu_top】。这两行,分别对应着某一个test bench模块。

在我这里,其实tb_ctrl_center测试块,我都忘记了它是否还能用了。因为,我是先完成的tb_ctrl_center测试块的,在此之后,我才是完成的tb_cpu_top测试块的。完成了前者以后,在进行后者的代码调试的过程中,我又修改了设计块中的一些个代码。所以,有可能,tb_ctrl_center已经是不能用了。在我这里,之所以保留着它,那是因为,我写完了整个的cpu代码以后,还没有细致地去整理着。有点偷懒了,不太乐意做这种整理工作。

在图11所示的界面里,Name一栏的tb_cpu_top是能用的。我们点击【tb_cpu_top】一行,然后点击右侧的【Edit】按钮,打开【Edit Test Bench Settings】对话框,过程如下图所示。

图12
图13

在图13所示的对话框里面,我们要关注一下以下几个地方。

图14

在图13所示的对话框中的图14所示区域的红色框线里面,分别指示了测试文件名和测试文件里面的顶层模块名。

图15

在图13所示对话框的图15所示的区域,我们是设置了仿真的持续时间,为10us。

10微妙,对于生活中的时间来讲,它并不长。而对于仿真来讲,它不短了。

在基础的FPGA学习里面,可能我们只需要将仿真时间设置为500ns,就已经是很长了。设置为1us,那就更长一些了。在我这里,我将其设置为10us,其实已经是不短了。

在图13所示的对话框里,我们要了解的信息,也就这么多了。接下来,请大家关闭Quartus中的相关的对话框,只保留Quartus软件本身是打开着的就好了。

接下来,我们来学习ModelSim的一些个使用后方法。

三.   ModelSim的基本使用方法。

(一)找到波形图的界面

我们回到ModelSim软件里面。如果,你通过Quartus软件启动了ModelSim以后,你的界面下方是多个选项卡,如下图所示。

图16

那么,接下来,你在图16所示的这些个选项卡之中,点击【Wave】选项卡,切换到【Wave】选项卡的界面中去,结果如下图所示。

图17

如果,你启动了ModelSim以后,你的界面如下图所示。

图18

并且呢,在图18里面,还有着下图所示的区域。

图19

那么,就请你点击图19中的红色框线所示的【+】按钮,然后呢,你也会切换到图17所示的波形界面中。

然后呢,我们在波形界面中,来开始各个部分的功能讲解。

(二)仿真运行控制类按钮

首先呢,请大家将界面调整到图17所示的模样。也就是,在软件的下方有多个选项卡,且我们自己用鼠标左键点击选择了【Wave】选项卡。

在这样的基调之下,我们来讲解ModelSim的第一组按钮 。

图20

请大家自行在软件里面查找上图所示的几个按钮。

从左边数,第1个按钮,它是【Restart】按钮。为什么是【Restart】按钮呢?因为当我们将鼠标指针停在这个按钮上面的时候,略等一会儿,下面就会出现一个提示框 ,并且显示【Restart】字样。我就不截图了,大家自己去试就好了。右边的剩余的按钮的名称,我们都可以通过将鼠标指针停在上面,并且略等片刻,而观察出现的提示框的文字,而了解到按钮的名称。

第1个按钮,它是复位按钮。点击它以后,出现如下所示的对话框。

图21

当我们修改代码编译成功了以后,我们不需要重新打开仿真界面,而是可以直接点击【Restart】按钮,将上面的对话框中的复选框全部勾选后,点击下面的【OK】按钮,就可以重新开始仿真运行。

白色的输入框加上右边的上下指向的三角号,我们将其视为第2个按钮。第2个按钮,叫做【Run Length】按钮。这个按钮,它可以用来编辑单次仿真运行的时间。当使用单次仿真运行的功能时,需要由本按钮和本按钮右边的按钮搭配起来使用。在我们的截图里面,第2个按钮里面显示的是100 ps,也就是100皮秒。你也可以将其设置为100 ns,30 us, 90 ms, 9s等等的时间值。在白色输入框里面,你可以编辑时间数值与单位。而右边的上下指向的两个三角号,点击它们,可以修改时间数值,每次增加或者减少一些个时间值。向上的三角号用于增加时间数值,向下的三角号用来减少时间数值。

第3个按钮,是【Run】按钮。这个按钮,可以用于单次仿真运行。使用它,需要和第2个按钮搭配使用。在第2个按钮设置为100ps的情况下,我们第一次单击【Run】按钮,则ModelSim会仿真运行100 ps。再按一次,则ModelSim会再次仿真运行100 ps。第1次点击了【Run】按钮以后,则仿真软件会运行100 ps,我们可以通过其他的波形观察按钮,观察到0到100 ps的波形图变化情况。第2次点击了【Run】按钮以后,则仿真软件会再次运行100 ps。此时,加上第1次的100 ps,我们就一共是运行了200 ps。此时,我们可以通过后面讲到的一些个波形观察按钮,观测到0到200 ps的波形图。

第4个按钮,是【Continue Run】按钮。点击它,可以让仿真连续运行下去。直到点击了第5个按钮——【Break】按钮,或者第6个按钮——【Stop】按钮以后,仿真会暂停或停止。

第5个按钮,是【Run -All】按钮,点击【Restart】按钮后,再点击【Run -All】按钮,仿真会一直运行,直到点击了第6个按钮——【Break】按钮,或者第7个按钮——【Stop】按钮以后,仿真会暂停或停止。

第4个和第5个按钮都可以让仿真连续运行。而第4个按钮,它可以用于,在我们点击了第6个按钮——【Break】按钮以后,想要继续运行仿真,则可以点击第4个按钮,让仿真继续运行下去。而第5个按钮,常常用于,在我们点击了【Restart】按钮以后,这个时候我们再去点击第5个按钮——【Run -All】按钮。

第6个按钮,是【Break】按钮,用于暂停当前编译或仿真。

第7个按钮,是【Stop】按钮,在下一步或下一时间之前停止仿真。

以上几个按钮,在我们的这个课程里面,其实基本上不会用到。既然用不到,为何又要讲呢?因为书到用时方恨少,多储备一些,以备不时之需,还是好的。

这些个按钮,它们的功能,以及我们后面要去介绍的许多的功能按钮,其实它们就和编写C语言时候的调试工作的作用差不多。我们在学习C语言的时候,我们可以用集成开发环境或者调试器中的调试功能,对程序的运行进行观察。

通过调试,我们可以了解程序运行的过程,了解运行机制。我们也可以在程序出了问题的时候,通过调试,来辅助我们找出程序代码中的错误。

而ModelSim中的许多的功能按钮,是用来让我们观察我们设计的硬件电路的运行情况的。它可以起到学习功能,也可以帮助我们排查错误。

对于一个程序员来讲,不论是软件程序员,还是硬件程序员,调试,都是我们需要去锤炼的一项技能。因为,很多的代码,我们往往不能一次编写通过,会有各种各样的错误。尤其是当代码量很大的时候,就更容易出现各种各样的错误了。这个时候,调试代码,就成为很重要的一项技能了。

关于调试,这东西,应该是需要我们去逐步地积累经验,然而慢慢地来掌握技巧。

调试的过程,有点像推理破案。慢慢学吧。如果你目前还不太会的话,那么,我可以跟你说,我也是从你的这种不太会调试的阶段走过来的。

好了,接下来,我们来看看下一组的按钮。

(三)放大缩小类按钮

图22

如图22所示,我们从左边数,介绍左边的4个按钮。右边的两个按钮,我平时不用,目前我也不会用右边的两个按钮。

第1个按钮,它的形状是一个放大镜里面含有一个加号,这个是放大按钮。它的功能,是可以放大显示。这个按钮,我不熟,不太去用。

第2个按钮,是一个放大镜里面有一个减号。它是缩小按钮,可以缩小显示。

第3个按钮,是全局显示功能。

第4个按钮,它也是一个放大按钮。只不过,它是将我们所标记的位置进行放大显示。

对于第2个到第4个按钮,我来略微演示一下它们的使用方法。

我们从Quartus里面启动了ModelSim以后,我们通过一番操作,来到图17所示的界面。在那个界面里,我们看到,各个信号都是一些个平直的东西,我们还看不到有高低电平的变化。

接下来,请大家点击一下第3个按钮,如下图所示。

图23
图24

如图24所示,这样的话,我们就看到波形的变化了,有高低电平,也有向量类型的变量的值的变化。大家在图中,还能够看到蓝色的部分。某一条信号线,如果它有蓝色的部分,则蓝色的部分,代表着高阻态。以后,大家去做别的项目的时候,还可能会看到红色的信号线部分。红色部分,代表着的是Verilog中的未定值x。

何时会出现未定值x呢?

假定,A模块有一个输入信号【in_sig】,然后呢,我们在编写此模块的test bench的时候,我们给A模块的【in_sig】信号,连接了两个驱动信号,一个是【drive_01】,一个是【drive_02】。一个【in_sig】信号,被两个驱动源给驱动着,那么,你说,in_sig到底是应该与【drive_01】保持一致呢?还是应该与【drive_02】保持一致呢?在这种情况下,【drive_01】和【drive_02】在驱动A模块的【in_sig】的时候,就产生了竞争。由于产生了竞争,因而导致了【in_sig】信号处于未定值x的状态。

高阻态何时回出现呢?在这里,我先来将一个情况。

正常来讲,一个wire型变量是需要驱动源的。如果,某一个wire型变量,我们声明了它,却没有给它设置驱动源,则这个wire型变量的值,就会呈现高阻态。其余的高阻态的情形,我们以后遇到了再说。

高阻态,在我们的这个项目里面,其实还是很重要的。正是借助于高阻态,我们实现了总线的数值传递机制。

我们已经是点击完了第3个按钮,实现了全局显示的功能,将所有的波形给显示了出来。然而,我们此时所看到的波形,还太小,往往是有许多的细节是看不清的。

图25

在图25所示的区域,有许多的毛刺信号。有些国外教材的中文翻译版,也将毛刺信号称作尖峰信号。在图25出现毛刺信号,并不是说,这些个信号真的是毛刺,只不过,由于我们是全局显示,所以很多的细节未能够完整显示,所以才有了这些个所谓的毛刺。

那么,接下来,请大家将鼠标指针移动到某一个毛刺的旁边,然后呢,点击鼠标左键,结果如下图所示。

图26

出现了一个黄色的粗一些的竖线。这个黄色的粗竖线,就是用来标记位置的标记线我们可以通过这条标记线,来标记一个位置。

在某一个毛刺信号旁边点击了鼠标左键,设置好了黄色竖线位置标记以后,我们点击几次下图的红色框线所示的,第四个按钮,过程如下所示。

图27
图28

如图28所示,此时画面的最上方的波形,是一行方波信号,且间隔相等。这一行信号是时钟信号。而红色框线所示的位置,有一个高电平信号。这个高电平的时间长度,为一个时钟周期。图28中的一个时钟周期的高电平信号,便是图26中的看上去像毛刺的信号。

接下来,我们再将鼠标指针移动到红色框线所示的高电平信号的中间,然后点击鼠标左键,以便在高电平信号的中间设置标记。设置完了标记以后,我们再点击几次图27的红色框线所示的按钮。过程如下图所示。

图29
图30

通过这样的操作,大家应该就能够明白图27中的红色框线所示的按钮的作用了。

在操作的过程中,我们还可以发现以下特点。

第一点,点击完了图27的红色框线所示的按钮以后,黄色竖线会位于波形图区域的中间位置。我这里的截图可能没有将黄色竖线放在正中间,但是大家的软件里面,黄色竖线应该是位于波形区域的正中间的。

第二点,图27的红色框线所示的按钮,它的作用,是将黄色竖线所标记的位置作为中心,将这个中心的左右两侧,作放大显示,并显示在波形图区域的中央。

好玩吧?

说完了放大,再来说说图23中的第2个按钮,缩小按钮。它的作用,与第4个按钮的作用相反,是用来将波形图缩小显示的。

如果我们是用图27中的第4个按钮来进行放大显示的话,那么,操作完了以后,黄色竖线会位于波形图区域的中间,这个时候,我们点击缩小按钮,则缩小之后的波形图里面,黄色竖线仍然会位于波形图区域的中间。

对于缩小按钮,我就先说这些了。大家也可以自己去探索它的一点使用方法。

结束语

本节内容,到了这里,已经是字数不少了。关于ModelSIm的使用,还有很多的需要去讲解的地方。我们下一节会接着讲的。

本节,先到这里。

;