牛人的Systemverilog总结Word格式文档下载.docx

上传人:b****1 文档编号:1468727 上传时间:2023-04-30 格式:DOCX 页数:38 大小:32.79KB
下载 相关 举报
牛人的Systemverilog总结Word格式文档下载.docx_第1页
第1页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第2页
第2页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第3页
第3页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第4页
第4页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第5页
第5页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第6页
第6页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第7页
第7页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第8页
第8页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第9页
第9页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第10页
第10页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第11页
第11页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第12页
第12页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第13页
第13页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第14页
第14页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第15页
第15页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第16页
第16页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第17页
第17页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第18页
第18页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第19页
第19页 / 共38页
牛人的Systemverilog总结Word格式文档下载.docx_第20页
第20页 / 共38页
亲,该文档总共38页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

牛人的Systemverilog总结Word格式文档下载.docx

《牛人的Systemverilog总结Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《牛人的Systemverilog总结Word格式文档下载.docx(38页珍藏版)》请在冰点文库上搜索。

牛人的Systemverilog总结Word格式文档下载.docx

//释放空间

l队列

在队列中增加或删除元素比较方便。

l关联数组

当你需要建立一个超大容量的数组。

关联数组,存放稀疏矩阵中的值。

采用在方括号中放置数据类型的形式声明:

Bit[63:

0]assoc[bit[63:

0]];

l常量:

1)Verilog推荐使用文本宏。

好处:

全局作用范围,且可以用于位段或类型定义

缺点:

当需要局部常量时,可能引起冲突。

2)Parameter

作用范围仅限于单个module

3)Systemverilog:

参数可以在多个模块里共同使用,可以用typedef代替单调乏味的宏。

过程语句

l可以在for循环中定义变量,作用范围仅在循环内部

for(inti=0;

i<

10;

i++)

array[i]=i;

l任务、函数及void函数

1)区别:

Verilog中task和function最重要的区别是:

task可以消耗时间而函数不能。

函数中不能使用#100的延时或@的阻塞语句,也不能调用任务;

Systemverilog中函数可以调用任务,但只能在forkjoinnone生成的线程中。

2)使用:

如果有一个不消耗时间的systemverilog任务,应该把它定义成void函数;

这样它可以被任何函数或任务调用。

从最大灵活性角度考虑,所有用于调用的子程序都应该被定义成函数而非任务,以便被任何其它任务或函数调用。

(因为定义成任务,函数调用任务很有限制)

l类静态变量

作用:

1)类的静态变量,可以被这个类的对象实例所共享。

当你想使用全局变量的时候,应该先想到创建一个类的静态变量

静态变量在声明的时候初始化。

2)

类的每一个实例都需要从同一个对象获取信息。

l静态方法

当静态变量很多的时候,操作它们的代码是一个很大的程序,可以用在类中创建一个静态方法读写静态变量,但是静态方法不能读写非静态变量。

lref高级的参数类型

Ref参数传递为引用而不是复制。

Ref比input、output、inout更好用。

Functionvoidprint_checksum(constrefbit[31:

0]a[]);

1)也可以不用ref进行数组参数传递,这时数组会被复制到堆栈区,代价很高。

2)用带ref进行数组参数传递,仅仅是引用,不需要复制;

向子程序传递数组时,应尽量使用ref以获得最佳性能,如果不希望子程序改变数组的值,可以使用constref。

3)Ref参数,用ref传递变量;

可以在任务里修改变量而且,修改结果对调用它的函数可见,相对于指针的功能。

lReturn语句

增加了return语句。

Task任务由于发现了错误而需要提前返回,如果不这样,那么任务中剩下的语句就必须被放到一个else条件语句中。

体会下

Taskload_array(intlen.Refintarray[]);

If(len<

0)begin

$display(“Badlen”);

Returun;

//任务中其它代码

endtask

l局部数据存储automatic作用

Verilog中由于任务中局部变量会使静态存储区,当在多个地方调用同一个任务时,不同线程之间会窜用这些局部变量。

Systemverilog中,module和program块中,缺省使用静态存储;

如果想使用自动存储,需加入automatic关键词。

测试平台

lInterface

背景:

一个信号可能连接几个设计层次,如果增加一个信号,必须在多个文件中定义和连接。

接口可以解决这些问题。

如果希望在接口中增加一个信号,不需要改变其他模块,如TOP模块。

(1)接口中去掉信号的方向类型;

(2)DUT和测试平台中,信号列表中采用接口名,例化一个名字

注意:

因为去掉了方向类型,接口中不需要考虑方向信号,简单的接口,可以看做

是一组双向信号的集合。

这些信号使用logic类型[d1]。

双向信号为何可以使用logic呢?

这里的双向,只是概念上的双向,不想verilog中databus多驱动的双向。

双向信号如何做接口?

(1)仲裁器的简单接口

Interfacearb_if(inputbitclk);

Logic[1:

0]grant,request;

Logicrst;

Endinterface

DUT使用接口:

Modulearb(arb_ifarbif);

Always@(posedgearbif.clkornegedgearbif.rst)

endmodule

(2)DUT不采用接口,测试平台中使用接口(推荐)

DUT中源代码不需要修改,只需要再top中,将接口连接到端口上。

Moduletop;

Bitclk;

Always#2clk=~clk;

Arb_ifarbif(clk);

Arb_portal(.grant(arbif.grant),

.request(arbif.grant),

.rst(arbif.rst),

.clk(arbif.clk)

);

Testt1(arbif);

Endmodule

lModport

背景:

端口的连接方式包含了方向信息,编译器依次来检查连续错误;

接口使用无信号的连接方式。

Modport将接口中信号分组并指定方向。

例子:

l在总线设计中使用modport

并非接口中每个信号都必须连接。

Data总线接口中就解决不了,个人觉得?

因为data是一个双驱动

l时钟块

一旦定义了时钟块,测试平台就可以采用@arbif.cb等待时钟,而不需要描述确切的时钟信号和边沿,即使改变了时钟块中的时钟或边沿,也不需要修改测试代码

应用:

将测试平台中的信号,都放在clocking中,并指定方向(以测试平台为参考的方向)。

并且在modprottest(clockingcb,

最完整的接口:

Interfacearb_if(inputbitclk);

Logic[1:

Logicrst;

Clockingcb@(posedgeclk);

Outputrequest;

Inputgrant;

Endclocking

Modporttest(clockingcb,

Outputrst);

Modportdut(inputclk,request,rst,

Outputgrant);

endinterface

变化:

将request和grant移动到时钟块中去了,test中没有使用了。

l接口中的双向信号

Interfacemaster_if(inputbitclk);

//在类中为了,不使用有符号数,常用bit[]定义变量

Wire[7:

0]data;

Clockingcb@(posedgeclk);

Inoutdata;

Endclocking

ModportTEST(clockingcb);

endinterface

programtest(master_ifmif);

initialbegin

mif.cb.data<

=‘z;

@mif.cb;

$display(mif.cb.data);

//总线中读数据

Mif.cb.data<

=8’h5a;

//驱动总线

//释放总线

注:

(1)interface列表中clk采用的是inputbitclk;

为什么要用bit?

(2)时钟块clockingcb中,一般将testbench中需要的信号,方向指定在这里;

而在modprot指定test信号方向的时候,采用clockingcb。

(3)interface中信号,不一定都用logic,也可采用wire(双驱动);

systemverilog

中如果采用C代码的风格(参数列表中方向和类型写一起),必须采用logic类型

(4)现在的风格,DUT没才用clockingcb,测试平台和DUT的时钟如何统一?

l激励时序

DUT和测试平台之间时序必须密切配合。

l测试平台和设计间的竞争状态

好的风格:

使用非阻塞赋值可以减少竞争。

systemverilog验证中initial中都采用<

=赋值,而等待延迟采用@arbif.cb等待一个周期来实现。

而verilog中采用的风格时,initial中采用=阻塞赋值,沿时可以采用#2,等实现。

因此时钟发生器,只能放在module中,而不能放在program中

lProgram中不能使用always块

测试平台可以使用initial但不能使用always,使用always模块不能正常工作。

原因:

测试平台的执行过程是进过初始化、驱动和响应等步骤后结束仿真。

如果确实需要一个always块,可以使用initialforever来完成。

比如:

在产生时钟时。

l类中static变量

如果一个变量需要被其他对象所共享,如果没有OPP,就需要创建全局变量,这样会污染全局名字空间,导致你想定义局部变量,但变量对每个人都是可见的。

1)作用:

类中static变量,将被这个类的所有实例(对象)所共享,使用范围仅限于这个类。

例:

classtransaction;

Staticintcount=0;

Intid;

Endclass

Trasactiontr1,tr2;

Id不是静态变量,所以每个trasaction对象都有自己的id;

count是静态变量,所有对象只有一个count变量。

如何用?

当你打算创建一个全局变量的时候,首先考虑创建一个类的静态变量。

2)static变量的引用

句柄或类名加:

:

4)static变量的初始化

static变量通常在声明时初始化。

不能在构造函数中初始化,因为每一个新的对象都会调用构造函数。

l静态句柄:

当类的每一个对象,都需要从同一个对象(另一个类)中获取信息的时候。

如果定义成非静态句柄,则每个对象都会有一份copy,造成内存浪费。

当使用更多静态变量的时候,操作他们的代码会很长。

可以在类中创建一个静态方法用于读写静态变量。

systemverilog不允许,静态方法读写非静态变量。

l类之外的方法

解决类太长的问题。

类最好控制在一页内,如果方法很都很长。

lThis

如果在类很深的底层作用域,却想引用类一级的对象。

在构造函数中最常见。

this指向类一级变量

l如何做类,类做多大?

上限:

类不能太大

当类中存在多处相同的代码,你需要将这段代码做成当前类的一个成员函数或父类的成员函数。

下限:

类不能太小

类太小,增加了层次。

方法:

如果一个小类只被例化了一次,可以将它合并到父类中去。

l动态对象

概念区分:

方法中修改对象和修改句柄

修改对象——将对象的变量重新赋值。

修改句柄——在任务中new()对象。

1)当你将对象传递给方法

句柄,new()后变成对象,在将其作为参数传递给方法。

实质和作用:

传递的是句柄。

这个方法可以读取对象中的值;

也以改变对象中的值

2)修改标量变量的值

在方法的参数中,前面加ref;

(用ref传递,ref传递的是变量的地址)。

方法可以修改变量的值,并将修改的值,传递给主程序。

引申:

方法可以改变对象,即使没有使用ref修饰句柄。

因为传递的是句柄,句柄是地址。

不要将句柄和对象混为一谈,如果传递的是对象,对象是单向的,那方法以外也不能传递回来。

可以这样理解吧。

读写对象中的值:

Tasktransmit(Transcationt);

Cbbus.rx_data<

=t.data;

t.stats.startT=$time;

//在任务中,改变了对象

endtask

trancationt;

initilalbeign

t=new();

t.addr=42;

transmit(t);

end

既然传递的是句柄,那数据就没传过去,如何读取值?

答:

主程序中new()创建了一个对象,而句柄是指向对象的指针,传递的是句柄,transmit中也指向了对象,所以transmit中可以读写对象。

3)在任务中修改句柄

在方法中,参数为句柄,前面加ref。

可以在方法中new()对象,并将初始化放在方法中;

在主程序中仅仅调用。

正确的事物发生器,参数是带ref的句柄

Functionvoidcreate(reftransactiontr)

Endfunction

方法的参数是句柄,句柄前有ref和没ref的差别:

没ref,在方法中不能new()该句柄的对象,因为没ref,句柄是不能传递到主程序的;

有ref,可以在方法中new()该句柄的对象。

原因:

没ref传递的是句柄,不能修改句柄,有ref,传递的是句柄的地址,可以修改句柄。

Functionvoidcreate(Transcationtr)

tr=new();

不正确

tr.addr=42;

Transcationt;

Initialbegin

Create(t);

$diasplay(t.addr);

End

l程序中修改对象

应该在循环中,new()多个对象,而不是先new()对象再循环发送事物。

创建多个对象

正确产生器,创建多个对象:

Taskgenerator(intn);

Transactiont;

Repeat(n)begin

t=new();

t.addr=$random();

transmit(t);

将new()放在循环内,这样创建了许多对象。

l对象的复制

目的:

防止对象的方法修改原始对象的值。

或在一个发生器中保留约束。

分两种情况,类中不包含其他类的句柄和包含

1)使用new复制一个对象——简易复制(shallowcopy)

Transactionsrc,dst;

Src=new()//

dst=newsrc//复制

局限:

如果类中包含一个指向另外一个类的句柄,那么只有最高一级的对象被new复制,下层的对象都不会被复制。

会出现意想不到的错误。

当前类中变量和句柄被复制,这样两个对象,都有指向另外一个类的对象statistic(会带来意想不到的错误),但是statistic没有被复制。

如果其中一个transaction对象,修改了statistic对象值,会影响到另一个transaction看到static的值。

2)简单的复制函数

如何实现:

Copy函数一般放在类内部,函数名为该类的一个句柄,copy函数中new()对象。

类中不包含其他类。

3)深层的复制函数——深层copy

解决类中包含另外一个类,copy带来的问题。

实现:

在copy函数中,将调用另一个类的copy函数,赋值给该句柄;

同时需要为statistic类和层次结构中每一个类增加一个copy()方法;

copy函数的ID域也要保持一致,copy函数,copy本类,所以ID也要++.

Copy.stats=stats.copy();

Id=count++;

约束

l约束块中,只能包含表达式,不能赋值。

1)dist权重分布

dist带有一个值的列表及相应的权重,中间用:

=或:

/分开。

值或权重可以是常量或变量。

权重的和不必是100.

:

=表示范围内,每一个值的权重是相同的;

/表示范围内,权重要均匀分布

2)Inside

产生一个值的集合,在值的集合中取随机值时,机会相等。

3)在集合中使用数组

l条件约束

Systemverilog支持两种关系操作–>

和if—else

—>

可产生和case效果类似的语句块,可以用于枚举类型的表达式。

l双向约束

l控制多个约束块

可以打开或关闭某个约束

可以使用内建的Handle.constraint.constraint_mode()打开或关闭。

l内嵌约束

很多测试只会在代码的一个地方随机化对象,但是约束越来越复杂时,

Systemverilog可以使用randomizedwith来增加额外的约束,这和在类里增加的约束是等效的。

lPre_randomize和post_randomize函数

有时候需要再调用randomize()之前或之后立即执行一些操作。

随机化前:

设置类里的一些非随机变量(如上下限、权重),

随机化后:

计算数据的误差矫正值。

l约束的技巧

1)约束中使用变量

2)使用非随机值

如果一套约束在已产生了几乎所有想要的激励向量,但还缺少几种。

可以使用rand_mode把这些变量设置为非随机变量。

l数组约束

Systemverilog可以用foreach对数组中的每一个元素进行约束。

线程及线程间的通信

l测试平台使用许多并发执行的线程。

测试平台隶属于程序块。

Systemverilog引入两种新的创建线程的方法—fork…join_none和fork…join_any

1)使用fork…join_none来产生线程

在调度其内部语句时,父线程继续执行。

2)使用fork…join_any实现线程同步

在调度块内语句,当第一个语句执行完,父线程才继续执行。

l动态线程

Systemverilog中可以动态创建线程。

用法:

fork…join_none放在了任务中,而不是包含两个线程。

主程序中有连个线程:

发送和检测线程。

但是不能同时启动,发送事物后,才能检测,否则还未产生数据,就开始检测;

但是检测又不能阻塞下一次发送事物的线程。

所以fork…join_none放在了检测task任务(后作用的线程中)中,

测试平台产生随机事物并发送到DUT中,DUT把事物返回到测试平台。

测试平台必须等到事物完成,但同时不希望停止随机事物的发送。

Programautomatictest(bus_ifc.Tbbus);

Taskcheck_trans(Transactiontr);

Fork

Begin

Wait(bus.cb.addr==tr.addr);

End

Join_noe

Endtask

Repreat(10)begin

Tr=new();

Assert.(tr.randomize());

//把事物发送到DUT中

Transmit(tr);

//等待DUT的回复

Check_trans(tr);

#100;

endprogram

l并发线程中务必使用自动变量来保持数值。

l#0延迟,使得当前线程必须等到fork…join_none语句中产生的线程执行完后,才得以运行。

l停止线程

1)停止单个线程

使用fork..join_any后加disable。

3)停止多个线程

Disablefork能停止从当前线程中衍生出来得所有子线程。

应该使用fork..join把目标代码包含起来,以限制Disablefork的作用范围。

l事件

Verilog中当一个线程在一个事件上发生阻塞的同时,正好另一个线程触发了这个事件,则竞争就出现了。

如果触发线程先于阻塞线程,则触发无效(触发是一个零宽度的脉冲)。

解决方法:

Systemverilog引入了triggered()函数,用于检测某个事件是否已被触发过,包括正在触发。

线程可以等待这个结果,而不用在@操作符上阻塞。

Evente1,e2;

->

e1;

@e2;

e2;

@e1;

上面的代码,假设先执行第一个块,再执行第二个块。

第一个块会阻塞在@e2(阻塞先执行),直到e2触发,再运行(触发后执行);

在执行第二个块时,会阻塞在@e1,但是e1已经触发(触发先执行,阻塞后执行,触发是个零宽度的脉冲,会错过第一个事件而锁住)

用wait(e1.triggered())来代替阻塞@el,如果先触发,也可以执行。

l等待多个事件

最好的办法是:

采用线程计数器来等待多个线程。

l旗语

Get()可以获取一个或多个钥匙,put()可以返回一个或多个钥匙。

Try_get()获取一个旗语而不被阻塞。

l信箱

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2