域(scope):为了区分硬件设计、软件世界,将定义的软件变量或者例化的硬件其所在的空间称之为域。module/endmodule,interface/endinterface可以被视为硬件世界,program/endprogram和class/endclass可以被视为软件世界
initial和always
-
always是为了描述硬件行为,而在使用时需要注意哪种使用方式是时序电路(寄存器、锁存器)描述,哪种使用方式是组合电路描述。initial不可综合(为测试而生),always可综合。
-
always中的@(event..)敏感列表是为了模拟硬件信号的触发行为,需正确对标硬件行为和always过程块描述。
-
initial符合软件的执行方式,只执行一次。一般初始化就在软件中(initial)或定义时。
-
initial和always一样,无法被延迟执行,即在仿真一开始它们会同时执行,不同initial和always之间在执行顺序上没有顺序可言
-
initial从执行路径属性来看,不应该存在于硬件设计代码中,它本身不可综合,对于描述电路没有任何帮助
-
initial就是为了测试而生,由于测试需要按照时间顺序的习惯即软件方式来完成,所以initial便可以实现这一要求
-
所有测试语句都可以放置在initial中,为了便于统一管理测试顺序,建议将有关测试语句都放置在同一个initial过程块中
-
initial过程块可以在module、interface和program中使用
-
对于过程块的书写,用begin..end将作用域包住,同样适用于控制语句、循环语句等
function
-
可以在参数列表中指定输入参数(input)、输出函数(output)、输入输出函数(inout)或者引用参数(ref)
-
可以返回数值或者不返回数值(void)
function int double (input a); //输入为a,double变量,返回int
return 2*a;
endfunction
initial begin
$display("double of %0d is %0d",10,double(10));
end
-
默认数据类型为logic,如input[7:0] addr
-
数组可以作为形式参数传递
-
function可以返回或者不返回结果,如果返回即需要用关键词return。如果不返回则应该在声明function时采用void function()
-
只有数据变量可以在形式参数列表中被声明为ref类型,而线网类型不能被声明为ref类型 //验证里面只能传递变量,不能传递硬件的信号和存储器
-
在使用ref类型时,有时候为了保护数据对象只被读取不被写入,可以通过const方式来限定ref声明的参数
-
在声明参数时,可以给入默认数组,例如input[7:0]addr=0,同时在调用时如果省略该参数的传递,那么默认值即会被传递给function
-
function不标明方向,默认为input
typedef structure{
bit [1:0] cmd;
bit [7:0] addr;
bit [31:0] data;
}trans;
function automatic void op_copy(trans t,trans s); //此时,t和s都默认为input
t=s;
endfunction
initial begin
trans s;
trans t;
s.cmd='h1;
s.addr='h10;
s.data='h100;
op_copy(t,s); //调用函数,退出后s得到三个参数为{0,0,0}
t.cmd='h2;
end
task
-
task无法通过return返回结果,因此只能通过output、inout或ref的参数来返回
-
task内可以置入耗时语句,而function则不能。常见的耗时语句包括@event、wait event、#delay等。
task mytask1 (output logic [31:0] x,input logic y);
...
endtask
注意:
-
task可以内置耗时语句
-
非耗时方法定义时使用function,在内置耗时语句时使用task。可以知道function只能应用于纯粹的数字或者逻辑运算,而task则可能会被应用于需要耗时的信号采样或者驱动场景
-
如果要调用function,则使用function和task均可对其调用;而如果要调用task,建议使用task调用,因为如果调用的task内置有耗时语句,则外部调用它的方法类型必须为task。
变量生命周期
-
在sv中,将数据的生命周期分为动态(automatic)和静态(static)
-
局部变量的生命周期同其所在的域共存亡,例如function、task中的临时变量,在其方法调用结束后,临时变量的生命也将终结,所以它们是动态生命周期
-
全局变量即伴随着程序执行开始到结束一直存在,例如module中的变量默认情况下全部为全局变量,用户也可理解为module中的变量由于在模拟硬件信号,所以它们是静态生命周期(硬件从仿真0时刻开始就构建好,固定下来)
-
如果数据变量被声明为automatic,那么在进入该进程/方法后,automatic变量会被创建,而在离开该进程/方法后,automatic变量会自动销毁。而在static变量在仿真开始时即会被创建,而在进程/方法执行过程中,自身不会被销毁,且可以被多个进程和方法所共享。
module 里默认生命周期为static
class 里默认生命周期为automatic,类里需要静态变量
动态变量每次调用都会赋予新值;静态变量则不会;不标明静态或动态默认为静态变量
-
对于automatic方法,其内部的所有变量默认也是automatic,即伴随automatic方法的生命周期建立和销毁
-
对于static方法,其内部的所有变量默认也是static类型
-
对于automatic或者static方法,用户可以对其内部定义的变量做单个声明,使其类型被显式声明为automatic或者static
-
对于static变量,用户在声明变量时应该同时对其做初始化 ,而初始化只会伴随它的生命周期发生一次,并不会随着方法调用被多次初始化
-
在module、program、interface、task和function之外声明的变量拥有静态的生命周期,即存在于整个仿真阶段
-
在module、interface和program内部声明,且在task、process或者function外部声明的变量也是static变量,且作用域在该块中
-
在module、program和interface中定义的task、function默认都是static类型
-
在过程块中(task、function、process)定义的变量均跟随它的作用域,即过程块的类型。如果过程块为static,则它们也默认为static,反之亦然。这些变量也可以由用户显式声明为automatic或者static
-
为了使得在过程块中声明的变量有统一默认的生命周期,可以在定义module、interface、package或者program时,通过限定词automatic或者static来区分。对于上述程序块默认的生命周期类型为static