IC验证——SystemVerilog基本语法

导读:本篇文章讲解 IC验证——SystemVerilog基本语法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

SV和Verilog的语法类似,和C/C++也有些共性,基本SV可包含Verilog的所有规则,本文会在以下博文内容外做补充,若有相异处下文会特意指出。Verilog语法可参考该链接第1大节。链接:IC设计——Verilog HDL学习笔记_KGback的博客-CSDN博客

1. 数据类型

  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间的所有字符 

2. 关键词

  • typedef
    定义新的类型

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

  • 一些关键词
  1. ref…
    用在function/task赋值,ref参数只能被用于带自动存储的子程序中,若使用automatic,则整个子程序内部都是自动存储的
    function automatic void f1( ref data )  //此处data和外部定义的data共用同一物理地址,因此两值同时变化
    endfunction                                         //若希望内部值的改变不影响外部值的改变,可在内部定义为const ref data
  2. automatic和static
    automatic变量类似于软件中的局部变量,在它的作用域生命结束时被销毁回收存储空间
    static变量若在仿真开始时被创建,在进程执行过程中自身不会被销毁,且会被多个进程共享。
  3. inside
    if((a==1)||(a==2)||(a==3)) 等价于if( a inside {1,2,3})
  4. with
    一般和randomize(value)连用,with后接对value的约束
  5. time
    64bit的整数,小数部分会被舍入。描述时间,单位为timescale定义的精度
    time tdelay = 800fs                   //0.1ps
  6. 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; }         
  7. dist { value1 := weight1   value2 :/ weight2 }
    划分权重,用在随机化操作中
    [1:2]:=40;   //每个数权重都是40
    [1:2]:/40;    //每个数权重是20
  8. solve…before…
    用在随机数产生时,constraint魔窟啊里面
    solve y before x    //先产生x,再产生y
  9. semaphore
    创建对象可复用
  10. mailbox
    创建一个信箱,使通信数据在不同process交换,可理解成fifo
  • 类相关
  1. virtual function
    SystemVerilog提供了2种构造方法来创建一个可以共享的基类:第一种是抽象类,即可以被扩展但是不能被直接实例化的类,使用virtual关键词进行修饰;第二种是纯虚方法,用关键词pure修饰,如下2所述。
    通过virtual function,在派生类必须具有该函数的具体实现
  2. pure virtual function
    定义没有实体的纯虚函数。这是一种没有实体的方法原型,并且纯虚方法只能够在抽象类中定义。
    virtual和pure virtual的区别:如果有一个带有pure virtual function的Virtual Class,则必须在子类中对其具体实现。但对于virtual function,可以覆盖也可以不覆盖。System Verilog的抽象类(abstract class)和纯虚方法(pure virtual )_飞飞飞C的博客-CSDN博客

3. 特殊符号及搭配

  1. 通配符 .*
    可以把相同名字的net和port连在一起
    test   test1( .a(a), .b(b), .c(c) );      //或者 test   test1( .a, .b, .c );  
    等同于   test t1( .* );
  2. ==? / !=?
    判断符号两边是否像相等或不等,x,z处做通配处理
  3. ++
    SV里面可以用自增符
  4. ‘value 赋值
    data0 = ‘x                //SV中赋值可无需添加b,o,h等代表进制的符号
    data1 = ‘1               //SV中可给某个值赋值全1,注意Verilog中不行
  5. int'( value)
    强制类型转换,int也可以是其他数据类型或unsigned,signed
  6. value1′( value2 )
    位宽强制转换,value1是位宽值
  7. ##
    延迟一个时钟周期
  8. |->
    在当前时钟周期
  9. ::
    域名索引符

4. 内建函数

  • $cast

参考链接:SystemVerilog中类型转换$cast的使用_254、小小黑的博客-CSDN博客

  • 随机函数
  1. randomize(value)
  2. urandom()                             //返回无符号的32bit数
  3. urandom_range(val1, val2)                  //返回在[val1, val2]区间内无符号的32bit数
  4. random()                               //返回有符号的32bit数
  • 数组的操作函数

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)

5. 语句块

  • 循环语句块
    循环语句用于控制执行语句的执行次数,用于在仿真代码中生成仿真激励信号。循环里面一定要有时间在运行,如@(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;     

  • 过程语句块
  1. always_comb
    组合逻辑,无需敏感信号,避免产生latch警告

    always_comb         //若需要latch警告,则使用always_latch
           if (a == 1)
           b <= 5;

  2. always_ff
    时序电路,需要敏感信号,且一定是边沿敏感,避免出现flip-flop触发器警告

    always_ff( posedge clk )
           if ( en )
           a <= b;

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

  • interface
    作为中间模块将testbench和DUT连接,可以将它看成一个“路由器”,集合多个信号,简化连接,方便调用。

interface可以包含过程语句(always和initial)和连续赋值语句
interface若要与DUT相连需要用四值逻辑,不能用二值逻辑
interface可以定义参数、输入输出端口,若接口对不同连接模块的方向不同,可以将端口定义在modpot中。

  1. clocking
    为了避免冒险竞争,通常会把clk驱动的同步信号封装在一个模块里,由interface调用。
  2. 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连接

  • 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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/82522.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!