对Verilog仿真过程的理解

阅读: 评论:0

对Verilog仿真过程的理解
前⾔
顺序执⾏的仿真器如何仿真并⾏的Verilog语⾔?仿真器中Verilog的并⾏性是通过其语义(Verilog的语⾔含义)仿真出来的,Verilog语⾔的语义是专门为仿真定义的。如果设计的Verilog源代码不符合Verilog仿真语义,对Verilog代码的仿真可能出现仿真歧义,也就是代码仿真结果会与综合后时间的门级⽹表功能不⼀致。
⼀、概念理解:
1、仿真时间
仿真电路所⽤的真实时间进⾏建模。仿真时间由硬件电路的延时参数决定,也是硬件电仿真时间是指由仿真器维护的时间值,⽤来对仿真电路所⽤的真实时间进⾏建模
路的实际⼯作时间,所有仿真⼯作都是严格按仿真时间向前推进的,在什么时间执⾏什么操作。仿真时间与仿真软件在计算机上的运⾏时间没有任何关系。
若优先级相同,如果在当前仿真时间有多个事件需要执⾏,那么⾸先要根据它们的优先级(在事件队列中的优先级)来判定谁先谁后。若优先级相同,则执⾏顺序随机(不同仿真器的⾏为可能不同)。
重组胶原蛋白则执⾏顺序随机(不同仿真器的⾏为可能不同)
2、事件驱动
事件驱动类型的仿真。Verilog语⾔⽤来对数字系统的功能和时序进⾏建模,模型的仿真过程是围绕时间来组织的。
功能仿真是⼀种事件驱动类型的仿真
事件是指在特定时刻,模型中值的变化。⼀个更新事件被执⾏后,所有对改事件敏感的进程都将以随机的顺序计算。⽐如,在被仿真电事件
路中,线⽹或寄存器的值的任何改变被认为是⼀个更新事件。
计算事件和更新事件之间循环往复的互相触发,
计算事件。计算事件和更新事件之间循环往复的互相触发,进程
进程对更新事件敏感,进程(门或⾏为模型)的计算也是⼀个事件,叫做计算事
推动了仿真时间向前推进。
推动了仿真时间向前推进
3、进程
进程是Verilog语⾔中的独⽴执⾏单元,⽤Verilog描述的数字系统正式由⼀个个进程组成的。
进程包括原语、模块(module)、initial过程块、always过程块、连续赋值语句、异步任务、过程赋值语句等。
进程可以被激活或被挂起。仿真器总是在处理被激活的⼀个进程,⽽其他的所有进程则处在挂起状态。
所有进程都是并⾏执⾏的,它们之间的顺序随机,仿真⼯具可根据⾃⾝原则安排它们的执⾏顺序。⽐如在下⾯例⼦中,0仿在仿真时,所有进程都是并⾏执⾏的
执⾏完所真时刻的所有进程都是按它们在代码中出现的先后顺序执⾏的。但仿真器的这种⾏为并不违背Verilog语⾔的并⾏性,因为在执⾏完所
时刻。
有进程前,仿真时间是不会向前推进的,仿真时刻仍然是0时刻
进程将被挂在执⾏⼀个进程时,如果遇到⼀个事件语句(“@”)、延时预计(“#”)、其表达式为FALSE的等待语句(wait),则进程将被挂起,直到发⽣该事件、已经过延时中的时间单位数、等待语句表达式变为真时,进程才会重新被激活。
在Verilog语⾔中,进程之间是并⾏独⽴执⾏的,但它们在执⾏的过程中右交织在⼀起。⼀个进程被挂起,另⼀个进程被激活;改进程被挂起,另⼀个进程⼜开始执⾏,这就是Verilog语⾔在仿真时的⼀⼤特点。
Verilog也会出现进程锁死的现象。⽐如若将下⾯代码中forever语句改为“forever sclk_i = ~sclk_i;”仿真时间会⼀直停留在0仿真时刻⽽不会向前推进。这是由于该initial进程已经被锁死,永远在执⾏“sclk_i = ~sclk_i;”语句。该进程永远不会挂起,其他进程就永远⽆法执⾏,仿真时刻就⽆法向前推进。
4、调度
调度实际上就是安排事件的执⾏顺序。
在Verilog中,同⼀仿真时刻也许有很多事件需要执⾏,不同的事件之间有⼀个先后的优先级关系,事件的执⾏也会产⽣新的当前时刻事件和将来时刻事件,这都需要被合理调度。
5、时序控制
在Verilog仿真时,主要通过3种⽅法来进⾏时序控制:事件语句(“@”)、延时语句(“#”)、等待语句。
时序控制总是伴随着进程的激活和挂起。
6、进程、事件和仿真时间的关系
事件会在不同的时间发⽣。
序列化,因为计算机不同于所Verilog是⼀种并⾏语⾔,允许描述多个同时发⽣的动作。但执⾏这些操作时要求将它们序列化
建模的硬件,它不是并⾏的。
7、Verilog语⾔的不确定性
在Verilog仿真时,有2个不确定的⾏为来源:⼀个是0时刻的任意执⾏顺序;⼀个是进程之间语句的随机交叉。
仿真器执⾏被调度到同⼀时刻的⼀组事件时,也许需要⼏个仿真周期来完成,因为⼀个事件可能产⽣本时刻的其他事
并不是说执⾏这些事件不需要时间,⽽
的时间内执⾏他们,并不是说执⾏这些事件不需要时间
⽽件。这⾥所说的执⾏同⼀时刻的事件
长度为0的时间内执⾏他们
同⼀时刻的事件,是指在长度为
是说所有的事件的发⽣并不会促使仿真时间向前推进。这种0长度时间内事件的任意执⾏顺序是仿真语⾔不确定性的是说所有的事件的发⽣并不会促使仿真时间向前推进
根源。
⼆、时序模型与延时
1、仿真时序模型
(1)仿真模型
在Verilog语⾔中,将数字硬件的模型称为仿真模型。⽐如module块与其中的always语句块都可被称作仿真模型。
时序模型是仿真器的时间推进模型,反映了推进仿真时间和调度事件的⽅式。时序模型分为门级时序模型、过程时序模型2种。
(2)2个概念:敏感表、扇出表。
敏感表:是仿真模型的输⼊表,由接收新值的元素组成,它告诉我们当输⼊发⽣变化时,哪些输⼊的变化会导致仿真模型的执⾏。
扇出表:由将产⽣新值的元素组成,它告诉我们当⼀个事件发⽣时需要计算哪些元素。
(3)门级时序模型
Verilog门级时序模型主要⽤于分析连续赋值语句、过程连续赋值语句、门级原语、⽤户⾃定义原语等。
改模型特点是,任意时刻、任意输⼊发⽣变化,门⽰例模型都将重新计算其输出。所有仿真模型都对输⼊变化敏感,⽽这种变换右会导致仿真模型的执⾏。
Verilog的门级模型具有惯性延时的特性,门级时序模型⾮常精确的模拟了电路中的惯性延时特性
电路中的惯性延时特性。
⼀般来说,门级时序模型对应于组合逻辑建模。
(4)过程时序模型
Verilog中过程时序模型不同于门级时序模型,它不是对任何输⼊的变化都敏感,它的敏感依赖于控制的上下⽂。⼀般来说,initial、always语句只对输⼊的⼀个⼦集敏感。⽐如,always语句只对@符号后⾯括号中的变量敏感。
⼀般来说,过程时序模型对应时序逻辑建模。
(5)⽰例
对于语句:assign #8000000 sclk_dely = sclk;时间单位`timescale为1ns,sclk信号是周期为488ns的时钟信号(远⼩于8ms 的延时值)。原意是将sclk信号延时8ms赋值个sclk_dely,但仿真的结果是
真的结果是sclk_dely信号⼀直未发⽣变化
信号⼀直未发⽣变化,仅延时值⼩于
仅延时值⼩于244ns时(assign #235 sclk_dely = sclk;),sclk_dely信号才会正常变化
信号才会正常变化。
该语法中,⽆论sclk信号何时发⽣变化,都会⽴即执⾏⼀个计算事件
计算事件,计算等号右边的表达式
计算等号右边的表达式,同时产⽣⼀个更新事件(把计算出的值赋给sclk_dely),但该更新事件并未⽴即执⾏,⽽是被调度到当前仿真时间之后8ms再执⾏。
根据门级时序模型
门级时序模型的特征,在上⼀更新事件执⾏前还会有新的更新事件产⽣
上⼀更新事件执⾏前还会有新的更新事件产⽣。因为sclk的周期为488ns,每个244ns 会变化⼀次。这样,在每个244ns以后,Verilog调度器就会撤销先前已调度的事件,⽽调度新的事件。因此更新事件被
更新事件被不断产⽣,右不断被撤销,最后导致没有⼀条更新事件被真正执⾏
不断产⽣,右不断被撤销,最后导致没有⼀条更新事件被真正执⾏,因此出现sclk_dely⼀直未初始化的现象。
2、在Verilog语⾔中增加延时
(1)电路的2种延时
惯性延时:由输⼊变化过快(⼩于门本⾝延时值),⽽导致输出⽆响应的特性,称为电路的惯性延时。也就是说电路存在⼀定的惯性,或者说是惰性。例如在与⾮门电路中,门延时为5ns,因此任何⼩于这个延时值的输⼊变化都不会对输出造成影响。如:语句assign #5 B = ~A;模拟了电路的惯性延时,A信号任何⼩于5ns的变化都将被过滤,⽽不会反映到B信号的变化中。
传导延时:信号A经过5ns的传输线到达另⼀端B,信号A的任何变化都将在5ns后体现在B端,这种延时被称为传导延时。
(2)在阻塞赋值语句中增加延时(不推荐)
在阻塞赋值中增加延时,不能模拟任何电路中的延时类型(惯性延时和传导延时),因此不推荐使⽤。
(3)在⾮阻塞赋值语句中增加延时(只在“<=”右边表达式中有意义)
1 2 3always @(a or b) begin
sum <= #5 a+b;
end
⽐如上⾯语句可⽤于传导延时模型建模。当某个T时刻a发⽣变化,导致always语句开始执⾏。执⾏“sum <= #5
a+b;”时,⾸先计算a+b的值,然后将相加结果赋值给sum的更新事件
更新事件调度到T+5ns之后执⾏。此后,任何a、b的变化都将导致always语句的执⾏,也就是说a和b上的任何变化都不会被忽略,⽽总是在5ns以后体现在sum上。
在⾮阻塞赋值语句的右表达式中增加延时,可以精确的模拟电路中的传导延时。
(4)在连续赋值语句中增加延时
在连续赋值语句中,只有⼀种延时是合法的:assign #5 B = ~A;,A信号上的任何⼩于5ns的变化都将
被过滤,不会
体现到B 的变化上的变化上。这种延时精确的模拟了电路中的惯性延时。惯性延时。
三、如何提⾼代码仿真效率代码仿真精度越⾼,仿真效率越低;减少层次结构;进程越少,放着效率越⾼;减少啊门级原语使⽤,尽量采⽤⾏为描述;
尽量使⽤case 语句,⽽不是if...else 语句;
减少d 语句块的使⽤
减少仿真输出显⽰。
四、防⽌仿真和综合结果不⼀致不完全敏感列表:
case 语句:初始化:
代码中的延时参数:
五、以如下代码⽰例对Verilog 仿真过程进⾏理解
1、源代码如下
INV_DFF.v
1
2
3
4
5
6
7
8
9
10
11
直排溜冰鞋教程12
13
14
15
16
17
18module  INV_DFF(    input    wire    sclk,    input    wire    rst_n,    input    wire    DataIn,    output  reg      DataOut );wire  DataInv;always  @(posedge  sclk or  negedge  rst_n) begin    if (1'b0 == rst_n)        DataOut <= 1'b0;    else        DataOut <= DataInv;end  assign  #3 DataInv = ~DataIn;endmodule
TestBench 模块:
TB_Adder.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
管式直线电机17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34`timescale  1ns/100ps module  TB_Adder();reg  sclk_i, rst_n_i, Din_i;wire  Dout_i;//时钟产⽣initial  begin      sclk_i = 0;    forever        #10 sclk_i = ~sclk_i;end //复位操作initial  begin    rst_n_i = 1;    #5 rst_n_i = 0;    #55 rst_n_i = 1;end  //产⽣输⼊initial  begin    Din_i = 0;    #80 Din_i = 1;    #40 Din_i = 0;end  //实例化模块INV_DFF INV_DFF_inst(    .sclk(sclk_i),    .rst_n(rst_n_i),    .DataIn(Din_i),    .DataOut(Dout_i));
35endmodule
2、仿真时序和波形如下
3、下⾯针对以上代码,对各个仿真时刻进⾏分析:
(1)0仿真时刻:
进程同时在0时刻执⾏,包括3个initial进程和⼀个INV_DFF实例化进程INV_DFF_inst;
在顶层仿真TB中,4个进程
同时执⾏的进程其执⾏顺序是不固定的,跟其在代码中出现的顺序⽆关,但某些仿真器将它们在代码中出现的顺序作为执⾏顺序;
⾸先执⾏时钟产⽣语句中的“sclk_i = 0;”,然后执⾏“forever #10”语句,当仿真器遇到“#10”语句时将该进程挂起,转⽽执⾏其他进程。这是由于后⾯语句是在10ns仿真时刻所需执⾏的语句,⽽当前的仿真时刻还是0,没有向前推进;
然后执⾏复位操作中的语句“rst_n_i = 1;”,当仿真器遇到“#5”时将该进程挂起;
再执⾏产⽣输⼊中的语句“Din_i = 0;”,当仿真器遇到“#80”时,将该进程挂起;
敏在执⾏实例INV_DFF_inst时,虽然always语句也是在0时刻开始执⾏,但它仅对sclk_i的上升沿和rst_n_i的下降沿敏感,⽽在这时(0时刻)并没有这2个时间发⽣,因此不会执⾏该always语句;
在执⾏实例INV_DFF_inst中的语句“assign #3 DataInv = ~DataIn;”时,需要分2部分进⾏分析:即计算事件和更新事
重新随机进程计算事件和更新事ktkp-073
真正更新DataInv变量的事件却在3ns以后处理。
以后处理。这就是为何在仿真在0时刻⾸先计算“DataInv = ~DataIn;”,但真正更新
件。在
波形上看DataInv,3ns前是“X”,3ns以后才更新为1的原因。
⾄此,0仿真时刻的语句全部执⾏完毕,仿真时间轴向前推进。由于仿真时间3ns以前已经没有需要执⾏的任务了,因此仿真器会将时间轴推进到3ns时刻。
(2)3ns仿真时刻:
将DataInv更新为1。3ns仿真时刻的语句全部执⾏完毕后,仿真时间轴在3ns仿真时刻,只有⼀个任务需要执⾏,就是将
向前推进。由于在仿真时间3ns时刻以前已经没有需要执⾏的任务了,因此仿真器将时间轴推进到5ns时刻。
(3)5ns仿真时刻:
在5ns仿真时刻,在0时刻被挂起的复位操作进程将被重新唤醒,开始执⾏“rst_n_i = 0;”语句。当遇到“#55”后,该进程将再次被挂起,等待“5+55=60ns”时刻,该进程将被再次唤醒;
这时,由于rst_n_i被置位于0,这⼀rst_n_i的下降沿将触发INV_DFF_inst中的always语句。执⾏“if(1'b0 == rst_n)
DataOut <= 1'b0;”语句,这样,DataOut变量降重初始的“X”状态被复位为0。always语句的进程将被挂起。
⾄此,5ns仿真时刻的语句执⾏完成,仿真时间轴向前推进。由于在仿真时间5ns以前,已经没有需要执⾏的任务了,因此仿真器会将时间轴推进到10ns时刻;
(4)10ns仿真时刻:
在10ns仿真时刻,0时刻被挂起的时钟产⽣进程将被重新唤醒,执⾏“sclk_i = ~sclk_i;”语句。由于原来sclk_i是0,因此这
仿真器继
是⼀个永远循环的语句,因此仿真器继⾥sclk_i出现了⼀个上升沿。这时,该进程的执⾏过程还未完成。由于forever是⼀个永远循环的语句
时刻的进程语句。
续返回到“forever #10”语句,知道遇到#10以后,该进程才被挂起,执⾏其他10ns时刻的进程语句
sclk_i的上升沿必然触发INV_DFF_inst中的always语句,但这时rst_n_i还为0,因此依然执⾏“if(0 == rst_n_i) DataOut <= 1'b0;”语句,但DataOut的值并未发送变化。这实际上模拟了D触发器的特性
触发器的特性:当触发器处于复位状态时,⽆论是否有时钟沿出现,输出都不会改变。
在这以后的时刻,如30ns、50ns等,虽有时钟沿出现,但DataOut仍为0,仿真时刻推进到60ns后,将唤醒产⽣复位进程,rst_n_i被重新置1,撤销对D触发器的复位,同时该initial进程执⾏完成,并将永远被挂起。
(5)70ns仿真时刻:
这时,时钟产⽣进程的initial语句的执⾏将再产⽣⼀个sclk_i的上升沿。这⼀时间将触发INV_DFF_inst中的always语句。由于此时rst_n_i 为1,因此执⾏语句“DataOut <= DataInv;”。这样DataOut的值在70ns更新为1。
(6)80ns仿真时刻:
在80ns仿真时刻,产⽣输⼊的initial进程将被唤醒,执⾏“Din_i=1;”语句。这⼀时间将触发INV_DFF_inst 中的“assign #3 DataInv =
时刻执⾏。即在83ns仿真时~DataIn;”语句的执⾏。⾸先计算DataInv的新值0,然后将DataInv的更新事件调度到3ns以后的83ns时刻执⾏
5g通讯模块刻,DataInv更新为0。
(7)90ns仿真时刻:
在90ns仿真时刻将再次出现sclk_i的上升沿。这⼀事件将触发INV_DFF_inst中的always进程语句,执⾏语句“DataOut <= DataInv;”。因此DataOut的值在90ns时刻将更新为0。
(8)120ns仿真时刻:
在120ns仿真时刻,产⽣输⼊进程将被唤醒,执⾏“Din_i = 0;”语句,同时该initial进程执⾏完成后将被永远挂起。这⼀事件继续触发INV_DFF_inst进程中的“assign #3 DataInv = ~DataIn;”语句的执⾏。⾸先计算DataInv的新值1,然后将DataInv的更新事件调度到123ns
执⾏。于是在123ns仿真时刻,DataInv的值将被更新为1。
(9)130ns仿真时刻:
在130ns仿真时刻将再次出现sclk_i的上升沿。这⼀事件将触发INV_DFF_inst中always进程语句,执⾏语句“DataOut <= DataInv;”。这样DataOut的值在130ns时刻将更新为1。

本文发布于:2023-05-16 14:11:02,感谢您对本站的认可!

本文链接:https://patent.en369.cn/patent/2/101677.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:语句   延时   模型   事件   进程   时间   时刻   时序
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 369专利查询检索平台 豫ICP备2021025688号-20 网站地图