IC前端设计需要设计人员编写testbench来进行对模块的初步前仿,一般是用verilog来写,但是verilog语言结构简单,编写一个稍微复杂的仿真环境很是费力,所以systemverilog便被开发设计出来。systemVerilog是一门高级语言,官方描述为:
SystemVerilog简称为SV语言,是一种相当新的语言,它建立在Verilog语言的基础上,是 IEEE 1364 Verilog-2001 标准的扩展增强,兼容Verilog 2001,将硬件描述语言(HDL)与现代的高层级验证语言(HVL)结合了起来,并新近成为下一代硬件设计和验证的语言。
既可以用作设计又可以用作验证,功能十分强大,所以多学一门systemverilog会让你如虎添翼。笔者本着学无止境的态度,开始接触学习systemverilog.开始吧,一步一个脚印。
数据类型
内建数据类型
什么语言一开始都要从基本数据类型学起,sv也不例外。sv的数据类型像是继承了c语言的很多特色,除了byte 、int、shortint、longint这些c语言里经常看到的,SV还加了logic
、bit等。接下我解释一下这些类型怎么个用法。
logic: 只能双状态,0或者1,reg型和wire型综合,只能被单一驱动
bit: 四状态,0,1,高阻z,未知x,这个比较常用,可以自由定义位宽,例如
bit b; //单比特
bit [15:0] data;//16比特数据,无符号
rand bit [15:0] r_data;// 写testbench时经常会加上rand,用于生成随机化数据。
byte: 8比特有符号数据
int: 32比特有符号数据
shortint: 16比特有符号整数
longint: 64比特有符号整数
这些都可以用bit来代替,而且Bit是无符号的,bit [7:0] 等于 byte unsigned
数组
数组可分成定宽数组和动态数组,定宽数组又可以分成压缩数组和非压缩数组,
bit [7:0] data [0:3] //生成位宽8,深度为4的数组,这是非压缩数组
//也可以这样表达
bit [0:3] [7:0] data;//压缩数组,作用类似上面表达
bit [0:3] [7:0] barray[3]//这个用法我也没见过,书上是这么定义的,其存放示意图如下
压缩数组的结构是这样的
动态数据是位宽暂时无法确定的数组,其在声明时使用空的[],在使用时需要调用new[],同时方括号里要填上数组的宽度,例如:
int data[];//声明动态数组
initial begin
data=new[4];//数组宽度为4
end
自定义数据类型
sv提供typedef 和 struct来为用户自定义数据类型
struct和typedef用法和c语言的差不多,直接代码解释:
typedef struct {int a,b,c;} usr_data;//创建usr_data结构,包括三个int数据
typedf int unsigned uint;//uint 和 int unsigned等效,下面语句可以直接使用uint
接口
interface
端口与端口之间的连接一般通过input和output来传输信号线,当这些信号线数目大的时候,信号线连接就会出现很多的问题和麻烦,于是接口interface就被提出,interface的作用就是把一群信号线捆绑成一个接口,里面可以有时钟有复位信号,也有多位宽数据。接口可以被方便引用,例如:
interface arb_if (input bit clk);
logic [1:0] grant,request;
logic rst;
endinterface
发没发现接口里的数据类型都是logic,而不是wire,想想什么原因?
以上是我们定义了一个接口,接下来展示我们怎么使用它。如果需要用到接口里的某个信号,我们只要写接口名.信号名
,例如:
module test (arb_if arbif);//接口名 自定义名
always @ (posedge arbif. clk or posedge arbif. rst)
begin
if (arbif. rst)
arbif.grant<=2'bOO;
else
•••
end
endmodule
PS:接口里的信号必须使用非阻塞赋值
modport
modport的功能是把interface里的信号分组,选取一些信号作为输入,一些信号作为输出,modport 名(input/output 信号名,…..)例如:
interface arb_if (input bit clk);
logic [ 1: O] grant, reques 七;
logic rst;
modport TEST (output request, rst,
input grant, clk);
modport DUT (input request,rst,clk,
output grant);
modport MONITOR (input request,grant,rst,clk);
endinterface
那我们怎么去用它呢?很简单,就是 接口名.modport名 自定义接口名 ,例如:
module arb (arb_if.DUT arbif);
•••
endmodule
clocking
每个接口可以有多个时钟块,clocking cb @(posedge clk)
的作用是表明我这个区域里的信号只在我这时钟上升沿有效,具体怎么表达例如:
clocking cb @ (posedge clk) ;//声明 cb
output request;
input grant;
endclocking
既然学了clocking,那么modport的写法我们又能这么写了:
modport TEST (clocking cb, //使用 cb
output rst);
怎么用这个接口呢?那就是像套娃一样,怎么套进去的怎么拿出来,
program automatic test (arb_if.TEST arbif); //program习惯加上automatic,
initial begin
arbif. cb. request<= 2'bOl; //接口里的clocking模块里的信号
$display( "@%t: Drove req= 01", $time);
repeat (2) @arbif.cb; //重复两个clocking模块里的时钟
if (arbif.cb.grant! =2'b01)
$display ("@%t: al: grant! = 2'bOl", $time);
end
endprogram : test