SV绿皮书笔记(二)

您所在的位置:网站首页 山西大同几个县人口排名 SV绿皮书笔记(二)

SV绿皮书笔记(二)

2023-09-10 09:55| 来源: 网络整理| 查看: 265

第二章. 数据类型

在这里插入图片描述

2.1 基本数据类型的两个属性

双状态/四状态:根据存储数据类型中的每一bit位的可能数分为双状态类型和四值状态类型。

双状态:可能值0,1,双状态默认初始值为0。双状态值具有更低的内存消耗,更好的性能。四状态:可能值0,1,X(未知状态),Z(高阻态),四状态默认初始值为X。

有符号数/无符号数:根据数据有无符号分为有符号数(signed),无符号数(unsigned)。有符号数的最高比特位代表的是符号位(0为正,1位负)。

注:如果分配的数据类型为双状态值,但是被测试设计试图产生X或Z,这些值会被转换为双状态值。可以使用$isunknown在表达式任意位出现X或Z时返回1。

2.2 基本数据类型分类

类型双/四状态描述bit双状态1bit无符号数byte双状态8bit有符号整数shortint双状态16bit有符号整数int双状态32bit有符号整数longint双状态64bit有符号整数integer四状态32bit有符号整数logic四状态SV独有数据类型real双状态双精度浮点数time四状态64bit无符号整数

其中logic为逻辑类型,四状态类型,此数据类型为SV独有。对verilog的reg数据类型进行改进而引入,可以被连续赋值,门单元和模块驱动。任何使用wire的地方均可以使用logic,但是不能有多个结构性驱动,如果logic存在多个驱动,编译时就会报错(如双向总线(inout)要被定义为wire类型)。

module logic_data_type(input logic rst_h); parameter CYCLE = 20; logic q, q_l, d, clk, rst_l; initial begin clk = 0; //过程赋值 forever #(CYCLE/2) clk = ~clk; end assign rst_l = ~rst_h; //连续赋值 not n1(q_l, q); //q_l被非门驱动 my_dff d1(q, d, clk, rst_l) //q被模块驱动 endmodule

注:某些有符号数可以通过unsigned声明为无符号数。

int unsigned ui; //双状态,32bit无符号整数

2.3 数据类型(数据结构)

2.3.1 数组

定宽数组:声明时指定宽度,宽度在编译时就确定了,属于静态数组。 //定宽数组声明 int lo_hi[0:15]; //16个整数[0]...[15] int c_style[16]; //16个整数[0]...[15] //多维数组声明,第一个数为行数,第二个数为列数,下面两个声明都为8行4列的二维数组 int array2[0:7][0:3]; //完整声明 int array3[8][4]; //紧凑声明

当为数组中的元素赋值常量时,需要使用一个'加上大括号来初始化数组,这种数组叫做常量数组。

使用常量初始化数组(注意初始化方式): int array_const [4] = '{0,1,2,3}; //对4个元素初始化 array_const = '{4{8}}; //四个值全部是8 array_const = '{1,2,default:1}; //{1,2,1,1}

注:如果从一个越界的地址中读取数据,SV将返回数组元素的缺省值,线网在没有驱动时输出的是Z。

动态数组

在仿真时分配空间/调整宽度,可以在仿真中使用最小的存储量。动态数组的宽度不在编译时给出,而是在程序运行时再指定,因此必须使用new[ ]操作符来分配空间,同时在方括号中传递数组宽度。可以把数组名传递给new[ ]构造符,并把已有数组的值复制到新数组里。

//声明动态数组 int dyn[], dyn2[]; dyn = new[5]; //分配5个元素 foreach(dyn[j]) dyn[j] = j; //初始化动态数组dyn dyn2 = dyn; //复制动态数组dyn到dyn2 dyn = new[20](dyn); //分配20个元素并进行复制 dyn.delete(); //删除dyn中所有元素

注:系统函数$size()返回数组的宽度,动态数组有内建方法,如delete,size等。当基本数据类型相同,定宽数组和动态数组之间可以相互赋值。元素基本数据相同,元素数目也相同时,可以把动态数组的值复制到定宽数组。

关联数组

当数组元素索引不连续时,如果用定宽数组存储会消耗过大的内存空间,内存利用率低。**SV中引入了关联数组保存稀疏数组。**仿真器可以使用树或哈希表的形式来存放关联数组,但是有一定的额外开销。与其他数组不同,关联数组的索引类型可以为其他类型,如字符串。 在这里插入图片描述

//关联数组声明,需要在数组名后使用[]声明索引的类型 bit[31:0] assoc[bit[63:0]]; //声明了索引为64bit,元素为32bit的关联数组assoc bit[31:0] idx = 1; //关联数组初始化 repeat (32) begin assoc[idx] = idx; idx = idx 7); //2,元素大于7的个数 total = d.sum with ((item > 7) * item); //17,表达式中(item>7)返回0或1 count = d.sum with (item < 8); //4,元素小于8的个数 total = d.sum with (item < 8? item : 0); //12 count = d.sum with (item == 4); //2 数组排序 int d[] = '{9,1,8,3,4,4}; //reverse,shuffle方法不能与with结合,作用范围为整个数组 //sort,rsort方法还可以与with结合使用 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中还提供了从数组中随机取出一个元素的方法。对于定宽数组,队列,动态数组和关联数组可以使用$urandom_range($size(array)-1),对于队列和动态数组还可以使用$urandom_range(array.size-1)。如果想从关联数组中随机取出一个元素,你需要逐个访问它之前的元素,原因是没有办法直接访问第N个元素(无法直接获取第N个元素的索引)。

int aa[int], rand_idx, element, count; element = $random_range(aa.size -1 ); //遍历关联数组,获取第element个元素的索引rand_idx foreach(aa[i]) begin if(count++ == element) begin rand_idx = i; break; end end $display("%0d element aa[%0d] = %0d", element, rand_idx, aa[rand_idx]);

2.6 合并数组与非合并数组

很多SV仿真器在存放数组元素时使用32bit的字边界(字长word,可以理解为存储最小单元)当数组元素不足32bit时也分配32bit的空间(数据从低位开始存放,未使用空间为高位),像byte(8bit),shortint(16bit),int(32bit)都存储在一个字长中,而longint(64bit)则存放在两个字长中。非合并数组就是按照上述规则分配内存空间,而合并数组就是不按照SV仿真器的字边界为最小单元分配空间,简单的来说合并数组就是将数组内的数据的bit位连续存放。

//合并数组与非合并数组的声明不同(便于记忆:合并数组数据个数/数据位宽集中在一起声明) //声明非合并数组 bit[7:0] b_unpack[3]; //声明合并数组,注意[7:0]为第一个维度,[3:0]为第二个维度,存储为4个8bit数据而不是8个4bit数据 //合并数组的数组大小的定义格式必须是[msb:lsb],而不能是[size] bit[3:0][7:0] b_pack;

内存空间示意图: 在这里插入图片描述在这里插入图片描述

//合并和非合并混合数组的声明 bit {3:0][7:0] barray [3] //3*32bit [3:0]和[7:0]维度为合并数组 [3]维度为非合并数组

内存空间示意图:

在这里插入图片描述

合并数组和非合并数组的选择

如果需要和标量(基本数据类型)进行相互转换(如以字节或字为单位对存储单元进行操作)时,使用合并数组。任意数组类型都可以合并,包括动态数组,队列和关联数组。如果需要等待数组中的变化,必须使用合并数组。当测试平台需要通过存储器数据的变化来唤醒时,使用@操作符,@操作符只能用于标量或者合并数组。

2.7 选择存储类型

索引为连续非负整数,则应该使用定宽或动态数组。宽度确定使用定宽数组,宽度可变使用动态数组。索引不规则,选择关联数组。元素变化很大的数组,可以使用队列代替数组。双状态较四状态可以较少仿真时的内存用量。

2.8类型转换

如果源变量和目标变量的比特位分布完全相同,那么他们之间可以直接相互赋值。如果比特位分布不同,需要使用流操作符对比特位分布重新调整。

静态转换

静态转换不对转换值进行检查。转换时指定目标类型,并在需要转换的表达式前加上单引号即可。

int i; real r; i = int '(10.0-0.1); //转换是非强制的 r = real '(42); 动态转换

动态转换函数$cast允许你对越界的数值进行检查。

流操作符

流操作符用在复制表达式的右边,后面带表达式,结构和数组**。流操作符用于把其后的数据打包成一个比特流**。操作符>>把数据从左至右变成流,



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3