本篇文章对SV的其他数据类型进行介绍。包括:链表、队列、枚举、typedef重定义、用户自定义、常量、字符串。


目录

一、队列

二、枚举

三、字符串

四、其他


一、队列

队列,它结合了链表和数组的优点。队列与链表相似,可以在一个队列中的任何地方增加或删除元素,这类操作在性能上的损失比动态数组小得多,因为动态数组需要分配新的数组并复制所有元素的值 ;队列与数组相似,可以通过索引实现对任一元素的访问,而不需要像链表那样去遍历目标元素之前的所有元素。

[$]。队列元素的编号从0到$。注意队列的常量(literal)只有大括号而没有数组常量中开头的单引号。

当元素增加到超过原有空间的容量时, SystemVerilg 会自动分配更多的空间。其好处是,你可以扩大或缩小队列,但不用像动态数组那样在性能上付出很大代价,SV会随时记录闲置的空间。注意不用对队列使用构造函数 new[]。

举个栗子:

int jj = 1,
q2[$] = {3,4},
q[$] = {0,2,5};

initial begin

    q.insert(1,jj); //在索引1的位置插入jj
    $display("1:q[$] = %0p",q); //{0,1,2,5}

    q.delete(1); //删除索引1的元素
    $display("1:q[$] = %0p",q); //{0,2,5}

    //q.insert(3,q2); //在索引3的位置插入q2,但不是所有仿真器都支持

end

运行结果:

systemverilog队列赋值_systemverilog

         

        队列中的元素是连续存放的,所以在队列的前面或后面存取数据非常方便。无论队列有多大,这种操作所耗费的时间都是一样的。在队列中间增加或删除元素需要对已经存在的数据进行搬移以便腾出空间。相应操作所耗费的时间会随着队列的大小线性增加。你可以把定宽或动态数组的值复制给队列。

        所以,下面的操作执行速度很快:

q.push_front(6); //在队列前面插入6
$display("1:q[$] = %0p",q); //{6,0,2,5}

jj = q.pop_back; //取出队列最后一个元素
$display("2:jj = %0d",jj); //5

q.push_back(8); //在队列末尾插入8
$display("3:q[$] = %0p",q); //{6,0,2,8}

jj = q.pop_front; //取出队列第一个元素
$display("4:jj = %0d",jj); //6

foreach(q[i])
    $display(q[i]);

q.delete; //删除队列

运行结果:

systemverilog队列赋值_systemverilog_02

 

        注:队前插入和队末插入可以使用等效语句:

//在队列前面插入6

语句1:q.push_front(6);

语句2:q={6,q};

//在队列末尾插入8

语句1:q.push_back(8);

语句2:q={q,8};

二、枚举

可增强代码的可读性。这种类型的变量,主要还是定义应用,内置函数用得较少。有个经常说到的问题是整型变量与枚举变量之间的转换,枚举转换成整型可用直接赋值,反过来则不行,需要用case函数进行强制转换,避免越界。

最简单的枚举类型声明如下:

enum {RED,BLUE,GREEN}color;

可以使用typedef创建一个署名的枚举类型,有利于声明更多新变量。

//创建代表0,1,2的数据类型

typedef enum{INIT,DECODE,IDLE}fsmstate_e;

fsmstate_e pstate, nstate;//声明自定义类型变量

initial begin

    case(pstate)

        IDLE:nstate=INIT;//数据赋值

        INIT:nstate=DECODE;

        default:nstate=IDLE;

    endcase

    $display("Next state is %s",nstate. name());//显示状态的符号名

end

当pstate = DECODE时,运行结果如下:

systemverilog队列赋值_systemverilog队列赋值_03

 

SV提供的一些可以遍历枚举类型的函数:

名称

描述

first()

返回第一个枚举常量

last()

返回最后一个枚举常量

next()

返回下一个枚举常量

next(N)

返回以后第N个枚举常量

prev()

返回前一个枚举常量

prev(N)

返回以前第N个枚举常量

遍历枚举成员时,使用do...while

typedef enum{RED,BLUE,GREEN}color_e;

color_e color;

color=color. first;

do

    begin

        $display("Color=%0d/%s", color, color. name);

        color=color. next;

    end

 while(color!=color. first);//环形绕回时即完成

运行结果如下:

systemverilog队列赋值_字符串_04

 

枚举类型的转换

  枚举类型的缺省类型为双态int。可以使用简单的复制表达式把枚举变量的值直接赋值给非枚举变量如int。反过来则不能简单的直接赋值,因为会存在越界的情况。

举个栗子:

typedef  enum{RED ,BLUE, GREEN}color_e;
color_e c1,c2;
int c;

//枚举转为整型:

c1 = BLUE;
c = c1; //直接赋值 c=1;

//整型转枚举:

c++; // c= 2;
if(!$cast(c1,c))
    $display (“Cast failed for c = %0d”,c);
$display (“c1 is %0d/%s", c1, c1.name);

c++; // c= 3;
c2=color_e’(c); //强制类型转换 ;越界了
$display("c2 is %0d/%s", c2, c2.name);

运行结果如下:

systemverilog队列赋值_赋值_05

 

type’(val),该转换不做类型检查;另外一种是把$cast当做函数使用,将右边的值赋给左边,如果赋值成功,则$cast()返回1。如果因为数值越界导致赋值失败,则不进行赋值,函数返回0。

三、字符串

string类型来保存长度可变的字符串。单个字符串是byte类型。

最终目标还是打印平台的仿真信息。

方法

描述

getc(N)

返回位置N上的字节

toupper

返回一个全大写的字符串

tolower

返回一个全小写的字符串

putc(M,C)

把字节C写到字符串的M位上,M在长度范围内

substr(start,end)

提取start到end之间的所有字节

{}

字符串拼接

举个栗子:

string s;

initial begin

    s = "IEEE ";

    $display(s.getc(0)); //73 'I'

    $display(s.tolower()); //ieee

    s.putc(s.len()-1,"-"); //IEEE-

    s={s,"P1800"}; //IEEE-P1800

    $display(s.substr(2,5)); //EE-P

end

运行结果如下:

systemverilog队列赋值_字符串_06

 

四、其他

其他数据类型不是很常用,简单认识一下。

typedef重定义

typedef 已有类型名 新类型名;//其实就是换个命名

链表

SV支持链表的数据类型,但是不推荐使用。使用队列替代。