数组类型
非组合型(unpacked)
对于Verilog,数组经常会被用来做数据存储,
例如 reg [15:0] RAM [0:4095]; //memory array 4096*16-bit
SV将Verilog这种声明数组的方式称之为非组合型声明,即其中的成员之间存储数据都是互相独立的。
Verilog也不会指定软件去如何存储数组中的成员。
wire [7:0] table [3:0];// 4*8-bit
SV保留了非组合型的数组声明方式,并且扩展了允许的类型,
包括event,logic,bit,byte,int,longint,shortreal和real类型。
SV也保留了Verilog索引非组合型数组或者数组片段的能力,这种方式为数组以及数组片段的拷贝带来了方便。
1
2
3
4
5
6
7
8int a1 [7:0][1023:0]; //unpacked array //8*1024-bit
int a2 [1:8][1:1024]; //unpacked array 索引的时候高维在左边,低维在右边
a2 = a1; //copy an entire array
a2[3] = a1[0]; //copy a slice of an array
//声明数组(非组合型)的方式,以下两种皆可 (单位元素 数组名 数组大小)
logic [31:0] data [1024]; //1024*8-bit
logic [31:0] data [0:1023]; //1024*8-bit
组合型(packed)
SV将Verilog的向量作为组合型数组声明方式
1
2wire [3:0] select; //4-bit"packed array"
reg [63:0] data; //64-bit"packed arraySV也进一步允许多维组合型数组的声明
1
2logic [3:0][7:0] data; //2-D packed array 4*8-bit (单位元素 数组大小 数组名)
bit [3:0][7:0] data; //2-D packed array组合型数组会进一步规范数据的存储方式,而不需要关心编译器或者操作系统的区别。
1个word 大小32-bit 对于二值逻辑来说,32位用一个word存放、对于四值逻辑来说要用两个word存放
组合型(packed)除了可以运用的数组声明,也可以用来定义结构体的存储方式(连续存放)
1
2
3
4
5
6
7typedef struct packed{
logic [7:0] crc;
logic [63:0] data;
}data word;
data word [7:0] darray;//1-D packed array of
//packed structures
//连续存放,节省空间、易读取组合型数组和其数组片段也可以灵活选择,用来拷贝和赋值等
1
2
3
4
5
6
7
8
9
10logic [3:0][7:0] data; //2-D packed array
wire [31:0] out = data; //whole array
wire sign = data[3][7]; //bit-select
wire [3:0] nib = data [0][3:0]; //part-select
byte high_byte;
assign high_byte = data[3]; //8-bit slice
logic [15:0] word;
assign word = data[1:0]; //2 slices
int [1:0][2:0] arr [3:0][4:0]; // 四维数组 4*5*2*3 (混合型数组)
数组初始化
组合型(packed)数组初始化时,同向量初始化一致
1
2
3logic [3:0][7:0] a = 32'h0; //vector assignment
logic [3:0][7:0] b = {16'hz, 16'h0}; //concatenate operator
logic [3:0][7:0] c = {16{2'b01}}; //eplicate operator非组合型(unpacked)数组初始化时,则需要通过 ‘{} 来对数组的每一个维度进行赋值。
1
2
3
4
5
6
7
8
9int d[0:1][0:3]='{'{7,3,0,5},'{2,0,1,6}};
//d[0][0]=7
//d[0][1]=3
//d[0][2]=0
//d[0][3]=5
//d[1][0]=2
//d[1][1]=0
//d[1][2]=1
//d[1][3]=6非组合型数组在初始化时,也可以类似结构体初始化,通过 ‘{} 和default关键词即可以完成
1
int a1 [0:7][0:1023] = '{default:8'h55};
数组赋值
以下是组合型数组的赋值方法
1
2
3
4
5
6logic [1:0][1:0][7:0] a; //3-D packed array 2*2*8-bit
a[1][1][0]=1'b0; //assign to one bit
a=32'hF1A3C5E7; //assign to full array
a[1][0][3:0]=4'hF; //assign to a part select
a[0]=16'hFACE; //assign to a slice
a={16'bz,16'b0}; //assign concatenation非组合型数组的数据成员或者数组本身均可以为其赋值
1
2
3byte a [0:3][0:3];
a[1][0]=8'h5; //assign to one element
a[3]='{'hF, 'hA, 'hC, 'hE}; //assign list of values to slice of the array
数组拷贝
对于组合型数组,由于数组会被视为向量,因此当赋值左右两侧操作数的大小和维度不相同时,也可以做赋值。
如果当尺寸不相同时,则会通过截取或者扩展右侧操作数的方式来对左侧操作数赋值。
1
2
3
4
5
6
7bit [1:0][15:0] a; //32 bit 2-state vector
logic [3:0][7:0] b; //32 bit 4-state vector
logic [15:0] c; //16 bit 4-state vector
logic [39:0] d; //40 bit 4-state vector
b = a; //assign 32-bit array to 32-bit array
c = a; //upper 16 bits will be truncated
d = a; //upper 8 bits will be zero filled对于非组合型数组,在发生数组间拷贝时,则要求做左右两侧操作数的维度和大小必须严格一致。
1
2
3logic [31:0] a [2:0][9:0]; //3*10*32-bit
logic [0:31] b [1:3][1:10]; //3*10*32-bit
a=b; //Iassign unpacked array to unpacked array 维度相同非组合型数组无法直接赋值给组合型数组,同样地,组合型数组也无法直接赋值给非组合型数组。
foreach循环结构
SV添加foreach循环来对一维或者多维数组进行循环索引,而不需要指定该数组的维度大小。
1
2
3int sum [1:8][1:3];
foreach (sum[i,j])
sum[i][j] = i+j; //initialize arrayforeach循环结构中的变量无需声明。
foreach循环结构中的变量是只读的(可以引用不能修改),其作用域只在此循环结构中。
系统函数
1 | $dimensions(array_name) //用来返回数组的维度。 |
动态数组
与之前的定长数组相比,SV还提供了可以重新确定大小的动态数组。
动态数组在声明时需要使用[ ],这表示不会在编译时为其制定尺寸,而是在仿真运行时来确定。
动态数组一开始为空,而需要使用new[]来为其分配空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20int dyn[], d2[]; //peclare dynamic arraye
initial begin
dyn = new[5]; //A:Allocate 5 elements 默认5个0
foreach (dyn[j])
dyn[j] = j; //B:Initialize the elements
d2 = dyn; //C:Copy a dynamic array
d2[0] = 5; //D:Modify the copy
$display(dyn[0],d2[0]); //E:See both values(0 & 5)
dyn = new[20](dyn); //F:Allocate 20 ints©
dyn = new[100]; //G:Allocate 100 new ints
//old values are lost
logic [31:0] vec1, vec2, vec3;
vec1 = 'h11223344; //11223344
vec2 = {'h11, 'h22, 'h33, 'h44}; //00000044
vec2 = {8'h11, 8'h22, 8'h33, 8'h44}; //11223344
dyn.delete(); //数组清空操作
`{}或者new[0] //数组清空操作
end内建方法size()可以返回动态数组的大小。
内建方法delete()可以清空动态数组,使其尺寸变为0。
动态数组在声明时也可以完成其初始化
1
2
3
4
5
6
7
8
9bit [7:0] mask[]='{8'b00000000,
8'b00000001,
8'b00000011,
8'b00000111,
8'b00001111,
8'b00011111,
8'b00111111,
8'b01111111,
8'b11111111};
队列
- 可以在队列的任何位置添加或者删除数据成员。
- 可以通过索引来访问队列的任何一个成员。
- 通过[$]来声明队列,队列的索引值从0到$(**最后一个元素为$**)。
- 可以通过内建方法push_back(val)、push_front(val)、pop_back()和pop_front()来顺序添加或者移除并且获得数成员。
- 可以通过insert(pos,val)来在指定位置插入数据成员。
- 可以通过delete()来删除所有数据成员。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28int j=1,
q2[$] = {3,4}, //Queue literals do not use()
q[$] = {0,2,3}; //{0,2,3}
initial begin
q. insert(1,j); //{0,1,2,3} Insertj before ele #1
q. delete(1); //{0,2,3} Delete element #1
//These operations are fast
g.push_front(6); //{6,0,2,3} Insert at front
j=g.pop_back(); //{6,0,2} j=3
q.push_back(8); //{6,0,2,8} Insert at back
j=q.pop_front; //{0,2,8} j=6
foreach (q[i])
$display(q[i]); //print entire queue
q.delete(); // {} Delete queue end
int j=1,
q2[$] = {3,4}, //Queue literals do not use
q[$] = {0,2,5}; //{0,2,5} initial begin //Result
q = {q[0],j,q[1:$]}; //{0,1,2,5} Insert 1 before 2
q = {q[0:2], q2, q[3:$]}; //{0,1,2,3,4,5} Insert queue in q 字符拼接可以,无法用insert插入队列
q = {q[0],q[2:$1}; //{0,2,3,4,5} Delete elem.#1
//These operations are fast
q = {6,g}; //{6,0,2,3,4,5} Insert at front
j = q[$]; //j=5 pop back g=q[0:$-11;//{6,0,2,3,4} equivalent g={q,8};//{6,0,2,3,4,8} Insert at back
j = q[0]; //j=6 pop_front g=g[1:$];//{0,2,3,4,8} equivalent
q = {}; //{} pelete contents
end
关联数组
由于处理器在访问存储时的访问的随机或者散乱的,这意味个测试中,处理器也许只会访问几百个存储地址,而剩下大的地址都将被初始化为0并且浪费了仿真时的存储空间。
- 关联数组可以用来存放散列的数据成员。索引类型可以为任何类型,而散列存储的数员也可以为任意类型。
1
2
3
4
5
6
7
8
9
10
11initial begin
bit [31:0] mem [int unsigned];
int unsigned data,addr;
repeat(5)begin
std::randomize(addr,data)with {addr[31:8]==0; addr[1:0]== 0; data inside {[1:10]};}; //随机生成数据
$display("address:'h80x,data:'h80x",addr,data);
mem[addr]=data;
foreach(mem[idx])
$display("mem address:'ha0x,data:'h80x", idx, mem[idx]);
end
end
缩减方法
- 基本的数组缩减方法是把一个数组缩减成一个值。
- 最常用的缩减方法是sum,它对数组中的所有元素求和
1
2
3
4
5byte b[$]={2, 3, 4, 5};
int w;
w=b.sum(); //14=2+3+4+5
w=b.product(); //120=2*3*4*5
w=b.and(); //00000000=2&3&4&5其它的数组缩减方法还有product(积),and(与),or xor(异或)
定位方法
对于非合并数组,可以使用数组定位方法,其返回值将是一列而非一个数据成员。
1
2
3
4
5
6
7int f[6]={1, 6, 2, 6, 8, 6}; //Fixed-size array
int d[]={2, 4, 6, 8, 10}; //Dynamic array
int g[$]={1, 3, 5, 7}; //Queue
tq[$]; //Temporary queue for res
tq=g.min(); //{1}
tg=d.max(); //{10}
tq=f.unique(); //{1,6,2,8}使用foreach也可以实现数组的搜索,不过使用find…with查找满足条件的数据成员时,更为方便。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15int d[]={9, 1, 8, 3, 4, 4};
tq[$];
//Find all elements greater than 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]);
tg=d.find_index with (item>3); //{0,2,4,5}
tg=d.find_first with (item>99); //{-none found
tg=d.find_first_index with (item==8); //{2}d[2]=8
tg=d.find_last with (item==4); //{4}
tq=d.find_last_index with (item==4); //{5}d[5]=4
排序方法
- 可以通过排序方法改变数组中元素的顺序,可以对它们进行逆向或者乱序的排列。
1
2
3
4
5int 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}
思考问题
- 链表和SV中的哪些数组类型(定长数组、动态数组、队列、关联数组)相似,相似的特性有哪些?
- 从存放方式来看,定长数组、动态数组、队列都是连续存放,关联数组和链表是非连续存放
- 从查找数据上来看,链表和关联数组依靠指针来维系顺序,查找低效,但增、删、改方便
- 队列借鉴了链表的数据结构特点,依靠更多的指针维系元素前后关系
- 如何使用动态数组来实现队列的几个方法(1)function void push_back(T val),(2)function T pop_front),(3)function void insert(int POS,T val)。可以在module中,通过声明一个动态数组 int array[],并且实现以上三个方法,再通过在initial begin.…end过程块中测试、打印以上方法的结果来完成代码实现和测试。需要提交代码截图和测试的打印结果。