芯学长 | 掌握芯资讯,引领芯未来

您当前所在位置:首页 > 芯片设计 > 数字IC验证

IC验证丨SystemVerilog基本语法

发布时间:2023-07-27来源:芯学长 0

image


一. 数据类型
 

1、二值逻辑和四值逻辑

 
编码时一定要注意操作符左右两侧的符号类型要一致二值逻辑:包括 bit, byte, shortint, int, longint等,均为有符号类型(bit除外);这些类型的值只包含{0,1},目的是模拟计算机验证环境,提高仿真性能,节约空间。若有四值逻辑数给其赋值,x,z会默认被赋值为0,因此二值逻辑数要远离DUT四值逻辑:包括logic, integer, reg, wire等,均为无符号变量(integer除外);目的是模拟外部物理世界
 

2、logic

 
用法既拓展了传统的reg型,也可以像wire那样连线,但既然被应用在验证场景中,因此无需关心对应的逻辑是否被综合为寄存器还是线网,只需作为简单的变量赋值即可。
注意:当含有多驱动(multi-drive)的场景是必须使用wire型。
 

3、动态数组和联合数组

 
int  array[];               //动态数组,compile时长度不确定,simulation时长度确定
array = new[20];      //new分配空间
 
logic [31:0] array[*];    //联合数组,目的是节约空间
 
4、队列

概念类似C语言里的队列,用法也类似,可自由插入删除数据,
shortint  queue[$] = { 1,2,3 }
queue.insert(1,7);    //{1,7,2,3}   在第一个数后面插入一个7
queue.delete(1);      //{1,2,3}
queue.push_front(6);   //{6,1,2,3}  在队列前插入6
queue.push_back(7);   //{6,1,2,3,7} 在队列后插入7
j = queue.pop_front;    //{1,2,3,7}    同理pop_back
 
5、结构体

类似C语言里的结构体,包括概念和用法
struct {bit [7:0] r,g,b;}  pixel;
 
6、枚举

enum logic [1:0] {...} state;  //可定义z,x状态
 
7、字符串

SV里的字符串单个字符是用byte类型,且不像C语言,字符串的结尾不带标识符null,因此\0相关操作将无效
string s = "Kevin ";
$display(s.getc(0));     //显示第一个字符的ASCII码
$display(s.tolower());     //显示整个字符串的小写kevin
$display(s.toupper());     //显示整个字符串的大写KEVIN
s.putc(s.len()-1, "-");       //将字符-写在字符串的s.len()-1位置上   Kevin-
s = {s, "Gu"}                    //合并字符串
$display(s.substr(2,5));   //提取从位置2到5间的所有字符 
 

二、 语句

 

1、数组的操作语句

 
bit on[10] ={ 2{1,2,3,4,5}};
int tq[*];
sum1 = on.sum;             //数组求和,积(product),与(and),或(or),异或(xor),最小值(min),最大值(max),相异值(unipue)
sum2 = on.sum with (int'(item));            //使用带32比特有符号数显示,在条件语句with中,item是缺省值
tq = on.find with(item >3);         //找出所有大于3的元素
on.reverse();                 //数组反向排列,升序(sort),降序(rsort),混排(shuffle)
 

2、循环语句

 
循环语句用于控制执行语句的执行次数,用于在仿真代码中生成仿真激励信号。循环里面一定要有时间在运行,如@(clk)等等,否则会陷入死循环且时间不走动。
for(int i=0; i<=5; i++)...           //for循环,相较于verilog,i可以内部定义,且作为局部变量,作用范围只限于for循环
while(i<5) begin...end    //while循环,同样存在do...while()
foreach(src[i])                //foreach循环,和for功能类似,但foreach多了可编译的功能,及判断条件里可有变量
       src[i] = i;      
 

3、随机函数

 
randomize(value)
urandom()                             //返回无符号的32bit数
urandom_range(val1, val2)                  //返回在[val1, val2]区间内无符号的32bit数
random()                               //返回有符号的32bit数
 

4、typedef

 
定义新的类型
typedef reg [15:0]   opreg_t;            //创建新的类型opreg_t等价于reg [15:0]
typedef class Packet                       //创建一个类别名
 

5、一些关键词

 

ref...

 
用在function/task赋值,ref参数只能被用于带自动存储的子程序中,若使用automatic,则整个子程序内部都是自动存储的function automatic void f1( ref data )  //此处data和外部定义的data共用同一物理地址,因此两值同时变化endfunction     //若希望内部值的改变不影响外部值的改变,可在内部定义为const ref data
 

automatic和static

 
automatic变量类似于软件中的局部变量,在它的作用域生命结束时被销毁回收存储空间
static变量若在仿真开始时被创建,在进程执行过程中自身不会被销毁,且会被多个进程共享。
 

inside

 
if((a==1)||(a==2)||(a==3)) 等价于if( a inside {1,2,3})
 

with

 
一般和randomize(value)连用,with后接对value的约束
 

time

 
64bit的整数,小数部分会被舍入。描述时间,单位为timescale定义的精度
time tdelay = 800fs                   //0.1ps
 

rand

 
随机成员要带有关键字rand,只能在类中声明
rand bit [7:0] data1;         //data1值取0-255中的任意数
randc bit [7:0] data2;       //data2取0-255中任意值,但每次取值都不一样,只限于bit和enum类型
constraint range1{                       //约束data1范围 
       data1>100; data1<200;
       data1>data2; }         
 

dist { value1 := weight1   value2 :/ weight2 }

 
划分权重,用在随机化操作中
[1:2]:=40;   //每个数权重都是40
[1:2]:/40;    //每个数权重是20
 

solve...before...

 
用在随机数产生时,constraint魔窟啊里面
solve y before x    //先产生x,再产生y
 

semaphore

 
创建对象可复用
 

mailbox

 
创建一个信箱,使通信数据在不同process交换,可理解成fifo
 

4、一些符号及搭配

 

通配符 .*

可以把相同名字的net和port连在一起
test   test1( .a(a), .b(b), .c(c) );      //或者 test   test1( .a, .b, .c );  
等同于   test t1( .* );/
 

==? / !=?

判断符号两边是否像相等或不等,x,z处做通配处理
 

++

SV里面可以用自增符
 

'value 赋值

data0 = 'x                //SV中赋值可无需添加b,o,h等代表进制的符号
data1 = '1               //SV中可给某个值赋值全1,注意Verilog中不行
 

int'( value)

强制类型转换,int也可以是其他数据类型或unsigned,signed
 

value1'( value2 )

位宽强制转换,value1是位宽值
 

##

延迟一个时钟周期
 

|->

在当前时钟周期
 

::

域名索引符
 

三、 语句块

 

1、过程语句块

 

always_comb

组合逻辑,无需敏感信号,避免产生latch警告
 
always_comb         //若需要latch警告,则使用always_latch
       if (a == 1)
       b <= 5;
 

always_ff

时序电路,需要敏感信号,且一定是边沿敏感,避免出现flip-flop触发器警告
 
always_ff( posedge clk )
       if ( en )
       a <= b;
 

final块

这个块在Verilog中是没有的,当遇到$finish的时候,会进入到final块中。一般用在打印一些信息,注意final块中是不能加延迟#操作的,不然会报错。
 

2、program

 
program将DUT和testbench作了清晰的划分,可以将设计和验证的调度区域通过显式的方式来安排。
 
可以将program看成是按软件的方式运行的,编写该部分代码时完全可以用C语言的思维,无需考虑代码并行执行的情况(除了jork等并行语句),所以非常适合在该块中进行该测试用例产生,发送以及比较数据的接收。
 
可在module直接被例化,但是语句块里面不能出现和硬件相关的过程语句和实例(如always,module等),也不能有其他program语句,可以有initial,task等块。
 
program内部定义的变量赋值的方式应该采用阻塞赋值(软件方式),在驱动外部的硬件信号时应该使用非阻塞赋值(硬件方式)。$exit()强制结束该系统函数所在的program。
 
program中的initial块会在reactive区执行,外部的initial会在active区执行。
 
program execute_test
initial begin
...
end
task task1()
...
endtask
endprogram

 

3、interface

 
作为中间模块将testbench和DUT连接,可以将它看成一个“路由器”,集合多个信号,简化连接,方便调用。
interface可以包含过程语句(always和initial)和连续赋值语句
interface若要与DUT相连需要用四值逻辑,不能用二值逻辑
interface可以定义参数、输入输出端口,若接口对不同连接模块的方向不同,可以将端口定义在modpot中。
 

4、clocking

 
为了避免冒险竞争,通常会把clk驱动的同步信号封装在一个模块里,由interface调用。

 

5、modport

 
用来来连接不同的测试情况
interface signal_test( input bit clk );                //定义
     logic [1:0] A, B;
     logic reset;
    clocking dram @( posedge clk)
           input #1ps A;
           input #5    
           output #6 B;
     endclocking
     modport user1( output A, input  B, reset);  //声明的端口可以在不同的modport里定义为不同的输入输出模式
     modport user2( clocking cb, input reset);    //调用clocking,port输入输出和user1不一样
endinterface
 
always #5 clk = ~clk;
signal_test.user1  signal1(clk);                     //声明user1模式
signal_temp signal2( .A(signal1.A), ......);     //interface互相直接连接
test test1( signal1 );                                      //interface与module连接

 

6、package

 
可利用package(包)实现在module,interface和program中共享parameter、data、task、class等。
即将不同模块的类定义规整到不同package中,在单一的命名空间下,使得分数不同模块验证环境的类来自不同的package,从而解决类的归属问题。
package regs_pkg;                     //定义
    `include "simulator.sv"
    `include "monitor.sv"
endpackage
package arb_pkg;                    //定义
    `include "simulator.sv"
    `include "monitor.sv"
endpackage
module mcdf_tb;                               //调用
reg_pkg::monitor mon1 = new();    //l两个package中同名类monitor的内容不同,实现不同的功能
arb_pkg::monitor mon2 = new();
endmodule
 

7、sequence

 
序列内的语句按顺序执行。

【免责声明】:本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。

原文标题:
IC验证丨SystemVerilog基本语法
文章来源:
https://mp.weixin.qq.com/s/A1VnLt6vF7MCulukYDhpSg

文章评价

-   全部 0 条 我要点评

有疑惑?
在线客服帮您
029-81122100

立即咨询 >