SV数据类型
SV共有6种内建的数据数据类型,分别为
- logic
- bit
- byte
- shortint
- int
- longint
注:SV对Verilog数据类型也是兼容的,即Verilog的数据类型SV也都包括。例如Verilog net类型的wire,variable中的reg, integer, real等。
logic:
SV中logic为4值逻辑,即其值可以有4个,分别为 0/1/x/z
logic既可被过程赋值也能被连续赋值,编译器可自动推断logic是reg还是wire。
唯一的限制是logic只允许一个输入,不能有多个结构性的驱动,例如inout类型端口不能定义为logic。所以单驱 动时用logic,多驱动时用wire。
bit:
-
SV中bit类型为双状态,2值逻辑,分别为0/1
-
bit类型为无符号数
**byte/shortint/int/longint: **
- byte/shortint/int/longint:
- 数据类型都为双状态,2值逻辑,分别为0/1
- 位宽分别为 8/16/32/64
- 都是有符号数
数据类型 | 2值逻辑/4值逻辑 | 默认值 | 是否有符号数 |
---|---|---|---|
logic | 4 | x | 无符号 |
bit | 2 | 0 | 无符号 |
byte | 2 | 0 | 有符号 |
shortint | 2 | 0 | 有符号 |
int | 2 | 0 | 有符号 |
longint | 2 | 0 | 有符号 |
SV定宽数组
1. 定宽数组格式声明 |
定宽数组的格式声明为:Type name [constant] , 其中 type 为存储数据的类型。
例如:
int array[16], array为数组的名字,int为数组数据的存储类型为int型数据,16为数组的大小,共16个元素。
int array[8] [4], array为数组的名字,int为数组数据的存储类型为int型数据,数组的大小为8行4列,共32个元素。
2. 数组的基本操作 |
使用赋值符号:`{} and `{n{}}。
int mouer[4] = ‘{0,1,2,3}; // 对4个元素进行初始化
int mouer[5];
mouer = ‘{4,3,2,1,0}; // 为5个元素赋值
mouer[0:2] = ‘{5,6,7}; // 为前3个元素赋值
mouer = ‘{4{8}}; // 4个值全部为8 {8,8,8,8}
mouer= ‘{9, 8, default:-1}; // {9,8, -1, -1, -1}
遍历数组常用方法之一为for循环,如下例所示:
initial begin
bit [31:0] src[5],dst[5];
for (int i=0; i<$size(src); i++)
src[i] = i;
end
注:
-
i变量为for循环内部的局部变量;
-
在SV中,$size函数返回数组的宽度;
-
可以不使用$size函数,可以直接使用数组的最大维度,上例中为4(索引为0~4);
二维数组亦为SV常用数组,二维可以理解为数组存储的数据为两个维度。
使用foreach也可以方便快捷的遍历数组:只需要指定数组名,并且在后面的方括号中给出索引变量,SV会自动遍历数组,索引变量将自动声明,并只在循环内有效。如下例所示:
initial begin
bit [31:0] src[5],dst[5];
foreach (dst[j])
dst[j] = src[j]* 2; // dst doubles src values
end
3. 数组的注意事项 |
-
数组可以是一维的,也可以是多维的
-
byte/short/int类型的数据,都是占据一个字(32bit)的存储空间
-
longint占据两个字(64bit)的存储空间
-
如果越界读取数组的话,则返回数组元素类型的缺省值:
4.1 如果数组元素为4值逻辑,如logic,则返回x;
4.2 如果数组元素为2值逻辑,如int或者bit,则返回0
4.3 本规则适用于定宽数组,动态数组,关联数组和队列,以及地址中含有x或z的情况。
-
如果越界写数组的话,则写数据被忽略.
SV动态数组
1. 动态数组 |
SV提供了动态数组,可以在仿真时分配空间或者调整宽度。
动态数组的声明使用空的[],数组的宽度不是在编译时给出,而是在程序运行时指定。
动态数组在最开始是空的,所以必须调用new[]操作符来分配空间,同时在方括号中传递数组宽度。
动态数组声明格式为:type name [] , 其中 type 为存储数据的类型。声明时[]为空。
例如: int array[], 其中array为数组的名字,int为数组数据的存储类型为int型数据。在使用数组之前一定要调用**new[]**函数为数组分配空间后才可使用。
int x[];
x=new [5];
2. 动态数组一些方法 |
delete和 size
int test[];
test=new[4]; //用new来创建数组
num=test.size(); //用size获取数组大小
test.delete(); //用delete删除数组
注:$size(test)同样可以获取数组的大小
SV关联数组
1. 关联数组 |
SV提供了关联数组类型,用来保存稀疏型的数据。与其他数组不同的是,关联数组有利于使用零散的存储空间,如下图所示,关联数组存储数据并不是连续的存储空间。
data | |||||||||
---|---|---|---|---|---|---|---|---|---|
index | 0 | – | 3 | 50 | 100 |
关联数组声明方式:data_type array_name[index_type],
例如 int array[string] 其中int为关联数组存储数据的类型为int型数据,数组的索引为字符串索引。
2. 关联数组的索引 |
关联数组的索引可以为以下几种:
-
通配符索引:任意数据类型进行索引:int array_name [*];
-
字符串索引:int array_name[string];
-
类索引:int array_name [some_Class];
-
integer(或int)索引:int array_name[integer];
-
有符号的压缩数组索引:typedef bit signed [4:1] Nibble; int array_name[Nibble];
-
无符号的压缩数组索引:typedef bit [4:1] Nibble; int array_name [Nibble];
-
其它用户定义类型索引:typedef struct { real R; int I[*]; } Unpkt; int array_name [Unpkt];
实际上,关联数组实现了一个所声明类型的元素的查找表。用作索引的数据类型作为查找表的查找键值,并强制了一种顺序。例如:
integer i_array[*]; // 整数关联数组(未指定索引)
bit [20:0] array_b[string]; // 21位向量的关联数组,使用字符串类型作为索引
event ev_array[myClass]; // 事件类型的关联数组,使用类myClass索引
3. 关联数组的一些用法 |
num() //返回数组长度
delete() //删除指定元素或者所有元素
exists() //检查是否元素存在,存在返回1,否则返回0
first() //将指定的索引变量赋值为数组第一个索引的值
last() //将指定的索引变量赋值为数组最后一个索引的值
next() //索引变量被赋值为下一个条目的索引
prev() //索引变量被赋值为上一个条目的索引
4. 关联数组注意事项 |
简单的for循环不能遍历关联数组,需要使用foreach遍历数组,还可以使用内建的first()和next()函数。
读取不存在的关联数组元素,4值逻辑返回x,2值逻辑返回0。
关联数组的索引可以是多种数据类型:string、int,甚至是class以及用户自定义的数据类型。
initial begin
bit [63:0] assoc[int],idx =1;
// Initialize widely scatteredvalues
repeat (64) begin
assoc[idx] =idx;
idx = idx << 1;
end
// Step through all index values withforeach
foreach (assoc[i])
$display("assoc[%h]= %h", i, assoc[i]);
// Step through all index values withfunctions
if (assoc.first(idx))
begin // Get first index
do
$display("assoc[%h]=%h",idx, assoc[idx]);
while (assoc.next(idx)); // Get next index
end
// Find and delete the first element
assoc.first(idx);
assoc.delete(idx);
$display("The array now has %0delements", assoc.num);
end
SV数组方法
SV提供了很多数组方法,这些方法用于任何一种非合并的数组类型,包括定宽数组,动态数组,队列和关联数组。这些方法有繁有简,种类繁多,包括求和,求积,排序等。
1. 数组缩减方法 |
数组缩减方法:sum(求和)、product(求积)、and(与)、or(或)、xor(异或)
byte b[] = ‘{ 1, 2, 3, 4};
int y;
y = b.sum ; // y becomes 10 => 1 + 2 +3 + 4
y = b.product ; // y becomes 24 => 1 * 2 *3 * 4
y = b.xor with ( item + 4 ); // y becomes12 => 5 ^ 6 ^ 7 ^ 8
logic [7:0] m [2][2] = '{ '{5, 10}, '{15, 20} };
int y;
y = m.sumwith (item.sumwith (item)); // y becomes 50 => 5+10+15+20
logic bit_arr[1024];
int y;
y = bit_arr.sumwith ( int'(item) ); // forces result to be 32-bit
1. 数组定位方法 |
int f[6] =‘{1,6,2,6,8,6};
int d[] = ‘{2,4,6,8,10};
int q[$] = {1,3,5,7}, tq[$];
tq = q.min();// {1}
tq = d.max();// {10}
tq = f.unique();// {1,6,2,8}
注意:tq是一个队列,即本例中的数组方法返回一个队列。
1. 数组排序方法 |
int d[] = ‘{9,1,8,3,4,4};
d.reverse();// ‘{4,4,3,8,1,9}
d.sort();// ‘{1,3,4,4,8,9}
d.rsort();// ‘{9,8,4,4,3,1}
d.shuffle();// ‘{9,4,3,8,1,4}
SV结构体与枚举
1. 结构体 |
struct {
int a;
int b;
bit c;
} struct_name
结构体内引用变量的方式为“.”,即struct_name.a可以引用结构体内部的a变量。
typedef struct {
int addr = 1 ;
int crc;
byte data [4] = '{4{1}};
} packet1;
packet1pi = '{1,2,'{2,3,4,5}};
typedef struct {
int addr = 1 + constant;
int crc;
byte data [4] = '{4{1}};
} packet1;
Packet1 p1;
p1.addr = 32’h0000_0080;
p1.data = 32’h8c;
struct{ bit [7:0] opcode; bit[23:0]addr; }IR;
IR.opcode = 1; // set field in IR.
typedef struct {
bit [7:0] opcode;
bit [23:0] addr;
} inst; // named inst type
inst IR; // define variable
2. 枚举 |
SV枚举提供了一种强大的变量类型,可以使得用户自定义特定名称的集合,例如指令中的操作码或者状态机中的编码等,可阅读性比较好。这种数据类型称为枚举。
枚举的声明格式为:enum {变量名,…,变量名} enum_name
例如:enum {RED,BLUE,GREEN} color
SV队列
1. 队列 |
SV引入了队列的数据类型,使用方便,性能上比动态数组好很多。
队列可以存储任意的数据类型,包括SV内建的数据类型,也可以是用户自定义的数据类型。队列相当于维护了一个表格,其中表格可以实现任意的增删改查。
队列的顺序是由用户来维护的。
队列的声明格式为 data_type queue_name [$]
例如:**int data_q [ ] ∗ ∗ ,其中 i n t 为队列中存储的数据类型为 i n t 型数据,声明队列时使用符号 [ ]**,其中int为队列中存储的数据类型为int型数据,声明队列时使用符号[ ]∗∗,其中int为队列中存储的数据类型为int型数据,声明队列时使用符号[]
2. 队列的方法 |
queue_name.size //返回queue的大小
queue_name.insert(index,item) //在某个索引处插入某元素
queue_name.delete(index) //刪掉某元素或整个queue
queue_name.pop_front() //去除第一个元素
queue_name.pop_back() //去除最后一个元素
queue_name.push_front() //插入元素到queue(0)
queue_name.push_back() //插入元素到queue($)
3. 队列的注意事项 |
-
队列的存储空间无限大,理论上为物理内存的最大空间,从0到$。
-
可以在队列的任意地方实现元素的增删改查。
-
注意不要对队列使用构造函数new[]。
int q[$] = {2, 4, 8};
int p[$];
int e, pos;
e = q[0]; // 读取第一个(最左边)条目。
e = q[$]; // 读取最后一个(最右边)条目。
q[0] = e; // 写第一个元素
p = q; // 读和写整个队列(拷贝)
q = {q, 6}; //在队列的尾部插入'6'
q = {e, q}; // 在队列的头部插入'e'
q = q[1:$]; // 删除第一个(最左边)元素
q = q[0:$-1]; // 删除最后一个(最右边)元素
q = q[1:$-1]; // 删除第一个和最后一个元素
q = {}; // 清除队列(删除所有的元素)
q = {q[0:pos-1], e, q[pos:$]}; // 在位置'pos'处插入'e'
q = {q[0:pos], e, q[pos+1:$]}; // 在位置'pos'之后插入'e’
SV过程语句
1. initial 语句 |
与verilog 相同不在概述
2. always语句 |
SV对Verilog 的always语句做了扩展,除了always外还有:
-
always_comb //组合逻辑建模
-
always_latch //为latch建模
-
always_ff //为时序逻辑建模
其中,always_comb为SV中增加的为组合逻辑建模的语句,always_latch为latch建模,always_ff为时序逻辑建模。
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
...
end else begin
...
end
end
always_comb
a = b & c;
always_ff @(posedge clockor posedge reset) begin
r1 <= reset ? 0 : r2 + 1;
...
end
always_latch
if(ck) q <= d;
3. final语句 |
-
final块类似于intial块,它们都定义了一个过程化的语句块,不同的是,final块仅仅在仿真结束前执行。典型情况下,final块用来显示有关仿真的统计信息。
-
在final块中可以使用的语句同允许在一个函数声明中使用的语句一样。与intial块不同,final块不会作为一个单独的进程执行。
-
当一个显式或隐式的$finish调用引起仿真结束的时候会执行final块。
-
final块只能在一次仿真中触发一次。
final
begin
$display("Number of cyclesexecuted %d",$time/period);
$display("Final PC = %h",PC);
end
initial begin
…
$finish();
end
SV控制流
1. 选择语句 |
unique if
-
unique if指示在一系列if…else…if条件中不应该有任何交迭,也就是说,这些条件是互斥的。
-
如果有多于一个条件为“真”,则会报错。指定了所有的有效选项,不存在遗漏。
-
如果没有条件为“真”,或者没有条件为“真”,并且最后的if没有对应的else语句,同样会报错。
unique if ((a==0) || (a==1)) $display("0 or 1");
else if (a == 2)$display("2");
else if (a == 4)$display(“4”); // 值3,5,6,7会引起一个错误
priority if
-
priority if指示一系列if…else…if条件应该按列出的顺序计算。
-
如果多个条件同时满足,那么选择第一个满足条件的分支,即具有优先级逻辑。
-
如果软件工具发现没有条件为“真”,或者没有条件为“真”,并且最后的if没有对应的else语句,则会报错。
priority if (a[2:1]==0) $display("0 or 1");
else if (a[2]==0) $display("2 or 3");
else $display("4 or 7");// 覆盖了所有可能的其他值,因此没有错误。
priority case
bit [2:0] a;
priority case(a) // 值4,5,6,7会引起一个运行时警告
3'b00?: $display("0 or 1");
3'b0??: $display("2 or 3");
endcase
unique case
unique case应该检查case条目的交迭。如果多于一个case条目匹配于case表达式,那么unique case应该发布一条警告信息。
bit [2:0] a;
unique case(a) // 值3,5,6,7会引起一个运行时警告
0,1: $display("0 or 1");
2: $display("2");
4: $display("4");
endcase
2. 循环语句 |
-
Verilog提供了for、while、repeat以及forever循环。
-
SystemVerilog增强了Verilog的for循环。
-
SystemVerilog加入了一个do…while循环和一个foreach循环。
//for
for (int i = 0; i <= 255; i++)
//do while
do // do...whileloop
sum += array[j]; // Accumulate
while(j--);
//foreach
foreach (prod[k,m])
prod[k][m] = k * m; // 初始化
//repeat
repeat(5) @(posedge clk)
3. 跳转语句 |
SystemVerilog加入了像C语言中一样的跳转语句:break、continue和return。
break
像C语言一样跳出循环,结束循环体。
continue
像C语言一样跳转到循环的尾部,结束本次循环,继续下一次循环。
return expression
退出一个函数且具有返回值,expression必须具有正确的类型,否则会报错。
return
退出一个任务或void函数
4. disable语句 |
disable语句用于终止正在运行的代码块。可以分为diable fork以及disable lable
SystemVerilog可以在任务中使用return,但也支持disable。
如果disable被应用到一个命名的任务,那么这个任务中所有当前正在运行的部分都会被关闭。
for (int i = 0; i < 5; i++)begin : forloop
if( i == 3 )
disable forloop
$display("i=%0d",i);
end
5. event语句 |
-
event是一个静态的句柄,用于同步多个线程,在一个线程中触发事件,在另外一个线程中等待事件。
-
事件的句柄可以是null。
-
声明一个事件如下方式:event done;
-
事件的触发使用**->,事件的等待使用@或者.triggered**
module tb;
event done;
initial begin
#20 ->done;
$display("[%0t] Thread1: triggered,",$time);
end
initial begin
$display("[%0t] Thread2: waiting,",$time);
@(done);
$display("[%0t] Thread2: received,",$time);
end
initial begin
$display("[%0t] Thread3: waiting,",$time);
@(done.triggered);
$display("[%0t] Thread3: received,",$time);
end
SV块语句
SV task与function
1. task |
SV对task进行了增强和扩展,扩展内容如下:
- SystemVerilog加入了在静态任务和函数中声明自动变量的功能。
- SystemVerilog加入了在自动任务和函数中声明静态变量的功能。
- 无需一个begin…end块或fork…jion块就可以在一个任务或函数中使用多条语句的能力。
- 在到达任务或函数的结尾之前从任务或函数返回的能力。
input // 在开始的时候拷贝值
output // 在结束的时候拷贝值
inout // 在开始的时候拷贝,在结束的时候输出
ref // 传递引用
其中,input为输入参数的传递,为task开始调用时,将实参的数值copy一份传递给task的形参。类似的,output与inout同样是参数的copy传递给形参。ref为实参引用的传递,传递的是实参的地址,值得注意的是,如果在task内部将参数修改,那么实参的数值对应的也会被修改。
在SystemVerilog中,如果没有指定参数的方向,那么它的缺省方向是输入。一旦指定了一个方向,那么它就成为后续参数的缺省方向。在下面的例子中,形式参数a和b缺省为输入,u和v都是输出。
task mytask(a, b, output logic [15:0] u, v);
...
endtask
每一个形式参数都具有一个数据类型,它或者显式声明,或者从一个缺省类型继承。SystemVerilog中任务参数的缺省类型为logic。
SystemVerilog允许将一个数组声明为task的形式参数。例如:
task mytask(input [3:0][7:0] b[3:0], output[3:0][7:0]y[1:0]);
...
endtask
-
在SystemVerilog中,多条语句可以在task和endtask之间被写入,可以省略begin…end。如果省略begin…end,这些语句也是顺序地执行,这与它们被包含在begin…end中是一样的。
-
task中没有任何语句也是合法的,即空task。
-
在Verilog中,当task到达endtask的时候任务退出。而对于SystemVerilog,在endtask关键字之前可以使用return语句退出task。
2. function |
SystemVerilog扩展了Verilog函数的能力,它允许函数具有与任务相同的形式参数。参数用法与task一致
input // 在开始的时候拷贝值
output // 在结束的时候拷贝值
inout // 在开始的时候拷贝,在结束的时候输出
ref // 传递引用
在Verilog中,函数必须具有返回值。返回值是通过为函数的名字赋值来完成的。
SystemVerilog允许将函数声明成void类型,它没有返回值。对于非void函数,可以使用return语句实现。例如:
function int myfunc (input int x,y);
return x * y - 1; //使用return语句指定返回值
endfunction
SV 丢弃函数返回值:通过将函数返回值强制转换成void类型,SystemVerilog允许使用void数据类型来忽略一个函数的返回值。调用方法如下:
void'(some_function());
3. task与function参数传递 |
V中task与function的参数传递,共有四种方式,分别为:通过值传递,通过引用传递,缺省的参数值以及通过名字传递。
-
通过值传递是向function传递参数的缺省机制,它也是Verilog-2001提供的唯一的参数传递机制。这种参数传递机制是通过将每一个参数拷贝到function区域的方式实现的。
-
如果function是automatic的,那么function在它的堆栈中保留一个参数的本地拷贝。如果参数在function中被改变,那么这种改变在function外是不可见的。
-
当参数很大的时候,我们可能不希望拷贝这个参数。拷贝会造成性能上的影响。
-
通过值传递是向function传递参数的缺省机制,它也是Verilog-2001提供的唯一的参数传递机制。这种参数传递机制是通过将每一个参数拷贝到function区域的方式实现的。
-
如果function是automatic的,那么function在它的堆栈中保留一个参数的本地拷贝。如果参数在function中被改变,那么这种改变在function外是不可见的。
-
当参数很大的时候,我们可能不希望拷贝这个参数。拷贝会造成性能上的影响。
通过引用传递的参数不会拷贝到function区域,相反,一个对原始参数的引用会被传递到function。然后function可以通过引用访问参数数据。为了指示通过引用传递的参数,参数声明需要以ref关键字开始。基本语法如下:
subroutine(ref type argument);
function int crc(ref byte packet[1000:1]);
for(int j=1; j<=1000; j++) begin
crc ^= packet[j];
end
endfunction
byte packet1[1000:1];
int k = crc(packet1); // 无论是通过值传递还是通过引用传递,调用方法是一样的
当通过引用传递参数的时候,在调用者内或function中对参数所作的任何改变对两者都是可见的。在function的外部可以立即(在function返回之前)看到变量的变化。
为了保护通过引用传递的参数不被子例程修改,可以将const限定符与ref一起使用,用来表明尽管这个参数是通过引用传递,但它是一个只读变量。例如:
task show (constref byte [] data);
for (int j = 0; j < data.size; j++)
$display(data[j]); // 数据可以被读出但不能被修改
endtask
为了处理一些共用的情况或者考虑一些不用的参数,SystemVerilog允许子例程声明为每一个单一参数指定一个缺省值。 缺省参数的语法如下:
subroutine([direction] [type]argument = default_value);
//可选的direction可以是input、inout、或ref(不能指定输出端口的缺省值)。
例如:这个例子声明了一个任务,read(),它具有两个缺省参数,j和data。接着这个任务就可以使用不同的缺省参数调用:
task read(int j=0, int k, intdata=1);
...
endtask;
read( , 5); // 等价于read(0, 5, 1);
read(2, 5); // 等价于read(2, 5, 1);
read( , 5, ); // 等价于read(0, 5, 1);
read( , 5, 7); // 等价于read(0, 5, 7);
read(1, 5, 2); // 等价于read(1, 5, 2);
read(); // 错误;k没有缺省值
function int fun(int j=1, strings="no");
...
endfunction
fun(.j(2), .s("yes")); //fun(2, "yes");
fun(.s("yes")); // fun(1, "yes");
fun(, "yes"); // fun(1, "yes");
fun(.j(2)); // fun(2, "no");
fun(.s("yes"), .j(2)); //fun(2 , "yes");
fun(.s(), .j()); // fun(1 , "no");
fun(2); // fun(2, "no");
fun(); // fun(1, "no");
SV进程之间的通信
Verilog提供了基本的同步机制(也就是->和@),它们仅限于静态对象,然而它们却不能满足高度动态、灵活的测试平台的需要。
SystemVerilog加入了强大而又易于使用的同步和通信机制的集合,所有这些都可以动态地产生和回收。
SystemVerilog加入了一个内建的semaphore类,这个内建类可以用来同步以及相互排斥地访问共享资源。一个mailbox内建类可以用作是进程间的通信通道。SystemVerilog还增强了Verilog的命名事件数据类型以便满足许多系统级的同步需求。
1. event |
event status; //定义了一个事件,事件的名称为 status
SV中触发一个事件时,被命名事件可以通过-> 操作符触发。触发一个事件可以为当前等待这个事件的所有进程解除阻塞。
等待一个事件被触发的基本机制是通过事件控制操作符 @。@操作符阻塞调用进程直到指定的事件被触发。
等待进程在触发进程执行触发操作符->之前必须执行@语句。如果触发器首先执行,那么等待进程会保持在阻塞状态。
2. semaphore |
semaphore是SV中一个内建的类,它提供了下列方法:
- 产生一个具有指定数目键值的semaphore:new() 方法。new()函数返回semaphore的句柄,如果没有产生semaphore则返回null。
- 从桶中获取一个或多个键值:get()方法。get()指定了需要从semaphore中获得的键值的数目,它的缺省值为1。如果指定的键值数目有效,那么方法返回并且进程会继续执行;如果指定的键值数目无效,进程会阻塞直到键值变成有效。
- 向桶中返回一个或多个键值:put()方法。当调用semaphore.put()任务的时候,指定数目的键值被返回到semaphore中。
- 尝试无阻塞地获取一个或多个键值:try_get()方法。如果指定的键值数目有效,那么try_get()方法返回1并且进程会继续执行;如果指定的键值数目无效,那么try_get()方法返回0。
3. mailbox |
maibox是一种通信机制,它使得消息能够在进程间通信。一个进程发送到mailbox的数据可以被另外一个进程获得。
mailbox的行为就像一个真实的邮箱一样。SystemVerilog的mailbox以一个可控的方式来传输和接收数据。在产生mailbox的时候,它可以具有固定大小也可以无限大。当一个具有固定大小n的mailbox包含了 n个消息的时候,mailbox会变满。
声明一个mailbox的方法如下:mailbox boxname;
mailbox同样是SV一个内建的类,它提供了下列方法:
- 产生一个mailbox:new()
- 将一个消息放置到一个mailbox中:put()
- 尝试将一个消息无阻塞地放置到一个mailbox中:try_put()
- 从一个mailbox中重新获得一个消息:get()或者peek()
- 尝试无阻塞地从一个mailbox中重新获得一个消息:try_get()或try_peek()
- 获得mailbox中消息的数目:num()
如果mailbox已满,则put()会阻塞,如果mailbox为空,则get()会阻塞。peek()任务可以获取对mailbox里面数据的拷贝而不会移除原始数据
- mailbox new()方法的原型为 function new(int bound = 0);
- new()函数返回mailbox的句柄,如果不能产生mailbox则返回null。
- 如果bound参数为0,那么mailbox是无边界的(缺省情况),此时一个put()操作应该永远不会阻塞;
- 如果bound为非0,那么它表示mailbox队列的尺寸。
- bound必须是正的。负的边界是非法的并会导致不确定的行为。
- put()方法将一个消息放入到一个mailbox中。
- put()方法严格按FIFO的顺序将一个消息存储在mailbox中。如果mailbox使用一个有界的队列产生,那么put()进程应该被挂起直到队列中有足够的空间。
- try_put()方法尝试将一个消息放置在一个mailbox中。try_put()方法严格按FIFO的顺序将一个消息存储在mailbox中。这个方法仅仅对有界的mailbox才有意义(因为对于无界的mailbox来说,put()永远不会阻塞,即put()永远能够成功)。
- 如果mailbox没有满,那么指定的消息被放置在mailbox当中并且函数返回1。如果mailbox满了,那么函数返回0。
- get()方法从一个mailbox中获得一个消息。
- get()方法从mailbox中获得一个消息,mailbox则在队列中删除这个消息。
- 如果mailbox是空的,那么当前的get()进程阻塞直到一个消息被放置到mailbox中。
- 如果在消息变量和mailbox中的消息间存在类型不匹配,那么会产生一个运行时错误。
- try_get()方法尝试无阻塞地从一个mailbox中获得一个消息。
- 如果mailbox是空的,那么try_get()方法返回0。
- 如果在消息变量和mailbox中的消息间存在类型不匹配,那么try_get()方法返回-1。
- 如果一个消息是有效的,并且消息类型与消息变量的类型匹配,那么消息被重新获得并且try_get()方法返回1。
- peek()方法从一个mailbox中拷贝一个消息但不会将其从队列中删除。
- 如果mailbox是空的,那么当前的进程会阻塞直到一个消息被放置到mailbox中。
- 如果在消息变量和mailbox中的消息间存在类型不匹配,那么会产生一个运行时错误。
- try_peek()方法尝试从一个mailbox中拷贝一个消息但不会将其从队列中删除。
- 如果mailbox是空的,那么try_peek()方法返回0。
- 如果在消息变量和mailbox中的消息间存在类型不匹配,那么try_peek()方法返回-1。
- 如果一个消息是有效的,并且消息类型与消息变量的类型匹配,那么消息被拷贝并且try_peek()方法返回1
一个mailbox中消息的数目可以通过num()方法获得
缺省的mailbox是无类型的,一个mailbox可以发送和接收任何类型的数据。这是一个非常强大的机制。但是也会因为一个消息与get()到消息变量间的类型不匹配而导致运行时错误。
一个mailbox经常被用来传输一个特定的消息类型,在这种情况下,如果能够在编译时发现类型不匹配,那会是很有用的。 此时需要一个参数化的mailbox。
参数化的mailbox声明如下:mailbox#(type=dynamic_type),其中dynamic_type代表一个特殊的类型,它能够执行运行时的类型检查。
参数化的mailbox提供了所有与无类型的mailbox相同的标准方法:num()、new()、get()、peek()、put()、try_get()、try_peek()、try_put()。
无类型的mailbox与参数化mailbox之间唯一的不同是:对于一个参数化的mailbox,编译器能够确保put()、try_put()、peek()、try_peek()、get()和try_get()与mailbox消息类型兼容,从而所有的类型不匹配都可以被编译器在编译期间捕获而不是在运行时捕获。