加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

从电路到verilog | 欲要系统能跑起,仿真验证是真谛

2016/08/23
14
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

 

数字逻辑系统的设计实际上包含两个相关又独立的领域:设计与测试。这套书重点是设计,因为老衲对于测试不在行,所谓“藏拙”者也。但是完全不介绍测试也不成:这样设计出来的代码不知道对错了。所以,今晚给大伙儿讲点皮毛。


但凡人造系统都是有输入输出的,要不然这个系统对设计者或者用户没有用场,没人无聊去设计 ---- 即使是鲁布·戈德堡机械 。前面介绍的数字逻辑电路是通过芯片的管脚实现输入输出的,无论进出的信号都是数字信号。仿真代码理论上实在计算机上面运行的,和程序语言类似;所以,仿真部分的输入输出也和程序语言类似,是各种计算机的输入输出设备,包括文件。


1. 时钟复位,自己编程
当设计的单元简单、输入输出关系明确的时候,可以由设计者直接设计测试向量完成验证。这也是大多数资料里面介绍的方法,其中内容不必详述。比如,你做一个计数器,然后你做一下 reset 和 clock 信号,于是在 reset 不生效之后,输出就是 1-2-3…了,OK,恭喜啊,你得设计正确。再如,你设计一个加法器,你就需要做不同的加数和被加数的组合,如果得到 0 + 1 = 1,1 + 1 =2, 1 + 2= 3…那么就可以交货了。


时钟信号无疑是最重要,但是也是最简单的信号之一了。50%占空比的时钟信号的代码见例 8.19。其他占空比的时钟稍稍麻烦一些,forever 块需要两个时延和两次赋值。实际上,占空比并不会影响数字逻辑系统的性能,所以尽量用 50%占空比的时钟就好了。


【例 8.19】时钟生成部分代码

parameter DELAY;

reg clk;

begin
//Clock generation
    initial
    begin
      clk = 0;
      //Reset
      forever 
      begin
           #DELAY clk = !clk;
           //Reverse the clock in each 10ns
      end
end


据说时钟产生有多种方法的,和“‘回’字的三种写法”道理一样。咱们不是孔乙己类似的腐儒,会一种用熟了就好了。


复位信号也不复杂,开机的复位代码见例 8.20 所示(高电平复位)。其他时刻的复位,就是再外加一个时延的问题,施主们自便。


【例 8.20】开机复位的代码

parameter RESET_PERIOD;

reg clk;

//Reset operation
    initial 
    begin
      reset = 0;
      //Reset enable
      # RESET_PERIOD  reset = 1;
     //Counter starts
    end

 


2. 外部芯片,讨要厂家
当设计了一个对于复杂芯片的接口或者控制器,你就需要外部芯片的行为模型配合你得测试了。举个例子,你需要设计一个 SDRAM 的控制器,用来控制大容量的 SDRAM 来存储数据。首先,你会得到一个对应 RAM 的 datasheet(数据手册),里面会有芯片的电气特征啊什么的,数字逻辑设计不感兴趣的内容。大家需要注意的就是读写的时序,还有 SDRAM 的刷新要求(不懂这个?没关系了,就是一个例子。等到你做的时候,自然就了解了。如果还是不懂,那么等着丢数据吧,别抱怨我没说过)。这个控制器基本就是一个状态机。在设计完之后,需要进行验证的时候,问题来了:“如何测试这种系统呢?”。竖起耳朵听着,找供货商要对应芯片的行为模型,这个可以有的。


事实上,在选择芯片的时候,有没有这种模型是很重要的一个指标,必须向采购要求。叫甲方们在吃回扣之余,一定要选择有行为模型的芯片。要不然,逻辑设计的工作量就是加倍了。而且即使我们按照自己的理解设计了外部芯片的行为,还不能保证我们做的模型是完全正确的。这就陷入了验证 - 再验证的死循环了,命苦啊。


这种验证最简单,除了时钟、复位什么的以外,就需要构建几个线型变量,把我们的设计和片外芯片链接在一起就好了。简单说,就是实例化外部新片、实例化设计的系统加上连线,三步走。


3. 复杂处理,文件读取
在数字信号处理类的系统验证的时候,一般需要算法工程师一起来联合工作。例如:通讯里面的发射机、接收机等。和我们一样辛劳的算法工程师们,需要给我们提供测试向量。这种向量一般是以文件形式提供的,里面有输入和输出数据,一般是十六进制的几列。大家需要利用文件读写函数,读取输入(可能也要输出)数据,输入需要验证的系统,来考察设计是否正确。


这里主要涉及到了 Verilog 语言的文件操作的系统任务,下面给大伙儿做一个介绍。Verilog 语言里面的文件操作基本是照搬 C 语言里面的,有打开文件、读文件、写文件和关闭文件等功能。最后啰嗦一句:全部文件操作都不能综合。常用任务见表 8.10 所示。


表 8.10 常用文件有关的系统任务

功能

格式

打开文件

integer file_id = $fopen ( " file_name " );

integer file_id = $fopen ( " file_name ", type );

关闭文件

$fclose (file_id);

写文件

$fdisplay(file_id , list_of_arguments);

$fwrite(file_id , list_of_arguments);

$fstrobe(file_id , list_of_arguments);

$fmonitor(file_id , list_of_arguments);

写数据的格式

$swrite(output_reg, list_of_arguments);

$sformat(output_reg, format_string, list_of_arguments);

读一个字符

c = $fgetc ( file_id );

读一行

integer code = $fgets ( str, file_id );

按照格式读

integer code = $fscanf ( file_id, format, args );

读二进制文件

integer code = $fread( myreg, file_id);

integer code = $fread( mem, file_id);

integer code = $fread( mem, file_id, start);

integer code = $fread( mem, file_id, start, count);

integer code = $fread( mem, file_id, , count);

文件位置操作

integer pos = $ftell ( fIle_id );

code = $fseek ( file_id, offset, operation );

code = $rewind ( file_id );

文件调入内存

$readmemb ( " file_name " , memory_name  , start_addr  , finish_addr  ) ;

$readmemh ( " file_name " , memory_name  , start_addr  , finish_addr ) ;

文件操作状态

integer errno = $ferror ( file_id, str );


表 8.11 打开文件中的打开方式

字符串

含义

字符串

含义

"r" 或者"rb"

只读

"w" 或者 "wb"

调整长度为 0 或者建立新文件,用于写

"a" 或者 "ab"

读,打开文件在末尾写或者新文件,用于写

"r+","r+b"或者 "rb+"

更新

"w+","w+b"或者 "wb+"

调整长度为 0 或者或者建立新文件,用于写

"a+", "a+b"或者"ab+"

读,打开文件或者新建文件,在文件末尾写


写文件任务中 display、monitor 和 strobe 和显示任务对应功能类似。

 


虽然 Verilog 里面基本继承了有关文件的全部操作,但是一般没人会聪明到用 Verilog 语言来拷贝一个文件什么的,虽然这个真的可以做到。前面说了,这些任务中最常用的是有打开文件、读文件、写文件和关闭文件等功能。


使用 Verilog 语言进行系统仿真或者验证的时候,最常用的文件处理有两种。其一是读取需要输入系统的或者用于系统输出对比用的测试向量,在仿真环境中检验系统设计是否正确;还有就是把系统的输出写入文件里面,用外面的程序调用来测试性能。
读取文件中测试向量数据一般使用 ---- 针对二进制数值以 ASCII 码存储的文本文件的$readmemb,或者针对十六进制数值以 ASCII 码存储的文本文件的$readmemh 。它们针对的文件中数据格式不同,但是基本功能相同。参数中,“file_name”为需要读的文件名;“memory_name”是数值在 Verilog 代码中的存储位置,一般为数组;“start_addr  ”和“finish_addr”为开始读取和结束读取的位置,可以忽略(此时从头到尾地读文件)。


读取的文本文件中,只允许存在空白符号(空格、换行、tab 或者制表符)、注释(两种格式均可)和二进制 / 十六进制数值。


文件中的数据由开始到结束,存储到以“memory_name”命名的存储单元从最小标号开始的顺序空间中。如果存储单元对应的区域不够存储全部数据的时候,系统会给出警告。


4. 更加复杂,外部接口
Verilog 语言的基本语法部分,前面已经竹筒倒豆子地交代给施主们了。在目前介绍过的语言体系下,完成上面所说的完全依赖机器进行的测试和验证,还是有难度的。所以,Verilog 语言标准里面最后面的大约三分之一的章节,都在介绍一种 Verilog 和高级语言(标准里主要指 C 语言)的接口机制 ---- 这个机制就是 PLI。当然靠这里的几页纸的介绍,不可能教会大伙儿如何使用这个高级玩意儿。贫道的目的是叫到家知道 PLI 这回事,万一急需时候不会茫无头绪。


PLI 提供一种接口,将用户编写的 C 或 C++程序连接到 Verilog 仿真器上,实现 Verilog 仿真器的功能扩展和定制。

 


PLI 接口主要提供以下三种功能。


首先, PLI 接口允许用户编写自定义的系统任务与函数。用户写出相应的 PLI 程序并连接到仿真器后,就可以在自己写的代码中使用这些系统任务与函数了。一旦这些在仿真过程中被调用,仿真器就会找到对应的用户编写的 PLI 程序来执行,从而实现仿真 器的定制。


其次,PLI 接口还允许用户在自己的 PLI 程序中与仿真器中实例化的 Verilog 硬件进行交互,比如读一个信号的值,给一组触发器写值,设置一个单元的时延等。对于 PLI 程序而言,仿真器中的 Verilog 实例完全是透明的。在不改变系统结构的前提下。用户想对这些硬件做什么 操作都可以。


最后,某些特定的操作需要对仿真过程中一些信号的变化做出响应。虽然我们可以用 always 来监控少量信号的变化,但如果需要监测大量信号,这种机制并不现实。PLI 接口提供了一种函数回调机制解决这个问题。用户可以将某个信号挂上一个 PLI 程序中的 C 函数,以后每当该 信号变化,这个 C 函数都会被调用,从而很方便地实现信号监测。
除了上面所说的这些机制外,PLI 还能让用户控制仿真的过程,比如暂停,退出,往 log 文件里写信息等。还可以采集仿真过程的数据,比如当前仿真时间等等。实际的 PLI 程序中这些功能同样少不了。


PLI 的典型应用主要包括:
第一, 实现 Verilog 模型和 C 模型的共同仿真。对于比较复杂的系统,开发者经常需要首先制作一个能够工作的 C 模型,然后逐模块地将其改写为 Verilog。这样可以实现一个比较安全、渐进的开发过程。还有一些比较复杂的硬件单元库,尤其是定制的浮点单元库,其仿真模型都是 C 模型。这时也必须 使用 PLI 来连接两种模型。


其次,产生测试激励,产生验证矢量或直接进行验证。这是 PLI 程序最常用也是最主要的功能。比较复杂的系统经常需要根 据上一个激励的响应决定下一个激励,这就要求过程控制做得很好。可是,大家用过 Verilog 的都知道,Verilog 的过程控制非常弱,很难在 测试代码中写出复杂的程序控制。而 C 在这一点上有绝对的优势。因此,使用 PLI 可以取长补短,实现一个高效的仿真系统。


第三,捕获仿真过程和结果,并以用户易于接受的方式输出。举一个典型的例子,打波形就是这种应用。


第四,分析仿真过程,计算性能参数。例如功耗分析的实现方式,就是用 PLI 纪录每个单元的翻转情况,然后根据翻转次数计算功耗。


最后, 软硬件联合仿真。现在,纯以硬件方式工作的芯片已经不多了,大量的芯片工作时都需要软件的参与和控制。离开这些控制软件进行仿真,工作环境不真实,可能造 成激励模式的遗漏。如果不用 PLI,我们就只能用 Verilog 来在测试代码中模拟软件控制,但 Verilog 可能根本描述不了复杂的控制。使 用 PLI 连接控制软件和仿真器,我们就能够模拟实际的芯片工作环境和过程。另外还有一个额外的好处:控制软件本身也能在这个过程中进行调试。


到 1995 年 Verilog 成为 IEEE 标准时,就停止发展了,而 VPI 从此时开始诞生,到现在也还在演化。IEEE Verilog 工作组的本意是从那以后逐步使用 VPI 替代 PLI 1.0,但这个目标到现在也没有实现,而且还看不到实现的希望。


在使用上,PLI 和 VPI 各有特点。PLI 的 API 又多又全又乱,而 VPI 的 API 就非常精炼,一个词,漂亮。常用的 PLI 的函数集中恐怕至少有近百个,写程序时不查手册几乎不可能;而且,很多 API 是重复的。而 VPI 是标准组织制订出来的,而且融入了相当多的面向思想,因此在精炼方面显然远远超过 PLI。整个 VPI 的函数集才二十来个函数。但是,VPI 并不好 学,因为它的结构远比 PLI 复杂。VPI 最大的弱点还是仿真器对其支持不好。很多 03 年出的仿真器都还不敢声称自己很好地支持 VPI。


电子产品有关的市场早就进入了白热化的竞争阶段,一个效果总会有很多不同的方案来竞争。说老实话,Verilog 语言用于测试和验证不是十分方便,所以有了 SystemVerilog 语言来占领这个领域。PLI 在 Verilog 语言里面也是很麻烦的部分,对应 SystemVerilog 里面的 DPI(Direct Programming Interface,直接编程接口)就简单多了。


SystemVerilog 是一种由 Verilog 发展而来的硬件描述、硬件验证统一语言,前一部分基本上是 2005 年版 Verilog 的扩展,而后一部分功能验证特性则是一门面向对象程序设计语言。面向对象特性很好地弥补了传统 Verilog 在芯片验证领域的缺陷,改善了代码可重用性,同时可以让验证工程师在比寄存器传输级更高的抽象级别,以事务而非单个信号作为监测对象,这些都大大提高了验证平台搭建的效率。


SystemVerilog DPI,是 SystemVerilog 与其他外来编程语言的接口。能够使用的语言包括 C 语言、C++、SystemC 等。直接编程接口由两个层次构成:SystemVerilog 层和外来语言层。两个层次相互分离。对于 SystemVerilog 方面,另一边使用的编程语言是透明的,但它并不关注这一点。SystemVerilog 和外来语言的编译器各自并不需要分析另一种语言的代码。由于不触及 SystemVerilog 层,因此支持使用不同的语言。不过,目前 SystemVerilog 仅为 C 语言定义了外来语言层。


凡是 PLI 可以做的事情,DPI 都可以做;凡是 PLI 可以应用的场合,DPI 都可以应用。比较而言,DPI 具有很大的优势:它允许用户调用现有的 C 代码,而不需要 Verilog 语言 PLI 或者 VPI 接口。它还提供了一种替代的、简单的方式调用,虽然不能完成全部 PLI 的功能。但是 DPI 实现了最有用的部分。用 C 实现的函数,在可以使用“引入 DPI”声明后,在 SystemVerilog 代码中调用。只需在 SystemVerilog 里面简单声明,即可使用这些函数作为输入的任务和函数。


看到前面的说法,很多施主会认为 DPI 比起 PLI 来简单。这个结论对也不对。如果您老熟悉 SystemVerilog 这门语言,这句话没错:原来 PLI 里面几十行的代码,在 DPI 里面一般也就只要几行就完成了。但是,如果贵客还没学习过 SystemVerilog 语言,那就要重新学习了。而且,对于设计、验证双肩挑的工程师而言,这还意味着要在两种语言之间不停切换。这个大概相当于双手互博的难度,很考验功力的。


DPI 还有一个问题就是支持的软件有限。例如:Windows 平台下 Modelsim 对 DPI 支持就不好。这个情况也限制了它的推广。


这正是:

凡人总有出错时,代码怎能无问题?欲要系统能跑起,仿真验证是真谛。
测试系统自动机,信号产生不离弃。文件读写取数值,外部模块来联系。

与非网原创内容,谢绝转载!

系列汇总:

之一:温故而知新:从电路里来,到 Verilog 里去!

之二:Verilog 编程无法一蹴而就,语言层次讲究“名正则言顺”

之三:数字逻辑不容小窥,电路门一统江湖

之四:Verilog 语言:还真的是人格分裂的语言

之五:Verilog 不难学,聊聊时序逻辑那些事儿

之六:数字电路设计:有理论、有电路、有代码“三位一体”

之七:熟读语言要素,不会编程也懂 verilog

之八:IP 设计可企及,宏和参数只是为了合并同类模块

相关推荐

电子产业图谱

本名:吴涛,通信专业博士,毕业后十多年从事无线通讯产品的研发工作。了解W-CDMA、TDS-CDMA和LTE的标准协议、接收机算法以及系统架构和开发。从事过关于W-CDMA的FPGA IP core设计工作,也完成过W-CDMA和TDS-CDMA的接收机理论研究和链路仿真工作。综合上面的工作,最终选择了无线通讯的系统设计和标准设计工作。目前拥有100多个已授权的发明专利,是某通讯行业标准文件的第一作者,亦有专利思想被写入3GPP协议。已出版FPGA设计专业著作《IP核芯志-数字逻辑设计思想》和《Verilog传奇-从电路出发的HDL代码设计》。