SystemVerilog验证
2 数据类型
相比较于Verilog,SystemVerilog引入了新的数据结构,具有如下优点:
- 双状态数据类型:更好的性能,更低的内存消耗
- 队列、动态和关联数组:减少内存的消耗,自带搜索和分类功能
- 类和结构:支持抽象数据结构
- 联合和合结构:允许对同意对数据有多种视图
- 字符串:支持内奸字符序列
- 枚举类型:方便代码编写,增加可读性
2.1 内建数据类型
逻辑(logic)类型
SystemVerilog改进了Verilog中的reg类型,使得可以杯连续赋值、门单元和模块驱动,但要求logic不能有多个结构性的驱动。
module logic_data_type(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被模块驱动
logic只能由一个驱动,可以用来查找网络中的漏洞,把所有的信号声明为logic,如果存在多个驱动,那么编译就会报错,希望由多个驱动的时候定义成线网类型,例如wire
双状态数据类型
integer i4; // 四状态,32比特有符号整数
time t; // 四状态,64比特无符号整数
real r; // 双状态,双精度浮点数
// SystemVerilog独有
bit b; // 双状态,单比特
bit [31:0] b32; // 双状态,32比特无符号整数
byte b8; // 双状态,8比特有符号整数
shortint s; // 双状态,16比特有符号整数
int i; // 双状态,32比特有符号整数
longint l; // 双状态,64比特有符号整数
int unsigned ui; // 双状态,32比特无符号整数
将双状态变量连接到被测设计时,尤其是被测设计的输出时,务必要小心,如果试图产生X、Z,这些值会被转换成双态值。可以使用($isunkonwn)操作符,在出现X或Z时返回1。
if($isunkown(iport) == 1)
$display("@%0t: 4-state value detected on iport %b", $time, iport);
使用%0t和参数$time可以打印出当前的仿真时间,打印的格式在$timeformat()子程序中指定
2.2 定宽数组
在SystemVerilog中,定宽数组的定义和C语言类似,相比于Verilog,增加了紧凑的定义方式。例如:
int a_1[0:15]; // 16个整数[0]...[15]
int a_2[16]; // 16个整数[0]...[15]
int array2[0:7][0:3]; // 完整声明
int array3[8][4]; // 紧凑声明
array2[7][3] = 1; // 给最后一个元素赋值
数据越界访问的时候,SystemVerilog会返回数组类型的缺省值。例如四状态类型logic类型返回X,双状态类型int和bit返回0.这适用于所有的数组类型,包括定宽数组、动态数组、关联数组和队列。
很多仿真器的使用32位的字边界,byte、shortint、int都是存放在一个字中,longint存放在两个字中。所以非合并数组在存放四状态变量时双状态变量多用一倍空间。
常量数组
和C语言一样,SystemVerilog支持在定义时为数组赋值,给数组一个缺省值。
int ascend[4] = '{0,1,2,3}; // 为四个元素进行初始化
int descend[5];
descend = '{4,3,2,1,0}; // 赋值五个元素
descend[0:2] = '{5,6,7}; // 赋值前三个元素
descend = '{4{8}}; // 四个值全部为8
descend = '{9,8,default:1}; // {9,8,1,1,1}
基本的数组操作-for和foreach
使用for遍历时,可以使用$size函数返回数组的宽度,再循环中声明局部变量,使用foreach循环,指定数组名并在后面的方括号中给出索引变量。例如:
initial begin
bit [31:0] src[5], dst[5];
for (int i = 0; i < $size(src); i++)
src[i] = i;
foreach (dst[j])
dst[j] = src[j] * 2;
end
对于多维数组来说,例如:
byte two[4][6]; // 二维数组
foraeach(two[i]) begin // 遍历第一个维度
$write("%2d:", i);
foreach(two[,j])
$write("%3d", two[i][j]); // 遍历第二个维度
$display;
end
foreach循环遍历的时原始声明中的数组范围。例如:
f[5]等同于f[0:4],而foreach(f[i])等同于for(int i = 0; i <= 4; i++)
rev[6:2]中,foreach(rev[i])等同于for(int i = 6; i >= 2; i--)
基本的数组操作-复制和比较
对于数组的复制和比较可以通过运算符直接进行。
initial begin
bit [31:0] src[5] = `{0,1,2,3,4},
dst[5] = `{5,4,3,2,1};
// 聚合比较
if (src == dst)
$dislpay("src == dst");
else
$display("src != dst");
// 复制
dst = src;
同时使用位下标和数组下标
在SV中,数组下标和位下标可以同时使用
initial begin
bit [31:0] src[5] = `{5{5}};
$displayb (src[0],, // ’b101 或 ‘d5
src[0][0],, // ‘b1
src[0][2:1]); // ’b10
end
合并数组
可以将多个数组合并成一个数组使用,与非合并数组不同,合并数组的存储空间是连续的。数组定义大小的格式必须必须是[msb:lsb]。
bit [3:0][7:0] bytes; // 4个字节数组仔庄承32比特
bytes = 32'hCafe_Dada;
$display(bytes,, // 显示所有的32比特
bytes[3],, // 最高字节“CA”
bytes[3][7]); // 最高位比特“1”
2.3 动态数组
如果程序运行前不知道数组的宽度,那么如果使得数组容量足够大,会导致资源浪费,可以使用动态数组来存储数据。动态数组只有一维,而且不能被合并。
int dyn[], d2[]; // 动态数组的声明
initial begin
dyn = new[5]; // 给数组dyn分配五个元素
foreach (dyn[j]) dyn[j] = j; // 对元素进行初始化
d2 = dyn; // 复制一个动态数组
d2[0] = 5; // 修改动态数组的值
$display(dyn[0], d2[0]); // 显示数值(0和5)
dyn = new[20](dyn); // 不改变原值的方式进行拓展
// 首先为dyn申请了20个元素
// 然后将dyn之前的五个元素拷贝进去
// 释放之前五个元素占用的空间
dyn = new[100]; // 为dyn分配100个元素,释放之前元素的空间
dyn.delete(); // 删除dyn所有元素
end
可以使用动态列表来保存不定长但是定宽的列表,并且只要基本的数据类型相同,定宽数组和动态数组之间就可以相互赋值。
2.4 队列
SV中引入的新的数据类型,结合了链表和数组的优点。可以在一个队列中任何地方增加和删除元素,性能损失比动态数组小,队列的声明式使用美元符号的下标,队列的编号可以从0到$。
int j = 1,
q2[$] = {3, 4}, // 队列的常量不需要使用'
q[$] = {0, 2, 5}; // {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中插入一个队列
q.delete(1); // {0,2,3,4,5} 删除第1个元素
// 下面的操作执行速度很快
q.push_front(6); // {6,0,2,3,4,5} 在队列前插入6
j = q.pop_back; // {6,0,2,3,4} 取出队尾 j = 5
q.push_back(8); // {6,0,2,3,4,8} 在队尾插入8
j = q.pop_front; // {0,2,3,4,8} 取出队首 j = 6
foreach (q[i])
$display(q[i]); // 打印整个队列
q.delete(); // 删除整个队列
end
队列同样也可以使用字下标串联方式来对队列进行插值。
int j = 1,
q2[$] = {3, 4}, // 队列的常量不需要使用'
q[$] = {0, 2, 5}; // {0, 2, 5}
initial begin
q = {q[0], j, q[1:$]}; // {0,1,2,5}
q = {q[0:2],q2,q[3:$]}; // {0,1,2,3,4,5}
q = {q[0],q[2:$]}; // {0,2,3,4,5}
// 下面的操作执行速度很快
q = {6,q}; // {6,0,2,3,4,5} 在队列前插入6
q = q[0:$-1]; // {6,0,2,3,4} 取出队尾 j = 5
q = {q,8}; // {6,0,2,3,4,8} 在队尾插入8
q = q[1:$]; // {0,2,3,4,8} 取出队首 j = 6
q = {}; // 删除整个队列
end
2.5 关联数组
如果需要创建一个超大容量的数组,例如有个几个G字节寻址的处理器进行建模,这个处理器可能只访问了存放代码和数据的几百或几千个字节,这种时候对于几个G的存储空间是一种浪费。
SV中可以使用关联数组来保存稀疏矩阵的元素,在对一个风长达的地址空间进行寻址的时候,SV只为实际写入的元素分配了空间。
关联数组实现了一个所声明类型的元素的查找表,用作索引的数据类型作为查找表的查找键值,并强制了一种顺序。
关联数组采用在方括号中放置数据类型的方式来进行声明,例如[int]或[Packet]
例如在声明中:bit [63:0] assoc[bit[63:0]]
前面的bit [63:0] 表示为一个64位bit向量的关联数组,后面表示使用64位bit作为关联数组的索引。
使用起来更像是字典,例如 使用字符串作为索引,数据类型位int,这样就构建了一个字符串和数字的对照表,或者数据类型位int,索引依然是int,用来保存地址对应的数据。
`include "uvm_pkg.sv"
module tb();
import uvm_pkg::*;
`include "uvm_macros.svh"
initial begin
bit [63:0] assoc[bit[63:0]], idx = 1;
// 对关联数据进行初始化
repeat (64) begin
assoc[idx] = idx;
idx = idx << 1;
end
// 使用foreach遍历
foreach (assoc[i])
$display("assoc[%h] = %h", i, assoc[i]);
// 使用 do while 遍历
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 arry now has %0d elements", assoc.num);
end
endmodule
assoc.first(idx) 获取了关联数组的首项索引,然后把索引值给idx
assoc.next(idx) 获取了比idx大的最小索引,然后把索引值给idx
通过first next 和 do while 配合可以很好完成对于关联数组的遍历
num():数据长度
delete():删除指定索引或所有元素
exists():检查是否存在该索引,有返回1否则返回0
first():将指定的索引变量赋值为数组第一个索引的值
last():将指定的索引变量赋值为数组最后一个索引的值
next():索引变量被赋值为下一个条目的索引
prev():索引变量被赋值为上一个条目的索引
2.6 链表
虽然SV中提供了链表的数据结构,但是应当避免使用它,应当使用更简单高效的队列。
2.7 对数组的多种操作
-
缩减操作:以求和操作为例,对数组的所有元素求和,如果是单比特的数组,使用求和后也只有单比特。也可以使用with表达式,更改求和得到的位宽。在学习该部分的内容时,发现实际编译得到的结果和书本中不符合。具体如下。
bit on[10]; // one bit int total; // 32 bit initial begin foreach (on[i]) on[i] = i; $display("on.sum = %0d", on.sum); $display("on.sum = %0d", on.sum + 32'd0); total = on.sum; $display("on.sum = %0d", total); if (on.sum >= 32'd5) $display("sum has 5 or more"); $display("int sum = %0d", on.sum with (int'(item)));
以下是得到的结果:
on.sum = 1 on.sum = 1 on.sum = 1 int sum = 5
所以,在求和时,如果想改变求和得到的位宽,最好使用with表达式来完成。由于后面在学习约束的时候会展开说with,这里就不展开说了。
类似的缩减操作有 product (求积)、 and (与)、or(或)、xor(异或)
在SV中没有提供专门从一个数组里随机选取一个元素的方法,对于定宽数组、队列、动态数组和关联数组可以使用$urandom_range($size(arry)-1),对于队列和动态数组还可以使用$urangom_range(array.size()-1)
-
数组的定位方法:
数组的定位方法有很多:
min():返回最小值
max():返回最大值
unique():返回在数组中具有唯一值的队列
find():通过限定条件返回符合条件的数值队列
find_index():通过限定条件返回符合条件的数值队列的索引
find_first():返回满足条件的第一个数值
find_first_index():返回满足条件的第一个索引
find_last():返回满足条件的最后一个值
find_last_index():返回满足条件的最后一个索引int array[7] = '{1,3,3,9,9,10,21}; int temp[$]; initial begin $write("The min is : "); temp = array.min(); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("The max is : "); temp = array.max(); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("The unique is : "); temp = array.unique(); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("Find item more 3 : "); temp = array.find() with (item > 3); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("Find index that item more 3 : "); temp = array.find_index() with (item > 3); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("Find the first item that equal to 9 : "); temp = array.find_first() with (item == 9); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("Find the first index of item that equal to 9 : "); temp = array.find_first_index() with (item == 9); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("Find the last item that equal to 9 : "); temp = array.find_last() with (item == 9); foreach(temp[i]) $write("%0d ", temp[i]); $display(); $write("Find the last index of item that equal to 9 : "); temp = array.find_last_index() with (item == 9); foreach(temp[i]) $write("%0d ", temp[i]); $display(); end
得到的结果如下:
(Specify +UVM_NO_RELNOTES to turn off this notice) The min is : 1 The max is : 21 The unique is : 1 3 9 10 21 Find item more 3 : 9 9 10 21 Find index that item more 3 : 3 4 5 6 Find the first item that equal to 9 : 9 Find the first index of item that equal to 9 : 3 Find the last item that equal to 9 : 9 Find the last index of item that equal to 9 : 4 V C S S i m u l a t i o n R e p o r t Time: 0 ps CPU Time: 0.200 seconds; Data structure size: 0.2Mb Thu Jul 27 16:31:27 2023
-
数组的排序:SV中有几个可以改变数组中元素顺序的方法,可以对元素进行正排序、逆排序,或是打乱他们的顺序
包含以下方法:
reverse():反向,倒置
sort():正排序,从小到大
resort():逆排序,从大到小
shuffle():打乱
由于reverse和shuffle的作用域都是作用域整个数组的,所以不能使用with来限定区域
struct packed {byte red, green, blue;} c[]; initial begin c = new[5]; foreach (c[i]) c[i] = $urandom; $display("Array item is : "); foreach (c[i]) begin $write("%0d,", c[i].red); $write("%0d,", c[i].green); $write("%0d\n", c[i].blue); end // sort the red firstly c.sort with (item.red); $display("Array item is : "); foreach (c[i]) begin $write("%0d,", c[i].red); $write("%0d,", c[i].green); $write("%0d\n", c[i].blue); end // sort the green and blue c.sort(x) with ({x.green, x.blue}); $display("Array item is : "); foreach (c[i]) begin $write("%0d,", c[i].red); $write("%0d,", c[i].green); $write("%0d\n", c[i].blue); end end
得到的输出为:
(Specify +UVM_NO_RELNOTES to turn off this notice) Array item is : -30,53,54 -34,-95,60 127,-25,125 -86,51,-30 121,68,11 Array item is : -86,51,-30 -34,-95,60 -30,53,54 121,68,11 127,-25,125 Array item is : -86,51,-30 -30,53,54 121,68,11 -34,-95,60 127,-25,125 V C S S i m u l a t i o n R e p o r t Time: 0 ps CPU Time: 0.200 seconds; Data structure size: 0.2Mb Thu Jul 27 16:52:48 2023
由于对于约束的部分不太熟悉,这里保留一个疑问:关于先对green排序之后对于blue拍寻的结果,和我理解的不一样。
-
使用数组定位的方法来建立计分板:
先定义一个包结构,然后建立一个由包结构队列组成的记分板
module tb(); typedef struct packed { bit [7:0] addr; bit [7:0] pr; bit [15:0] data; } Packet; Packet scb[$]; initial begin Packet temp; temp = '{ 8'd200, 8'd100, 0 }; scb.push_back(temp); $display("scb is : %p", scb); $display("scb[0] addr is : %0d", scb[0].addr); check_addr(8'd100); check_addr(8'd200); check_addr(8'd200); end function void check_addr(bit [7:0] addr); int intq[$]; intq = scb.find_index() with (item.addr == addr); case (intq.size()) 0: $display("Addr %0d not found in scoreboard", addr); 1: scb.delete(intq[0]); default: $display("ERROR: Multiple hits for addr %h", addr); endcase endfunction: check_addr endmodule
代码的运行效果如下:
(Specify +UVM_NO_RELNOTES to turn off this notice) scb is : '{'{addr:'hc8, pr:'h64, data:'h0}} scb[0] addr is : 200 Addr 100 not found in scoreboard Addr 200 not found in scoreboard V C S S i m u l a t i o n R e p o r t Time: 0 ps CPU Time: 0.200 seconds; Data structure size: 0.2Mb Thu Jul 27 18:33:40 2023
2.8 选择存储类型
灵活性
如果需要数组的索引时连续的非负整数,那就是用定宽数组或动态数组。
编译前就已经知道数据有多大的数组,直接用定宽数组就可以了,否则就用动态数组。
当你编写需要处理数组的子程序的时候,最好使用动态数组,这样可以只需要类型匹配就可以了,可以处理不同长度的数组。
如果数组的索引不规则时候,例如由于随机数产生的稀疏分布的索引,就应该使用关联数组了。
如果在仿真的过程中元素的数目变化很大的数目,例如计分板,那么队列是一个很好的选择。
存储器的用量
双状态类型可以减少仿真时的存储器用量,为了避免浪费,应当尽量选择32比特的整数倍作为数据位宽,因为仿真器通常会把小于32位宽的数据存放到32比特的字里。使用合并数组可以减少空间浪费。
对于具有大量数据的数组,数据的类型对于存储器的用量影响不大。如果
队列需要用到额外的指针,所以存取效率要比顶宽或动态数组稍微差一点。如果你把长度经常变化的数据集使用动态数组存放,那么在进行new[]来分配和复制内存的时候,这个代价很高。
速度
队列的读写速度和定宽数组、动态数组速度相当,但是如果队列长度过长,在进行插入曹茹的时候,可能会变得很慢。
关联数组常用的方法是使用哈希表和树形结构,所以关联数组的存取速度是最慢的。
排序
因为SV可以对任何一种一维数组进行排序,所以应该按照元素增加的繁琐程度来选择数组的类型,例如如果一次性全部加入的话就是用定宽数组或动态数组,如果元素是逐个加入到话,就是用队列。如果数组的值不连续并且彼此互异,那么可以使用关联数组,并把元素值本身作为索引。
选择最优的数据结构
- 网络数据包:特点是长度固定,顺序存取,针对长度固定和可变的数据包,可分别使用定宽或动态数组。
- 保存期望值的计分板:特点是仿真前长度位置,按值存取,长度经常变化,一般可以使用队列,方便在仿真期间连续增加和删除元素。如果计分板有数百个元素,并且需要在元素之间进行增删操作,则使用关联数组可能在速度上更快一些。
- 有序结构:如果数据按照可预见的顺序输出,那么可以使用队列,如果输出顺序不确定,则使用关联数组。
- 对超过百万个条目的特大容量存储器进行建模:如果你不需要用到所有的存储空间,可以使用关联数组来实现稀疏存储,在没有办法减少数据的使用量的情况下,请确保使用的是双状态的32比特合并数据。
- 文件中的命名名或操作码:特点是要把字符串转换成固定值,可以使用关联数组。
2.9 定义新的类型
使用typedef来定义新的类型,和C类似:
typedef bit [31:0] unit;
typedef int unsigned unit;
typedef struct{
int addr,
int pr,
int data
} SCB;
2.10 创建用户自定义结构
即结构体,和C语言中的结构体基本一致。可以利用结构体,将不同的类型封装在一起,成为一种新的类型,如果在声明时,加入packed关键字,说明结构体是压缩的,存储空间之间没有间隙,属于合并结构。如果操作经常是针对结构体内部成员的,那么就应该优先使用非合并结构,尤其是不同元素之间长度不一的时候,对子元素的访问会比较消耗资源,如果经常对结构体的整体进行复制,那么应该使用合并结构,来减少存储空间的占用。以下是例子:
module tb();
typedef union packed
{
shortint si;
shortint unsigned usi;
} my_union;
typedef struct packed
{
bit [7:0] id;
bit [7:0] addr;
my_union un;
}my_packet;
initial begin
my_packet pkt;
pkt =
'{
8'd100,
8'd200,
-16'd5
};
$display("The struct is : %p", pkt);
$display("The si of union is : %0d", pkt.un.si);
$display("The si of union is : %0d", pkt.un.usi);
end
endmodule
以下是仿真输出结果:
(Specify +UVM_NO_RELNOTES to turn off this notice)
The struct is : '{id:'h64, addr:'hc8, un:'{}}
The si of union is : -5
The si of union is : 65531
V C S S i m u l a t i o n R e p o r t
Time: 0 ps
CPU Time: 0.220 seconds; Data structure size: 0.2Mb
Fri Jul 28 01:20:12 2023
结构体的部分很好理解,对于联合体的部分,是不同种类型的数据占用同一片存储区域,占用空间由最大位宽的数据种类决定。-5在有符号的情况下得到的是-5,在无符号的情况下得到的是65531,他们在二进制存储的时候是一致的。
2.11 类型转换
静态转换
在对静态的数据进行转换的时候,只需要在需要的类型后面加上',例如:
int i;
real r;
i = int'(10.0-0.1);
r = real'(42);
动态转换
使用 $cast(a,b) $cast被当成函数的时候,会把右边的值赋给左边的量,如果赋值成功返回1,如果不成功则返回0.这样做转换的好处是,会进行越界检查。如果把$cast当作任务来使用,则会打印出错误信息。
流操作符
流操作符用于把后面的数据打包成一个比特流,>>表示从左到右打包,<<表示从右到左打包。例子如下:
module tb();
function void pack_array_int(const ref bit [7:0] array[4], output int a);
a = {<<byte{array}};
endfunction
initial begin
bit [7:0] array[4] = '{8'h11, 8'h22, 8'h33, 8'h44};
int pack_result;
pack_array_int (array, pack_result);
$display("The result is : 0x%h", pack_result);
end
endmodule
以下是仿真结果:
(Specify +UVM_NO_RELNOTES to turn off this notice)
The result is : 0x44332211
V C S S i m u l a t i o n R e p o r t
Time: 0 ps
CPU Time: 0.200 seconds; Data structure size: 0.2Mb
Fri Jul 28 01:40:14 2023
使用流操作符,实现了逆序之后,并打包成一个int输出。
类似的基础操作还有很多,如下:
initial begin
int h;
bit[7:0]b,g[4],[4]='8 ha,8"hb,8'hc,8'hd);
bit[7:0] q,r,s,t;
h={>>{j}}; // 0a0b0c0d- 把数组打包成整型
h={<<{j}}; // b030d050 位倒序
h={<<byte{j}}; // 0d0c0b0a 字节倒序
g={<<byte {j}}; // 0d,0c,Ob,0a 拆分成数组
b={<<{8'b0011_0101}}; // 1010 1100位倒序
b={<<4{8 b0011_01011}}; //01010011半字节倒序
{>>{q,r,s,t}}=j; // 把分散到四个字节变量里
h=(>>{t,s,r,g}}; // 把字节集中到 h 里
end
2.12 枚举类型
由于宏的作用域很大,多数情况下是对调试着可见的。对于具有一些特定名称的集合,例如装药集中的状态名,就可以使用枚举类型来实现。下面是一个例子:
module tb();
typedef enum {INIT, DECODE, IDLE} fsmstate_e;
fsmstate_e pstate,nstate;
initial begin
case (pstate)
IDLE: nstate = INIT;
INIT: nstate = DECODE;
default: nstate = IDLE;
endcase
$display("The next state is %s", nstate.name());
$display("The value of state is %0d %0d %0d", INIT, DECODE, IDLE);
end
endmodule
以下是仿真结果:
(Specify +UVM_NO_RELNOTES to turn off this notice)
The next state is DECODE
The value of state is 0 1 2
V C S S i m u l a t i o n R e p o r t
Time: 0 ps
CPU Time: 0.220 seconds; Data structure size: 0.2Mb
Fri Jul 28 01:55:34 2023
可以观察到,枚举类型和C语言的枚举类型类似,默认从左到右从0开始赋值,默认数值为0。在这种情况下,如果将DECODE=2,那么INIT依然为0,此时IDLE会从DECODE之后开始加,所以IDLE值为3。
枚举类型有如下子函数:
first():返回第一个枚举常量
last():返回最后一个枚举常量
next():返回下一个枚举常量
next(N):返回后面第N个枚举常量
prev():返回前一个枚举常量
prev(N):返回前面第N个枚举常量
2.13 常量
常量可以用宏来定义,但是宏的作用域太广了,可以使用常量来定义。
initial begin
const byte colon = ":";
...
end
除了使用常量,之前还会使用typedef parameter来替换掉宏
2.14 字符串
string类型可以保存长度可变的字符串,单个字符使用byte类型保存,也就是双状态有符号8位。字符串结尾不代标识符null,所有尝试使用\0的操作都会被忽略。
关于使用字符串有一些内建子函数:
getc(N):获得第N个字符
touper():返回一个所有字符大写的字符串
tolower():返回一个都是小写的字符串
putc(M,C):把字符C写在M位上
len():获取字符串长度
substr(start,end):闭区间 左右都取从 从字符串中提取字符串
2.15 表达式的位宽
要多注意由尾款带来数据计算不准确的问题。
标签:0d,temp,int,数据类型,SV,write,数组,第二章,display From: https://www.cnblogs.com/MinxJ/p/17594154.html