目录
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 时间参数
从硬件实现的角度来看,Verilog通过always,initial过程语句块、信号数据连接来实现进程间通信,我们将不同的module作为独立的程序块,他们之间的通信和同步通过信号的变化(event触发)或者通过等待特定事件(时钟周期)或者时间(固定延时)来完成。
1)always是为了描述硬件的行为,而在使用时需要注意哪种使用方式是时序电路描述,哪种使用方式是组合电路描述。
2)always中的@(event..)敏感列表是为了模拟硬件信号的触发行为,同学们需要正确对标硬件行为和always过程块描述。
3)always过程块是用来描述硬件时序电路和组合电路,只可以在module或者interface中使用。
1)initial与always在执行路径上有明显区别,initial非常符合软件的执行方式,即只执行一次。
2)initial和always一样,无法被延迟执行,即在仿真一开始它们都会同时执行,而不同的initial和always之间在执行顺序上没有顺序可言,不能将它们在代码中的前后顺序与它们的执行顺序画上等号。
3)initial从其执行路径的属性来看,它不应该存在于硬件设计代码中,它本身不可综合,对于描述电路没有任何帮助。
4)initial是为了测试而生的,由于测试需要按照时间顺序的习惯,即软件方式。
5)在Verilog时代,所有的测试语句都可以被放置在initial中,为了便于统一管理测试顺序,建议将有关测试语句都放置在同一个initial过程块中。
6)initial过程块可以在module、interface和program中使用。对于过程块的书写方式,需要使用begin..end将其作用域包住,同样适用于控制语句、循环语句等等。
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。
1)task无法通过return返回结果,因此只能通过output、 inout或者ref的参数来返回
2)task内可以置入耗时语句,而function则不能。常见的耗时语句包括@event、wait event、# delay等。
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。 |
形参变量 |
函数中至少包含一个输入变量进行传参 |
任务中可以没有输入、输出变量 |
调用方式 |
函数的调用可以以语句的一部分出现 |
任务的调用通过一条**单独的语句实现 |
调用关系 |
函数中只能调用函数,不能调用任务 |
任务中可以调用任务,也可以调用函数 |
systenverilog对任务和函数做出了重大改进,子程序中的begin end语句是可选的。在SystemVerilog中可以在task和function中直接使用多条语句,这些语句是顺序执行的。
在SystemVerilog中任务和函数的形式参数可以声明在其名字后的圆括号中(参数的默认方向是input,默认类型是logic)。每一个形式参数可选的方向属性如下:
SystemVerilog提供了两种方式来为函数和任务传递参数:值传递和引用传递。
引用传递的优势:
使用引用传递的示例:
function void print_check(const ref bit[31:0] a[]);
bit [31:0] checksum=0;
for(int i=0; i
添加了const修饰符的ref参数,子程序不能修改其数组的值
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返回各种数值;
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
SV中,将数据的生命周期分为动态(automatic)和静态(static);
1)局部变量的生命周期同其所在域共存亡,例如function/task中的临时变量,在其方法调用结束后,临时变量的生命也将终结,所以它们是动态生命周期;
2)全局变量即伴随着程序执行开始到结束一直存在,例如module中的变量默认情况下全部为全局变量,用户也可理解为module中的变量由于在模拟硬件信号,所以它们是静态生命周期;
3)如果数据变量被声明为automatic,那么在进入该进程/方法后,automatic变量会被创建,而在离开该进程/方法后,automatic变量会被销毁。而static变量在仿真开始时即会被创建,而在进程/方法执行过程中,自身不会被销毁,且可以被多个进程和方法所共享;
4)在类class中定义的任务和函数总是自动automatic的。所有形式参数和局部变量都存储在堆栈上;
5)在module、program和interface中定义的task、function默认都是static类型。
任务定义为自动存储automatic可以采用以下两种方式:
SV通过program可以将DUT与TB的领地做清晰的划分,根本上从调度区域的不同执行顺序来解决的。
方式一:'timescale
方式二:timeunit和timeprecision
`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