SV实用备忘录

语法

打印相关

%0d:抛去输出中的0与空格,使输出更紧密

%p:十进制打印全部元素(数组,动态数组,队列,结构体),用的较多

数据类型相关

packed array 与 unpacked array

1
2
bit [7:0] packed_array;     // packed array
event unpacked_array[0:7]; // unpacked array

1.packed array

  • 常由按位(bit-wise)类型(logic、bit、reg,etc)、线网(wire、uwire、wand、tri、triand,etc)、枚举类型和其他的packed数据结构等构成,而像byte、shortint、int、longint、integer和time等这些数据类型,因为其本身就已经包含特定的位宽信息,所以不能指定这些类型为packed array,但是这并不影响这些类型声明的变量在满足一定要求的情况下同packed array进行交互的
  • packed array中数组元素在存储时是按照位连续的方式进行存放的,即其中所有元素是连续存放的,并且存放不依赖于任何EDA工具,一般情况下一维的packed array与vector类似,根据packed array的特点,其存储方式如下图示例所示:
  • 对packed array赋值时采用的是拼位操作实现的

img

2.unpacked array

  • 基本上通吃所有数据类型,数组的索引范围声明在数组名的右侧,数组的维数也可以像packed array一样指定为多维的,与packed array维数指定不同的是,unpacked array维数的指定有两种方式:一种是传统的数组声明方式int Array[0:7][0:31],另一种方式是采用C语言方式int Array[8][32]
  • 在数据存储方面,unpacked array中所有的数据元素在存储时是相互独立的,在数组元素存储时,数组中元素具体存储在几位的空间一般情况下取决于编译器,例如如果定义的unpacked array中每个元素宽度为8位,但是编译器在存储数组元素时给每个元素分配了32位空间,那么unpacked array中每个元素将仅使用32位中的8位,从而导致部分空间的浪费,下图为一个unpacked array的存储示意图:
  • 对unpacked array进行赋值操作时采用的是数值列表的方式,即'{},注意不能使用拼位方式,但是packed array赋值方式不仅可以使用拼位方式也可以使用数值列表方式

img

3.多维数组
在实际使用时,除了会单独定义使用packed array和unpacked array之外,经常还会将两者组合在一起形成多维数组进行使用,特别是在声明定义一些memory模型时。下图为一个多维数组的示例,其中标明了索引时各索引号对应的索引位置。

img

多维数组索引的顺序如上图所示,即先从unpacked维数开始,并且unpacked的维数中按照从左到右的顺序进行索引,完成unpacked侧所有维数索引之后,再从packed侧的维数开始索引,也是按照从左到右的顺序进行索引

4.使用场景

unpacked array:

  • 非bit-wise类型(例如byte、int、integer、real、其他unpacked类型结构)
  • 构建存储类模型时,常需要一次访问一个多位元素的数组时

packed array:

  • bit-wise类型、所有的packed类型结构都可以采用
  • 如果需要经常访问数组中的某些位时,可以考虑构建数组时采用packed array

字符串

判断字符串为空

if(str == "")

字符串拼接

  • 方法一:s3 = {s1, " to ", s2}; // concatenation operator '{...}'
  • 方法二:s4 = $sformatf("%s to %s", s1, s2); // system format function
  • 方法三:s5 = $psprintf("%s to %s", s1, s2); // system format function

整形转字符串:itoa

字符串拼接的时候一定要用{}预留足够的位宽:s3 = {s1.len()+s2.len(){" "}};

结构体

接口与struct放在module外面仿真时才能看见,而且这种方式更常见

接口

  • 接口内有时间概念
  • 接口与struct放在module外面仿真时才能看见,而且这种方式更常见
    • 接口中不能在类里面或包里面定义,因为他是软件的概念
  • 接口内部定义的函数与module里面定义的是一样的,换句话module里做一件事在interface也都是可以做

类和对象

在类里面声明interface的指针一定要加virtual不然报错

一种奇怪的例化?我还没查过手册,第一次出现在svlab2的tb1,也不用特意连接interface的接口信号

1
2
3
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);

又一种奇怪的例化传参:

1
2
3
4
5
this.init = new({name, ".init"});
//init的构造函数原型为
function new(string name = "chnl_initiator");
this.name = name;
endfunction

clocking时序块

clocking基于时钟进行采样,使得testbench不在苦恼于如何即使准确的对信号进行驱动和采样,消除了信号竞争问题,我们应该在验证消耗的驱动环境就添加固定延时,使得在仿真波形中更容易体现出时钟与被驱动信号前后的时序关系

image-20230215205604746

  • 第一行@(信号):定义上升沿事件
  • 第二行指定的是:默认情况下所有的输出相比较上升沿之后2ns延迟,对所有输入信号在上升沿之前10ns做一个采样。和电路里面建立保持时间差不多。
  • 第三行:我们要对data,ready和enable信号做采样了,且以及声明方向了
  • 属于输出驱动额外强调在时钟下降沿
  • #1step 过上一个单位的step表示,可以看作一个时间片或者上升沿上一个时间片之间的采样域内

定义与使用:

image-20230215210723515

除了上述例子,总结下我们通常用到clocking的方式:

  • 如何让clocking后的信号介入:接口实例.clocking块.信号
    • 如果单纯:接口实例.信号则没有clocking块下过
  • 如何检测clocking的变化:@clocking块

  • 对于时钟而言可以定义在:module,Interface,program中
  • clocking内列举的信号是由interface或其他的地方去定义
  • 对于时钟块他只声明而不定义
  • 没有定义的话默认输入在事件前1step,驱动在事件后#0(一个时钟片相当于无穷多个step,肯定比step大)
  • 处理定义默认的采样和驱动时间,可以用新的时间对默认事件覆盖

program

  • program是软件的领域
  • module是硬件的领域(不可以出现硬件行为相关的过程快:always, module, interface)
  • interface硬件软件的媒介

program的作用:

  • 我们建议将设计部分放到module,测试采样放到program
  • 发起多个initial(全部执行完毕则结束仿真,这种方式是隐式结束,使用较少)
  • 调用program专用系统函数$exit()结束仿真,这种方式是显式结束

package的使用

包的意义:相关的类可以组织在同一命名空间中(多个module、Interface和program之间共享数据的方法)

要点:命名空间实际上就是个package

定义:

1
2
3
4
5
package reg_pkg;
`include "xxx_1.sv"
`include "xxx_2.sv"
...
endpackage

导入:

1
2
3
4
5
module
import xxx_pkg::*;
或者直接引用
xxx_pkg::monitor mon1 = new();
endmodule

注意:

  • 包不用例化,包就是个容器
  • 软件的东西一定要放到包里面,类是包的好朋友,包是类的归宿,我们会把类都放到包里面
  • 不能定义与硬件相关的部分(module,interface,program都不能)

包、library和编译:

  • package会被编译到library里面。编译的默认路径为包的路径,若包中有其他include的文件不在包路径下,则编译包时候需要加上额外指定的搜索路径选项
  • 包就像一个library一样,只不过比Library低一个层级,你把定义好的东西放到包里面
  • 同名类,不同包编译的结果:

image-20230215212744440

随机化

软约束soft

  • 约束有冲突时soft声明的约束不起作用
  • 软约束用于为随机变量指定默认值和分布

local::

image-20230216232618130

  • 这里不能用thisthis仍然代表该处的trans的成员变量

随机变量和约束初始化为负数的好处(070-实验3代码讲解,15:00)

image-20230216233004611

image-20230216233140031

  • 具体约束位置(send_trans()位于generator类里)

image-20230216233219546

  • 可以完成从该generator到chnl_trans的层层递进的控制(通过ch_id >= 0 约束了generator,进一步通过->约束了ch_id)

调用

调用randomize()或者randomize() with {约束条件}前/后

  • 该类内重载过的pre_randomzie();post_randomize()函数会自动被调用

断言

注意:system verilog是包含有assert语句的,但是raw verilog没有

简单写法:assert(表达式);

一种写法:

1
2
assert(表达式)//为真则正常运行,为否则报错终止并运行else语句
else 语句;

事件控制

1.iff(表达式),只有当iff表达式/事件表达式为真时,事件控制才会被触发

1
2
3
4
5
6
7
8
9
@(posedge clk iff(vld));
do_something;
//它产生的效果和下面的代码一样
forever begin
@(posedge clk);
if(vld)
break;
end
do_something;

iffif 之间的区别是:这个表达式只有在 vld 发生变化时计算,而不是 clk 发生变化的时候

优点:会使得 iffif 效率更高,因为它作为一个线程被唤醒的概率更小。所以更推荐使用 iff

disable iff:《System Verilog Assertions应用指南》

img

表达式

等于与不等于

=====不同体现在对含有不定制X和高阻态Z数据的处理中:

  • ==为逻辑相等,没有x/z时相同输出1,不同输出0;有x/z时,若其余位相同,输出是x,其余位不同,输出为0
  • !=为逻辑不等,没有x/z时相同输出0,不同输出1;有x/z时,若其余位相同,输出是x,其余位不同,输出为1
  • ===!==为逻辑全等和逻辑不全等,x/z当作普通元素处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$displayb ( 4’b0011 == 4’b1010 ); // 0
$displayb ( 4’b0011 == 4’b0011 ); // 1


$displayb ( 4’b1010 == 4’b1x10 ); // x
$displayb ( 4’b0x10 == 4’b1x10 ); // 0
$displayb ( 4’b1z10 == 4’b1z10 ); // x

$displayb ( 4’b0011 != 4’b1x10 ); // 1
$displayb ( 4’b1x10 != 4’b1x10 ); // x

$displayb ( 4’b01zx === 4’b01zx ); // 1
$displayb ( 4’b01zx !== 4’b01zx ); // 0
$displayb ( 4’b01zx === 4’b00zx ); // 0
$displayb ( 4’b01zx !== 4’b11zx ); // 1

系统函数

stop() //停止运行

统计类

1.$countbits(expression , control_bit{ , control_bit }):其他统计函数的原型,该函数用于统计某个变量中满足control_bit条件的bit个数。比如可以统计某个变量中1的个数,0的个数,x态的个数等,示例如下:

1
2
3
bit[15:0] a=16'hFF00;
int b = 9;
$display("countbits of a =%0d\ncounbits of b =%0d", $countbits(a, '1), $countbits(b, '0));
1
2
3
结果为:
countbits of a = 8
countbits of b = 30

2.$countones():统计变量中1的个数,等同于$countbits(expression,'1),在MCDT-svlab3中用于统计ready信号为1的个数

3.$onehot():判断某个变量是否是onehot,即1的个数等于1

4.$isunknown():判断某个变量是否含有x态或者z态,等同于:returns true(1'b1) if $countbits(expression,'x,'z)!=0, otherwise it returns false (1'b0)

上述的bit统计函数在断言检测中经常使用,比如断言仲裁时序,仲裁出口只有1bit有效,可以使用$onehot函数


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!