systemverilog


第二章 数据类型

一、内建数据类型

  1、逻辑(logic)类型
  SystemVerilog中有两种基本数据类型:变量和线网。各自有四种取值:0、1、x、z。RTL代码使用变量来存放组合和时序值,线网可以用来连接设计中的不同部分,例如门和模块实例。
  syestemverilog对reg数据类型进行了改进,使得它除了作为一个变量外,还可以被连续赋值、门单元和模块驱动,这种数据类型被称为logic。任何使用线网的地方都可以使用logic,但logic不能多驱,例如在对双向总线建模时。
  2、双状态数据类型
  bit b            双状态,单比特;
  int unsigned ui      双状态,32bit无符号整数;
  int i           双状态,32bit有符号整数;
  byte b8          双状态,8bit有符号整数;
  shortint s         双状态,8bit有符号整数;
  longint l         双状态,64bit有符号整数;
  integer i4         四状态,32bit有符号整数;
  time t          四状态,64bit无符号整数;
  real r           双状态,双精度浮点数;

  在把双状态变量连接到被测设计,尤其是被测设计的输出时务必要小心。如果被测设计试图产生X或Z,这些值会被转换成双状态值,而测试代码可能永远也察觉不了。使用($isunknow)操作符,可以在表达式的任意位出现X或Z时返回1,如例2.3所示。
  例2.3 对四状态值的检查。

if($isunknow(iport) == 1)$display ("@%0t: 4-state value detected on iport %b",$time,iport);/*使用格式符%0t和参数 time 可以打印出当前的仿真时间,打印的格式在$timeformat()子程序中指定。*/

二、定宽数组

1.定宽数组的声明和初始化

例2.4 定宽数组的声明:

int lo_hi[0:15];     //16个整数[0]...[15]
int c_style[15];        //16个整数[0]...[15]

例2.5 声明并使用多维数组:

int array2[0:7][0:3];    //完整的声明
int array3[8][4];       //紧凑的声明
array2[7][3] = 1;      //设置最后一个元素

  如果你的代码试图从一个越界的地址读取数据,那么systemverilog将返回数组元素类型的缺省值。对于四状态类型的数组,例如logic返回X,对于二状态类型例如int,则返回0。这使用于所有数组,也适用于地址中含有X或Z都情况。线网在没有驱动的时候输出是Z。

2.常量数组

  使用了一个单引号加大括号的方法初始化数组。
  例2.7 初始化一个数组

int ascend[4] = '{0,1,2,3};        //对四个元素进行初始化
int descend[5];descend = '{4,3,2,1,0};            //为五个元素赋值
descend[0:2] = '{5,6,7};      //为前三个元素赋值
ascend = '{4{8}};             //四个值全为8
descend = '{9,8,default:1};       //{98,1,1,1}

三、数组操作

1 、操作数组最 常用的方式是使用for或foreach循环,foreach循环中只要指定数组名并在其后面的方括号给出索引变量,SV会自动遍历数组中的元素。

例 2.8 在数组操作中使用for和foreach

initial beginbit [31:0] src[5],dst[5];for (int i = 0;i<$size(src);i++)src[i] = i;foreach (dst[j])dst[j] = src[j]*2;      //dst 的值是 src 的两倍end

2、数组操作——复制与比较

例 2.13 数组的复制于比较操作

initial beginbit [31:0] src[5] = '{0,1,2,3,4};dst[5] = '{5,4,3,2,1};//两个数组的聚合比较if(src == dst)$display("src == dst");else$display("src != dst");//把 src 所有元素值复制给 dstdst = src ;//只改变一个元素的值src[0] = 5;//所有元素的值是否相等(否!)$display("src %s dst",(src == dst) ? "==" : "!=");//使用数组片段对第 1-4 个元素进行比较$display("src[1:4] %s dst[1:4]",(src[1:4] == dst[1:4]) ? "==" : "!=");

3、合并数组与非合并数组

1)合并数组:

  存储方式是连续的,中间没有闲置空间。例如,32bit的寄存器,可以看成是4个8bit的数据,或者也可以看成是1个32bit的数据。

  表示方法:数组大小和位,必须在变量名前指定,数组大小必须是 [msb:lsb] ;如 Bit [3:0] [7:0] bytes ;

2)二维数组和合并数组识别:

合并数组: bit [3:0] [7:0] arrys; 大小在变量名前面放得,且降序

二维数组: int arrays[0:7] [0:3] ; 大小在变量名后面放得,可降序可升序

位宽在变量名前面,用于识别合并和非合并数组,位宽在后面,用于识别数组中元素个数。

3)非合并数组

  一般仿真器存放数组元素时使用32bit的字边界,byte、shortint、int都放在一个字中。非合并数组:字的低位存放变量,高位不用。

  表示方法:Bit [7:0] bytes[0:2] ;

4)合并数组于非合并数组的选择

  (1)当需要以字节或字为单位对存储单元操作。

  (2)当需要等待数组中变化的,则必须使用合并数组。

    例如测试平台需要通过存储器数据的变化来唤醒,需要用到@,@只能用于标量或者合并数组。

    Bit[3:0] [7:0] barray [0:2] ; 表示合并数组,合并数组中有3个元素,每个元素时8bit,4个元素可以组成合并数组可以使barry[0]作敏感信号。

四、动态数组

  动态数组在声明时使用空的下标[ ],所以必须调用 new[ ]操作符来分配空间,同时在方括号中传递数组宽度。可以吧数组名传给 new[ ]构造符,并把已有数组的值复制到新数组里,如例 2.17 所示。
  例2.17 使用动态数组

int dyn[],d2[];                      //声明动态数组initial begindyn = new[5];                 //A:分配5个元素foreach (dyn[j]) dyn[j] = j;  //B:对元素进行初始化d2 = dyn;                      //C:复制一个动态数组d2[0] = 5;                     //D:修改复制值$display(dyn[0],d2[0]);            //E:显示数值(0和5)dyn = new[20](dyn);             //F:分配2个整数值并进行复制dyn = new[100];                    //G:分配100个新的整数值dyn.delete();                    //H:删除所有元素
end

五、队列

  可以在队列中的任何位置删除或增加元素,队列的声明是使用带有美元符号的下标:[$]。不要对队列使用构造函数 new[ ]。
  例2.19 队列的操作

int j = 1,q2[$] = {3,4},                   //队列的常量不需要使用“'”q[$] = {0,2,5};
initial beginq.insert(1,j);                 //{0,1,2,5}在 2 之前插入 1q.insert(3,q2);                    //{0,1,2,3,4,5}在q中插入一个队列q.delete(1);                    //{0,2,3,4,5}删除第一个元素//下面的操作执行速度很快
q.push_front(6);                    //{6,0,2,3,4,5}在队列前面插入
j = q.pop_back;                        //{6,0,2,3,4} j = 5
q.push_back(8);                     //{6,0,2,3,4,8}在队列末尾插入
j = q.pop_front;                   //{0,2,3,4,8} j = 6
foreach (q[i])$display(q[i]);                   //打印整个队列
q.delete();                         //删除整个队列
end

六、关联数组

  当建立一个超大容量数组时,关联数组用于保存稀疏矩阵的元素。
  关联数组采用在方括号中放置数据类型的形式来声明,例如[int]或[packet]。
例 2.21 关联数组的声明、初始化和使用

initial beginbit [63:0] assoc[bit[63:0]],idx = 1;//d对稀疏分布的元素进行初始化repeat (64) begin                  //循环64次assoc[idx] = idx;idx = idx << 1;                 // << 左移符号end//使用foreach遍历数组foreach (assoc[i])$display("assoc[%h]",i,assoc[i]);//使用函数遍历数组if(assoc.first(idx))begin                      //得到第一个索引do$display("assoc[%h]",idx,assoc[idx]);while (assoc.next(idx));  //得到下一个索引end//找到并删除第一个元素assoc.first(idx);assoc.delete(idx);$display("The array now has %0d elements",assoc.num);
end

  例 2.22 使用带字符串索引的关联数组

/*
输入文件的内容如下:42 min_address1492 max_address
*/int switch[string],min_address,max_address;
initial beginint i,r,file;string s;file = $fopen("switch.txt","r");        //$fopen打开文字文件,"r"只读while (! $feof(file)) begin               //$feof(file),文件结束符(文件结束为非0值,文件没结束则为0)r = $fscanf(file,"%d %s",i,s);        //$fscanf函数,每次只读一行switch[s] = i;
end
$fclose(file);                              //关闭文件//获取最小地址值,缺省为0
min_address = switch["min_address"];//获取最大地址值,缺省为1000
if(switch.exists("max_address"))          //使用exists函数判断switch数组中是否含有“max_address”max_address = switch["max_address"]; //含有的话,将内容的地址赋值给  max_address
elsemax_address = 1000;                        //否则,地址值为1000//打印数组的所有元素
foreach (switch[s])$display("switch['%s'] = %0d",s,switch[s]); //打印数组的内容和相应的地址
end

七、数组的方法

1、数组定位方法

  在非合并数组中查找数据,可以使用数组定位方法,这些方法返回值通常是一个队列。
  例 2.25 数组定位方法:min.max.unique

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,2,6,8}

  2.26 数组定位方法:find

int d[] = '{9,1,8,3,4,4},tq[$];
//找出所有大于 3 的元素
tq = d.find with (item > 3);    // {9,8,4,4}
//等效代码
tq.delete();
foreach (d[i])if (d[i] > 3)tq.push_back(d[i]);tq = d.find_index with (item > 3);     //{0,2,4,5}
tq = d.find_first with (item > 99);     //{} 没有找到
tq = d.find_first_idex with (item == 8);//{2} d[2] = 8
tq = d.find_last with (item == 4);       //{4}
tq = d.find_last_index with (item == 4);//{5} d[5] = 4

2、数组的排序

  对元素进行正排序、逆排序或打乱他们的顺序。
例 2.29 对数组排序

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}  随机排序

  例 2.30 对结构数组进行排序

struct packed {byte red,green,blue;} c[];
initial beginc= new[100];              //分配 100 个像素foreach(c[i])c[i] = $urandom;      //填上随机数c.sort with (item.red);      //只对红色(red)像素进行排序//先对绿色像素后对蓝色像素排序c.sort(x) with (x.green,x.blue);
end

八、使用 typedef 创建新的类型

  typedef可以用来创建新的数据类型,用户自定义的最有用的类型是双状态的32比特的无符号整数。
  例 2.34 unit的定义

typedef bit [31:0] unit;     //32比特双状态无符号数
typedef int unsigned unit;      //等效的定义

九、创建用户自定义结构

1、使用struct创建新类型

  你可以把若干变量组合到一个结构中。例 2.36 创建了一个名为 pixel 的结构,它有三个无符号的字节变量,分别代表红、绿和蓝。
  例 2.36 创建一个 pixel 类型

struct {bit [7:0] r,g,b} pixel;

  例 2.36 中的声明只是创建了一个 pixel 变量。想要在端口和程序中共享它,则必须创建一个新的类型,如例 2.37 所示。
  例 2.37 pixel 结构

typedef struct {bit [7:0] r,g,b} pixel;
pixel_s my_pixel;           //后缀“_s”方便用户识别自定义类型

2、对结构进行初始化

  可以在声明或过程赋值语句中把多个值赋给一个结构体。
  例 2.38 对struct 类型进行初始化

initial begintypedef steuct {int a;byte b;shortint c;int d;} my_struct_s;my_steuct_s st = '{32'haaaa_aaad,8'hbb16'hcccc32'hdddd_dddd};$display("str = %x %x %x %x",st,a,st.b,st.c,st.d);
end

十、类型转换

  SV数据类型具有多样性,因此可能需要在它们之间进行转换。如果源变量和目标变量的比特分布完全相同,例如整数和枚举类型,那么他们之间可以直接相互赋值。如果比特位分布不同,例如字节数组和字数组,则需要使用流操作符对比特分布重新安排。

1、静态转换

  静态转换操作不对转换值进行检查。
  例 2.41 在整型和实型之间进行静态转换

int i;
real r;i = '{10.0 - 0.1};
r = '{42};                //非强制转换

2、流操作符

  流操作符 << 和 >> 用在赋值表达式的右边,后面带表达式、结构或数组。流操作符用于把其后的数据打包成一个比特流。操作符 >> 把数据从左至右变成流,而 << 则把数据从右至左变成流,如 2.42 所示。
  例 2.42 基本的操作符

initial beginint h;bit [7:0]b,g[4],j[4] = '{8'ha,8'hb,8'hc,8'hd};bit [7:0] q,r,s,t;h = { >> {j}};               // 0a0b0c0d 把数组打包成整型h = { << {j}};               //b030d050 位倒序h = { << byte{j}};         //0d0c0b0a 字节倒序g = { << byte{j}};            //0d,0c,0b,0a 拆分成数组b = { << {8'b0011_0101}};    //1010_1100 位倒序b = { << 4{8'b0011_0101}};   //0101_0011 半字节倒序{ >> {q,r,s,t}} = j;        //把 j 分散到四个字节变量里h = { >>{t,s,q,r}};          //把字节集中到 h 里
end

  例 2.44 使用流操符在结构和数组直接进行转换

initial begintypedef struct {int a;byte b;shortint c;int d}; ny_struct_s;my_struct_s st = '{32'haaaa_aaaa,8'hbb,16'hcccc,32'hdddd_dddd};byte b[];//将结构转换成字节数组b = { >> {st}};        //{aa aa aa aa bb cc cc dd dd dd dd}//将字节数组转换成结构b = '{8'h11,8'h22,8'h33,8'h44,8'h55,8'h66,8'h77,8'h88,8'h99,8'haa,8'hbb};st = { >> {b}};        //st = 11223344,55,6677,8899aabb
end

十一、枚举类型

  枚举类型可以自动为列表中的每个名称分配不同的数值。
  例 2.45 一个简单的枚举类型

enum {RED,BLUE,GREEN} color;

例 2.46 枚举类型

// 创建代表 0,1,2 的数据类型
typedef enum {INIT,DECODE,IDLE} fsmstate_e;
fsmstate_e pstate,nstate;           //声明自定义类型变量initial begincase(pstate)IDLE : nstate = INIT;      //数据赋值INIT : nstate = DECODE;default : nstate = IDLE;endcase$display("Next state is %s",nstate.name()); //显示状态的符号数
end

  使用后缀“_e”表示枚举类型。

1、定义枚举值

  枚举值缺省为从 0 开始递增的整数。
  例 2.47 指定枚举值。

typedef enum {INIT,BECODE = 2,IDLE} fsmyype_e; //{0,2,3}

  如果没有特别指出,枚举类型会被当作 int 类型存储。由于 int 类型的缺省值是0,所以在给枚举常量赋值时务必要小心。

2、枚举类型的子程序

  SV中提供了一些可以遍历枚举类型的函数。
  (1)、first()返回第一个枚举常量。
  (2)、last()返回最后一个枚举常量。
  (3)、next()返回下一个枚举常量。
  (4)、next(N)返回第N个枚举常量。
  (5)、prev()返回前一个枚举常量。
  (6)、prev(N)返回以前第N个枚举变量。

3、枚举类型的转换

  枚举类型的缺省类型为双状态 int 。可以使用简单的赋值表达式把枚举变量的值直接赋给非枚举变量如 int 。但SV不允许在没有进行显示类型转换的情况下把整型变量赋值给枚举变量。SV要求显式类型转换的目的在于让你意识到可能存在的数值越界情况。
  例 2.51 整型和枚举类型之间的相互赋值

typedef enum {RED,BLUE,GREEN} COLOR_E;
COLOR_E color,c2;
int c;initial begincolor = BLUE;                   //赋一个已知的合法值c = color;                      //将枚举类型转换成整型(1)c++;                         //整型递增(2)if(!$cast(color,c))              //将整型显式转换回枚举类型$display("Cast failed for c = %0d",c);$display("Color is %0d /%s",color,color.name);c++;                           // 3 对于枚举类型已经越界c2 = COLOR_E'(c);              //不做类型检查$display("c2 is %0d/%s",c2,c2.name);
end

十二、字符串

  SV中的 string 类型可以保存长度可变的字符串,字符串使用动态的存储方式,所以不用担心存储空间会全部用完。
  例 2.53 字符串方法

string s;initial begins = "IEEE ";$display(s.getc(0));                //显示:73('I')$display(s.tolower());               //显示:ieee s.putc(s.len()-1,"-");               //将空格变成'-'s = {s,"P1800"};                 //"IEEE-P1800"$display(s.substr(2,5));            //显示:EE-P创建临时字符串,注意格式my_log($psprintf("%s %5d",s,42));
endtask my_log(string message);//把信息打印到日里$display("@%0t: %s",$time,message);
endtask

  函数 getc(N) 返回位置 N 上的字节,toupper 返回一个所有字符大写的字符串,tolower 返回一个小写的字符串。大括号{}用于串接字符串。任务 putc(M,C) 把字节 C 写到字符串的 M 位上,M 必须介于 0 和 len 所给出的长度之间。函数 substr(start,end) 提取出从位置 start 到 end 之间的所有字符。函数 $psprintf() 返回一个格式化的临时字符串,并且可以直接传递给其他子程序。

绿皮书读书笔记(一)相关推荐

  1. 20220530数据结构绿皮书读书笔记

    个人博客 https://blog.hylstudio.cn/archives/964 20220530数据结构绿皮书读书笔记 9 表格和信息检索 9.1 简介 第七章我们证明过,仅仅使用比较的方式从 ...

  2. 20220518数据结构绿皮书读书笔记

    个人博客 https://blog.hylstudio.cn/archives/945 为了良好的阅读体验,建议到个人博客或CSDN,QQ空间就是备份用的,tx看起来彻底放弃日志了.... 20220 ...

  3. 20220601数据结构绿皮书读书笔记

    个人博客 https://blog.hylstudio.cn/archives/970 20220601数据结构绿皮书读书笔记 11 多叉树 11.1 定义 数学定义上的树有着广泛的概念,它是任意顶点 ...

  4. 20220517数据结构绿皮书读书笔记

    个人博客 https://blog.hylstudio.cn/archives/942 为了良好的阅读体验,建议到个人博客或CSDN,QQ空间就是备份用的,tx看起来彻底放弃日志了.... 20220 ...

  5. 20220527数据结构绿皮书读书笔记书笔记

    个人博客 https://blog.hylstudio.cn/archives/961 20220527数据结构绿皮书读书笔记 8 排序 各种排序算法来咯 插入排序.选择排序.希尔排序.快排.堆排 8 ...

  6. 20220519数据结构绿皮书读书笔记

    个人博客 https://blog.hylstudio.cn/archives/949 20220519数据结构绿皮书读书笔记 2.5.3 优化数据结构的定义 4个层次,1.概念,2.算法,3.编程, ...

  7. 20220513数据结构绿皮书读书笔记

    20220513数据结构绿皮书读书笔记 效率分析 程序运行哪里耗时最长?显然不是输入,因为只有一次,输出一般来说也很快.大量的计算都是在update方法个neighborCount方法的调用上,在每代 ...

  8. 20220512数据结构绿皮书读书笔记

    20220512数据结构绿皮书读书笔记 维护程序的第一步就是review.分析.评估.思考如下几个问题 1.程序是否按需求正确解决了问题? 2.程序在所有条件下都能正常的工作吗 3.程序是否有一个好的 ...

  9. systemerilog绿皮书读书笔记(一)

    1. 验证导论 HVL拥有的典型性质: 受约束的随机激励产生 功能覆盖率 更高层次的结构,如面对对象 多线程及线程间的通信 支持HDL数据类型,例如0,1,x,z 集成事件仿真器,便于对设计加以控制 ...

最新文章

  1. php环境搭建 warmp_PHP环境搭建
  2. Cloudify — REST Plugin
  3. django-pagination---七步教你实现Django网站列表自动分页
  4. java可比较的和比较器的区别_Java中Compareable和Comparator两种比较器的区别
  5. IntelliJ IDEA 中的Java Web项目的资源文件复制新增如何更新到部署包中?
  6. Windows固态硬盘,unbuntu机械硬盘双系统方案
  7. Eclipse中好用的快捷键
  8. 零基础自学python-零基础如何自学python?
  9. 依旧是输入输出(存字符矩阵,空格,换行)
  10. 禁止电脑任何软件弹出窗口
  11. 用C语言编写爱心代码
  12. steam饥荒服务器配置修改,饥荒联机版专用服配置修改器
  13. C++ TCP Socket的使用(阻塞)
  14. 【Questasim】报错001 Failed to access library
  15. [wordpress] Easy Custom Auto Excerpt Options 插件头图样式修改
  16. 天数智芯亮相2019世界人工智能大会 软硬协同深耕AI极致算力
  17. Ubuntu指令失效解决问题之一——错误配置环境变量
  18. 如何做好硕士论文的排版
  19. XRAY项目--电荷积分放大器AD8488介绍
  20. Android 10.0 关机对话框UI定制化开发(一)

热门文章

  1. 拿了offer,签了三方,毁约流程及建议
  2. 李忠汇编语言-初学-第八章详解
  3. 老Kindle秒变电子日历,提醒你穿衣收快递,敲几句命令行就行
  4. mysql只读库的数据同步_mysql只读模式下数据迁移,保证数据一致性
  5. 2023石家庄铁道大学计算机考研信息汇总
  6. 【名道,电商代运营】电商活动策划的要点
  7. 人形机器人,穷途末路还是光明未来?
  8. Mac睡眠 注销 重启 关机的快捷键
  9. OpenJ_Bailian-4116. 拯救公主
  10. scare机器人如何手眼标定_标定系列一 | 机器人手眼标定的基础理论分析