过程语句和子程序
任务与函数之间的区别
任务可以消耗时间,而函数不能。即函数里不能带有#100,@(posedge clk),wait(ready)之类的阻塞语句。Verilog中函数不能调用任务,而SystemVerilog中可以,但只能由fork…join_none语句生成的线程中调用。
所以,一般来说,不消耗时间的任务最好都定义成void函数,以方便被任何函数或任务调用。可以使用void’(function)来忽略函数的返回值。
子程序参数
子程序的参数默认的类型和方向是logic输入。
ref参数可以避免子程序将参数复制进去,而是直接引用原参数,因此子程序中的修改是会影响到原参数的。
参数可以设置缺省值。参数传递也有类似模块端口连接的方式。
function void printsum(ref bit [31:0] a[],
input bit [31:0] low=0, /*这里没有指明端口则默认为ref*/
input int high = 1);
endfunction
printsum(.a(a),.low(low),.high(high));
局部数据存储
默认情况下,module和program都是静态存储的,内部变量在仿真开始时就有了初始值,并且多次调用会覆盖先前的值。可以利用automatic参数实现自动存储。
时间值
可以使用timeunit以及timeprecision代替timescale语句。
timeunit 1ns;
timeprecision 1ps;
$timeformat(-9,3,"ns",8);/*时间标度,-9即ns;小数点精度;时间值后缀字符串;显示数值的最小宽度*/
连接设计与测试平台
验证一个设计的步骤是,生成输入激励,捕获输出相应,决定对错和衡量进度。需要一个测试平台,将设计、激励、输出包裹起来进行验证。
为了解决模块之间大量的连接问题,需要使用接口。
interface arb_if (input bit clk);
logic [1:0] grant, request;
logic rst;
/*使用时钟块控制同步信号*/
clocking cb @(posedge clk);
output request;
input grant;
endclocking
/*利用modport指定方向*/
modport TEST (output rst,
clocking cb);
modport DUT (input request,rst,clk,
output grant);
modport MONITOR (input request,grant,rst,clk);
endinterface
测试与设计平台之间可能存在竞争状态。当测试平台给出一个起始信号,如果其他相关信号在这之后给出,那么设计平台就无法及时获得这些信号的最新值。
接口信号的采样和驱动
时钟块的默认时序是在#1step延时之后采样输入信号,在#0延时之后驱动输出信号。1step延时规定了信号在前一个时间片的Postponed区域,在设计有任何新动作之前被采样,这样可以在时钟上升之前捕获输出值。
可以从下面两张图看出其中差异。
这是采样过程。DUT中的grant输出发生变化,验证平台能够在时钟上升沿之前捕获,并在下一个上升沿到来后,立即反应出来。
这是驱动过程。验证平台驱动request之后,DUT会在下一个时钟上升沿体现出来。如果在时钟上升沿之间驱动,那么则不会被捕获。
程序块中不允许使用always块
因为程序块会在initial块都执行完成后结束,如果有always块,则无法自动结束,应当用initial forever代替。