文章目录
数据类型
1. 内建数据类型
四值逻辑属于硬件的世界-硬件设计,二值逻辑属于软件的世界-验证环境
logic和bit均可以构建矢量类型
类型 | 逻辑 | 逻辑值 |
---|---|---|
logic(32) | 四值 | 0、1、X、Z |
bit | 二值 | 0、1 |
数据类型分类
习题1
习题2
类型转换方式
显式转换
需要操作符号或者系统函数介入
静态转换
unsigned'(signed_vec);
动态转换
$cast(tgt, src)
隐式转换
不需要进行转换的一些操作
// 答案
x_vec = 'b111x
b_vec = 'b110
数据类型操作注意点
- 逻辑数值类型
- 符号类型
- 矢量位宽
2. 软件常用类型
定宽数组
数组声明
数组声明
int lo_hi [0:15];
int c_style [16];
多维数组声明和使用
- 多维数组,类比矩阵,
高维度为行,低维度为列
- 声明时,写在矩阵名左侧用[7:0],右侧用[0:7]
// 变量右设置维度 左高维度,右低维度
int array2 [0:7] [0:3]; // 完整声明
int array3 [8] [4]; // 紧凑声明
array2[7][3] = 1; // 设置最后一个元素
// 变量左设置维度(暂不介绍)
reg [7:0] val;
初始化和赋值
// 赋相同值
int descend [5] = `{N{val}};
// 组合型数组初始化
logic [3:0] [7:0] a = 32'h0;
logic [3:0] [7:0] b = {16'hz, 16'h0};
logic [3:0] [7:0] c = {16{2'b01}};
// 非组合型数组初始化
int d [0:1] [0:3] = '{'{7, 3, 0, 2}, '{2, 0, 1, 6}};
// 或者
d[0][0] = 7;
...
d[1][3] = 6;
存储空间考量
组合型 & 非组合型
非组合型类型:event, logic, bit, byte, int, longint, shortreal, real
声明方式
// 合并型/组合型 更节省空间
bit [3] [7:0] b_back; // 二维 左高维度,右低维度 -- 同一侧左侧维度更高
// 非合并型/非组合型
bit [7:0] b_unpack [3]; // 不同方向,高维度在右,非连续;低维度在左,连续
bit [7:0] b_unpack [0:2];
int a1 [7:0] [1023:0]; // 同一方向,高(第二)维度在左,非连续;低(第一)维度在右,连续
int a2 [1:8] [1:1024];
a2 = a1;
a2[2] = a1[0];
组合型定义结构体存储方式
typedef struct packed {
logic [7:0] crc;
logic [63:0] data;
} data_word;
data_word [7:0] darrary;
用 logic
存储24bit的数据,考虑实际的存储空间
logic
四值逻辑,在软件上要用两位来存储
logic [3] [7:0] l_back; // 24 * 2位 = 48位 2 WORD
logic [7:0] l_unpack [3]; // 3*(8*2) 3 WORD
基本数组操作
for和foreach循环
initial begin
bit [31:0] src[5], dst [5];
// 系统函数拿到size
for (int i=0; i<$size (src) ; i++)
src [i] =1;
// j默认最高维度的索引,用数组做循环变量声明时 foreach 比较方便,相对于loop
foreach (dst [j])
dst [j] = src[j] * 2; // dst doubles src values
end
关于 $size
函数
int [3:0] unpacked_a [0:7];
$size(unpacked_a,1); // 数组高维度8
$size(unpacked_aa,2); // 数组低维度4
$size(unpacked_a); // 默认高维度8
复制/拷贝 & 比较
- 数组复制 =
- 数组比较 == !=
bit [31:0] src[5] =`{0,1,2,3,4},
dst[5] =`{5,4,3,2,1};
if(src==dst)$display("src == dst"); // 比较数组
else $display("src != dst");
dst = src; // 数组复制
src[0] = 5; // 修改数组中的一个元素
Note:
- 对于非组合型数组间拷贝时, 要求左右两侧的操作数的维度和大小必须严格一致;
- 组合型数组和非组合型数组之间无法直接赋值;
动态数组
int dyn[], d2[]; // 声明动态数组
initial begin
dyn = new[5]; // 分配5个元素
foreach (dyn[j]) dyn[j] = j; // 对元素初始化
d2 = dyn; // 复制一个动态数组
d2[0] = 5; // 修改复制值
$display(dyn[0],d2[0]); // 显示数值0和5
dyn = new[20](dyn); // 分配20个数值,将原来dyn数据分配给低5个元素
dyn = new[100]; // 重新分配100个数值,而旧值不复存在,默认0
dyn.delete(); // 删除所有元素
// 删除元素还可以
dyn = new[0]; // 必须带0
dyn = `{};
end
队列
- 【队列】结合了链表和数组的优点,可以在它的任何地方添加或删除元素,并且通过索引实现对任一元素的访问。
- 队列的声明是使用带有美元符号的下标:[$],队列元素的标号从0到$。
- 队列不需要
new[]
去创建空间,你只需要使用队列的方法为其增减元素,一开始其空间为0。 - 队列的一个简单使用即是通过其自带方法
push_back()
和pop_front()
的结合来实现FIFO的用法。
int j = 1, q2[$] = {3,4}, q[$] = {0,2,5}; // 队列复制不使用 '
initial begin
q.insert(1,j); // {0,1,2,5}在2前插入1
q.insert(3,q2); // {0,1,2,3,4,5}在q中插入队列q2
q.delete(1); // {0,2,3,4,5}删除第一个元素
// 下列操作运行速度更快(自有方法)
q.push_front(6); // {6,0,2,3,4,5}在队列头部插入
j = q.pop_back(); // {6,0,2,3,4}从队列尾部取出
// 以下更常用 FIFO
q.push_back(8); // {6,0,2,3,4,8}从队列尾部插入
j = q.pop_front(); // {0,2,3,4,8}从队列头部取出
foreach(q[i])
display(q[i]); // 打印整个队列
q.delete(); // {}删除整个队列
end
关联数组
-
如果只是偶尔需要创建一个大容量的数组,那么动态数组就足够了,但是如果需要一个超大容量的数组,则会受到限制。
-
动态数组的限制在于其存储空间在一开始创建时即被固定下来,那么对于超大容量的数组这种方式无疑存在着浪费,因为很有可能该大容量的数组中有相当多的数据不会被存储和访问。
-
可以用来保存稀疏矩阵的元素。当对一个非常大的地址空间寻址时,该数组只为实际写入的元素分配空间,这种实现方法所需要的空间比定宽或动态数组所占用的空间要小得多。
-
此外,关联数组有其它灵活的应用,在其它软件语言也有类似的数据存储结构,被称之为哈希(Hash)或者词典(Dictionary),可以灵活赋予键值(key)和数值(value)
bit [63:0] assoc[int], idx = 1; // 索引标量类型是int类型
repeat (64) begin // 对稀疏分布的元素进行初始化
assoc[idx] = idx;
idx = idx << 1; // 1,2,4,8...
end
foreach(assoc[i]) // 使用foreach遍历数组
$display("assoc[%h] = %h", i, assoc[i]);
// 使用函数遍历数组
if (assoc.first(idx)) begin // .first() 得到第一个索引
do
$display("assoc[%h] = %h",idx,assoc[idx]);
while (assoc.next(idx)); // Get next index
end
//找到并删除第一个元素
assoc.first(idx);
assoc.delete(idx);
结构体
- Verilog的最大缺陷之一是没有数据结构,在sv中可以使用struct语句创建结构,跟c语言类似。
- 不过struct的功能少,它只是一个数据的集合,其通常的使用的方式是将若干相关的变量组合到一个struct结构定义中。
- 伴随typedef可以用来创建新的类型,并利用新类型来声明更多变量。
struct {bit [7:0] r, g, b;} pixel; // 创建一个匿名结构体,声明一个pixel结构体变量
// 为了共享该类型,通过typedef来创建新类型,下面语句更常见
typedef struct {bit [7:0] r, g, b;} pixel_s;
pixel_s my_pixel; // 声明变量
my_pixel = '{'h10, 'h10, 'h10}; // 结构体类型的赋值
注:
动态数组
和结构体
用了{}
和’
单引号。即如果数据(是非合并型的数组)不是紧挨着存储存放的,而是非连续存放的,则这个数组或者结构体要用’
表示。
枚举类型
- 规范的操作码和指令如ADD,WRITE,IDLE等有利于代码的编写和维护,它比直接使用‘h01这样的常量使用起来可读性和可维护性更好。
- 枚举类型enum经常和typedef搭配使用,便于用户自定义枚举类型的共享使用。
- 枚举类型的出现保证了一些非期望值的出现,降低的设计风险。
typedef enum {INIT, DECODE, IDLE} fsmstate e;
fsmstate_e pstate,nstate; // 声明自定义类型变量
case(pstate)
IDLE: nstate = INIT; // 数值赋值
INIT: nstate = DECODE;
default: nstate = IDLE;
endcase
$display("Next state is %s", nstate.name()); // 显示状态名
思考:就上面的例子中,给nstate如果直接用整数赋值,那么合法的范围是多少呢?
答:本赋值行为本身就不合法。
注解:如果等式右侧本身是enum枚举类型,左侧如果是INT,则可以直接赋值。但如果右侧是INT整型,要赋值给左侧enum类型,一定要做一个类型转化,将右侧的INT类型转换为枚举类型后再赋值给左侧的枚举类型。
字符串
- 所有与字符串相关的处理,都要使用string来保存和处理。
- 与字符串处理相关的还包括字符串的格式化函数,如果想形成一个期望的字符串句子,可借助SV系统方法 $sformatf(),如果只需打印输出,则可以使用$display()
string s;
initial begin
s ="IEEE ";
$display(s.getc(0)); // 'I'
$display(s.tolower()); // ieee
s.putc(s.len()-1,"-"); // 转换,将空格变为-
s ={s,"P1800"}; // 拼接,"IEEE-P1800"
$display(s.substr(2,5); //显示EE-P
//创建一个临时字符串并将其打印
my_log($sformatf("%s, %5d", s, 42));
end
task my_log(string message); // 打印消息
$display("@%0t:s", $time,message);
endtask