SystemVerilog

SV data type

There are 6 built-in data types in SV, namely

  1. logic
  2. bit
  3. byte
  4. shortint
  5. int
  6. longint

Note: SV is also compatible with the Verilog data type, that is, the Verilog data type SV is also included. For example, wire of Verilog net type, reg, integer, real in variable, etc.

logic:

​ The logic in SV is 4-value logic, that is, its value can have 4 values, namely 0/1/x/z

​ logic can be assigned by a process or continuously, and the compiler can automatically deduce whether logic is reg or wire.

​ The only limitation is that logic only allows one input and cannot have multiple structural drivers. For example, an inout type port cannot be defined as logic. Therefore, logic is used for single drive, and wire is used for multiple drives.

bit:

  1. The bit type in SV is two-state, 2-valued logic, 0/1 respectively

  2. The bit type is an unsigned number

**byte/shortint/int/longint: **

  1. byte/shortint/int/longint:
  2. The data types are all two-state, 2-valued logic, 0/1 respectively
  3. The bit width is 8/16/32/64 respectively
  4. are signed numbers
type of data 2-valued logic/4-valued logic Defaults Is there a signed number
logic 4 x unsigned
bit 2 0 unsigned
byte 2 0 signed
shortint 2 0 signed
int 2 0 signed
longint 2 0 signed

SV fixed-width array

1. Fixed-width array format declaration

The format declaration of a fixed-width array is: Type name [constant], where type is the type of stored data.

For example:

int array[16], array is the name of the array, int is the storage type of the array data is int data, 16 is the size of the array, a total of 16 elements.

int array[8] [4], array is the name of the array, int is the storage type of the array data is int data, the size of the array is 8 rows and 4 columns, a total of 32 elements.

2. Basic operation of array
array initialization

Use assignment notation: `{} 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}
traverse-for

One of the common methods of traversing an array is a for loop, as shown in the following example:

initial begin
  bit [31:0] src[5],dst[5];
  for (int i=0; i<$size(src); i++)
    src[i] = i;
end

Note:

  1. The i variable is a local variable inside the for loop;

  2. In SV, the $size function returns the width of the array;

  3. Instead of using the $size function, you can directly use the maximum dimension of the array, which is 4 in the above example (the index is 0~4);

traverse-foreach

A two-dimensional array is also a commonly used array in SV. Two-dimensional can be understood as the data stored in the array has two dimensions.

Using foreach can also traverse the array conveniently and quickly: just specify the array name, and provide the index variable in the following square brackets, SV will automatically traverse the array, the index variable will be automatically declared, and it is only valid within the loop. As shown in the following example:

initial begin
  bit [31:0] src[5],dst[5];
  foreach (dst[j])
    dst[j] = src[j]* 2;   // dst doubles src values
end
3. Notes on arrays
  1. Arrays can be one-dimensional or multidimensional

  2. The data of byte/short/int type occupies the storage space of one word (32bit)

  3. longint occupies two words (64bit) of storage space

  4. If the array is read out of bounds, the default value of the array element type is returned:

    ​ 4.1 If the array element is 4-valued logic, such as logic, return x;

    ​ 4.2 If the array element is 2-valued logic, such as int or bit, then return 0

    ​ 4.3 This rule applies to fixed-width arrays, dynamic arrays, associative arrays and queues, as well as cases where x or z is included in the address.

  5. If the array is written out of bounds, the written data is ignored.

SV dynamic array

1. Dynamic array

SV provides a dynamic array, which can allocate space or adjust the width during simulation.

The declaration of a dynamic array uses empty [], and the width of the array is not given at compile time, but is specified at program runtime.

Dynamic arrays are initially empty, so the new[] operator must be called to allocate space, passing the array width in square brackets.

The declaration format of a dynamic array is: type name [] , where type is the type of stored data. [] is empty when declared.

For example : int array[], where array is the name of the array, and int is the storage type of the array data as int data. Before using the array, you must call the **new[]** function to allocate space for the array before it can be used.

int x[];
x=new [5];
2. Some methods of dynamic array

delete sum size

int test[];
test=new[4];  //用new来创建数组
num=test.size();  //用size获取数组大小
test.delete();  //用delete删除数组

Note: $size(test) can also get the size of the array

SV associative array

1. Associative array

SV provides an associative array type to store sparse data. Different from other arrays, associative arrays are conducive to the use of fragmented storage space. As shown in the figure below, the data stored in associative arrays is not continuous storage space.

data
index 0 3 50 100

Associative array declaration method: data_type array_name[index_type],

For example, int array[string] , where int is an associative array, the type of data stored is int type data, and the index of the array is a string index.

2. Indexing of associative arrays

The index of an associative array can be one of the following:

  1. Wildcard index: index any data type: int array_name [*];

  2. String index: int array_name[string];

  3. class index: int array_name[some_Class];

  4. integer (or int) index: int array_name[integer];

  5. Signed packed array indexing: typedef bit signed [4:1] Nibble; int array_name[Nibble];

  6. Unsigned packed array indexing: typedef bit [4:1] Nibble; int array_name [Nibble];

  7. Other user-defined type indexes: typedef struct { real R; int I[*]; } Unpkt; int array_name [Unpkt];

In effect, an associative array implements a lookup table of elements of the declared type. The data type used as the index acts as the lookup key for the lookup table and enforces an order. For example:

integer i_array[*];   // 整数关联数组(未指定索引)
bit [20:0] array_b[string];  // 21位向量的关联数组,使用字符串类型作为索引
event ev_array[myClass];   // 事件类型的关联数组,使用类myClass索引
3. Some uses of associative arrays
num()  //返回数组长度
delete()  //删除指定元素或者所有元素
exists()  //检查是否元素存在,存在返回1,否则返回0
first()  //将指定的索引变量赋值为数组第一个索引的值
last()  //将指定的索引变量赋值为数组最后一个索引的值  
next()  //索引变量被赋值为下一个条目的索引
prev()  //索引变量被赋值为上一个条目的索引
4. Associative array considerations

A simple for loop cannot traverse an associative array, you need to use foreach to traverse the array, and you can also use the built-in first() and next() functions.

Reads an associative array element that does not exist, returns x for 4-valued logic, and 0 for 2-valued logic.

The index of the associative array can be a variety of data types: string, int, even class and user-defined data types.

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 array method

SV provides a number of array methods for any non-coalescing array type, including fixed-width arrays, dynamic arrays, queues, and associative arrays. These methods range from complex to simple and come in a wide variety, including summation, product, sorting, and more.

1. Array reduction method

Array reduction methods: sum (sum), product (product), and (and), or (or), xor (exclusive or)


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. Array positioning method
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}

Note: tq is a queue, i.e. the array method in this example returns a queue.

1. Array sorting method
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 structure and enumeration

1. Structure
struct {
    int   a;
    int   b;
    bit   c;  
} struct_name

The method of referencing variables in the structure is ".", that is, struct_name.a can refer to the a variable inside the structure.

define assignment
typedef struct {
  int addr = 1 ;
  int crc;
  byte data [4] = '{4{1}};
} packet1;
Individual assignment
packet1pi = '{1,2,'{2,3,4,5}};
Reference member variable assignment
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;
example
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. Enumeration

The SV enumeration provides a powerful variable type that allows users to customize a collection of specific names, such as opcodes in instructions or codes in state machines, with better readability. This data type is called an enumeration.

The declaration format of the enumeration is: enum {variable name, ..., variable name} enum_name

例如:enum {RED,BLUE,GREEN} color

SV queue

1. queue

SV introduces the data type of queue, which is easy to use and much better in performance than dynamic arrays.

Queues can store any data type, including SV built-in data types, or user-defined data types. The queue is equivalent to maintaining a table, in which the table can be added, deleted, modified and searched arbitrarily.

The order of the queue is maintained by the user.

The declaration format of the queue is data_type queue_name[$]

For example: **int data_q [ ] ∗ ∗ , where int is the data type stored in the queue is int type data, use the symbol [ ]** when declaring the queue, where int is the data type stored in the queue is int type data, declare Use the symbol [ when queuing], where int is the data type stored in the queue is int type data, and the symbol [ ]

2. Queue method
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. Considerations for queues
  1. The storage space of the queue is infinite, theoretically the maximum space of physical memory, from 0 to $.

  2. Elements can be added, deleted, modified, and checked anywhere in the queue.

  3. Be careful not to use the constructor new[] for the queue.

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 procedural statement

1. The initial statement

same as verilog not in overview

2. The always statement

SV has extended Verilog's always statement, in addition to always:

  1. always_comb //combination logic modeling

  2. always_latch // model the latch

  3. always_ff //Modeling sequential logic

Among them, always_comb is a statement added in SV to model combinational logic, always_latch is modeled for latch, and always_ff is modeled for sequential logic.

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 statement
  1. The final block is similar to the intial block, they both define a procedural statement block, the difference is that the final block is only executed before the end of the simulation. Typically, final blocks are used to display statistics about the simulation.

  2. The statements that can be used in a final block are the same as those allowed in a function declaration. Unlike intial blocks, final blocks are not executed as a separate process.

  3. The final block is executed when an explicit or implicit call to $finish causes the simulation to end.

  4. A final block can only be triggered once in a simulation.

final
   begin
        $display("Number of cyclesexecuted %d",$time/period);
        $display("Final PC = %h",PC);
   end
initial begin
   $finish();
end

SV control flow

1. Select statement
if-else statement

unique if

  1. unique if indicates that there should not be any overlap in a series of if...else...if conditions, that is, the conditions are mutually exclusive.

  2. If more than one condition is true, an error will be thrown. All valid options are specified, none are omitted.

  3. If no condition is "true", or if no condition is "true", and the last if does not have a corresponding else statement, an error will also be reported.

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

  1. priority if indicates that a series of if...else...if conditions should be evaluated in the order listed.

  2. If multiple conditions are met at the same time, then select the first branch that meets the conditions, which has priority logic.

  3. If the software tool finds that no condition is "true", or no condition is "true", and the last if does not have a corresponding else statement, an error will be reported.

priority if (a[2:1]==0) $display("0 or 1");
else if (a[2]==0) $display("2 or 3");
else $display("4 or 7");// 覆盖了所有可能的其他值,因此没有错误。
case statement

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. 循环语句
  1. Verilog提供了for、while、repeat以及forever循环。

  2. SystemVerilog增强了Verilog的for循环。

  3. 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语句
  1. event是一个静态的句柄,用于同步多个线程,在一个线程中触发事件,在另外一个线程中等待事件。

  2. 事件的句柄可以是null。

  3. 声明一个事件如下方式:event done;

  4. 事件的触发使用**->,事件的等待使用@或者.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进行了增强和扩展,扩展内容如下:

  1. SystemVerilog加入了在静态任务和函数中声明自动变量的功能。
  2. SystemVerilog加入了在自动任务和函数中声明静态变量的功能。
  3. 无需一个begin…end块或fork…jion块就可以在一个任务或函数中使用多条语句的能力。
  4. 在到达任务或函数的结尾之前从任务或函数返回的能力。
task的参数
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
task的注意事项
  1. 在SystemVerilog中,多条语句可以在task和endtask之间被写入,可以省略begin…end。如果省略begin…end,这些语句也是顺序地执行,这与它们被包含在begin…end中是一样的。

  2. task中没有任何语句也是合法的,即空task。

  3. 在Verilog中,当task到达endtask的时候任务退出。而对于SystemVerilog,在endtask关键字之前可以使用return语句退出task。

2. function
function的参数

SystemVerilog扩展了Verilog函数的能力,它允许函数具有与任务相同的形式参数。参数用法与task一致

input     // 在开始的时候拷贝值
output   // 在结束的时候拷贝值
inout     // 在开始的时候拷贝,在结束的时候输出
ref        // 传递引用
function返回值

在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的参数传递,共有四种方式,分别为:通过值传递,通过引用传递,缺省的参数值以及通过名字传递。

通过值传递
  1. 通过值传递是向function传递参数的缺省机制,它也是Verilog-2001提供的唯一的参数传递机制。这种参数传递机制是通过将每一个参数拷贝到function区域的方式实现的。

  2. 如果function是automatic的,那么function在它的堆栈中保留一个参数的本地拷贝。如果参数在function中被改变,那么这种改变在function外是不可见的。

  3. 当参数很大的时候,我们可能不希望拷贝这个参数。拷贝会造成性能上的影响。

通过值传递
  1. 通过值传递是向function传递参数的缺省机制,它也是Verilog-2001提供的唯一的参数传递机制。这种参数传递机制是通过将每一个参数拷贝到function区域的方式实现的。

  2. 如果function是automatic的,那么function在它的堆栈中保留一个参数的本地拷贝。如果参数在function中被改变,那么这种改变在function外是不可见的。

  3. 当参数很大的时候,我们可能不希望拷贝这个参数。拷贝会造成性能上的影响。

通过引用传递

通过引用传递的参数不会拷贝到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里面数据的拷贝而不会移除原始数据

new()方法
  • mailbox new()方法的原型为 function new(int bound = 0);
  • new()函数返回mailbox的句柄,如果不能产生mailbox则返回null。
  • 如果bound参数为0,那么mailbox是无边界的(缺省情况),此时一个put()操作应该永远不会阻塞;
  • 如果bound为非0,那么它表示mailbox队列的尺寸。
  • bound必须是正的。负的边界是非法的并会导致不确定的行为。
put()方法
  • put()方法将一个消息放入到一个mailbox中。
  • put()方法严格按FIFO的顺序将一个消息存储在mailbox中。如果mailbox使用一个有界的队列产生,那么put()进程应该被挂起直到队列中有足够的空间。
try_put()方法
  • try_put()方法尝试将一个消息放置在一个mailbox中。try_put()方法严格按FIFO的顺序将一个消息存储在mailbox中。这个方法仅仅对有界的mailbox才有意义(因为对于无界的mailbox来说,put()永远不会阻塞,即put()永远能够成功)。
  • 如果mailbox没有满,那么指定的消息被放置在mailbox当中并且函数返回1。如果mailbox满了,那么函数返回0。
get()方法
  • get()方法从一个mailbox中获得一个消息。
  • get()方法从mailbox中获得一个消息,mailbox则在队列中删除这个消息。
  • 如果mailbox是空的,那么当前的get()进程阻塞直到一个消息被放置到mailbox中。
  • 如果在消息变量和mailbox中的消息间存在类型不匹配,那么会产生一个运行时错误。
try_get()方法
  • try_get()方法尝试无阻塞地从一个mailbox中获得一个消息。
  • 如果mailbox是空的,那么try_get()方法返回0。
  • 如果在消息变量和mailbox中的消息间存在类型不匹配,那么try_get()方法返回-1。
  • 如果一个消息是有效的,并且消息类型与消息变量的类型匹配,那么消息被重新获得并且try_get()方法返回1。
peek()方法
  • peek()方法从一个mailbox中拷贝一个消息但不会将其从队列中删除。
  • 如果mailbox是空的,那么当前的进程会阻塞直到一个消息被放置到mailbox中。
  • 如果在消息变量和mailbox中的消息间存在类型不匹配,那么会产生一个运行时错误。
try_peek()方法
  • try_peek()方法尝试从一个mailbox中拷贝一个消息但不会将其从队列中删除。
  • 如果mailbox是空的,那么try_peek()方法返回0。
  • 如果在消息变量和mailbox中的消息间存在类型不匹配,那么try_peek()方法返回-1。
  • 如果一个消息是有效的,并且消息类型与消息变量的类型匹配,那么消息被拷贝并且try_peek()方法返回1
num()方法

一个mailbox中消息的数目可以通过num()方法获得

参数化的mailbox

缺省的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消息类型兼容,从而所有的类型不匹配都可以被编译器在编译期间捕获而不是在运行时捕获。

参考链接-IC学社

Guess you like

Origin blog.csdn.net/Mouer__/article/details/126021572