System Verilog学习
Data types
System Verilog引进了几种新的数据类型。C语言程序员会熟悉其中的大多数。引进新的数据类型构思是这样的,如果C语言和System Verilog有相同的数据类型可以使C语言算法模型更容易的转化为System Verilog模型。
Verilog的变量类型有四态:既是0,1,X,Z。SystemVerilog引进了新的两态数据类型,每一位只可以是0或是1。当你不需要使用的X和Z值时,譬如在写Testbench和做为for语句的循环变量。使用两态变量的RTL级模型,可以使模拟器更有效率。并且使用得当的话将不会对综合结果产生影响。
常量
在变量前面加const,如果用户把这个变量改变了,那么仿真器会报错。按照C语言中的const来理解。
整型常量
8‘b0 32’d0 '0 '1 'x 'z
省略位宽则意味着全位宽都被赋值。
实型常量
支持小数或者科学型表示,例如:3.14 2.0e3
real a = 3.14;
字符串常量
和c++类似,但是字符串末尾不是“\n”
string s=“123”
bit [7:0] d = “sv”; //也可以赋值给整型
数组常量
和c++类似
int array[1:2] = {5,6};
结构体常量
typedef struct{
int a;
string b;
real c;
} name_1;
name_1 var;
var = {10, "sv", 1.24};
时间文本值
和verilog类似
`timescale 1ns/100ps //时间单位是1ns,时间精度是100ps
module test;
initial begin
#20; //相当于20ns
#20ns;
#5.18ns; //相当于5.2ns,因为时间精度是100ps,即0.1ns,自动进位了
#1step; //相当于100ps
end
endmodule
`timescale必须定义在模块的外面,使用关键字timeunit和timeprecision只能定义在模块内部,只会影响单个模块。
整型
类型 | 描述 | 符号 |
---|---|---|
bit | 2态,自定义位宽 | 无符号 |
byte | 2态,8bit | 有符号 |
int | 2态,32bit | 有符号 |
shortint | 2态,16bit | 有符号 |
longint | 2态,32bit | 有符号 |
logic | 4态,自定义位宽 | 有符号 |
reg | 4态,自定义位宽 | 有符号 |
integer | 4态,32bit | 有符号 |
time | 4态,64bit | 无符号 |
2态只有1和0;4态是0,1,Z,X。2态数据类型不初始化,则默认是0;4态数据类型不初始化,则默认是X。不要用2态的数据类型去做ADC数据采集。在对4态数据类型做比较的时候,使用三个等号(a===b)。2态和4态数据类型是可以相互转化的。从4态转化倒2态,那么4态中的Z和X会默认为2态的0。注意time类型是整数类型,64bit位宽。
logic类型
在SV中增加了logic类型,是reg类型的增强。在verilog中,一般输入会定义成wire类型,但是在SV中,输出和输入都定义成logic就行。 在verilog中,reg类型只能在always块中被赋值,不能用assign来赋值,但是logic类型可以在always块中和assign中被赋值。
module(
output reg a,
input wire b
);
endmodule
SV:
module(
output logic a,
input logic b
);
endmodule
实数
类型 | 描述 | 符号 |
---|---|---|
real | 2态,64bit | 有符号 |
shotreal | 2态,32bit | 有符号 |
分为real data和shortreal data。real data相当于C语言中的double类型,64bit位宽,2态;shortreal data相当于C语言中的float类型,32bit位宽,2态。
字符串
初始值是空字符。字符串的比较按照ASIC码来比较。使用[]来索引,随意改变和取值。SystemVerilog字符串类型支持很多操作和函数。
操作 | 描述 |
---|---|
{strA,strB} | 连接——扩展指定的字符串。操作符可以是字符串类型,也可以是字符串文字。当所有的操作符都是字符串文字时,此操作完成整体的连接,结果也是一个字符串文字。 |
str.len | 长度——返回代表字符串的长度的整数 |
str.putc(i, c) | 字符输入——将字符串str中的第i个字符替换成字符c。i必须是一个整数,c必须是一个字节类型的字符 |
str.getc(i) | 获取字符——返回字符串str的第i个字符。i必须是整数,返回的字符表示为一个字节。 |
str.toupper() | 转成大写——返回str中所有字符变成大写的字符串 |
str.tolower() | 转成小写——返回str中所有字符变成小写的字符串 |
strA.compare(strB) | 比较——比较strA和strB.从第一个字符开始比较。如果相等,则继续后续字符,知道两者有不同或者到达某个字符串的结尾。如果相等,返回’0‘;如果strA在strB之后,则返回整数;如果strA在strB之前,则返回负数. |
strA.icompare(strB) | 比较——和compare方法类似,但是不关心大小写。 |
str.substr(i, j) | 子串——i和j都是整数,返回一个新的字符串,由str的第i和和第j个中间的所有字符组成 |
str.atoi() | 字符串转整数——返回一个32bit整数(不考虑此字符串表示的数字的长度),对于atoi, 字符串将被看作十进制;对于atoh,十六进制;对于atooct,八进制;对于atob,二进制。对于比较大的数字,使用系统任务$sscanf更加合适. |
str.atoreal() | 字符串转实数——此方法返回字符串str表示的实数 |
str.itoa(i) | 整数转字符串——atoi,atohex,atooct,atobin的反运算。此系列方法输入一个整数i,将其对应的表示方式存在字符串str中。 |
str.realtoa® | 实数转字符串——atoreal的反运算。此方法输入一个实数r,将其对应的表示方式存在字符串str中。 |
空类型
SV中定义function也是可以有返回值的,如果不想有返回值,那么在定义function后面添加void。调用有返回值的函数,但是不想用返回值,这个时候在调用函数的前面加void’(fun1());就可以了。不然会有warning,在验证中最好不要有警告。
动态数组
在run-time才知道元素个数,在compile-time不知道,可以使用动态数组,在仿真的时候再确定元素个数。也可将固定数组赋值给动态数组,要求是元素个数相同。
data_type name_of_dynamic_array[];
name_of_ dynamic_array = new[number of elements];
实例:int dyn[]; dyn = new[5];dyn.delete();
队列
和C++的类似,可插入,删除,sort,search,push,pop等
data_type queue_name[$] = {…} //队列赋值时大括号前面不加单引号
实例:
int b[$] = {3,4}; //{3,4}
b.insert(1,1); //{3,1,4} 在第一个元素后面添加1
b.delete(1); //{3,4} 删除元素1
b.push_front(6) ; //{6,3,4}
j = b.pop.back; //{6,3}, j = 4
联合数组
充分利用内存里的离散空间,不连续空间;索引值可以为整型,字符型,一维数组。有如下的操作,遍历(foreach),first,next,prev,delete,exits
data_type associative_array_name[*/string]
实例:
logic [63:0] assoc [*],idx=1;
repeat(64) begin
assoc[idx]=dix;
idx=idx<<1;
end
说明:标准数组存储时,所有的存储器都用到了;联合数组使用内存时,稀疏。
数组的操作
/*
Exercsise platform : Questa Sim 10.1b
*/
class Array;
int array[9:0] ;
function new();
for( int i = 0 ; i < 10 ; i++ )
array[i] = i ;
/*
array = '{'{1,2,3},'{5{5}},default:0};
无法使用这种基本的赋值方式,可能是编译器的版本过低了
*/
endfunction:new
function void print();
foreach(array[i]) begin
$display(" array[%d] = %d ",i,array[i]);
end
for ( int i = 0 ; i < 10 ; i++ )begin
$write(" ** ");
end
$display();
endfunction:print
function void funcs();
int pos[$] ;
// 缩减操作 xor , or , and , xnor , sum , product 等
$display("the sum is %d ",array.sum());
$display("the and is %d ",array.and());
$display("the or is %d ",array.or() );
$display("the xor is %d ",array.xor());
$display("the product is %d ",array.product());
// 索引操作:取得相应的所需要的数组原素位置
$display(" max value is %d ",array.max()[0]);
$display(" min value is %d ",array.min()[0]);
array = array.unique();
print(); // 在类中时,调用自己类的函数,可以直接调用。这点与cpp相同
pos = array.find with ( item == 7 ) ;
// find 有多种变体: find_first find_last find_first_index find_last_index
$display(" pos : %d ? value: %d ",pos[0],array[pos[0]]);
// 排序操作: sort , rsort , reverse , shuffle ,
// 其中 sort ,rsort 可以与with相结合,根据with后的条件排序
array.shuffle();
$display("shuffled:");
print();
array.sort();
$display("Sorted:");
print();
array.rsort();
$display("Resorted:");
print();
array.reverse();
$display("Reversed:");
print();
endfunction:funcs
endclass:Array
module top;
Array array;
int arr[] ; // 动态数组的声明
initial begin
// initial process
array=new();
array.print();
array.funcs();
arr = new[20]; // 分配内存大小
foreach(arr[i]) $display(" arr[%2d] = %d ",i,arr[i]);
$display("***************");
arr.delete();
end
endmodule
// 所有关于定长数组的操作都可以用在动态数组上
仿真结果:
# ** Note: (vsim-3812) Design is being optimized...
#
# Loading sv_std.std
# Loading work.array_sv_unit(fast)
# Loading work.top(fast)
>>>run
# array[ 9] = 9
# array[ 8] = 8
# array[ 7] = 7
# array[ 6] = 6
# array[ 5] = 5
# array[ 4] = 4
# array[ 3] = 3
# array[ 2] = 2
# array[ 1] = 1
# array[ 0] = 0
# ** ** ** ** ** ** ** ** ** **
# the sum is 45
# the and is 0
# the or is 15
# the xor is 1
# the product is 0
# max value is 9
# min value is 0
# array[ 9] = 0
# array[ 8] = 1
# array[ 7] = 2
# array[ 6] = 3
# array[ 5] = 4
# array[ 4] = 5
# array[ 3] = 6
# array[ 2] = 7
# array[ 1] = 8
# array[ 0] = 9
# ** ** ** ** ** ** ** ** ** **
# pos : 7 ? value: 2
# shuffled:
# array[ 9] = 2
# array[ 8] = 8
# array[ 7] = 0
# array[ 6] = 4
# array[ 5] = 7
# array[ 4] = 1
# array[ 3] = 9
# array[ 2] = 5
# array[ 1] = 6
# array[ 0] = 3
# ** ** ** ** ** ** ** ** ** **
# Sorted:
# array[ 9] = 0
# array[ 8] = 1
# array[ 7] = 2
# array[ 6] = 3
# array[ 5] = 4
# array[ 4] = 5
# array[ 3] = 6
# array[ 2] = 7
# array[ 1] = 8
# array[ 0] = 9
# ** ** ** ** ** ** ** ** ** **
# Resorted:
# array[ 9] = 9
# array[ 8] = 8
# array[ 7] = 7
# array[ 6] = 6
# array[ 5] = 5
# array[ 4] = 4
# array[ 3] = 3
# array[ 2] = 2
# array[ 1] = 1
# array[ 0] = 0
# ** ** ** ** ** ** ** ** ** **
# Reversed:
# array[ 9] = 0
# array[ 8] = 1
# array[ 7] = 2
# array[ 6] = 3
# array[ 5] = 4
# array[ 4] = 5
# array[ 3] = 6
# array[ 2] = 7
# array[ 1] = 8
# array[ 0] = 9
# ** ** ** ** ** ** ** ** ** **
# arr[ 0] = 0
# arr[ 1] = 0
# arr[ 2] = 0
# arr[ 3] = 0
# arr[ 4] = 0
# arr[ 5] = 0
# arr[ 6] = 0
# arr[ 7] = 0
# arr[ 8] = 0
# arr[ 9] = 0
# arr[10] = 0
# arr[11] = 0
# arr[12] = 0
# arr[13] = 0
# arr[14] = 0
# arr[15] = 0
# arr[16] = 0
# arr[17] = 0
# arr[18] = 0
# arr[19] = 0
# ***************
语法
procedural statement
新操作符
- i++,++i,i–,--i 同c语言,但易出现race现象。
- ==?,!=? 如:a==?b ,x与z只能出现在右侧,即b的值有x或者z
- inside 用于值的范围,类似python的 in
强制转换
1、数据类型强制转换
通过赋值的方式,例如
longint a,y;
real r;
y=a+longint(r);
2、位宽强制转换
logic [15:0] a,b,c,sum;
logic carry
sum=a+16'(5)
{carry,sum}=17'(a+3)
sum=a+16'(b-2)/c;
3、符号位强制转换
将无符号数转化为有符号数,将有符号数转化为无符号数
例子:signed’(expression) unsigned’(expression)
循环
1、 for循环
verilog中,循环体内的变量需要在循环体外声明。sv里和c++一样,可在循环体内声明变量,这种变量是local的,在循环体外看不见。若在循环体内外同时声明同一变量,则互不干扰。
2、do while sv里增加的循环,verilog里没有。
3、增加unique,priority(优先级)选项;
function
不消耗时间;不带时序,function里不能包含延时信息,@,wait等时间信息关键字;由于task可以带时序,所以规定function不能调用task;
void function中,不返回值;在verilog里,function一定返回值,且返回的值是function的名字。
task
消耗时间,含有输入输出双向端口;可含delay,timing,event;
sv里task与function增加点
- 不需要加begin…end
- 添加return,直接退出函数
- function增加了void function
- 在verilog里function只有input,没有output,返回值就是函数值;但在sv里,function增加了output,inout变量
- 参数方向类型缺省时,类型默认为logic,方向默认为input
- 引用ref。所谓引用传递,就如c++里面的指针,也就是变量的入口地址;只有automatic型task,function可以使用ref;传值方式来传递参数,那么参数会被整体复制到其他地址,这样消耗一定的内存和操作时间;而用ref传值方式来传递参数,只是获得参数的入口地址,操作速度快,减少内存使用
automatic
一般硬件里的所有对象都是静态的;在verilog-1995,如果在多个地方调用同一个任务,本地变量是共同而且静态分配的,为此,不同的进程相互访问同一个值。在verilog-2001中,可以通过使用automatic关键字,将任务,函数和模块声明为自动存储模式,这样,仿真器就能够对所有形式的参数和内部变量使用堆栈的形式来存储。