1. 数据类型
1.1 logic
- 变量没有声明类型时,默认为logci
- verilog中有两种基本的数据类型:变量和线网(net)。都为4状态(0、1、X、Z)
- SystemVerilog中将Verilog中的reg进行了一定改进,使其除了作为一个变量外,还可以被连续赋值、门单元和模块所驱动。任何可以使用了reg和wire的地方都可以使用logic,除了存在多个结构性驱动场景时,例如,对双向总线建模时,应该申明线网类型(例如:wire),不能使用logic。
- logic为4状态类型。
类型整理表
类型名 | 位宽 | 有无符号 | 状态 | 默认值 |
---|---|---|---|---|
wire | 1 | 无 | 4 | Z |
reg | 1 | 无 | 4 | X |
logic | 1 | 无 | 4 | X |
bit | 1 | 无 | 2 | 0 |
int | 32 | 有 | 2 | 0 |
byte | 8 | 有 | 2 | 0 |
shortint | 16 | 有 | 2 | 0 |
longint | 64 | 有 | 2 | 0 |
integer | 32 | 有 | 4 | 32{1‘bX} |
time | 64 | 无 | 4 | 64{1’bX} |
- 还有 real 型为双状态,双精度浮点数。
- 相比2状态变量,仿真器通常需要多一倍的空间去存放4状态变量。
- 有符号的变量,可以通过 unsigned 关键字来声明无符号情况,例如 int unsigned ui;
变量的默认值图
1.2 4状态和2状态之间转化导致的错误
4状态有X和Z,转化为2状态时,不论转化为了0/1,都不再是原来的X和Z。
下例中DUT为一个异步复位的DFF。
4状态波形(logic):
2状态波形(bit):
2. 定宽数组
2.1 数组的声明
- verilog声明数组时要求给出数组的上下界
- SystemVerilog声明定宽数组的时候,可以给出数组的上下界,也可以只给出数组宽度。
//一维数组
int a[0:15]; //16个整数 【0】...【15】
int b[16]; //16个整数 【0】...【15】 只给出数组宽度就是大端模式
int c[15:0]; //16个整数, a和b是一样的,c与其相反
//多维数组
int a[0:7][0:3];
int a[8][4];
a[7][3] = 1;
2.2 数组的初始化
- 可以直接声明的时候就初始化,或者先声明后初始化
- 可以只给部分元素初始化赋值
- 可以指定未赋值元素的初始值
int a[4] = '{0,1,2,3}; //对4个元素分别赋值0-3。 a[0]=0; a[1]=1; a[2]=2; a[3]=3;
int a[4:0] = '{0,1,2,3}; //a[3]=0; a[2]=1; a[1]=2; a[0]=3;
int b[4];
b = '{0,1,2,3}; //先声明,后赋值
b[0:2] = '{0,1,2}; //部分赋值
int c[5] = '{5{8}}; //5个值全赋值为8
int d[5] = '{6,7,default:1}; //{6,7,1,1,1}
3. 合并数组与非合并数组
-
单比特的变量称为标量,多比特的变量称为向量。
-
在Verilog和Sverilog中声明数组时,可以在数组变量名前、后声明"维度"。
-
对于Verilog,声明在变量名前的"维度"叫做"vector width" dimension, 声明在变量名后的"维度"叫做"array" dimensions。
-
对于Sverilog,声明在变量名前的"维度"叫做 packed array,声明在变量名后的"维度"叫做unpacked array。
-
packed array 会将内容存放在一起,而 unpacked array 会随机存储。
1. 一般SV仿真器在存放数组元素时使用32bit的字边界,所以byte、shortint和int是放在一个字中,而longint则存放在两个字中。
2. 这里的三个b_unpack的存储地址是随机的,不一定在一起。
1. 这里的5个barray的存储地址是随机的,不一定在一起。
3.1 多维数组
bit [3:0] [7:0] array1 [2] [3];
array1 [1][2][3][7] = 0;
4. 动态数组
- 定宽数组的大小在编译时就确认了
- 动态数组的大小在使用时才会确认,可以认为动态数组是一个在运行过程中才会被确认大小的定宽数组
4.1声明与初始化
- 动态数组声明和赋值分开时,需要先使用new[ ]操作符来分配空间。
int dyn[]; //声明动态数组
dyn = new[5]; //分配5个元素,此时动态数组的宽度为5
dyn = new[20](dyn); //重新分配20个元素,并将原dyn的值赋给新的dyn
dyn = new[100]; //分配100个元素,原先的值已丢弃(释放)
- 动态数组声明和赋值一起的时候,就可以不需要new[ ]操作符
int dyn[] = '{0,1,2,3}; //声明动态数组,宽度为4,并赋值如下:dyn[0]=0;dyn[1]=1;dyn[2]=2;dyn[3]=3;
5.队列
5.1 声明与初始化
- 队列的初始化不需要 '{ },而是 { }
int q[$];
int qq[$] = {1,2,3,3,2,1};
5.2 队列的特殊函数
- push_front、push_back、pop_front、pop_back这四个函数只实用于队列,不能用于其他数组
int q2[$] = {1,2};
int q1[$] = {7,8,9};
q1.insert(1, q2); //q1为{7,1,2,8,9}。在q1的第1位上插入q2
6. 关联数组
6.1 声明与初始化
- 关联数组比较类似perl中的hash,python中的 dictionary
- 关联数组名后为index的类型,关联数组名前为value的类型
- 关联数组只会给已经赋值的元素分配存储空间,比较适合存储大容量的数组
bit [63:0] mem[bit [15:0]]; //变量名:mem; index类型: bit[15:0]; 值的类型: bit[63:0]
bit [3:0] assoc [string];
7. 创建用户自定义类型
- 关键字为typedef
- 注意将一个定宽数组定义为一种类型的写法
typedef bit [31:0] uint; //uint就代表 bit[31:0] 这种类型
uint aa; //等价于 bit[31:0] aa; aa这个变量的类型就时bit [31:0]
typedef int fixed_array5[5]; //fixed_array5为类型名,代表数组值为int、宽度为5的定宽数组类型
fixed_array5 aa; //等价于int aa[5];
8. 创建struct
- 关键字为typedef、struct、packed
- 使用typedef才是在声明一种类型,不然只使用struct那么就只临时申明一个变量
- 结构中有多个成员,如果加入packed关键字,这些成员会被挨着存储在一起
- struct packed 被当做一个向量存储,结构体的第一个成员在向量的最左边。向量的最低位是结构体最后一个成员最低位,其位编号为bit 0。(类似小端模式)
- 默认情况是unpacked的
- 经常会对结构这个整体进行操作时,建议使用packed
- 经常操作的是结构中的个体,建议不使用packed
- 注 意 下 面 代 码 中 包 含 t y p e d e f 和 不 含 t y p e d e f 的 区 别 \color{red}{注意下面代码中包含typedef和不含typedef的区别} 注意下面代码中包含typedef和不含typedef的区别
struct {bit[7:0] r,g,b;} pixel; //pixel为变量名,是一个结构体变量。
struct packed {bit[7:0] r,g,b;} pixel;
typedef struct {bit[7:0] r,g,b;} pixel_s; //pixel_s为类型名
pixel_s pixel ; //pxiel为变量名
pixel = '{8'haa, 8'hbb, 8'hcc} //初始化
$display("%x %x %x",pixel.r, pixel.g, pixel.b); //结构体成员的引用
9.创建Union
- 关键字为 t y p d e f 、 p a c k e d 、 u n i o n \color{red}{typdef、packed、union} typdef、packed、union
- 使用方式类似struct
- 联合体只存储一个元素,但该元素可以有多种表示方法,每种表示方法可以是不同数据类型
- Union内的成员公用同一存储空间。所以对其中一个成员赋值,其他成员也会相应变化,只是数据类型不同而已
- 默认情况为unpacked的
- VCS不支持编译unpacked的 union
- Packed union members must have same size
- 下图中my_u默认应该是my_u.a
- 注 意 下 面 代 码 中 包 含 t y p e d e f 和 不 含 t y p e d e f 的 区 别 \color{red}{注意下面代码中包含typedef和不含typedef的区别} 注意下面代码中包含typedef和不含typedef的区别
union packed {byte a;
bit [7:0] b;} my_u; //my_u为变量名,是一个union变量。
typedef union packed {byte a;
bit [7:0] b;} tmp_u; //tmp_u为类型名
tmp_u my_u ; //my_u为变量名
my_u.b = 8'hff; //初始化, my_u.a也变为8’hff,打印出来就是 -1
$display("%0d",my_u.b);
枚举类型
- 关键字为 t y p d e f 、 e n u m \color{red}{typdef、enum} typdef、enum
- 使用方式类似struct
- 枚举类型会自动为列表{…}中的每个名称分配不同的数值,默认从0开始递增,有指定缺省值则为缺省值
- 有点类似参数的作用,但是意义不大
- 枚举类型变量无特殊声明默认为int存储,缺省值为0,所对应的 .name()也就是列表中分配数值为0的名称(下例中curr_state 默认为0,curr_state.name()就是IDLE)
//用parameter描述一个状态机的几种状态
parameter IDLE = 2'd0;
parameter DETECT = 2'd1;
parameter POLLING = 2'd2;
parameter L0 = 2'd3;
//用枚举类型来表示
//当curr_state为 0~3 时,curr_state.name() 就分别为 IDLE ...
enum {IDLE, DETECT, POLLING, L0} curr_state;
- 注 意 下 面 代 码 中 包 含 t y p e d e f 和 不 含 t y p e d e f 的 区 别 \color{red}{注意下面代码中包含typedef和不含typedef的区别} 注意下面代码中包含typedef和不含typedef的区别
enum {IDLE, DETECT, POLLING, L0} curr_state_e; //curr_state_e 为变量名,是一个enum变量
typedef enum {IDLE, DETECT, POLLING, L0} CURR_STATE_E; //CURR_STATE_E为类型名
CURR_STATE_E curr_state_e; //curr_state_e为变量名
curr_state_e = 2;
$display("%s", curr_state_e.name()); //打印 POLLING
- 定义枚举值
enum {INIT, DECODE=2, IDLE} fsmtype_e; //0:INIT 2:DECODE 3:IDLE