【chips】个人笔记系列-SystemVerilog

Title:System Verilog 学习

背景与发展

什么是SV

  • 啥是SystemVerilog?

    就是用来专门写验证和测试的 Verilog 升级版——在verilog的基础上加了些C++的思想、语法、模块。

  • 为啥要搞出一个SystemVerilog?

    设计IC (integrated circuit)时用的是 verilog,语法内有 intial 和 task 用来写 testbench. (跑过综合就明白,综合的IC code里不能有intial等的,intial等只能用于testbench或功能级IC code) 但即使如此,Verilog写testbench时也很不方便:模块化、随机化验证都不够。

    1999年OSCI推出的StstemC本质是C++库,写一些给IC code用的reference model时方便很多(尤其是算法类设计)。但C++的指针,玩不好容易让人心碎——发生内存泄露、写一些构建异常的测试用例都让人泪目。

    2002年推出的SystemVerilog取他们精华、去他们糟粕。是verilog的扩展版,加了C++的OOP(面向对象)元素(封装、继承、多态)来方便写验证代码,加了新的验证特性(随机化、约束、功能覆盖率),简化了内存管理(多了内存回收机制),调用封装好的C程序也很方便,会verilog的人贼容易上手它,是IC验证人员的好伙伴~

    (个人写习惯了C/C++,刚开始写verilog的testbench时有感触verilog磨唧…)

  • SystemVerilog和UVM的关系

    我们用SystemVerilog写验证,而UVM既是一个思考模式,也是被实现好的规范、通用的验证库。关系像是C++和其内的 std容器。 UVM文件都是SystemVerilog文件,即后缀 .sv ;

验证方法学

  • 三种验证方法
    • 断言验证 (Assertion Based Verification, ABV)
    • 覆盖率驱动验证 (Coverage Driven Verification, CDV)
    • 约束随机激励测试 (CR TB)

2 数据类型和编程结构

2.1 数据类型介绍

数值类型分类

  • SV内新增的数据类型——图示

    P.S. 阿这,SV里没有char类型,只有byte类型

    请添加图片描述

    • Verilog 1995的数据类型

      • 基本数据类型:reg 和 wire

        四值类型:(0/1/X/Z)

      • 其他得注意的类型:integer (四态、有符号

      变量统统默认是static的。

    • SV内新增的数据类型

      两态类型、枚举/联合类型、自定义类型、动态/关联数组、队列、字符串、类;

      • 两值类型(only 0/1):bit、byte、shortint/int/longint;

        1位、8位、16位/32位/64位;

        3种int和byte都是默认有符号的.

      • 四值类型:logic

  • 二值/四值的区别

    1. Z和X态是什么?

      • Z是高阻态,悬空未驱动,电阻相当于无穷大;

      • X是不定态,需要自行check逻辑确保一下;

    2. 2值的意义是比4值占用空间少一半,故module的端口用4值、module内变量用2值.

  • 二值/四值分类总结

    • 二值逻辑:(bit, byte, shortint/int/longint)

      默认初值为0.

    • 四值逻辑:(logic, reg, wire, integer, time)

      默认初值为x.

    • 符号类型:byte, shortint, int, longint, integer

    • 无符号类型:bit, reg, logic, wire

    • 其他类型:(实数 real,初值为0)

SV的logic类型

  • 作用

    四态数据类型,可以代替reg.

  • 引入logic类型的意义

    SV中引入了logic类型,因为dv人员无所谓变量是wire还是reg,用logic表示单纯赋值操作;logic类型是基于reg增强的,但又具有了连续赋值assign、当端口的各种特性,故dv人员用起来很方便——缺陷:不能被多个驱动

  • 限制

    双向总线和多驱动的情况下不能使用,此场景只能用wire.

  • logic和 reg、wire 的区别

    • wire是net类型,映射到硬件 → 导线;

      作用:传递、驱动、必须用assign连续赋值;

      兄弟类型:tri、wand、supply0、inout等

      不可赋初值

    • reg是variable类型,映射到硬件 → 寄存器(没被综合优化的话);

      作用:存储结构、必须用过程赋值;

      兄弟类型:integer、time、real、realtime等;

      可赋初值,但初值不可综合

    • logic类型

      编译器可自行推断logic是 reg还是wire. 可综合.

      但logic只能有1个输入,不可多重驱动(不可既是in口也是out口).

      注:Verilog有专门的inout端口写法,是用wire来实现的.

枚举类型 enum

  • 语法

    //使用方法1:
    	//定义
    enum 类型{		   //  `类型`可略,默认是 int
    name0 = value0,		// 不写 `value` 则默认 从0递增 
    name1,
    name2 = value2    
    }var_name;		// 是变量,不是类型!
    
    	// 赋值
    var_name = name0;	// 不用加域
    	// 打印
    $display("变量var_name的name: %s, 值: %0d", var_name, var_name);
    
    //使用方法2:
    	//定义
    typedef enum 类型{
    name0 = value0,
    name1,
    name2 = value2    
    }var_type;		// 是类型,不是变量!
    
    	// 赋值
    var_type var_name = var_type::name0;	// 需要加域
    	// 打印
    $display("变量var_name的name: %s, 值: %0d", var_name, var_name);
    
    • 枚举的“子变量类型”与“子枚举内部值”可略

      “子变量类型” 默认是 int

      “子枚举内部值” 默认 是 从0递增接着前者增加

    • 枚举变量的赋值:可自动转换为int值;

      赋值为内部定义的枚举值时,不用加域

    • 枚举变量的打印,%s%d,会自适应打印name或value.

  • 内建函数

    • .first() 返回此类型第一个枚举值 (enum);
    • .last() 返回此类型最后一个枚举值 (enum);
    • .next(int N) 返回下N个枚举值,默认是下1个 (enum);
    • .prevt(int N) 返回上N个枚举值,默认是上1个 (enum);
    • .num() 返回此类型枚举值的个数 (int);
    • .name() 返回此枚举值的名称 (string);

自定义类型 typedef

  • 作用

    1. 起别名

      可以给任何已有的数据类型、联合体等,起新名字(别名)。

    2. 自定义新的变量类型

    3. 提前声明 (forward typedef):

      用户自定义类型,必须在使用前被定义(同C/C++)。

      故,可以在使用前,用typedef提前声明,后续再定义。

  • 语法

    //起别名
    typedef int hyp_defined_super_dilicious_type;	// 给原有类型起别名
    parameter SIZE = 666;
    typedef reg [SIZE-1:0] usr_type;	// 自定义类型:封装为向量
    typedef int Array[100];	// 自定义int的数组类型,大小100;使用时: Array a;
    typedef int Queue[$];	// 自定义int的队列类型;使用时:Queue q;
    
    // 提前声明
    typedef class usr_class;		// 提前声明自定义的 类或其他类型 
    typedef enum usr_enum;
    typedef 
    

字符串类型 string

  • 声明/初始化语法

    string name1;
    string name2 = "abc";
    

    串若未初始化,默认为空串

  • 运算符

    • == / != 字符串判等 or 判不等
    • < / <= / > / >= 字符串按字典序比大小
    • {} 字符串拼接
    • {n {Str}} 字符串复制n次
    • 串[i] 打印串的第i个字符(第i个字节)
  • 内建函数

    • .len() 返回串的长度,不含终结字符;

    • .putc(index, c) 把index的字符替换成c字符;越界访问不报错,不改原串

      :SV中单个字符也得用"",别用'';或者用ASCII码的整数。

    • .getc(index) 返回串[index]字符的ASCII;越界访问不报错,不改原串

    • .toupper() 返回串的大写形式;不改原串

    • .tolower() 返回串的小写形式;不改原串

    • .compare(string) 和其他串按字典序比较;类似C语言的strcmp()

    • .icompare(string) 和其他串按字典序比较,不区分大小写

    • .substr(i, j) 返回index是 [ i , j ] [i,j] [i,j] 的子串;越界则返回"" 空串.

    • .atoi() 将串转为数字——从串的头部(左边)寻找数字前缀,遇到非数字就停止;没找到就返回0.

    • .atohex() 同上,找前缀的十六进制串,遇到非数字就停止;没找到就返回0.

    • .atooct() 同上,找前缀的八进制串,遇到非数字就停止;没找到就返回0.

    • .atobin() 同上,找前缀的二进制串,遇到非数字就停止;没找到就返回0.

    • .atoreal() 同上,找前缀的浮点串,遇到非浮点数就停止;没找到就返回0.

    • .itoa(value) 把整数value,转为串存下来;

    • .hextoa(value) 把十六进制数value,转为串存下来;

    • .octtoa(value) 把八进制数value,转为串存下来;

    • .realtoa(value) 把实数value,转为串存下来;

结构体

  • 作用

    和C里的struct一样。

  • 定义与赋值语法

    1. 直接声明变量;

    2. typedef声明自定义类型,再用类型定义变量;

      (和别的数组啥,flow一样其实)

    // 方法1:
    struct{
        // 自定义成员
        int a;
        int b;
    }usr_var;
    
    // 方法2:
    typedef struct{
        // 自定义成员
    }usr_Type;
    usr_Type tmp;
    
    // 赋值方式
    c = '{a:0, b:0.0};		// 用成员名进行赋值
    c = '{default:0};		// 设置默认值
    c = '{int:1, real:0.0};	 // 为所有“此类型”的成员,设置默认值.
    
  • 结构体数组

    typedef struct {
      int a;
      real b;
    }Node;	// 定义结构体
    Node arr[1:0] = '{ '{1, 1.0}, '{2, 2.0}};  // 结构体数组的初始化
    
    // X: 非法的赋值过程(不能“并”在一起赋值)
    Node arr[1:0] = '{ 1, 1.0, 2, 2.0};  // 结构体数组的初始化
    
  • 压缩(packed)、非压缩(unpacked)与存储结构

    结构体可被定义为“压缩” 或 “非压缩” 的状态。这将影响其内成员的存储方式与计算方式。

    • 压缩结构体:内部成员将无缝拼接在一起,相当于1个自定义vector,参与运算时也可整个以vector进行计算——“压缩”的概念.

      • 压缩结构体可被视为1个vector,故可分为signed / unsigned,默认是unsigned

      • 压缩结构体内只要含有四态成员,整体struct即为四态类型;

      • 压缩结构体内不允许用real等非整数类型变量、非压缩数组

        (“压缩/非压缩”数组的内容,见后面)

    • 非压缩结构体:内部成员以字节为最小粒度进行存储,不足1字节就补齐。

      默认是非压缩结构体.
      请添加图片描述

    // 压缩类型、带符号
    struct packed signed{
        //...
    }packed_var;
    
    // 默认是非压缩结构体 (unpacked)
    // 就算是压缩结构体,默认也是 unsigned 的
    
    struct{
        bit [3:0] data[4][5];
    }mem;
    initial begin
        $display("%x",mem);		// 非法! 这可是非压缩结构体,不能当vector用.
        $display("%x",mem.data[2][1]);		// ok!这下用的data的压缩部分.
    end
    

常量

  • 定义

    不会被改变的数据。

  • 分类

    simulator中,一般分为compile time(编译环节)、elaboration time(映射计算hierarchyparameter的override传递 环节)、run-time(具体执行代码、仿真的环节)的三个运行环节。

    SV中提供了 3种elaboration-time类型 和 1种run-time类型的常量

    • elaboration-time类型

      必须定义时初始化,将在elaboration-time中被赋值并一直固定。

      • parameter
      • localparam
      • specparam

      可用于 module、interface、program、class、package.

    • run-time类型

      • const
  • 语法格式

    • parameter

      • 普通定义

        parameter [数据类型/默认int/也可以为type类型] 参数名 = 参数值;

      • 模板内类型定义

        parameter type T=shortint; 定义模板里的类型.

      • 使用$,表示无上限的边界

        parameter rv = $;
        property inq(lv,rv);
        endproperty
        assert inq(3);
        
    • localparam

      同上,但localparam不能在例化时通过传入进行更改,只能内部使用.

    • specparam

      只能在specify block内使用,用于定义延时参数.

      先略。

    • const

      • 特点

        虽然类似localpram,但因为是run-time阶段赋值,故可赋更多元的变量类型(甚至可以为OOP对象)、被层次化引用.

      • 定义方式

        const 类型 变量名 = 值;

        • 右边可以用new()、new()内实参必须是常量表达式
        • 但此变量不可被重写。

文本

  • 数值

    • 指定位宽

      e.g. 4'b1001

    • 指定位宽

      e.g. 4356默认十进制

      e.g. 'b10000110 自行确定位宽.

    • 批量所有位赋值

      使用'符号、并不指定基数:

      bit [7:0] data;
      data = '1;		// data为 8'b11111111;
      

      我靠,我一直以为是等于1!!!

      • 注意'1'b11 三个的区别!
  • 字符串

    • 语法:用""包裹.

    • 多行字符串:行尾 \来换行. (新行空白格也将被算作字符

    • 末尾间隔符:似乎会插入\0作为间隔符(和C一样,ASCII值为0).

      我很怀疑有没有…因为打印不出此间隔符…

  • 时间表示

    • 时间文本被解释为 realtime类型,将根据当前的时间精度四舍五入。
    • 可选的时间单位有:s、ms、us、ns、ps、fs.

注释

  • 同C,有行注释//和块注释\*, *\.

2.2 赋值语句

变量间赋值

  • 四态值赋给两态值

    这是合法的;所有XZ都会变成0.

    • 但关键是要检查是否有“未知态X传播”:

      Solution: 用$isunknown(),若表达式内有XZ,则返回1.

参数传递与设置

SV提供了四种参数设置/传递的方式:

  1. 顺序赋值

    类型 # (值1, 值2, ..., 值i);

  2. 显示引用形参名赋值

    类型 # (.形参名1(值1), .形参名2(值2), ..., .形参名i(值i));

  3. defparam语句(不推荐)

类型强制转换

  • 语法

    // 取一个极端的例子,强转成自定义类型
    typedef enum{red, green, blue}color;
    initial begin
        color tmp;
        tmp = color'(2);	// 把2转成color类型.
    end
    

模块例化的信号传递 ⭐️

Verilog/SV提供了三种模块例化的信号传递方式:

  1. 自动的默认连接:同名信号自动连接

    模块名 实例名(.*);

  2. 位置关联

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值