过程语句
system verilog为循环功能增加了两个新语句。第一个是continue,用于在循环中跳过本轮循环剩下的语句直接进入下一轮循环。第二个是break,用于终止并跳出循环。
在读取文件时使用break和continue
initial begin
bit [127:0] cmd;
int file,c;
file = $fopen("commands.txt","r");
while(!$feof(file)) begin
c = $fscanf (file,"%s",cmd);
case (cmd)
"":continue;
"done":break;
endcase
end
$fclose(file);
end
注:对文件操作方法不熟悉的请点击下方链接
链接: verilog 文件操作方法
任务及函数
在verilog中,任务(task)和函数(function)之间有很明显的区别,其中最重要的一点是,任务可以消耗时间而函数不能。函数里面不能带有诸如#100的时延语句或者@(posedge clock),wait(ready)的阻塞语句,也不能调用任务。另外,verilog中的函数必须有返回值,并且返回值必须被使用,例如用到赋值语句中。
system verilog对这条限制稍有放宽,允许函数调用任务,但只能在由fork…join_none语句生成的线程中调用。
如果你有一个不消耗时间的system verilog任务,你应该把他定义成void函数,这种函数没有返回值。这样它就能被任何任务或函数所调用。
在子程序中去掉begin…end
在system verilog中task/endtask和function/endfunction的关键词已经足以定义这些子程序的边界了。
高级的参数类型
system verilog中缺省的类型和方向是“logic 输入”。
verilog对参数的处理方式很简单:在子程序的开头把input和inout的值赋值给本地变量,在子程序退出时赋值output和inout的值。除了标量以外,没有任何把存储器传递给verilog子程序的办法。
在system verilog中,参数的传递方式可以指定为引用而不是复制。这种ref参数类型比input,output或inout更好用。首先,你现在可以把数组传递给子程序。
system verilog也支持const修饰符,允许在变量声明是对其进行初始化,但不能在过程代码中改变其值。
使用ref和const传递数组
function void print_checksum(const ref bit[31:0] a[]);
bit [31:0] checksum = 0;
for(int i=0;i<a.size();i++)
checksum ^= a[i];
$display("The array checksum is %0d",checksum);
endfunction
system verilog允许不带ref进行数组参数的传递,这时数组会被复制到对战区里。这种操作的代价很高,除非是对特别小的数组。
sysytem verilog的语言参考手册(LRM)规定了ref参数只能被用于带自动存储的子程序中。如果你对程序或模块指明了automatic属性,则整个子程序内部都是自动存储的。
ref参数的第二个好处是在任务里可以修改变量而且修改结果对调用他的函数随时可见。
在多线程间使用ref
task bus_read(input logic[31:0]addr,
ref logic[31:0]data);
//请求总线并驱动地址
bus.request = 1'b1;
@(posedge bus.grant) bus.addr = addr;
//等待来自存储器的数据
@(posedge bus.enable) data = bus.data;
//释放总线并等待许可
bus.request = 1'b0;
@(negedge bus.grant);
endtask
logic [31:0] addr,data;
initial
fork
bus_read(addr,data);
thread2:begin
@data;//在数据变化时触发
$display("Read %h from bus",data);
end
join
在上述代码中,一旦bus.enable有效,初始化块中的thread2块马上就可以获取来自存储器的数据,而不用等到bus_read任务完成总线上的数据处理后返回,这可能需要若干个时钟周期。由于参数data是以ref方式传递的,所以只要任务里的data一有变化,@data语句就会触发。如果把data声明为output,则@data语句就要等到总线处理完成后才能触发。
参数的缺省值
在system verilog中,可以为参数指定一个缺省值,如果在调用时不指明参数,则使用缺省值。(个人认为在这里可以理解为变量赋初值)。
带缺省参数值的函数
function void print_checksum(
const ref bit[31:0] a[],
input bit [31:0] low = 0,
input int high = -1);
bit [31:0] checksum = 0;
if(high == -1 || high >= a.size())
high = a.size() -1;
for(int i = low;i<=high;i++)
checksum+=a[i];
$display("The array checksum is %0d",checksum);
endfunction
调用上述函数的方法,如下代码所示:
print_checksum(a);//a[0:size()-1]中所有元素的校验和——缺省情况
print_checksum(a,2,4);//a[2:4]中所有元素的校验和
print_checksum(a,1);//从1开始
print_checksum(a,,2);//a[0:2]中所有元素的校验和
print_checksum();//编译错误:a没有缺省值
使用-1或其他任何越界值作为缺省值,对于获知调用时是否有指定值,不失为一个好方法。
采用名字进行参数传递
task many(input int a = 1,b = 2, c = 3,d = 4);
$display("%0d %0d %0d %0d",a,b,c,d);
endtask
initial begin
many(6,7,8,9);//6,7,8,9指定所有值
many();//1,2,3,4使用缺省值
many(.c(5));//1,2,3,4只指定c
many(,6,.d(8));//1,6,3,8混合方式
end
自动存储
在system verilog中,模块(module)和program块中的子程序缺省情况下仍然使用静态存储。如果要使用自动存储,则必须在程序语句中加入automatic关键词。
时间单位和精度
system verilog允许使用数值和单位来明确指定一个时间值。代码里可以使用类似0.1ns和20ps的时延。只要记得使用timeunit和timeprecision、或者’timescale即可。你还可以通过verilog时间函数 t i m e f o r m a t , timeformat, timeformat,time和 r e a l t i m e 来使代码在时间标度上更清楚。 realtime来使代码在时间标度上更清楚。 realtime来使代码在时间标度上更清楚。timeformat的四个参数分别是时间标度(-9代表纳秒,-12代表皮秒),小数点后的数据精度,时间值之后的后缀字符串,以及显示数值的最小宽度。代码如下:
module timing
timeunit 1ns;
timeprecision 1ps;
initial begin
$timeformat(-9,3,"ns",8);
#1 $display("%t",$realtime);//1.000ns
#2ns $display("%t",$realtime);//3.00ns
#0.1ns $display("%t",$realtime);//3.100ns
#41ps $display("%t",$realtime);//3.141ns
end
endmodule
$time与$realtime的对比
系统任务$time的返回值是一个根据所在模块的时间精度要求进行舍入的整数,不带小数部分,而$realtime的返回值则是一个带小数部分的完整实数。