9.2 Structured procedures
结构化语句
SystemVerilog中的所有结构化过程都是在以下结构之一中指定的:
—initial过程,用关键词initial表示(参考9.2.1)
—always过程,用以下关键词表示:
always(参考9.2.2.1)
always_comb(参考9.2.2.2)
always_latch(参考9.2.2.3)
always_ff(参考9.2.2.4)
—final过程,用关键词final表示(参考9.2.3)
—task
—function
这些结构化过程的语法如语法9-1所示。
initial和always过程语句在仿真开始的时候启用。initial语句只执行一次,当语句完成时,它的活动就停止了。相比之下,always语句重复执行,当仿真停止时,它的活动应该停止。
在initial和always过程语句之间没有隐含的执行顺序。initial过程不需要在always过程之前调度和执行。在一个模块中,定义的initial和always过程没有数量限制。变量初始化相对于过程执行的顺序见6.8。
final过程在仿真结束时启用,并且只执行一次。
任务和函数是从其他过程中的一个或多个位置启用的过程。task和function在第13条中描述。
除了这些结构化的过程外,SystemVerilog包含其他过程上下文,例如覆盖率点表达式(19.5)、断言序列匹配项(16.10、16.11)和操作块(16.14)。
SystemVerilog在过程中有以下几种类型的控制流:
—选择、循环和跳转(参见第12条)
—子程序调用(见第13条)
—顺序和并行块(见9.3)
—定时控制(见9.4)
—过程控制(见9.5至9.7)
9.2.1 Initial procedures
initial过程语句
一个initial过程语句只执行一次,当语句执行完后,它的活动就停止了。
以下示例演示了在仿真开始时变量初始化的initial过程语句的使用。
initial begin
a = 0; // initialize a
for (int index = 0; index < size; index++)
memory[index] = 0; // initialize memory word
end
另外一个initial过程语句的典型应用是波形描述规范的使用,波形描述只执行一次,为仿真电路的主要部分提供激励。
initial begin
inputs = 'b000000; // initialize at time zero
#10 inputs = 'b011001; // first pattern
#10 inputs = 'b011011; // second pattern
#10 inputs = 'b011000; // third pattern
#10 inputs = 'b001000; // last pattern
end
9.2.2 Always procedures
always过程语句
always过程有四种形式:always, always_comb, always_latch, 和 always_ff。always过程的所有形式在仿真期间重复连续执行。
9.2.2.1 General purpose always procedure
通用的always过程语句
关键字always表示一个通用的always过程,可以被用来使用代表重复行为,例如时钟振荡器。该构造还可以与适当的定时控制一起使用,以表示组合的、锁存的和顺序的硬件行为。
通用的always过程,因为它的循环特性,只有当和一些形式的时序控制一起使用才有用。如果一个always语句对仿真时间向前没有控制,它将会创建一个仿真死锁的状况。
例如,下面的代码创建了一个零延迟无限循环:
always areg = ~areg;
为前面的代码提供一个定时控制会创建一个潜在有用的描述,如下所示:
always #half_period areg = ~areg;
9.2.2.2 Combinational logic always_comb procedure
组合逻辑always_comb过程语句
SystemVerilog提供了一个特殊的always_comb过程来建模组合逻辑行为。例如:
always_comb
a = b & c;
always_comb
d <= #1ns b & c;
always_comb过程提供的功能不同于通用的always过程,如下所示:
—有一个推断敏感度列表,其中包含9.2.2.2.1中定义的表达式。
—写在赋值表达式左边的变量不应该被其它任何进程写入。但是,只要它们最长的静态前缀不重叠,对一个变量的独立元素进行多重赋值是允许的(参看11.5.3)。例如,一个非合并结构或数组的一bit可以被always_comb过程赋值,其它位可以被连续赋值或其它的always_comb过程赋值。详情请参见6.5。
—在所有initial过程和always过程启动之后,这个过程在零时刻自动触发一次,以便此过程的输出与输入一致。
如果always_comb过程中的行为不代表组合逻辑,软件工具应该执行额外的检查来发出警告,比如是否可以推断出锁存的行为。
9.2.2.2.1 Implicit always_comb sensitivities
隐式always_comb敏感性
always_comb的隐式敏感度列表包括每个变量,或者在块内读取的选择表达式,或在块内调用的任何函数的最长静态前缀的扩展但有以下例外:
a)在块中或在块中调用的函数声明的变量的任何展开
b)在块中或在块中调用的任何函数编写的任何表达式
最长静态前缀的定义请参见11.5.3。
层次函数调用和包中的函数调用被分析为普通函数,对类范围解析操作符引用的静态方法函数的调用也是如此。类对象的引用和类对象的方法调用不会向always_comb的灵敏度列表添加任何东西,除了传递给这些方法调用的参数表达式的任何贡献。
在always_comb中允许任务调用,但是任务的内容不会向灵敏度列表添加任何内容。
注意:一个不消耗时间的任务可以被一个void函数代替,以便对内容进行灵敏度分析。
在过程中的即时断言(见16.3)中使用的表达式,或在过程中调用的任何函数中使用的表达式,都有助于生成always_comb的隐式灵敏度列表,就好像这个表达式被用作if语句的条件一样。在断言操作块中使用的表达式不会参与到always_comb的隐式灵敏度列表。在下面的例子中,每当b、c或e发生变化时,always_comb就会触发。
always_comb
begin
a = b & c;
A1:assert (a != e) else if (!disable_error) $error("failed");
end
9.2.2.2.2 always_comb compared to always @*
always_comb和always@*比较
SystemVerilog always_comb过程与always @*(参见9.4.2.2)的区别如下:
—Always_comb在时间0时自动执行一次,而always @*会等待,直到推断敏感度列表中的一个信号发生变化。
—Always_comb对函数内容中的变化敏感,而always @*只对函数参数的变化敏感。
—在always_comb过程中,赋值左边的变量,包括被调用函数内容中的变量,不能被任何其他进程写入,而@*总是允许多个进程写入同一个变量。
—Always_comb中的语句不应该包括那些阻塞的、有阻塞计时或事件控制的语句,或者fork-join语句。
—Always_comb对过程中立即断言中的表达式和过程中调用的函数的内容敏感,而总是@*只对过程中立即断言中的表达式敏感。
9.2.2.3 Latched logic always_latch procedure
锁存逻辑always_latch过程语句
SystemVerilog还提供了一个特殊的always_latch过程,用于对锁存逻辑行为进行建模。例如:
always_latch
if(ck) q <= d;
always_latch结构与always_comb结构是相同的,除了如果always_latch结构中的行为不代表锁存逻辑,软件工具应该执行额外的检查并警告;而在always_comb结构中,如果行为不代表组合逻辑,工具应该检查并警告。9.2.2.2中的所有语句都应适用于always_latch。
9.2.2.4 Sequential logic always_ff procedure
顺序逻辑always_ff过程语句
always_ff过程可以用来建模可综合的顺序逻辑行为。例如:
always_ff @(posedge clock iff reset == 0 or posedge reset) begin
r1 <= reset ? 0 : r2 + 1;
end
always_ff过程施加了一个限制,即它包含且仅包含一个事件控制,并且没有阻塞计时控制。在always_ff过程中,赋值左边的变量,包括来自被调用函数内容的变量,不应被任何其他进程写入。
如果always_ff过程中的行为不代表顺序逻辑,软件工具应该执行额外的检查来发出警告。
9.2.3 Final procedures
final过程语句
final过程类似于initial过程,它定义了一个语句过程块,不同的是它发生在仿真时间的末尾,并且执行时没有延迟。finial通常用于显示有关仿真的统计信息。
final过程中允许的语句只有函数声明中允许的语句,以便它们在一个单独仿真循环执行。与initial过程不同,final过程并不作为单独的进程执行;相反,它在零时间内执行,作为来自单个进程的一系列函数调用。所有final程序应以任意顺序执行。在所有final程序执行完毕后,将不再执行剩余的计划事件。
当仿真结束时,由于对$finish的显式或隐式调用,执行final过程。
final
begin
$display("Number of cycles executed %d",$time/period);
$display("Final PC = %h",PC);
end
在final过程中执行$finish、tf_dofinish()或vpi_control(vpiFinish,…)将导致仿真立即结束。final程序在仿真中只能触发一次。
final过程应该在表示仿真结束的PLI回调之前执行。
SystemVerilog的final过程以任意但确定的顺序执行。这是可能的,因为final过程被限制为函数允许的合法语句集。
注意:SystemVerilog不指定执行final过程的顺序,但是实现应该定义保持运行之间的规则。这有助于保持输出日志文件的稳定,因为final过程主要用于显示统计信息。