SystemVerilog验证 测试平台编写指南 第三章 过程语句和子程序

SystemVerilog在任务task和函数function方面做了很多的改进,使其更加接近于C语言,从而使代码的编写变得更加容易,尤其是在处理参数传递上面。

3.1 过程语句
SystemVerilog从C和C++中引用了很多操作符和语句。下面就来简单介绍几点。
1)for循环,在for循环中定义循环变量,它的作用范围仅限于循环内部,从而有助于避免一些代码漏洞。
for (int i=0;i<10;i++)
2)自动递增符/自动递减符,++/–,既可作前缀,也可作后缀。
3)标识符,可以在begin或fork语句中使用标识符,那么在相对应的end或join语句中放置相同的标号。同样,你也可以将标识符放在endmodule、endtask、endfunction等语句中。

initial
	begin:example
		......
	end:example

4)循环功能,相比于Verilog,循环功能增加了continue,用于在循环中跳过本轮循环剩下的语句而直接进入下一轮;增加了break,用于终止并跳出循环。

3.2 任务、函数以及void函数
在Verilog中,任务task和函数function之间有着明显的区别,其中task可以消耗时间而函数不能。SystemVerilog允许函数调用任务,但只能在由fork…join_none语句生成的线程中调用。
如果有一个不消耗时间的SystemVerilog任务,你应该把它定义成void函数,这种函数没有返回值。

function void print_state(........);
	......
endfunction

3.3 在子程序中去掉begin…end
在SystemVerilog中,begin…end变成可选项了,因为task…endtask和function…endfunction这些关键词已经足以定义这些子程序的边界了。

3.4 子程序参数
SystemVerilog对子程序的很多改进使参数的声明变得更加方便,同时也扩展了参数的传递方式。
3.4.1 C语言风格的子程序参数
在Verilog中,对一些参数要进行两次声明,一次是方向声明,一次是类型声明

task my_task;
	output [31:0] x;
	reg   [31:0] x;
	input    y;
	......
endtask

SystemVerilog中,可以采用简明的C语言风格,将类型和方向合并,并且在开始进行声明,注意必须使用通用的数据类型logic

task my_task(output logic[31:0] x,
					input logic y);
			......
endtask

3.4.2 参数方向
子程序的参数的缺省值的类型和方向是1比特宽度的logic输入

task T3;
	input a,b;
	......
endtask

参数a和b是1比特宽度的logic输入。
3.4.3 高级的参数类型
Verilog对参数的处理方式很简单:在子程序的开头把input/inout的值复制给本地变量,在子程序退出时则复制output/inout的值。
在SystemVerilog中,参数的传递方式可以指定为引用而不是复制。采用ref参数类型可以将数组传递给子程序

function void print_check(const ref bit[31:0] a[]);
	bit [31:0] checksum=0;
	for(int i=0; i<a.size();i++)
	check^=a[i];
endfunction

ref参数只能被用于带自动存储的子程序中。如果你对程序或模块指明了automatic属性,则整个子程序内部都是自动存储的。
const修饰符决定了子程序不能修改数组的值。
任务里可以修改变量而且修改结果对调用它的函数随时可见
3.4.4 参数的缺省值
在SystemVerilog中,可以为参数指定一个缺省值,如果在调用时不指明参数,则使用缺省值。

function void print_check(ref bit[31:0] a[],
										input bit[31:0] low=0,
										input int high =-1);
	bit[31:0] check=0;
	if (high=-1||high>a.size())
	high=a.size()-1;
	for(int i=0;i<=high;i++)
		checksum+=a[i];
endfunction

在函数调用的时候,有以下几种方式:

print_check(a);//a[0:size()-1]中所有元素的校验和——缺省情况
print_check(a,2,4);//a[2:4]中所有元素的校验和
print_check(a,,1);//a[0:1]中所有元素的校验和
print_check(a,2);//a[2:size()-1]从2开始
print_check( );//编译错误:a没有缺省值

3.4.5 采用名字进行参数传递
类似于模块的端口,可以通过采用类似port的语法指定子程序参数名字的方式来指定一个子集。

task many(input int a=1,b=2,c=3,d=4);
	.......
endtask

函数调用的形式:

many(6,7,8,9); //6 7 8 9指定所有值
many();//采用1,2,3,4使用缺省值
many(.c(5));//1,2,5,4只指定c
many(,6,.d(8));//1,6,3,8混合方式

3.4.6 常见的代码错误
在缺省的情况下,参数的类型是与其前一个参数相同的,而第一个参数的缺省类型是单比特输入

task sticky(int a,b);//a、b为两个整型的输入。
task sticky(ref int array[50],int a,b);//默认a、b的方向为ref 错误的方向类型
task sticky(ref int array[50],input int a, b);//a、b为整型输入

3.5 子程序的返回
3.5.1 返回return语句
SystemVerilog增加了return语句,使得子程序的流程控制变得更加方便,执行到return,则可以直接返回而不执行其下面的代码。
3.5.2 从函数中返回一个数组
SystemVerilog中,函数可以采用多种方式返回一个数组。
1)定义一个数组类型,然后在函数的声明中使用该类型。

typedef  int fixed_array5[5];
fixed_array5  f5;

function fixed_array5 init(int start);
	foreach(init[i])
		init[i]=i+start;
endfunction

initial 
begin
	f5=init(5);
	foreach(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])
end

3.6 局部数据存储
对 注入于一个硬件工程师来说,Verilog语言中所有描述的对象都是静态的,特别是对于子程序参数和局部变量是被存放在固定位置上,而不是像其他编程语言那样存放在堆栈区内。对于递归子程序一类的动态代码没有对应的芯片实现方式,但是在验证过程中,这些动态的中间结果又有着重要的影响,那么我们又该如何对其进行建模呢?
3.6.1 自动存储
在Verilog中,如果你试图在测试程序里的多个地方调用同一个任务,由于任务里的局部变量会使用共享的静态存储区域,所以不同的线程之间会串用这些局部变量。这时候,可以指定任务、函数、模块使用自动存储,从而迫使堆栈区存储局部变量。
SystemVerilog中,模块(module)和program块中的子程序缺省情况下仍然使用静态存储,如果使用自动存储,则必须在程序语句中加入automatic关键词。这样的话,每次调用都使用不同的存储空间

program automatic test;

3.6.2 变量的初始化
我们希望在仿真进行了一段时间,再进行下面的操作。给出两个例子,让大家体会一下变量的初始化。

logic[7:0] local_addr=addr<<2;

对于静态变量,仿真一开始它就会有了初始值。可以将其修改为自动存储。

logic[7:0] local_addr;
local_addr=addr<<2;

将声明和初始化分开,这样就可以满足题目要求。

3.7 时间值
3.7.1 描述时间单位和精度
(1)方式一,'timescale
(2)方式二,timeunit和timeprecision
3.7.2 时间参数
a. $timeformat(时间标度(-9代表ns,-12代表ps),小数点后的数据精度,时间值之后的后缀字符,显示数值的最小宽度)
$timeformat(-9,3,“ns”,8);//1.000ns
b. $time
根据模块的时间精度要求进行舍入的整数。
c. $realtime
是一个带小数部分的完整实数。

`timescale 1ps/1ps
module ps;
	initial 
	begin
		real rdelay=800fs;    //   0.8ps 以0.800存储
		time tdelay=800fs;   // 1ps舍入后得到1
		$timeformat(-15,0,“fs”,5);
		#rdelay                    //1ps,在作延时时,去时间精度1ps
		$display("%t",rdelay);  // "800fs"
		#tdelay                     //1ps 再次延时1ps
		$display("%t",tdelay);  // "1000fs"
	end
endmodule
发布了38 篇原创文章 · 获赞 29 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_45270982/article/details/97513763