|
Systemverilog
数据类型
l
1)合并数组:
存储方式是连续的,中间没有闲置空间。
例如,32bit的寄存器,可以看成是4个8bit的数据,或者也可以看成是1个32bit的数据。
表示方法:
数组大小和位,必须在变量名前指定,数组大小必须是【msb:lsb】
Bit[3:0] [7:0] bytes
2)二维数组和合并数组识别:
合并数组:
二维数组:
位宽在变量名前面,用于识别合并和非合并数组,位宽在后面,用于识别数组中元素个数。
3)非合并数组
一般仿真器存放数组元素时使用32bit的字边界,byte、shortint、int都放在一个字中。
非合并数组:字的地位存放变量,高位不用。
表示方法:
l
随机事物不确定大小。
使用方法:数组在开始是空的,同时使用new[]来分配空间,在new[n]指定元素的个数。
Int dyn[];
l
在队列中增加或删除元素比较方便。
l
当你需要建立一个超大容量的数组。关联数组,存放稀疏矩阵中的值。
表示方法:
采用在方括号中放置数据类型的形式声明:
Bit[63:0] assoc[bit[63:0]];
l
1)Verilog 推荐使用文本宏。
好处:全局作用范围,且可以用于位段或类型定义
缺点:当需要局部常量时,可能引起冲突。
2)Parameter
3)Systemverilog:
过程语句
l
for(int i=0;i<10;i++)
array[i] =i;
l
1) 区别:
Verilog中task 和function最重要的区别是:task可以消耗时间而函数不能。函数中不能使用#100的延时或@的阻塞语句,也不能调用任务;
Systemverilog中函数可以调用任务,但只能在fork
2)使用:
l
作用:
1)类的静态变量,可以被这个类的对象实例所共享。
当你想使用全局变量的时候,应该先想到创建一个类的静态变量
静态变量在声明的时候初始化。
2)
类的每一个实例都需要从同一个对象获取信息。
l
作用:
当静态变量很多的时候,操作它们的代码是一个很大的程序,可以用在类中创建一个静态方法读写静态变量,但是静态方法不能读写非静态变量。
l
Ref 参数传递为引用而不是复制。Ref比 input 、output、inout更好用。
Function void print_checksum(const ref bit [31:0] a[ ]);
1)
2)
3)
l
增加了return语句。Task任务由于发现了错误而需要提前返回,如果不这样,那么任务中剩下的语句就必须被放到一个else条件语句中。体会下
Task load_array(int len. Ref int array[ ]);
If(len<0)
//任务中其它代码
…
l
Verilog中由于任务中局部变量会使静态存储区,当在多个地方调用同一个任务时,不同线程之间会窜用这些局部变量。
Systemverilog中,module和program块中,缺省使用静态存储;如果想使用自动存储,需加入automatic关键词。
测试平台
l
背景
一个信号可能连接几个设计层次,如果增加一个信号,必须在多个文件中定义和连接。接口可以解决这些问题。
好处:
如果希望在接口中增加一个信号,不需要改变其他模块,如TOP模块。
使用方法:
(1)接口中去掉信号的方向类型;
(2)DUT 和测试平台中,信号列表中采用接口名,例化一个名字
注意:
因为去掉了方向类型,接口中不需要考虑方向信号,简单的接口,可以看做
是一组双向信号的集合。这些信号使用logic类型[d1]
双向信号为何可以使用logic呢?
这里的双向,只是概念上的双向,不想verilog中databus多驱动的双向。
双向信号如何做接口?
(1)仲裁器的简单接口
Interface arb_if( input bit clk);
Endinterface
DUT 使用接口:
Module arb(arb_if arbif);
…
Always @(posedge arbif.clk or negedge arbif.rst)
endmodule
(2)DUT 不采用接口,测试平台中使用接口(推荐)
l
背景:
端口的连接方式包含了方向信息,编译器依次来检查连续错误;接口使用无信号的连接方式。Modport将接口中信号分组并指定方向。
例子:
l
并非接口中每个信号都必须连接。Data总线接口中就解决不了,个人觉得?
因为data是一个双驱动
l
作用:
一旦定义了时钟块,测试平台就可以采用@arbif.cb等待时钟,而不需要描述确切的时钟信号和边沿,即使改变了时钟块中的时钟或边沿,也不需要修改测试代码
应用:
将测试平台中的信号,都放在clocking 中,并指定方向(以测试平台为参考的方向)。并且在modprot test(clocking cb,
最完整的接口:
Interface arb_if(input bit clk);
Logic[1:0] grant,request;
Logic rst;
Clocking cb @(posedge clk);
Endclocking
Modport test (clocking cb,
Modport dut (input clk, request,rst,
变化:将request 和grant移动到时钟块中去了,test中没有使用了。
l
Interface master_if(input bit clk);
endinterface
program test(master_if mif);
initial begin
@mif.cb;
$display(mif.cb.data);
@mif.cb;
Mif.cb.data <= 8’h5a;
@mif.cb;
Mif.cb.data <= ‘z;
注:
(1)interface 列表中clk 采用的是input bit clk;为什么要用bit?
(2)时钟块 clocking cb 中,一般将testbench中需要的信号,方向指定在这里;
(3)interface中信号,不一定都用logic,也可采用wire(双驱动);systemverilog
中如果采用C代码的风格(参数列表中方向和类型写一起),必须采用logic类型
(4)现在的风格,DUT 没才用clocking cb ,测试平台和DUT的时钟如何统一?
l
DUT和测试平台之间时序必须密切配合。
l
好的风格:
使用非阻塞赋值可以减少竞争。
systemverilog验证中initial 中都采用<= 赋值,而等待延迟采用@arbif.cb等待一个周期来实现。
而verilog中采用的风格时,initial 中采用 =阻塞赋值,沿时可以采用#2,等实现。
因此时钟发生器,只能放在module 中,而不能放在program中
l
测试平台可以使用initial 但不能使用always,使用always 模块不能正常工作。
原因:测试平台的执行过程是进过初始化、驱动和响应等步骤后结束仿真。
如果确实需要一个always块,可以使用initial forever 来完成。比如:在产生时钟时。
点赞 |