目录
2. 双状态数据类型:bit, byte, shortint, int, longint
一、 Verilog HDL中的数据类型
Verilog HDL中有19种数据类型:
1. 物理数据类型(与实际硬件电路映射)
(1)线网(net)类型
① wire, tri:标准连线型
② wor, trior:多重驱动时,具有线或特性的连线型
③ wand, trand:多重驱动时,具有线或特性的连线型
④ trireg:具有电荷保持特性的连线型(用于电容节点的建模,缺省初始值为x)
⑤ tri1, tri0:上拉/下拉电阻(若无驱动源,值为1/0)
⑥ supply1, supply0:对电源(VCC)/地(GND)建模,即高电平1/低电平0
(2)寄存器型:reg
(3)存储器型:mem
2. 抽象数据类型(辅助设计和验证)
(1)整型:integer(32位有符号整数)
(2)时间型:time(64位无符号数)
(3)实型:real
(4)参数型:parameter
二、SystemVerilog的内建数据类型
SystemVerilog增加了很多新的数据类型,方便设计和验证:
(1)双状态数据类型:提高仿真器性能,减少内存消耗
(2)队列、动态数组、关联数组:减少内存消耗,自带搜索和分类功能
(3)类和结构:抽象数据结构
(4)字符串
(5)枚举类型:增强代码可读性
1. 逻辑类型:logic
(1)可以由过程赋值语句或连续赋值语句赋值、被门单元和模块驱动
(2)只能有一个驱动(否则编译时会报错)
2. 双状态数据类型:bit, byte, shortint, int, longint
3. 分类
(1)逻辑类型
四值逻辑:net-type(wire等),reg,integer,logic
二值逻辑:bit,byte,shortint,int,longint
* 四值逻辑 → 硬件(硬件设计)
* 二值逻辑 → 软件(验证环境)
(2)符号类型
有符号类型:integer,byte,shortint,int,longint
无符号类型:net-type,reg,bit,logic
4. 注意事项
(1)避免有符号变量、无符号变量的混用
例:
byte signed_vec ; // 8位有符号变量
bit [8:0] result_vec ; // 9位无符号变量
initial begin
signed_vec = 8'h80 ;
result_vec = signed_vec ; // 'h180
result_vec = unsigned'(signed_vec) ; // 'h080
end
(2)把双状态变量连接到被测设计的输出时,应检查未知值的传播
例:
logic [3:0] x_vec = 'b111x ;
bit [2:0] b_vec ;
initial begin
b_vec = x_vec ; // 'b110 (x → 0)
end
Tips: $isunknown( )可以在表达式的任意位出现X或Z时返回1。
5. 类型转换
systemverilog中的类型转换$cast_systemverilog string转int怎么实现-CSDN博客
(1)隐式转换
Verilog对整数和实数类型,或不同位宽的向量之间进行隐式转换。
(2)显式转换
(a)静态转换( ' )
静态转换在编译时完成,不对转换值进行检查。
① 即使等号两侧的数据类型不匹配,也会进行强制转换。
例:将string类型转换为int类型
string s = "abcd" ;
int c ;
initial begin
c = int'(c) ;
$display($sformatf("c = %0d", c)); // c = 0
end
② 如果转换失败,我们也无从得知。
例:枚举类型的数值越界
typedef enum {RED, BLUE, GREEN} COLOR_E;
COLOR_E color ;
int c ;
initial begin
c = 1 ;
color = COLOR_E'(c); // BLUE
$display($sformatf("color = %s (%0d)", color.name, color)); // color = BLUE (1)
c = 3 ;
color = COLOR_E'(c); // 越界
$display($sformatf("color = %s (%0d)", color.name, color)); // color = (3)
end
(b)动态转换( $cast )
① 在仿真时对转换结果进行动态检查(run-time check)。
① $cast是function还是task?
答:既可以用作task,也可以用作function(返回int类型的0或1)。
② 如果转换失败,destination变量的值不会发生变化。
用作function时,会返回0
typedef enum {RED, BLUE, GREEN} COLOR_E;
COLOR_E color ;
int c ;
initial begin
c = 3 ;
if(!$cast(color, c))
$display("Cast fail for c = %0d", c); // Cast fail for c = 3
end
用作task时,会报告run-time error
typedef enum {RED, BLUE, GREEN} COLOR_E;
COLOR_E color ;
int c ;
initial begin
color = GREEN;
$display("Before cast: color = %s (%0d)", color.name, color) ;
c = 3 ;
$cast(color, c) ;
$display("After cast: color = %s (%0d)", color.name, color) ;
end
// 仿真结果:
// # Before cast: color = GREEN (2)
// # ** Error: (vsim-3971) $cast to type 'enum int test.COLOR_E' from 'int' failed in file // D:/****.sv at line 12.
// # Time: 0 ns Iteration: 0 Instance: /test
// # After cast: color = GREEN (2)
三、定宽数组
1. 定宽数组的声明
在verilog中,数组的声明中必须给出索引的上、下界[m:n],例如[5:0], [0:5], [6:1]等都可以。
SystemVerilog则允许更加便捷的声明方式,只给出数组宽度:
bit[31:0] array1 [0:3]; // 完整的声明,一维数组
int array2 [0:7][0:3]; // 完整的声明,二维数组
int array3 [8][4]; // 紧凑的声明
bit[31:0] array4 [4]; // 这样也可以
2. 多维数组的维度和$size
对于多维数组,如何确定高维和低维?可以使用“逆时针索引法”:图中的红色箭头,由高维度指向低维度。
SystemVerilog的内建函数$size(array, n)返回指定维度的元素个数:
bit [3:0][4:0][5:0] a [0:1][0:2] ;
$display("$size(a) = %0d", $size(a)); // 2: 默认为最高维
$display("$size(a, 1) = %0d", $size(a, 1)); // 2
$display("$size(a, 2) = %0d", $size(a, 2)); // 3
$display("$size(a, 3) = %0d", $size(a, 3)); // 4
$display("$size(a, 4) = %0d", $size(a, 4)); // 5
$display("$size(a, 5) = %0d", $size(a, 5)); // 6
3. 合并数组、非合并数组
合并数组(packed array) :数组的维数全部在数组名的左侧。
(注意:合并数组的数据类型只能是bit或logic)
bit[3:0][7:0] packed_a1 ; // 这种声明方式更常见
bit[0:3][0:7] packed_a2 ;
非合并数组(unpacked array):数组名的维数全部或部分在数组名的右侧。
(左侧为合并部分,右侧为非合并部分)
bit[7:0] unpacked_a1 [0:3] ;
bit[7:0] unpacked_a2 [4] ;
bit unpacked_a3 [4][8] ;
那么,合并/非合并是什么意思?
合并,即打包,仿真器会将这部分数据打包成一个向量,在内存中连续存储。
非合并,即不打包,在内存中独立存储。
(很多SV仿真器在存放数组元素时使用32bit的字边界,字的低位用来存放数据,高位不使用)
注意:操作符@只能用于标量或合并数组。
4 定宽数组的初始化
(1)合并数组的初始化:可以看作矢量,整体赋值
bit [3:0][7:0] a ; // 高维为4,低维为8
bit [7:0][3:0] b ; // 高维为8,低维为4
a = 32'habcd_1234; // 作为整体赋值
$display("a = %0h", a); // abcd1234
b = a ; // 每个维度的元素数目不同,但可以直接赋值
$display("b = %0h", b); // abcd1234
(2)非合并数组的初始化:'{ }
byte unsigned a[4] ;
byte unsigned b[0:3] ; // 两种声明方式等价
bit c[4][2]; // 等价于[0:3][0:1]
a = '{'h00, 'h11, 'h22, 'h33} ;
b = '{'h00, 'h11, 'h22, 'h33} ;
c = '{'{'b1, 'b0}, '{'b0, 'b0}, '{'b1, 'b1}, '{'b0, 'b1}} ;
$display("a[0] = %h, b[0] = %h", a[0], b[0]); // a[0] = 00, b[0] = 00
$display("a[1] = %h, b[1] = %h", a[1], b[1]); // a[1] = 11, b[1] = 11
$display("a[2] = %h, b[2] = %h", a[2], b[2]); // a[2] = 22, b[2] = 22
$display("a[3] = %h, b[3] = %h", a[3], b[3]); // a[3] = 33, b[3] = 33
$display("c[0][0] = %b, c[0][1] = %b", c[0][0], c[0][1]); // c[0][0] = 1, c[0][1] = 0
$display("c[1][0] = %b, c[1][1] = %b", c[1][0], c[1][1]); // c[1][0] = 0, c[1][1] = 0
$display("c[2][0] = %b, c[2][1] = %b", c[2][0], c[2][1]); // c[2][0] = 1, c[2][1] = 1
$display("c[3][0] = %b, c[3][1] = %b", c[3][0], c[3][1]); // c[3][0] = 0, c[3][1] = 1
(3)混合数组:合并部分可以整体赋值
bit[7:0] a [0:3];
bit[0:7] b [3:0]; // 两种声明方式有区别
initial begin
a = '{8'haa, 8'hbb, 8'hcc, 8'hdd}; // 合并部分可以整体赋值
b = '{8'haa, 8'hbb, 8'hcc, 8'hdd}; // 等价于:b = a
$display("a[0] = %0h, b[0] = %0h", a[0], b[0]); // a[0] = aa, b[0] = dd
$display("a[1] = %0h, b[1] = %0h", a[1], b[1]); // a[1] = bb, b[0] = cc
$display("a[2] = %0h, b[2] = %0h", a[2], b[2]); // a[2] = cc, b[0] = bb
$display("a[3] = %0h, b[3] = %0h", a[3], b[3]); // a[3] = dd, b[0] = aa
$display("a[3][7] = %b, b[0][7] = %b", a[3][7], b[0][7]); // a[3][7] = 1, b[0][7] = 1
$display("a[3][6] = %b, b[0][6] = %b", a[3][6], b[0][6]); // a[3][6] = 1, b[0][6] = 0
$display("a[3][5] = %b, b[0][5] = %b", a[3][5], b[0][5]); // a[3][5] = 0, b[0][5] = 1
$display("a[3][4] = %b, b[0][4] = %b", a[3][4], b[0][4]); // a[3][4] = 1, b[0][4] = 1
5. 数组的基本操作:foreach
在foreach循环中,System Verilog会自动遍历数组中的元素。
索引变量自动声明,仅在循环内有效。
(注意:数组的不同声明方式会影响索引变量的遍历顺序)
一维数组:
int array1 [2] = '{45, 36} ; // [2]等价于[0:1]
foreach(array1[i]) begin
$display("array1[%0d] = %0d", i, array1[i]);
end
// array1[0] = 45
// array1[1] = 36
int array2 [0:1] = '{45, 36} ;
foreach(array2[i]) begin
$display("array2[%0d] = %0d", i, array2[i]);
end
// array2[0] = 45
// array2[1] = 36
int array3 [1:0] = '{45, 36} ;
foreach(array3[i]) begin
$display("array3[%0d] = %0d", i, array3[i]);
end
// array3[1] = 45
// array3[0] = 36
多维数组:
int array4 [2][3] = '{'{1, 2, 3}, '{4, 5, 6}} ;
foreach(array4[i,j]) begin
$display("array4[%0d][%0d] = %0d", i, j, array4[i][j]);
end
// array4[0][0] = 1
// array4[0][1] = 2
// array4[0][2] = 3
// array4[1][0] = 4
// array4[1][1] = 5
// array4[1][2] = 6
i为高维度索引变量,j为低维度索引变量。
遍历的顺序为:先固定i,遍历j,再遍历i。
也可以只遍历其中一个维度:
只遍历高维度:foreach(array[ i ])
只遍历低维度:foreach(array[, j ])
四、动态数组
定宽数组的宽度在编译时已经确定,而动态数组可以在仿真时分配空间或调整宽度,避免存储空间的浪费。
1. 动态数组的声明
int d1[] ; // 声明一个一维动态数组
int d2[], d3[] ; // 声明两个相同数据类型的动态数组
bit[31:0] d4[] ; // 声明二维动态数组
2. 动态数组的初始化
d1 = new[3] ; // 分配3个元素的空间
foreach(d1[i]) begin
d1[i] = i ; // 逐一初始化每个元素
end
d2 = '{0, 1, 2, 3} ; // 分配空间并初始化
int d5[]= '{5, 5, 5, 5,5} ; //也可以在声明时完成初始化
3. 动态数组的其他操作
(1)复制
int d1[], d2[], d3[], d4[], d5[]; // 声明5个int型动态数组
d1 = '{0, 1, 2} ; // 为d1分配空间并赋初值
d2 = d1 ; // d2 = '{0, 1, 2} ,不需要先调用new[]来分配空间
d3 = new[5] ; // 先给d3分配5个元素的空间
d3 = d1 ; // d3 = '{0, 1, 2} ,不需要重新new
d4 = new[1]; // 先给d3分配1个元素的空间
d4 = d1 ; // d4 = '{0, 1, 2} ,不需要重新new
d5 = new[5](d1) ; // d5 = '{0, 1, 2, 0, 0},分配空间并复制
d1 = new[5](d1) ; // d1 = '{0, 1, 2, 0, 0},改变宽度并复制
(2)删除所有元素
d1.delete() ;
d2 = new[0] ;
d3 = '{ } ;
五、队列
1. 队列的声明
int i, q1[$] ; // 一维队列
bit[7:0] q2[$] ; // 二维队列
2. 队列的初始化(不需要new[ ],不加')
(队列中的元素是连续存放的,所以不需要加 ' )
(加 ' 也不会报错,是存储方式有区别么?)
int q1[$] = {5, 4, 3, 2, 1} ; // 声明时初始化
int q2[$] ;
q2 = {1, 2, 3, 4, 5} ; // 先声明,再初始化
3. 队列的其他操作
(1)插入元素
(2)删除元素
(3)在索引中使用$
六、关联数组
七、数组的方法
-----------------未完待续-------------