SystemVerilog学习整理——过程块和方法

目录

2、SystemVerilog——过程块和方法(initial、always、task、function、automatic和static)

2.1initial和always

2.1.1always

2.1.2initial

2.2task和function

2.2.1函数function

2.2.2任务task

2.2.3Task和function的比较

2.3子程序参数

2.4子程序返回

2.5变量的生命周期

2.5.1定义为自动存储automatic

2.5.2program的优势

2.5.3Program的功能

2.6时间值

2.6.1 描述时间单位和精度

2.6.2 时间参数


2、SystemVerilog——过程块和方法(initial、always、task、function、automatic和static)

2.1initial和always

  1. always、module、interface:硬件世界
  2. program,class:软件世界

        从硬件实现的角度来看,Verilog通过always,initial过程语句块、信号数据连接来实现进程间通信,我们将不同的module作为独立的程序块,他们之间的通信和同步通过信号的变化(event触发)或者通过等待特定事件(时钟周期)或者时间(固定延时)来完成。

2.1.1always

        1)always是为了描述硬件的行为,而在使用时需要注意哪种使用方式是时序电路描述,哪种使用方式是组合电路描述。

        2)always中的@(event..)敏感列表是为了模拟硬件信号的触发行为,同学们需要正确对标硬件行为和always过程块描述。

        3)always过程块是用来描述硬件时序电路和组合电路,只可以在module或者interface中使用。

2.1.2initial

        1)initial与always在执行路径上有明显区别,initial非常符合软件的执行方式,即只执行一次。

        2)initial和always一样,无法被延迟执行,即在仿真一开始它们都会同时执行,而不同的initial和always之间在执行顺序上没有顺序可言,不能将它们在代码中的前后顺序与它们的执行顺序画上等号。

        3)initial从其执行路径的属性来看,它不应该存在于硬件设计代码中,它本身不可综合,对于描述电路没有任何帮助。

        4)initial是为了测试而生的,由于测试需要按照时间顺序的习惯,即软件方式。

        5)在Verilog时代,所有的测试语句都可以被放置在initial中,为了便于统一管理测试顺序,建议将有关测试语句都放置在同一个initial过程块中。

        6)initial过程块可以在module、interface和program中使用。对于过程块的书写方式,需要使用begin..end将其作用域包住,同样适用于控制语句、循环语句等等。

2.2task和function

2.2.1函数function

        1)可以在参数列表中指定输入参数(input)、输出参数(output).输入输出参数(inout)或者引用参数(ref)。

        2)返回数值或者不返回数值(void)。

        3)默认的数据类型是为logic,例如input [7:0] addr。数组可以作为形式参数传递。

        4)function可以返回或者不返回结果,如果返回即需要用关键词return ,如果不返回则应该在声明function时采用void function().

        5)只有数据变量可以在形式参数列表中被声明为ref类型,而线网类型则不能被声明为ref类型。

        6)在使用ref时,有时候为了保护数据对象只被读取不被写入,可以通过const的方式来限定ref声明的参数。

        7)在声明参数时,可以给定默认数值,例如input[7:0] addr = 0,同时在调用时如果省略该参数的传递,那么默认值即会被传递给function。

2.2.2任务task

        1)task无法通过return返回结果,因此只能通过output、 inout或者ref的参数来返回

        2)task内可以置入耗时语句,而function则不能。常见的耗时语句包括@event、wait event、# delay等。

2.2.3Task和function的比较

        1)可以多采用task来定义方法,因为它可以内置常用的耗时语句。

        2)对这两种方法类型加以区别:在非耗时方法定义时使用function,在内置耗时语句时使用task。好处是在遇到这两种方法定义时,知道function只能运用于纯粹的数字或者逻辑运算,而task则可能会被运用于需要耗时的信号采样或者驱动场景。

        3)如果要调用function,则function和task均可对其调用;而如果要调用task,一般使用task来调用,这是因为被调用的task内置可能有耗时语句。

function和task的比较整理:

函数function

任务task

执行与消耗

不消耗仿真时间

消耗仿真时间

仿真控制语句

函数中不能含有控制仿真时间的语句。如时间延迟#100,阻塞语句@(posedge clk)等

任务中可以包含仿真时间控制语句。如延迟#100,时钟周期@(),wait()以及事件event等语句

返回形式

函数的返回可以通过调用return语句实现,当不需要返回值时可以将函数定义为void类型

任务没有返回值,一般通过output信号直接输出,也不需要关键字void。

形参变量

函数中至少包含一个输入变量进行传参

任务中可以没有输入、输出变量

调用方式

函数的调用可以以语句的一部分出现

任务的调用通过一条**单独的语句实现

调用关系

函数中只能调用函数,不能调用任务

任务中可以调用任务,也可以调用函数

2.3子程序参数

        systenverilog对任务和函数做出了重大改进,子程序中的begin  end语句是可选的。在SystemVerilog中可以在task和function中直接使用多条语句,这些语句是顺序执行的。

        在SystemVerilog中任务和函数的形式参数可以声明在其名字后的圆括号中(参数的默认方向是input,默认类型是logic)。每一个形式参数可选的方向属性如下:

  1. input:在子程序的开头把input的值复制给本地变量;
  2. output:在子程序退出时复制本地变量给形式参数output;
  3. inout:l在子程序的开头把inout的值复制给本地变量;在子程序退出时复制本地变量给形式参数inout;
  4. ref:传递引用(句柄或者指针),不是复制;

SystemVerilog提供了两种方式来为函数和任务传递参数:值传递和引用传递

  1. 值传递:常见的向任务与函数传递值的方法是复制;需要关键字input、output,这种传递方式实际上是将实参内容复制到形参中,原来的实参与形参存放在不同的内存空间中。
  2. 引用传递(相当于指针):引用传递(reference)就如C++的句柄或者指针,也就是变量的入口地址。参数的类型应定义为ref,取代input/output,同时需要将任务和函数声明为automatic型。在通过引用传递的方式将实参传递给形参引用时,引用型的形参变量实际上相当于实参变量的句柄或指针,也就是变量的入口地址,两者具有相同的内存空间。

引用传递的优势:

  1. SystemVerilog通过值传递的方式来传递参数,实参将被整体复制,这将消耗一定的内存和操作时间。而使用引用传递(ref),只是获取实参的入口地址(句柄或者指针),操作速度快,减少内存的使用。
  2. 在子程序修改ref参数变量的时候,其变化对于外部是立即可见的。

使用引用传递的示例:

function void print_check(const ref bit[31:0] a[]);
	bit [31:0] checksum=0;
	for(int i=0; i

        添加了const修饰符的ref参数,子程序不能修改其数组的值

2.4子程序返回

        Verilog中函数必须有返回值,返回值通过函数名赋值来完成。

function logic[15:0] myfunc(input [7:0] x,y);
   myfunc = x*y-1;                //1. 通过函数名返回
endfunction

a = myfunc(.x(5), .y(6));       // 名字映射
a = myfunc(5, 6);             // 位置映射

        SystemVerilog中可以将函数声明为void类型,它没有返回值。这是相对于verilog的改进,也增加了return语句,使得子程序的流程控制变得更加方便,执行到return,则可以直接返回而不执行其下面的代码。其他函数同Verilog一样通过函数名赋值来返回一个值。

        当返回值的个数为1个时,用return返回;当返回值的个数为多个时,通过output返回各种数值;

  1. 函数或任务中通过return关键字返回时,为非void类型;
  2. 函数或任务中通过output返回时(或无返回值),须将其定义为*void类型*。
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

        void语句可以作为单个语句调用,其他情况下,函数都以表达式调用;

        在一个语句中调用非void类型的函数会导致一个警告信息,仿真器会将函数返回值强制转换成void类型,效果如同:void`(some_function());

a = b + myfunc(c, d);              //函数作为表达式调用

myprint(a);                        //函数作为一条语句调用
function void mypritn(int a);
  ...
endfunction

2.5变量的生命周期

        SV中,将数据的生命周期分为动态(automatic)和静态(static);

        1)局部变量的生命周期同其所在域共存亡,例如function/task中的临时变量,在其方法调用结束后,临时变量的生命也将终结,所以它们是动态生命周期;

        2)全局变量即伴随着程序执行开始到结束一直存在,例如module中的变量默认情况下全部为全局变量,用户也可理解为module中的变量由于在模拟硬件信号,所以它们是静态生命周期;

        3)如果数据变量被声明为automatic,那么在进入该进程/方法后,automatic变量会被创建,而在离开该进程/方法后,automatic变量会被销毁。而static变量在仿真开始时即会被创建,而在进程/方法执行过程中,自身不会被销毁,且可以被多个进程和方法所共享;

        4)在类class中定义的任务和函数总是自动automatic的。所有形式参数和局部变量都存储在堆栈上;

        5)在module、program和interface中定义的task、function默认都是static类型。

2.5.1定义为自动存储automatic

        任务定义为自动存储automatic可以采用以下两种方式:

  1. 显式声明:使用关键字automatic作为任务和函数声明的一部分;
  2. 隐式声明:通过将任务和函数定义在被定义为automatic类型的module, interface, program, or package中。

2.5.2program的优势

        SV通过program可以将DUT与TB的领地做清晰的划分,根本上从调度区域的不同执行顺序来解决的。

  1. program可用于执行测试案例(testcase),module不可;
  2. program可用于封装与测试案例相关的数据变量,module不可;
  3. 可以在program内部定义变量,以及发起多个initial块;
  4. program内部定义的变量赋值的方式应该采用阻塞赋值(软件方式);
  5. program内部在驱动外部的硬件信号时应该使用非阻塞赋值(硬件方式);
  6. program中的initial块(类软件的执行方式)会在reactive区域被执行,而program之外的initial块(module内部)则会在active区域被执行,这一点值得注意。

2.5.3Program的功能

  1. program可以在顶层文件中进行例化,以实现TB与DUT的连接;
  2. 可以像module一样使用interface和端口进行连接;
  3. 可以有initial、task、function代码块,但是不能存在always代码块;
  4. program中的initial语句块执行到结束时,隐式执行$finish语句;

2.6时间值

2.6.1 描述时间单位和精度

        方式一:'timescale

        方式二:timeunit和timeprecision

2.6.2 时间参数

  1. $timeformat:(时间标度(-9代表ns,-12代表ps),小数点后的数据精度,时间值之后的后缀字符,显示数值的最小宽度)。$timeformat(-9,3,“ns”,8);//1.000ns
  2. $time:根据模块的时间精度要求进行舍入的整数。
  3. $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

你可能感兴趣的:(集成测试,模块测试,功能测试)