2.6 链表
SystemVerilog提供了链表数据结构,但是应该避免使用它,因为SystemVerilog提供的队列更加高效易用。
2.7数组的方法
SystemVerilog提供了很多种数组的表示方法,那么我们对这些数组的操作方法又有哪些呢?下面就来一一介绍一下。
2.7.1 数组的缩减方法
基本的数组缩减方法就是把一个数组缩减成一个值。最常用的方法就是求和sum,除此之外还有product(乘)and(与)or(或)xor(异或)等。
在进行数组压缩的时候,应该特别重要的一点需要注意,那就是位宽的问题下面就用一个例子来进行说明。
module test_enum();
bit on[10];
int total;
initial
begin
foreach(on[i])
on[i]=i;
$display("on.sum=%0d",on.sum); //on.sum是单比特无符号的数
$display("on.sum=%0d",on.sum+32'd0); //on.sum是32比特数
$display("int sum=%0d",on.sum with (int'(item))); //利用with来限定on.sum的数据类型,这是一种比较好用的方式
end
endmodule
结果:
# on.sum=1
# on.sum=1 //Medesim SE 10.2c仿真出来的结果并不支持相加方式得到的数据转换
# int sum=5
SystemVerilog中,对定宽数组、队列、动态数组和关联数组可以使用$urandom_range($size(array)-1)来选取随机一个元素,而对于队列和动态数组还可以使用$urandom_range(array.size()-1)。
2.7.2 数组定位方法
如何选取数组中最大的值、最小的值?如何选取出数组中唯一值的队列?如何对数组中满足特定要求的值进行某种操作?下面我们就多这些问题一一作出求解方法。
module test_enum();
int f[6]={1,6,2,6,8,6},
d[]='{2,4,6,8,10},
q[$]={1,3,5,7},
tq[$];
initial
begin
tq=q.min(); //求最小值
foreach(tq[i])
$display("min:tq[%0d]=%0d",i,tq[i]);
tq=q.max(); //求最大值
foreach(tq[i])
$display("max:tq[%0d]=%0d",i,tq[i]);
tq=f.unique(); //求数组中唯一值的队列
foreach(tq[i])
$display("unique:tq[%0d]=%0d",i,tq[i]);
tq=d.find with (item>3); //利用find函数做操作
foreach(tq[i])
$display("find:tq[%0d]=%0d",i,tq[i]);
tq.delete(); //等价的操作
foreach(d[i])
if(d[i]>3)
tq.push_back(d[i]);
foreach(tq[i])
$display("tq[%0d]=%0d",i,tq[i]);
tq=d.find_index with (item>3); //输出的是index索引也就是第几位的值
foreach(tq[i])
$display("tq[%0d]=%0d",i,tq[i]);
end
endmodule
结果:
# min:tq[0]=1
# max:tq[0]=7
# unique:tq[0]=1
# unique:tq[1]=2
# unique:tq[2]=6
# unique:tq[3]=8
# find:tq[0]=4
# find:tq[1]=6
# find:tq[2]=8
# find:tq[3]=10
# tq[0]=4
# tq[1]=6
# tq[2]=8
# tq[3]=10
# tq[0]=1
# tq[1]=2
# tq[2]=3
# tq[3]=4
我想通过上述的代码和注释,大家都能够很好地理解。
注意:item被称为重复参数,它代表了数组中一个单独的元素,item是缺省的名字,你也可以指定别的名字。下面四种情况是等价的。
tq=d.find_first with (item==4);
tq=d.find_first() with (item==4);
tq=d.find_first(item) with (item==4);
tq=d.find_first(x) with (x==4);
当数组的缩减方法和条件语句with结合使用时,sum操作符的结果是条件表达式为真的次数。下面我们来看一个例子。
module test_enum();
int count,
total,
d[]='{9,1,8,3,4,4};
initial
begin
count=d.sum with (item>7); //比较表达式返回0或1
total=d.sum with ((item>7)*item);
$display("count=%0d total=%0d",count,total); //2,17
count=d.sum with (item<8);
total=d.sum with (item<8?item:0);
$display("count=%0d total=%0d",count,total);//4,12
count=d.sum with (item==4);
$display("count=%0d",count); //2
end
endmodule
2.7.3 数组的排序
SystemVerilog有几个可以改变数组中元素顺序的方法。包括反向、正序、逆序、随机。
int d[]='{9,1,8,3,4,4};
d.reverse();
d.sort();
d.rsort();
d.shuffle();
2.8 数据存储类型的选择
其实数据类型的选择是多方面的,我们要考虑灵活性、存储器用量、速度、排序和数据结构等多种方面,在我们以后的应用中,我们将会深入地理解每种不同的数据类型的利弊。
2.9 typedef创建新的类型
这是在原有数据类型之上定义新的数据类型。我们为了不混淆,约定所有用户自定义类型都带后缀“_t”。
下面我们来看几个例子。
parameter opsize=8;
typedef reg[opsize-1:0] opreg_t;
opreg_t op_a,op_b;
typedef bit[31:0] uint;
typedef int unsigned uint; //等价的两种方式
对于新的数组定义并不是很明显。你需要把数组的下标放在新的数组名称中。如下面的例子所示:
typedef int fixed_array5[5];
fixed_array5 f5;
initial
begin
foreach(f5[i])
f5[i]=i;
end
2.10创建用户自定义结构
Verilog中没有数据结构,这是它的一个比较大的缺陷。在SystemVerilog中,我们引入了数据结构的概念。struct只是把数据组织在一起,只是一个数据的集合。
2.10.1 使用struct创建新类型
struct可以把若干个变量组合到一起。我们统一将struct创建的新类型用“_s”来表示。
typedef struct{bit[7:0] r, g,b;} pixel_s;
pixel_s my_pixel;
initial
begin
typedef struct {int a,
byte b,
shortint c;} my_struct_s;
my_struct_s st='{32'haaaaaaaa,
8'hbb,
16'hcccc};
$display("st=%x %x %x",st.a,st.b,st.c);
end
2.10.2创建可容纳不同类型的联合
联合体,通常意义上来讲就是同一位置放置不同类型的数据。如果需要以若干不同的格式对同一寄存器进行频繁读写时,联合体相当有用。我们约定以“_u”为后缀。
typedef union { int i; real f;} num_u;
num_u un;
un.f=0.0;
2.10.3合并结构
通过一个例子我们来描述一下合并结构(packed)可以节省存储空间。
typedef struct packed {bit [7:0] r,g,b} pixel_p_s;
pixel_p_s my_pixel;
2.11类型的转化
SystemVerilog提供了多种数据类型,在我们实际的应用过程中,我们需要对数据类型进行转化。本部分就转化提供了几种方法,下面一一来介绍。
2.11.1静态转换
静态装换不对转换值进行检查。如果越界的话,我们也不能察觉到。
基本转换格式:type'(val)。
int i;
real r;
i=int '(10.0-0.1);
r=real '(42);
2.11.2动态转换
动态转换函数$cast允许对越界的数值进行检查,如果不越界返回1,否则返回0。
2.11.3流操作符
流操作符>>和<<用于把其后的数据打包成一个比特流。>>是把数据从左到右变成数据流,<<是把数据从右到左变成数据流。
基本的流操作
int h;
bit [7:0] b,
g[4],
j[4]='{8'ha,8'hb,8'hc,8'hd};
bit [7:0] q,r,s,t;
initial
begin
h={>>{j}}; //0a0b0c0d
h={<<{j}}; //b030d050
h={<>{q,r,s,t}}=j;//将分散到四个字节变量里
h={>>{t,s,r,q}};//将四个字节集中到h里
end
2.12 枚举类型
利用内建函数name()可以得到枚举变量值对应的字符串。我们统一用后缀“_e”来表示枚举的数据类型。
定义枚举值
枚举值缺省为从0开始递增的整数,可以自己定义枚举值。通常在我们把0指给枚举常量,可以避免一些不必要的错误。-
枚举类型的子程序
(1)first() 返回第一个枚举变量
(2)last() 返回最后一个枚举变量
(3)next() 返回下一个枚举变量
(4)next(N) 返回以后第N个枚举变量
(5)prev() 返回前一个枚举变量
(6)prev(N) 返回以前第N个枚举变量
遍历所有的枚举成员(注意对枚举类型值的定义)module test_typedef(); typedef enum{red,green,blue=6,yellow,white,black} colors; colors my_colors; initial begin my_colors=my_colors.first; do begin $display("my_colors=%0d/%s",my_colors,my_colors.name); my_colors=my_colors.next; end while (my_colors!=my_colors.first); end endmodule
结果:
# my_colors=0/red
# my_colors=1/green
# my_colors=6/blue
# my_colors=7/yellow
# my_colors=8/white
# my_colors=9/black
2.12.1枚举类型的转换
枚举类型的缺省类型为双状态的int。
可以通过简单的赋值表达式把枚举变量直接赋值给变量int。
-
不允许直接把int赋值给枚举变量,这种是出于越界情况的考虑。
module test_enum(); typedef enum {RED,BLUE,GREEN} COLOR_E; COLOR_E color,c2; int c; initial begin color=BLUE; c=color; c++; if(!$cast(color,c)) $display("cast failed for c=%0d",c); $display("color is %0d/%s",color,color.name); c++; c2=COLOR_E'(c); $display("c2 is %0d/%s",c2,c2.name); if(!$cast(color,c)) $display("cast failed for c=%0d",c); end endmodule
结果:
# color is 2/GREEN
# c2 is 3/
# cast failed for c=3
$cast(color,c)将int型动态转化为枚举类型,如果没有越界返回1,否则返回0;界内(0,1,2),3已经越界了。
2.13 常量
SystemVerilog中支持const修饰符,允许在变量声明时对其进行初始化,但不能在过程代码中改变其值。
initial
begin
const byte colon=":";
......
end
2.14 字符串
SystemVerilog中的string类型可以用来保存长度可变的字符串。单个字节是byte类型。字符串使用动态的存储方式,所以不用担心存储空间会全部用完。
module test_enum();
string s;
initial
begin
s="IEEE";
$display(s.getc(0));
$display(s.tolower());
s.putc(s.len()-1,"-");
s={s,"P1800"};
$display(s.substr(2,5));
my_log($psprintf("%s %5d",s,42));
end
task my_log (string message);
$display("@%0t:%s",$time, message);
endtask
endmodule
getc(N) 返回位置N上的字节
tolower()返回一个小写的字符串
putc(M,C)把字节C写到字符串的M位上,M必须介于0和len所给出的长度之间。
substr(start,end),读取从位置start到end之间的所有字符。
-
task函数是用来返回一个格式化的临时字符串,并且可以直接传递给其他子程序。
结果:# 73 对应字符“I” # ieee # E-P1 # @0:IEE-P1800 42