第三章 过程语句和子程序
1.过程语句
sv吸收了C++的一些特性,包括了break以及continue语句等。
//for循环语句以及do……while语句
initial begin:example //可以给这个initial起一个编号名,这里叫example
integer a[10],sum,j;
for(int i=0;i<10;i++) //为每一个数组元素赋值
a[i]=i;
sum=0;
j=9;
do
sum+=a[j] //将数组的每一个元素相加并求和
while(j--);
$display("Sum is %4d",sum);
end :example
读取文件的例子
initial begin
bit[127:0] cmd;
int file,c;
file=$fopen("a.txt"."r");
while(!$feof(file)) begin
c=$fscanf(file,"%s",cmd); //cmd读取一行数据
case(cmd)
"": continue; //读出来空行就继续读
"done": break; //读出来done就结束while循环
endcase
end
$fclose(file);
end
2.任务task和函数function
verilog | systemverilog | |
---|---|---|
task(任务) | 可以消耗时间(可以用#100) | 同v |
function(函数) | 不可以调用task,不能有时延语句和阻塞语句(不能用#100,wait(),posedge等语句),换句话说,任务可以消耗时间但是函数不可以 | 允许函数调用函数,但是只能用在(fork…join_none语句生成的线程中调用) |
//如果需要一个不消耗时间的任务,可以用function void函数
function void print_state(…);
$display("@%0t:state=%s",$time,cur_state.name());
endfunction
//如果想调用函数并忽略返回值,可以使用void'(在部分仿真器中可用)
void'($fscanf(file,"%d",i));
在sv中,task和function的定义都不再需要使用begin…end(显然V中需要)。
3.子程序
3.1子程序参数
众所周知,在一个C语言的函数程序中,不仅可以复制变量,也可以通过指针来直接修改变量本身的大小,在sv中也采用了这种指针技术,可以进行引用(ref)而不是复制。现在,可以把数组传递给函数来直接修改数组,这相当于给了一个指向数组的指针。
//使用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
//这里的const用来保证不修改指针对应数组的值大小
//使用ref可以保证修改变量并且修改结果对调用函数可见
//在sv中也可以不用ref来进行数组传递,但是这样操作实际上就是把数组复制到函数所在的局部变量堆栈中
函数也可以默认使用缺省值
function void print_state(input int low=0,input int high=-1);
endfunction
print_state(); //low=0,high=-1
print_state(1,3); //low=1,high=3
print_state(,2); //low=0,high=2
采用端口进行参数传递,相信正常人应该都知道v中的例化方法是采用 .a(a)这种模式,如果函数参数过多也可以采用端口传递。
function void print_state(input int a=1,b=2,c=3,d=4);
endfunction
print_state(); //1,2,3,4 默认缺省值
print_state(5,6,7,8); //5,6,7,8 指定所有值
print_state(.a(2)); //2,2,3,4 端口模式
print_state(.a(2),3); //2,3,3,4 混合模式
在缺省的情况下参数的类型是与前一个参数类型相同的。
task s(ref int a[10],int b,c);
//由于b跟c没有指明方向,会默认为ref,但是ref显然对int无效,需要修改为下面这样
task s(ref int a[10],input int b,c);
3.2 子程序返回
sv中可以在task和function中使用return语句。
返回数组的三种方式
//1.定义一个数组类型,并返回这个新的数据类型
typedef int my_arrray[5]; //定义一个类型,它是一个五个元素的数组
my_array f5;
function my_array init(int start)
foreach(init[i])
init[i]=i+start;
endfunction
initial begin
f5=init(5);
foreach(f5[i])
$display("f5[%0d]=%0d",i,f5[i]);
end
//2.用ref参考来返回数组
function void init(ref int f[5],input int start)
foreach(f[i])
f[i]=i+start;
endfunction
int fa[5];
initial begin
init(fa,5);
foreach(fa[i])
$display("fa[%0d]=%0d",i,fa[i]);
end
//3.将数组包装到一个类中,然后返回对象的句柄 (待补充)
4.局部数据存储
4.1自动存取
在verilog中对同一个task多次调用会因为局部变量使用共享的局部静态变量而造成覆盖,所以需要使用以下语句,强迫堆栈来存取。
program automatic test;
endprogram
4.2变量初始化
局部静态变量其实在仿真前就已经赋值了,而不是在begin以及end语句中再进行赋值。
解决方法1 automatic
program automatic test;
endprogram
解决方法2 将声明和初始化拆开
logic [31:0] local_addr;
local_addr = addr<<2;
5.时间值
//$time和$realtime都能返回时间,但是前者为整数,后者为小数
//time不能保存小数时延,如果需要小数时延,需要使用real
`timescale 1ps/1ps
module ps;
initial begin
real a = 800 fs;
time b = 800 fs;
$display("%t",a); //800fs
$display("%t",b); //1000fs,time会四舍五入
end
endmodule
//timeuint(单位),timeprecision(最小精度)
//$timeformat(时间标度,小数点后位数,后缀字符串,显示数值的宽度) -9为ns,-12为ps
module time;
timeuint 1ns;
timeprecision 1ps;
initial begin
$timeformat(-9,3,"ns",8);
#1 $display("%t",$realtime); //1.000ns;
#2ns $display("%t",$realtime); //3.000ns;
#0.1ns $display("%t",$realtime); //3.100ns;
#41ps $display("%t",$realtime); //3.141ns;
end
endmodule