和verilog相比system verilog提供了很多数据改进的数据结构,一些数据对于创建者和测试者都有用。
内建数据类型
逻辑(logic)类型
system verilog对经典的reg数据类型进行了改进,使他除了作为变量以外,还能被连续赋值、门单元、和驱动模块所驱动,这种改进的数据类型被称为logic。
它既可被过程赋值也能被连续赋值,编译器可自动推断logic是reg还是wire。唯一的限制是logic只允许一个输入,不能被多重驱动,所以inout类型端口不能定义为logic。不过这个限制也带来了一个好处,由于大部分电路结构本就是单驱动,如果误接了多个驱动,使用logic在编译时会报错,帮助发现bug。所以单驱动时用logic,多驱动时用wire。
在Jason的博客评论中,Evan还提到一点logic和wire的区别。wire定义时赋值是连续赋值,而logic定义时赋值只是赋初值,并且赋初值是不能被综合的。
wire mysignal0 = A & B; // continuous assignment, AND gate
logic mysignal1 = A & B; // not synthesizable, initializes mysignal1 to the value of A & B at time 0 and then makes no further changes to it.
logic mysignal2;
assign mysignal2 = A & B; // Continuous assignment, AND gate
module tb ;
logic [7:0]i; //声明logic结构数据
initial begin
i = 0;
repeat(10) begin //循环10次
$display("i=%d",i);
i = i + 1;
end
$display("test running!");
end
endmodule
仿真结果
Start run at Nov 2 19:42 2020
i= 0
i= 1
i= 2
i= 3
i= 4
i= 5
i= 6
i= 7
i= 8
i= 9
logic不仅能够作为变量,而且可以被连续赋值,门单元和模块所驱动。但是logic不能够被多个结构体驱动。下面这段代码展示了logic的使用方法。
module logic_test(input logic rst_h
);
parameter CYCLE = 20;
logic q,q_1,d,clk,rst_1;
initial
begin
clk = 0;
forever #(CYCLE/2) clk = ~clk; // 过程赋值
end
assign rst_1 = ~ rst_h; //连续赋值
not n1(q_1,q); //q_1被门驱动
my_dff d1(q,d,clk,rst_1); //q被模块驱动
endmodule
SystemVerilog logic的使用方法
- 单驱动时logic可完全替代reg和wire,除了Evan提到的赋初值问题。
- 多驱动时,如inout类型端口,使用wire。
双状态数据类型
相比于verilog中的4状态数据类型(1,0,x,z),SV引入双状态数据类型有利于提高仿真器的性能并减少内存的使用量。下面逐例解释。
bit b; // 双状态 单bit
bit [31:0] b32; // 双状态,32bit,无符号
int unsigned ui; // 双状态,32bit,无符号
int i; // 双状态,32bit,有符号
byte b8; // 双状态,8bit ,有符号
shortint s; // 双状态,16bit,有符号
longint l; // 双状态,64bit,有符号
integer i4; // 四状态,32bit,有符号
time t; // 四状态,64bit,无符号
real r; // 双状态,双精度浮点数。
双状态数据类型有利于提高仿真器的性能并减少内存。
对四态信号的检查:($isunknown)
这会让任意位置出现X或者Z时返回1,使用实例如下:
if( $isunknown(iport) == 1)
$display("@%0t: 4-state velue detected on iport %b",$time,iport)
部分仿真结果如下:
module tb ;
longint i; //声明logic结构数据
byte b8;
initial begin
i = 0;
b8 = 2;
repeat(3) begin //循环
$display("i=%0d , b8=%0d",i,b8);
i = i - 1;
b8=b8*2;
end
end
endmodule
Start run at Nov 2 20:01 2020
i=0 , b8=2
i=-1 , b8=4
i=-2 , b8=8
定宽数组
verilog要求在声明中必须给出数组的上下边界,因为几乎所有的数组都是用0作为索引下界,所以sv允许只给出数组的宽度,跟c语言类似。
例 :数组的声明
int arr[0:15]; //16个整数
int c_style[16]; //c风格,16个整数
可以通过在变量后面指定维度的方式来声明多维数组;
例 : 声明8行4列多维数据
int arr2 [0:7][0:3]; //完整声明
int arr2_cstyle[8][4];
特别注意:如果读取的数据地址越界,那么sv会返回数组元素的缺省值
- logic类型:返回 X
- 双状态类型:返回 0
- 线网在没有驱动时输出为 Z
数组的遍历
module tb ;
int md[2][3] = '{'{1,2,3},'{4,5,6}}; // 声明2行3列数组并赋初值,这里使用常量数组来初始化md数组
initial begin
foreach(md[i,j]) //遍历数组
$display("md[%0d][%0d]=%0d",i,j,md[i][j]);
end
endmodule
输出结果
VCS Build Date = May 24 2016 20:38:43
Start run at Nov 2 20:26 2020
md[0][0]=1
md[0][1]=2
md[0][2]=3
md[1][0]=4
md[1][1]=5
md[1][2]=6
按维度遍历
module tb ;
int md[2][3] = '{'{1,2,3},'{4,5,6}}; // 声明2行3列数组并赋初值
initial begin
foreach( md[i] ) begin //遍历第1维
$write("%2d:",i);
foreach( md[,j] ) //遍历第2维
$write("%3d",md[i][j]);
$display;//显示一个维度
end
end
endmodule
结果
0: 1 2 3
1: 4 5 6
数组的比较与复制
module tb ;
bit [31:0] src[5] = '{0,1,2,3,4},
dst[5] = '{5,4,3,2,1};
initial begin
//比较两个数组
if( src == dst ) $display("src == dst");
else $display("src != dst");
//把src元素拷贝到dst
dst = src;
//只改变一个元素
dst[0] = 5;
//所有元素的值是否相等
$display("src %s dst",(src == dst)? "==" : "!=");
//使用数组片段对第 1-4 个元素进行比较
$display("src[1:4] %s dst[1:4]",(src[1:4] == dst[1:4]) ? "==" : "!=");
end
endmodule
输出结果
Start run at Nov 2 20:41 2020
src != dst
src != dst
src[1:4] == dst[1:4]
使用数组位下标和数组下标
例 : 位下标和数组下标
module tb ;
bit [31:0] src[5] = '{5,1,2,3,4};
initial begin
$display(src[0],, // 5
src[0][0],, // 1
src[0][2:1]); // 2
end
endmodule
合并数组
对于一些数据你可能既希望作为整体访问,也希望拆分为更小单元访问,使用sv的合并数组可以实现这个功能。
例 : 合并数组例子
module tb ;
bit [3:0][7:0] bytes ; //四个字节组装而成的32 bit数据
initial begin
bytes = 32'hCafe_Dada;
$display(bytes,, //显示所有32 bit
bytes[3],, //显示最高字节 "CA"
bytes[3][7]); //显示最高bit位 ”1“
end
endmodule
结果
3405699802 202 1
扩展说明:
已知一个多维混合数组的定义为:
bit [3:0][7:0][15:0] Array [3:0][7][6];
那么当我们写下
Array[2][3][2][2] = xxxx;
的时候,到底是对哪个位置赋值了??
话不多说,直接看解答好啦~最后的答案其实很简单,因为有一个简单的图示估计很多人知道,就是逆时针索引法:
合并数组和非合并数组的选择
- 合并数组:和标量进行相互转换,等待数组中的变化必须使用合并数组
动态数组
sv提供动态数组,动态数组在声明时使用空下标[ ]。宽度将不会在编译时给出,而是在程序运行时在指定。数组开始时为空,因此你必须调用new [ ]
操作来分配空间。
例 :动态数组实例
module tb ;
int dyn[],d2[] ; //声明动态数组
initial begin
dyn = new[5] ;//分配5个元素空间
foreach( dyn[i] ) dyn[i] = i; //对5个元素空间的值进行初始化
d2 = dyn ; //复制数组dyn到d2
d2[0] = 5 ; //修改d2[0]的值为5
$display(dyn[0],d2[0]); //显示数值
dyn = new[20](dyn); //分配20个证书值并进行复制
dyn = new[100]; //分配 100 个新的整数值
dyn.delete(); //删除所有元素
end
endmodule
例 : 使用动态数组保存元素数量不定的列表
bit [7:0] mask[] = '{ 8'h00,8'h01,
8'h03,8'h04,
8'h23,8'h36};
队列
队列与链表类似,可以在一个队列中的任何地方增加和删除元素,这类操作在性能上的损失比动态数组小的多!
队列的声明使用[$]
,队列元素的编号从0到$。sv的队列类似与STL的双端队列。可以通过增加元素来创建队列。你可以扩大和缩小队列,但是不用向动态数组一样付出很大的代价。
例 : 队列操作
module tb ;
//注意,队列常量不需要使用“’”
int q2[$] = {3,4};
int q[$]= {0,2,5};
initial begin
//在2之前插入1
q.insert(1,1); // 0 1 2 5
foreach(q[i])$write("%3d",q[i]); $display; //显示结果
//在5之前插入队列q2
q.insert(3,q2); // 0 1 2 3 4 5
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
//删除q下标为1的元素
q.delete(1); //0 2 3 4 5
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
//以下操作速度很快
//在队列最前面插入 8
q.push_front(8); //8 0 2 3 4 5
foreach(q[i])$write( "%3d",q[i]); $display; //打印整个链表
//在队列最前后面插入 6
q.push_back(6); //8 0 2 3 4 5 6
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
//读出队列最前面的元素并从队列中移除该元素
$display(q.pop_front());
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果 0 2 3 4 5 6
//读出队列最后面的元素并从队列中移除该元素
$display(q.pop_back());
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果 0 2 3 4 5
end
endmodule
结果
Start run at Nov 2 21:56 2020
0 1 2 5
0 1 2 3 4 5
0 2 3 4 5
8 0 2 3 4 5
8 0 2 3 4 5 6
8
0 2 3 4 5 6
6
0 2 3 4 5
说明: 可以使用字下标串联来代替方法。对于队列
q[$]={0,2,4}
,如果把$
放在一个范围表达式的左边,那么$
将代表最小值,例如[$:2]
就代表[0:2]
。如果把$
放在一个范围表达式的右边,那么$
将代表最大值,例如[1:$]
就代表[1:2]
。
使用上述方法的操作如下:
例 : 队列串联表达式
module tb ;
//注意,队列常量不需要使用“’”
int q2[$] = {3,4};
int q[$]= {0,2,5};
initial begin
//在2之前插入1
q={q[0:1],1,q[2:$]}; // 0 1 2 5
foreach(q[i])$write("%3d",q[i]); $display; //显示结果
//在5之前插入队列q2
q={q[0:2],q2,q[3:$]}; // 0 1 2 3 4 5
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
//删除q下标为1的元素
q.delete(1); //0 2 3 4 5
foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
//删除整个队列
q={};
end
endmodule
关联数组
当你只需要偶尔创建一个大容量数组,那么动态数组已经足够好用了,但是如果需要超大容量的呢?sv提供了关联数组用来保存稀疏矩阵的元素。也就是说只为实际写入的数据开辟存储空间。可以将其理解为哈希表,虽然哈希表带来了额外开销,但是在这种情况下,这是可以也接受的。
例 : 关联数组声明,初始化和使用
module tb ;
bit [63:0] assoc[ bit [63:0] ],idx = 1;
initial begin
//对稀疏分布的元素进行赋值
repeat (3) begin
assoc[idx] = idx;
idx = idx << 1;
end
//foreach遍历数组
foreach(assoc[i])$display("assoc[%h]=%h",i,assoc[i]);
$display("+++++++++++++++++++++++");
//使用函数遍历数组
if( assoc.first(idx) ) begin
do
$display("assoc[%h]=%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
endmodule
结果
Start run at Nov 2 22:22 2020
assoc[0000000000000001]=0000000000000001
assoc[0000000000000002]=0000000000000002
assoc[0000000000000004]=0000000000000004
+++++++++++++++++++++++
assoc[0000000000000001]=0000000000000001
assoc[0000000000000002]=0000000000000002
assoc[0000000000000004]=0000000000000004
The array now has 2 elements
例 : 使用带字符串索引的关联数组
//关联数组也可以用字符串索引进行寻址,使用字符串索引读取文件,并建立关联数组switch,可以实现字符串到数字的映射。
/*
输入文件内容如下:
42 min_address
1492 max_address
*/
int switch[string],min_address,max_address; //定义变量
initial begin //初始化
int i,r,file;
string s;//string 用来年保存长度可变的字符串常量
file = $fopen(“switch.txt”,r); //在当前目录下以只读的方式打开文件switch.txt,使用指针向量file指向该文件
while(! $feof(file) ) begin //未读取到文件的结尾时
r=$fscanf (file, “%d %s”, i, s); //将文件中的数字以10进制的方式保存到i中,字符串保存到s中
switch[s] = i; //为数组中的字符串指定地址
end
$fclose(fire); //关闭文件
//获取最小地址值,缺省为0
min_address=switch[“min_address”] //将min_address对应的地址(数字)赋值给min_address
//获取最大地址值,缺省为1000
if(switch.exists(“max_address”)) //使用exists函数判断switch数组中是否含有“max_address”
max_address = switch[“max_address”]//含有的话,将内容的地址赋值给 max_address
else
max_address =1000 //不存在指定内容,则默认地址为1000
//打印数组的所有元素
foreach(switch [s])
$display(“switch[ ‘ %s ’ ] = %0d ”, s ,switch[s] );//打印数组的内容和相应的地址
end
例 : 字符关联数组操作
module tb ;
bit [63:0] assoc[ string ];
initial begin
//对稀疏分布的元素进行赋值
assoc["a"] = 1;
assoc["b"] = 2;
assoc["c"] = 3;
//foreach遍历数组
foreach(assoc[i])$display("assoc[%s]=%h",i,assoc[i]);
end
endmodule
链表
sv提供了链表数据结构,类似于STL的列表容器,这个容器被定义为参数化的类,可以根据用户所需存放各种类型的数据。
虽然sv提供了链表,但是应该避免使用它。sv使用队列更高效。
数组的方法
sv提供了很多数组的方法,可以用于任何一种非合并数组的类型,包括定宽数组,动态数组,队列和关联数组。
sum方法:数组求和
例 : 数组求和
bit on[5] = '{0,1,0,1,0};//单bit数组
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
//单bit求和
$display("on.sum=%0d",on.sum);
end
endmodule
结果
Start run at Nov 2 22:49 2020
0 1 0 1 0
on.sum=0
sum方法的结果位宽和数组定义的位宽是一致的。
product方法:数组求积
例 :数组求积
module tb ;
int on[3] = '{1,2,3};//单bit数组
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
//单bit求和
$display("on.product=%0d",on.product);
end
endmodule
结果
Start run at Nov 2 22:57 2020
1 2 3
on.product=6
##and,or,xor方法:数组求与,或,异或
例 :数组求与,或,异或
module tb ;
int on[3] = '{1,3,3};//单bit数组
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
$display("on.and=%0d",on.and);
$display("on.or=%0d",on.or);
$display("on.xor=%0d",on.xor);
end
endmodule
min,max方法:最大值最小值方法
注意返回值为一个队列!
例 :最大值最小值方法
module tb ;
int on[] = '{1,3,3,9};
int rt[$];
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
rt=on.min();foreach(rt[i])$write("%3d",rt[i]);$display;
rt=on.max();foreach(rt[i])$write("%3d",rt[i]);$display;
end
endmodule
结果
Start run at Nov 2 23:11 2020
1 3 3 9
1
9
unique方法:排除重复数值
注意返回值为一个队列!
例 :排除重复数值
module tb ;
int on[] = '{1,3,3,9};
int rt[$];
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
rt=on.unique();foreach(rt[i])$write("%3d",rt[i]);$display;
end
endmodule
结果
Start run at Nov 2 23:15 2020
1 3 3 9
1 3 9
size方法:获取数组大小
例 :获取数组大小
module tb ;
int on[] = '{1,3,3,9};
int rt[$];
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
//获取动态数组的大小
$display("len=%0d",on.size());
//获取各种数组大小的方法
$display("len=%0d",$size(on));
endmodule
结果
Start run at Nov 2 23:18 2020
1 3 3 9
len=4
len=4
find方法:数组定位方法
注意返回值为一个队列!
例 :find数组定位方法
module tb ;
int on[] = '{4,1,3,3,9};
int rt[$];
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
//找出所有大于等于3的元素 4 3 3 9
rt=on.find with (item >= 3);
foreach(rt[i])$write("%3d",rt[i]);$display;
//找出值大于等于2的索引 0 2 3 4
rt=on.find_index with (item >= 2);
foreach(rt[i])$write("%3d",rt[i]);$display;
//找出第一个大于4的值 9
rt=on.find_first with (item >4);
foreach(rt[i])$write("%3d",rt[i]);$display;
//找出最后一个小于9的值 3
rt=on.find_last with (item < 9);
foreach(rt[i])$write("%3d",rt[i]);$display;
//找出第一个大于4的数据的索引值 4
rt=on.find_first_index with (item >4);
foreach(rt[i])$write("%3d",rt[i]);$display;
//找出最后一个==3的数据的索引值
rt=on.find_last_index with (item == 3);
foreach(rt[i])$write("%3d",rt[i]);$display;
end
endmodule
结果
Start run at Nov 2 23:31 2020
4 1 3 3 9
4 3 3 9
0 2 3 4
9
3
4
3
例 : 等同的几种描述
rt=on.find_first with (item >= 3);
rt=on.find_first() with (item >= 3);
rt=on.find_first(item) with (item >= 3);
rt=on.find_first(x) with (x >= 3);
数组排序方法:reverse,sort,rsort,shuffle
例:数组排序
module tb ;
int on[] = '{4,1,3,3,9};
int rt[$],msum;
initial begin
foreach( on [i])$write("%3d",on[i]);$display;
//数组反序
on.reverse();
foreach( on [i])$write("%3d",on[i]);$display;
//数组 小->大
on.sort();
foreach( on [i])$write("%3d",on[i]);$display;
//数组 大->小
on.rsort();
foreach( on [i])$write("%3d",on[i]);$display;
//数组洗牌
on.shuffle();
foreach( on [i])$write("%3d",on[i]);$display;
end
endmodule
结果
Start run at Nov 3 00:12 2020
4 1 3 3 9
9 3 3 1 4
1 3 3 4 9
9 4 3 3 1
3 4 1 9 3
结构数组的使用和排序
例 : 结构数组的使用和排序例子
module tb ;
int rt[$],msum;
struct packed {byte red,green,blue;} c[]; //结构数组
initial begin
c=new[5];
foreach(c[i])c[i] = $urandom;//随机赋值
foreach( c[i])$write("%10d",c[i].red);$display;
c.sort with (item.red); //up排序red
foreach( c[i])$write("%10d",c[i].red);$display;
end
endmodule
结果
Start run at Nov 3 00:17 2020
-30 -34 127 -86 121
-86 -34 -30 121 127
typedef创建新的数据类型
例 :创建新数据类型
parameter N = 8;
typedef reg [N - 1 : 0 ] opreg_t;
opreg_t c;
struct创建新的数据类型
例 :struct创建新数据类型
module tb ;
int rt[$];
typedef struct packed {byte red,green,blue;} pixel_s;//结构数组
typedef struct packed {int a;bit[2:0] b;} m_s;//结构数组
pixel_s c='{1,2,3};
m_s cc = '{
32'd23,
3'b101
};
initial begin
$display("%5d %5d %5d",c.red,c.green,c.blue);
$display("%5d %5d",cc.a,cc.b);
end
endmodule
结果
Start run at Nov 3 00:38 2020
1 2 3
23 5
静态转换
静态转换操作不对转换值进行检查。
例 : 静态转换例子
module tb ;
int rt[$];
int i ;
real r;
initial begin
i = int '(10.0 - 0.1); //非强制转换
r = real '(42);
$display(i);
$display(r);
end
endmodule
流操作
module tb ;
int rt[$];
byte h ;
bit [3:0] q[2] = '{4'h04,4'h01} ;
initial begin
$display("%6b %6b",q[0],q[1]);
//将q打包为byte
h = {>>{q}}; //0100_0001
$display("%b",h);
//位倒叙
h = {<<{h}}; //1000_0010
$display("%b",h);
end
endmodule
上一篇:10.6 system verilog基本语法下一篇:10.6.2 枚举与字符串
相关评论(0)