创建一个简单的systemverilog程序

1. Hello world!

作为一个面向对象的语言,免不了来一个国际惯例: Hello world!
下面是完整的代码:

program tb();
  initial begin
    $display("Hello, world!");
  end
endprogram

用modelsim进行编译和运行:
Hello, world!

代码解释:跟c语言的main函数类似,sv语言需要一个仿真开始的入口,这个入口可以是module,program,interface等,这里使用的program,如果把program替换成module,仿真结果也完全一样。Program跟module的区别以后再讲。然后在program里面需要调用initial 语句来表明仿真的各个线程。一个program里面可以用很多个initial块,他们并行从时间0开始执行。Initial块里面只有一句打印,所以最终仿真的输出只有一句打印信息:hello, world!$display是sv语言规定的打印函数,跟c语言类似。

2.结构体和联合体(structure/union)

Sv中的结构体和联合体的声明遵从C语言的语法,但在“{”之前没有可选的结构体标签。 一个结构体可以作为一个整体赋值,并且可以作为一个整体向/从一个函数或任务传递。

SV中用struct关键字来声明一个结构体,用union关键字来声明一个联合体。

结构体可以声明成压缩的(packed),一个压缩结构体包含了位域,它们在存储器中被无缝地压缩在一起。这也就意味着,它们可以方便地转换到位向量,或从位向量转换。一个非压缩结构体具有独立于实现的压缩形式,通常与C编译器相匹配。

与压缩数组类似,在使用算术和逻辑操作符时,一个压缩结构体可以当作一个整体使用。第一个指定的成员成为最高有效位,后续的成员以降序排列。结构体在声明的时候可以使用packed关键字,根据期望的算术行为,packed关键字之后可以跟着signed或unsigned关键字。缺省情况下,结构体是无符号的。

只要压缩结构体内的数据类型存在4态数据类型,那么整个结构体就被当作是4态数据类型,并且其中的任何2态成员都会使用强制类型转换进行转换。压缩结构体的一个或更多的位可以使用[n-1:0]的编号方法来选择,就好像它是一个压缩数组一样。

非整数数据类型(例如real和shortreal)以及非压缩数组不允许在压缩结构体或压缩联合体中使用。

一个压缩联合体所包含的成员只能是压缩结构体、压缩数组或者是整数数据类型,并且所有的成员必须具有相同的尺寸(而对于一个非压缩联合体,其成员可以具有不同的尺寸)。这就可以保证你可以读回作为另外一个成员写入的联合体成员。在使用算术和逻辑操作符时,一个压缩联合体可以当作一个整体使用,并且它的行为由signed或unsigned关键字确定。如果没有显式指明signed或unsigned关键字,那么它缺省是unsigned的。如果一个压缩联合体包含了2态成员和4态成员,那么整个联合体是4态的。此时,如果读取2态成员,那么它具有一个隐式的4态到2态的转换;如果写入2态成员,则具有一个隐式的2态到4态的转换。

一个联合体可以使用不同的访问宽度进行访问。对一个成员的写入与对另外一个成员的读出独立于计算机的字节顺序,这与一个普通的联合体或普通的结构体不同,这些普通的联合体或普通的结构体与C语言兼容并且以降地址的顺序排列成员。

注:在我们的验证环境中,绝大多数时候使用的是结构体。
一个结构体和联合体的例子:

program tb;
    typedef struct {
            bit [47:0] da;
            bit [47:0] sa;
            byte       fcs[4];
            byte       data[];
            union  packed {
                int pkt_len;
                int pkt_type;
                }len_type;  //联合体
            }eth_pkt;      //eth_pkt结构体名称
    eth_pkt m_pkt;
    initial 
    begin
        m_pkt.data = new[2];   //创建一个含有3个元素数组
        m_pkt.data[0] = 'h5a;
        m_pkt.len_type.pkt_len =10;
        m_pkt.sa = '1;
        $display("sa='h%h",m_pkt.sa);
        $display("data.size",m_pkt.data.size);
        $display("len=%d,tpye=%d",m_pkt.len_type.pkt_len,m_pkt.len_type.pkt_type);
    
    end 
endprogram

仿真输出为:
sa='hffffffffffff
data.size=2
len=10,type=10

注:若m_pkt.sa = '0;则打印结果为sa='h000000000000;m_pkt.sa赋其它值则报错。

3. 联合数组(associated array)

对于处理成员数目会动态改变的连续变量集合而言,动态数组非常有用。然而,当集合的尺寸是未知的或者数据空间紧缺的时候,联合数组则是更好的选择。联合数组在使用之前不会分配任何存储空间,并且索引表达式不再被限制成integral表达式,而是可以具有任何数据类型。

联合数组实现了一个所声明类型的元素的查找表。用作索引的数据类型作为查找表的查找键值,并强制了一种顺序。 联合数组本质上是一种key-value的对应关系的hash表。

联合数组的声明语法如下:

data_type array_id [index_type];

其中:

data_type是数组元素的数据类型。固定尺寸数组可以使用的任何类型都可以作为联合数组的数据类型。

array_id是联合数组的名字。

index_type是用作索引的数据类型, 或者是*。如果指定了*,那么数组可以使用任意尺寸的integral表达式来索引。采用数据类型作为索引的联合数组将索引表达式限制成一个特定的数据类型。

联合数组的元素被动态地分配;一个条目在它第一次写入的时候产生。联合数组维护已经被赋值的条目并根据索引数据类型维护相对顺序。联合数组元素是非压缩的,也就是说,除了拷贝或比较数组之外,当联合数组在大多数表达式中使用之前,你必须从数组中选择单独的元素。

联合数组只能被赋值为另外一个具有兼容类型并且具有相同索引类型的联合数组。其它的数组类型不能赋值给一个联合数组,而联合数组也不能赋值给其它类型的数组,无论是固定尺寸数组还是动态动态数组。将联合数组赋值给另外一个联合数组会先将目的数组中所有现存条目清除,然后再将源数组中的每一个条目拷贝到目的数组。

除了索引操作符之外,SystemVerilog提供了几个内建方法来允许用户分析和处理联合数组,同时提供了对联合数组的索引或键值的迭代处理。
创建一个简单的systemverilog程序_第1张图片
创建一个简单的systemverilog程序_第2张图片
仿真结果为:
array num =4
tony’s age =14
last tony’s age = 14
pre lucy’s age = 12
first jack’s age = 9
next lily’s age = 10
array num =3

3 mailbox

mailbox的好处?

mailbox是一种通信机制,他使得数据可以在进程间传递和通信,数据被一个进程发送到一个mailbox中,而另一个进程可以从中取出。

mailbox是一个内建的类,它也提供了几种常用的方法:

new();//创建一个mailbox

put();//将一个数据放到mailbox中

try_put();//尝试将一个数据无阻塞的放到mailbox中
get()/peek();//从mailbox中取出一个数据
try_get()/try_peek();//尝试无阻塞的从mailbox中取出一个数据
num();//查询mailbox中数据的个数

////////////////////////////////////////////////////////

参数化的mailbox

缺省的mailbox是无类型的,也就是说,单个mailbox可以发送和接收任何类型的数据。如果这样的话,我们可以试想,往mailbox中存储数据的变量和从中取数据的变量的类型不匹配怎么办?这样岂不是会在运行的过程中发生严重的错误?因此,我们在定义mailbox的时候,经常会定义一个用来传输特定类型的数据,在这种情况下,在编译的时候就可以发现类型是不是匹配,从而避免运行时候发生的错误。

定义格式:

mailbox #(type=dynamic_type)

例子:

typedef mailbox #(string) my_mailbox;//自定义mailbox
my_mailbox sm;//创建一个对象

sm=new;//实例化一个对象,分配空间

string s;//定义一个字符串变量,用来存储字符串

sm.put("hello");//将hello放进定义的mailbox中

sm.get(s);//从mailbox中将hello取出,并传递给s

具体实例:

module mailbox_example;

      mailbox#(int) my_mailbox=new(2);//一次性完成mailbox的定义

/////////////////////////////////////////

上面一步等于下面几步

//mailbox #(int) my_mailbox;

//my_mailbox my_mailbox1;
//my_mailbox1=new(2);//深度为2

////////////////////////////////////////

task send();//定义一个发送任务

     for(int i=0;i<4;i++)

begin

          my_mailbox.put(i);//以此将0,1,2,3放进my_mailbox中

        #5;

end

endtask

task receive();//定义一个接收任务

int temp;//定义一个变量,用来存储值

while(1) begin//一直去执行下面的语句

my_mailbox.get(temp);//将取得的值赋给temp变量

$display("Received:%d",temp);

end

endtask

initial begin

fork

    send();

    receive();

join

end

endmodule

你可能感兴趣的:(Systemverilog)