SystemVerilog:静态变量和静态⽅法以及数据⽣命周期属性
静态变量 static variable
对于普通的实例类属性,每⼀个类实例的每⼀个变量都有⾃⼰的copy(单独的内存空间),相互独⽴。但是有时会要求⼀个类的所有实例都共享变量的⼀个版本,也就是说所有实例都共享同⼀个copy,该变量对所有实例都是可见并相同的。
这样的类属性就是静态属性(静态变量),使⽤关键字static产⽣。通过下⾯的例⼦可以清楚的看见静态属性的特点。
class Packet;
bit [15:0] addr;
bit [7:0] data;
static int static_ctr =0;
int ctr =0;
function new(bit [15:0] ad, bit [7:0] d);
addr = ad;
data = d;
static_ctr++;
ctr++;
$display ("static_ctr=%0d ctr=%0d addr=0x%0h data=0x%0h", static_ctr, ctr, addr, data);
endfunction
endclass
module tb;
initial begin
Packet p1, p2, p3;
p1 =new(16'hdead, 8'h12);
p2 =new(16'hface, 8'hab);
p3 =new(16'hcafe, 8'hfc);
end
endmodule
ncsim> run
static_ctr=1 ctr=1 addr=0xdead data=0x12
static_ctr=2 ctr=1 addr=0xface data=0xab
static_ctr=3 ctr=1 addr=0xcafe data=0xfc
ncsim:*W,RNQUIE: Simulation is complete.
静态属性还有⼀个重要的特点是其类⽆需实例化,就可直接使⽤静态属性。需要利⽤范围操作符::
静态⽅法
使⽤关键字static 可以类⽅法声明成静态⽅法。
⼀个静态⽅法遵守所有的类范围和访问规则,但是它可以在类的外部被调⽤,即使没有该类的实例。外部调⽤的⽅法同样需要使⽤范围操作符::。
⼀个静态⽅法不能访问⾮静态的属性或⽅法,但是可以直接访问静态属性,或者调⽤同⼀个类中的静态⽅法,原因很简单因为静态属性和静态⽅法可以在类没有实例化时被调⽤,具有全局的静态⽣命周期,⽽普通的⾮静态成员⽆法做到这点。
在⼀个静态⽅法内部访问⾮静态成员或者使⽤this句柄都是⾮法的。
静态⽅法不能是虚拟的。
class Packet;
static int ctr=0;
function new();
ctr++;
endfunction
static function get_pkt_ctr ();
$display ("ctr=%0d", ctr);
endfunction
endclass
module tb;
Packet pkt [6];
initial begin
for(int i =0; i < $size(pkt); i++) begin
pkt[i]=new;
end
Packet::get_pkt_ctr();// Static call using :: operator
pkt[5].get_pkt_ctr();// Normal call using instance
end
endmodule
static修饰的变量ncsim> run
ctr=6
ctr=6
ncsim:*W,RNQUIE: Simulation is complete.
注意上述代码中对get_pkc_ctr()的调⽤⽅式,⼀种是使⽤范围操作符::,⼀种是使⽤普通的层次化引⽤⽅式。两者结果没有区别,并且pkt[]的6个成员的调⽤结果都会是⼀样的(操作对象是只有⼀个copy的静态属性)。
⽣命周期属性
在上⾯的静态属性和静态⽅法声明中,都使⽤到了关键字static,但是SystemVerilog还有另外⼀个含义的static,它与automatic⼀起⽤来决定数据的⽣命周期属性。
static声明的数据(并不是类属性)具有静态的⽣命周期(在整个确⽴和仿真时间中存在)
automatic具有调⽤期或者激活期类的声明周期。
在类外使⽤static声明的数据和在类内部使⽤static声明的类属性都有着静态⽣命周期(⽆需实例化分配内存),这两者的声明⽅式没有区别都是在数据前⾯加上static。只不过前者如果是全局作⽤范围(即在模块,接⼝,函数和任务外声明的),那么其使⽤⽅式就是直接使⽤,但是静态类属性也可以全局范围内引⽤,不过需要使⽤范围操作符::。
static和automatic可以将⼀个任务或者函数显式地声明成静态或者⾃动的:⼀个⾃动任务、函数或块内声明的数据缺省情况下具有调⽤期或激活期内的⽣命周期,并且具有本地的作⽤范围; ⼀个静态任务、函数或块内声明的数据缺省情况下具有静态⽣命周期并具有本地的作⽤范围。
此外静态任务或函数或块内的数据可以被显式地声明成⾃动的,⽽⾃动的任务、函数或块内的数据也可以被显式地声明成静态的。
其实将⼀个任务函数或块声明成static或者automatic还是为了决定其内部数据的⽣命周期属性,这与静态⽅法的static有本质上的不同。⽽且两者的声明⽅式也有区别:
如果想声明⼀个静态的任务函数或块:
class test;
task static bar(); …… endtask //具有静态变量⽣命周期的⾮静态⽅法
endclass
如果想声明⼀个静态⽅法:
class test;
static task bar(); …… endtask //具有⾃动变量⽣命周期的静态⽅法
endclss
由上可见两个static不仅含义不同,声明⽅式也不同。
注意第⼆句话"具有⾃动变量⽣命周期的静态⽅法",也就是说这个任务虽然是静态任务,但是其内部数据的缺省情况下的⽣命周期是⾃动的,这是因为对于模块、接⼝或程序中定义的任务函数或块可以⽤⽣命限定符static和automatic来指定其中声明的所有变量的缺省⽣命周期,默认的⽣命限定符是static, 然⽽对类⽅法以及for循环声明的循环变量缺省的⽣命限定符是⾃动的automatic。举个例⼦
program automatic test;
int i;// 由于没有在⼀个过程块中,所以i还是静态的
task foo(int a);// 在foo内的⾃变量和变量是⾃动的
...
endtask
endmodule
补充两点:
在模块、接⼝、任务或函数外声明的任何数据都有全局的作⽤范围和静态⽣命周期
在模块或接⼝内但是在任务、函数或进程外声明的数据具有本地的作⽤范围,并具有静态⽣命周期。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论