0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

从仿真器的角度理解Verilog语言2

jf_78858299 来源:IC大家谈 作者:王君实 2023-05-25 15:10 次阅读

延迟行为

Verilog语言的延迟语句虽然不能综合,但是在仿真过程中应用得很多。延迟语句可以用在testbench中构建时钟信号和激励,也可以用在Verilog模块中模拟实际电路的延迟。延迟语句可以出现在两条赋值语句之间,也可以出现一条赋值语句中间。

#3 a = b; //延迟语句在赋值语句之间
a = #3 b; //延迟语句在赋值语句内部

在赋值语句之间的延迟语句可以延迟语句的执行。对于仿真器来说,处于赋值语句之间的延迟语句有两个作用。首先,延迟语句会暂停当前always块的执行,结束当前的仿真阶段,更新之前没有完成的赋值,完成当前事件的响应并将控制前交还给事件队列。然后,延迟语句会在事件队列中添加一个新的事件。这个事件表示在延迟语句指定的时刻开始执行这函数剩下的部分。例如

always @(a, b, c) begin : add_mux4
    t <= a + b;
    #1 d = t * c;
end

上述代码转化后的事件响应函数为

function add_mux4_1 :
    t_update = a + b;
    t = t_update;
    addEvent( curr_time + 1, add_mux4_2 );




function add_mux4_2 :
    d = t * c;

Verilog文件中的1个过程块被转换为两个函数。第一个函数add_mux4_1对应于延迟语句之前的部分,第二个函数add_mux4_2对应于延迟语句之后的部分。

add_event是本文定义的一个原语,表示向事件队列中添加一个事件。第一个参数表示事件响应的时间,第二个参数表示响应事件需要调用的事件响应函数。从第三个参数开始,之后的参数会作为事件响应函数的参数,传递给事件响应函数。

add_event( curr_time + 1, add_mux4_2 )表示在当前时间(curr_time)后1个时间单位的时候响应这个事件。事件需要调用add_mux4_2函数。响应函数不需要额外的参数。在调用add_mux4_2时,信号t已经完成更新。

在赋值语句中间的延迟语句将评估和更新阶段分割到两个时刻进行。评估过程仍然在语句执行的时候进行,但是更新过程延后到延迟语句指定的时刻进行。延迟语句是否阻塞过程块的执行,取决于赋值语句本身。如果是阻塞赋值语句,赋值语句中间的延迟语句会阻塞过程块的执行;如果是非阻塞赋值,延迟语句不会阻塞过程块的执行。例如

always @(a, b, c) begin : add_mux5
    t <= #1 a + b;
    d = #2 t * c;
end

上述代码转化后的事件响应函数为

function add_mux5_1:
    t_update = a + b; // 1
    d_update = t * c; // 2
    addEvent( curr_time + 1, update_t, t_update );
    addEvent( curr_time + 2, add_mux5_2, d_update );




function update_t( t_update ) :
    t = t_update; // 3




function add_mux5_2( d_update ) :
    d = d_update; // 4

如果没有延迟语句,事件响应函数的执行顺序应该是 1->2->4->3。由于第一个语句中的延迟语句,语句3需要在当前时刻之后1个时间单位时执行,即update_t。t_update作为事件响应函数的参数,在update_t中更新给信号t。由于第二个语句中的延迟语句,过程块被打断为两个部分,第二个函数需要在当前时刻之后2个时间单位时执行,即add_mux5_2。add_mux5_2需要使用d_update作为参数。

理解到这一层,就可以处理更加复杂的波形了。例如下面这一段代码。

module test;
    reg x,y,z;
    assign #25 a = 1;
    always begin
        #20;
        x = #10 a;
        #3 y = a;
        #3 z = a;
        #7;
    end
endmodule

经过仿真器的转换,上面的Verilog语句会形成如下的事件响应函数。

function assign1 :
    a = 1;




function always1_1 :
    addEvent( curr_time + 20, always1_2 );




function always1_2 :
    x_update = a;
    addEvent( curr_time + 10, always1_3, x_update );




function always1_3( x_update ) :
    x = x_update;
    addEvent( curr_time + 3, always1_4 );




function always1_4 :
    y = a;
    addEvent( curr_time + 3, always1_5 );




function always1_5 :
    z = a;
    addEvent( curr_time + 7, always1_6 );




function always1_6 :
    addEvent( curr_time + delta, always1_1 );

在仿真开始时候,首先向事件队列中添加两个事件,分别是在0+25时刻调用assign1,以及在0+0时刻调用always1_1。事件响应过程如图4所示。always过程块被延迟语句分割成了6个响应函数。每个部分都向事件队列添加能够触发下一个响应函数的事件。信号x的第1次评估发生在20时刻,而第1次更新发生在30时刻,所以信号x的第一次赋值仍为X。直到第2次评估时(63时刻)才能获得有效的信号1,并且在73时刻更新给信号x。

图片

图4 示例过程的事件队列响应过程和波形图

需要说明的是,虽然本文提供了一种思路能够比较轻松地理解行为级描述的执行过程,但是仍然不建议大家在过程块中混用阻塞赋值和非阻塞赋值。混用赋值语句是危险的。

Assign赋值

前面介绍的侧重于过程块。对于Assign赋值语句,原理其实是也一样的。例如

assign a = #5 b & c;

这条assign语句同样可以看做一个事件响应函数。这个函数绑定的事件是信号b或信号c发生变化。延迟语句的效果也是一样的。延迟语句将评估和更新过程分开。当信号b或信号c发生变化时进行评估,并在事件队列中添加一个新的更新事件。5个时间单位之后,响应更新事件,将评估的值更新给信号a。

转换后的事件响应函数如下。

function assign1:
    a_update = b & c;
    addEvent( curr_time + 5, update_a, a_update );




function update_a( a_update ) :
    a = a_update;

调试

Verilog仿真器普遍提供了Verilog代码的调试能力,比如断点和单步运行。在VCS、ModelSim、Vivado和Quartus中都能找到调试模式。断点和单步运行是典型的软件调试手段,是软件工程师的看家本领。但是对于硬件来说,断点和单步运行却是不可理解的,因为硬件是并行的。如果将断点理解为硬件电路在某一个时刻的状态,那么此时应该有多条语句被同时中断。硬件电路不会像软件一样在某个函数中中断并且单步执行,而且其他过程块或语句毫无影响。

前面已经介绍过,Verilog并不是可执行语言。真正的可执行仿真程序是由仿真器提供的仿真框架源代码和由Verilog语言转换而来的仿真程序源代码构成的。实际上,Verilog语言调试的断点并不是添加给硬件的或者Verilog源文件的,而是添加到可执行仿真程序中对应的事件响应函数的。单步调试的对象也是可执行仿真程序中的事件响应函数。所以,Verilog代码可以引入断点和单步调试。

在进一步解释Verilog调试器的机制之前,必须先解释一下软件调试器是如何调试程序的。为了使得可执行文件可调试,编译器会在可执行程序中添加调试信息。以C语言为例,编译器会在可执行文件中添加调试信息(如图5所示)。添加的位置是在对应于C语言语句的汇编代码段起始的位置。在进行软件调试的时候,软件调试器会在有调试信息的地方暂停(比如0x4005a5)。进行单步调试的时候,每一步也都是停在C语言语句开始的地方(比如0x4005bf)。

图片

图5 可调试程序中添加的调试信息(利用objdump命令得到)

Verilog调试器的作用就是将可执行仿真程序和Verilog语言对应起来。一种思路是将编译器插入的调试信息与Verilog语言对应起来。编译器插入的调试信息是对应于可执行的仿真源文件,而这些源文件是由仿真器生成的。所以Verilog调试器可以获得调试信息与Verilog语句的对应关系。另一种思路是通过编译器直接给可执行仿真程序添加与Verilog语言对应的调试信息。这样Verilog调试器从可执行程序就可以获得必要的信息,而不需要额外的消息来源。

Verilog调试器只能对可以与Verilog语言对应的代码部分添加断点,也就是只能对事件响应函数添加断点。Verilog调试器不能调试由仿真器提供的仿真框架源代码。当单步调试遇到always块结束或者assign语句之后,调试器不会进入仿真引擎,而是直接跳转到下一个事件响应函数。此外,Verilog调试器还限制了断点和单步调试的粒度只能以Verilog语句为单位,而不能进一步缩小粒度到可执行仿真程序的语句甚至汇编层面。

图片

图6 Verilog程序加断点的过程

以图6中的Verilog程序为例,经过Verilog仿真器得到右边所示的仿真源文件,再经过编译得到可执行程序。在左边Verilog程序中的一行设置了一个断点(图5中左边第7行),这个断点实际上是设置在右边的可执行程序的事件响应函数always1_4中的(图5中右边第2行)。每当仿真程序运行到always1_4时就触发中断,暂停程序执行。通过仿真器添加的调试提示信息,调试器能够知道中断的位置是Verilog语言的第7行,从而在图形界面上显示。

从断点的位置开始单步调试。从可执行仿真程序的层面来说应该在第3行暂停,并且呈现第3行执行后状态。但是,Verilog调试器会过滤仿真框架的程序,也就是过滤掉无法对应到Verilog程序的语句。所以,可执行程序不会在第3行之后暂停,而是继续执行。第3行结束后,程序从事件响应函数返回,进入仿真框架的部分。仿真器框架的代码也会被调试器忽略,直到仿真程序进入下一个事件响应函数。最终,程序进入always1_5。调试器会在第6行暂停,并且将中断的位置对应到Verilog软件的第8行。程序运行的标志会显示在图6中第8行的位置。

以上的过程对于用户来说都是不可见的。从用户的角度看来,只能看到程序指针从第7行跳到第8行,并且第7行语句的效果在波形图上展现了出来。这就是Verilog语言调试背后隐藏的过程,其核心仍然是软件调试。

结语

本文的初衷是提供通过仿真器理解Verilog语言的思路。文中关于Verilog仿真器的描述采用了最简单、最直接的思路,当然也是效率最低的。实际的仿真器会通过各种软件技巧进行优化,提高仿真效率。文中使用的一些概念借鉴自SystemC,比如仿真阶段和“评估-更新”机制。电路仿真器的设计思路和概念都是类似的或者相通的,可以触类旁通。

如果有读者想进一步理解Verilog仿真器,不妨看一下开源Verilog仿真器iVerilog的源码。此外,SystemC也是一套很好的硬件电路仿真框架,建议学习SystemC标准。IEEE的SystemC标准会阐述SystemC需要的仿真引擎以及编程规范。

作者才疏学浅,挂一漏万,请大家多批评指正。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 仿真器
    +关注

    关注

    14

    文章

    988

    浏览量

    82995
  • 软件
    +关注

    关注

    67

    文章

    4348

    浏览量

    85622
  • Verilog
    +关注

    关注

    28

    文章

    1326

    浏览量

    109302
收藏 人收藏

    评论

    相关推荐

    Verilog语言中阻塞和非阻塞赋值的不同

    来源:《Verilog数字系统设计(夏宇闻)》 阻塞和非阻塞赋值的语言结构是Verilog 语言中最难理解概念之一。甚至有些很有经验的
    的头像 发表于 08-17 16:18 6046次阅读

    明德扬至简设计法--verilog的综合仿真器

    是不关心的。常用的仿真器是MODELSIM和VCS等。 由此可见,verilog的代码不仅可以描述电路,还可以用于测试。事实上,Verilog定义的语法非常之多,但绝大部分都是为了仿真
    发表于 10-08 15:19

    Verilog HDL入门教程(全集)

    的具体控制和运行。Verilog HDL语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。因此,用这种语言编写的模型能够使用 Ve r i l o g
    发表于 11-30 19:03

    Aldec 多语言仿真器锁定主流用户

    Aldec 多语言仿真器锁定主流用户   Aldec 公司日前推出了一款新型 Active-HDL 多语言仿真器,定价为 1,995 美元,据称
    发表于 02-08 10:09 1098次阅读
    Aldec 多<b class='flag-5'>语言</b><b class='flag-5'>仿真器</b>锁定主流用户

    模拟/混合信号仿真器

    Harmony单核模拟/混合信号仿真器实时地动态连接SmartSpice 电路仿真器和SILOS-XVerilog仿真器的性能。Harmony集精度、性能、产量和灵活性于一身,仿真
    发表于 03-31 13:09 73次下载

    VERILOG仿真器

    SILOS是一个遵循IEEE-1364-2001标准的Verilog仿真器,它简单易用,为众多IC设计师所推崇。自1986年作为工业标准以来,它强大的交互式调试功能为FPGA、PLD、ASIC和定制数字设计提供了现今最具
    发表于 04-05 23:03 150次下载

    快速理解Verilog语言

    Verilog HDL简称Verilog,它是使用最广泛的硬件描述语言
    的头像 发表于 03-22 17:29 4446次阅读

    Verilog硬件描述语言参考手册免费下载

    Verilog标准前,由于Cadence公司的 Verilog-XL 仿真器广泛使用,它所提供的Verilog LRM成了事实上的语言标准。
    发表于 02-05 16:24 72次下载
    <b class='flag-5'>Verilog</b>硬件描述<b class='flag-5'>语言</b>参考手册免费下载

    使用Vivado仿真器进行混合语言仿真的一些要点

    Vivado 仿真器支持混合语言项目文件及混合语言仿真。这有助于您在 VHDL 设计中包含 Verilog 模块,反过来也是一样。 本文主要
    的头像 发表于 10-28 16:24 2808次阅读

    如何通过仿真器理解Verilog语言的思路

    要想深入理解Verilog就必须正视Verilog语言同时具备硬件特性和软件特性。
    的头像 发表于 07-07 09:54 1157次阅读

    仿真器角度Verilog语言的语法规则进行解读

    综合工具读入源文件,通过综合算法将设计转化为网表,比如DC。能够综合的特性要求Verilog语言能够描述信号的各种状态(0,1,x,z)、信号和模块的连接(例化)以及模块的逻辑(赋值以及各种运算符)。
    发表于 07-07 09:53 756次阅读

    Vivado仿真器进行混合语言仿真的一些要点

    本文主要介绍使用 Vivado 仿真器进行混合语言仿真的一些要点。
    发表于 08-01 09:25 1043次阅读

    verilog仿真工具编译

    Icarus Verilog(以下简称iverilog )号称“全球第四大”数字芯片仿真器,也是一个完全开源的仿真器
    的头像 发表于 08-15 09:11 5397次阅读

    解码国产EDA数字仿真器系列之二 | 如何实现全面的SystemVerilog语法覆盖?

    持SystemVerilog语言,是开发仿真器的一个重要任务。   SystemVerilog的发展历程   数字芯片的验证技术是随着Verilog语法的演变而演变的。 最早,Verilog
    发表于 04-07 14:40 555次阅读
    解码国产EDA数字<b class='flag-5'>仿真器</b>系列之二 | 如何实现全面的SystemVerilog语法覆盖?

    仿真器角度理解Verilog语言1

    只作为语法设定来介绍,忽略了Verilog语言的软件特性和仿真特性。使得初学者无法理解Verilog语言
    的头像 发表于 05-25 15:10 674次阅读
    从<b class='flag-5'>仿真器</b>的<b class='flag-5'>角度</b><b class='flag-5'>理解</b><b class='flag-5'>Verilog</b><b class='flag-5'>语言</b>1