System Verilog笔记总结(4)

2020-02-21 22:21

3) 容量不为1,线程间同步

需要使用握手信号,以使producer不超前于consumer;如果consumer超前于prodecer会阻塞。

解决办法

1) 使用定容信箱和peek实现线程同步:(比较好)

消费方:consumer 使用信箱方法peek()获取信箱里的数据的copy而不将其移出,当consumer处理完数据后,便使用get()移出数据。

特点:信箱容量定义为1,不需要握手信号。

Calss consumer Repeat(n)begin Mbx.peek(i);

$display(―consumer:after get( )‖,i); Mbx.get(i); End endcalss

如果直接使用get()替代peek(),那么事务会被立刻移出,这样可能会在consumer完成事务前,producer生成新的数据。

-

2)使用信箱和事件实现线程同步

使用边沿敏感的阻塞语句@handshake 代替电平触发wait(handshake.triggered())。 因为:线程中任务run()使用循环,事件阻塞只能使用@handshake。

局限:如果遇到producer线程的阻塞和consumer线程的触发同时发生,则可能出现次序上的问题。

3)使用两个信箱实现线程同步

使用另一个信箱把consumer的完成信息发回给producer。

目的:在producer线程中,处理完事物后,用一个get()来阻塞。 特点:信箱容量大于1. Maibox mbx,rtn;

Class prodecer

For(int i=0; i<4;i++) begin …. Mbx.put(i); Rtn.get(i);

… End Endclass Class consumer Repeat(3) begin …. Mbx.get(i); Rtn.put(-i); … End Endclass

说明:信箱的构造函数中Mbx =new();Rtn =new(),信箱容量为无穷大。如何实现同步? 虽然信箱容量为无穷大,producer线程发完一个数据后遇到get()会阻塞,不能放入第二个数据;等到consumer得到第一个数据并且处理完后,通过另一个信箱返回一个数据,producer才继续放第二个数据。

因为get()得到数据后,将信箱中数据取出。表象:信箱容量定义为无穷大,但是实际上也是producer放一个数据,consumer取一个数据;然后producer再放第二个数据,依次类推。

这样确保producer不会超前于consumer线程,而将数据都写入信箱。

4) 其他的同步技术

通过变量或旗语阻塞也可以实现握手。事件是最简单的结构,其次是通过变量阻塞。旗语相当于第2个信箱,但是没有交换信息。Systemverilog中的信箱比其他技术要差,原因是无法在producer放入第一个事务时,让它阻塞。Producer一直比consumer提前一个事务的时间。

l Wait(handshake.triggered())和@handshake 使用范围 1) Wait(handshake.triggered()),用于等待一个事件; 2) 循环中等待事件,只能用@handshake

3) 两个线程的同步,一般任务run()使用循环,所以只能使用@handshake。

注意事项:

1) 在循环中,等待事件不能用Wait(handshake.triggered()),因为如果事件触发一次,wait()语句一直为真,进入不断的循环。下一次循环中,不会阻塞。

2) @handshake 如果触发事件,先于等待事件。会等不到事件,因为(事件触发,是一个零宽度的脉冲)

OPP的高级编程技巧

l 继承 背景:

为总线事务增加一个错误功能并带可变延时的复杂类。方法如下:

1) 使用合成,即在类中例化另一个类型的类。有时候很难将功能分成独立的部分。如果使用合成,则需要为正确和错误事务分别创建不同的类,正确类的测试平台需要重写以处理错误类的对象。

2)使用扩展类 作用:

当需要增加事务,而对现有的测试代码修改越少越好,。例如增加错误注入功能。 扩展类和类合成区别:

扩展类解决,增加新事务,使用类合成中,大量修改代码的麻烦。 如何使用:

扩展类共享基类的变量和子程序。

1)基本类中的方法,需标记为virtual,这样扩展类中才可以重新定义。扩展类中函数,和基类中函数名一样时,通过supper.函数名,调用基类中函数。Systemverilog中不允许supper.supper.new方式经行多层调用。

2)如果基类构造函数new()有参数,那么扩展类,必须有一个构造函数,并在构造函数的第一行调用基类的构造函数。

Class basel

Function new(input int var); this.var = var; endfunction endclass

class extended extends basel function new(input int var); super.new(var); endfunction endclass

3)OPP规则指出:基类的句柄,也可以指向扩展类的对象。(好好体会)

l 蓝图模式

1)背景:一个简单的发生器,通过信箱将数据传递给驱动器。 class generator mailbox gen2drv; transaction tr;

function new(input mailbox gen2drv) this.gen2drv = gen2drv; endfunction task run; forever begin tr = new(); assert(tr.randmize);

gen2drv.put(tr); //mail.put(x) end endtask endclass

存在问题:这个例子在循环内部创建事务对象,而不是在循环外部,避免了测试平台常见的错误。New()放在循环外部,错误原因是,mailbox中放入的是句柄,而不能是对象,所有的句柄都指向同一个对象。(1)任务Run创建了一个事物并立即随机化,意味着事务使用了默认的所有约束。要修改,必须要修改transaction类。(2)无法使用扩展

解决办法:将tr的创建和初始化分开,使用蓝图模式。

另一个问题:如果简单的把创建和初始化分开,而放在循环外部,而避免测试平台错误(P200),如何解决?蓝图模式如何解决

2)蓝图模式概念:

首先构建一个对象蓝图(金属模),然后修改它的约束,甚至可以用扩展对象替换它,随机化这个蓝图时,就得到想赋予的随机值;然后复制这个对象,将copy发给下游。 蓝图:是一个钩子,允许你改变发生器类的行为而无需修改其类代码。蓝图对象在一个地方构建(new()),在另一个地方(任务run)使用

3)P200与P221相对比分析:重要

蓝图模式,也就比new()在循环外地generator多了一个copy函数。问题(1)蓝图模式,new()在循环外,也只有一个对象,而mailbox中放入的只能是句柄,如何解决常见的平台错误?

因为copy,是对象的复制,而不是句柄的复制。这样蓝图模式只有一个句柄,但是随机化后,copy,相当于再循环中创建了许多对象。而测试平台常见错误的本质是,只创建了一个对象。这样就避免了问题。

(2)蓝图模式下,因为只有一个ID号,那么任务run循环中,下发了许多数据,这些只有一个ID号了?

因为copy是对象的复制,所以在copy中ID号也会增加。下发的每个数据,都有各自的ID号。

l 使用扩展的transaction

为了注入错误,需要将蓝图对象transaction变成Badtransaction(改变蓝图)。必须在环境的创建和运行阶段之间完成这个操作。注意:所有的badTr引用都在这一个文件中,这样就不需要改变environment类或generator类。

Env.build(); Begin

Badtr bad = new(); Env.gen.blueprint = bad; End Env.run

目的是:将一个对象取代另一个对象。New()后都是对象了,将对象赋值给对象,这是什么写法?不是复制呀?复制本质是将一个句柄指向一个对象。

解释:上述是句柄的复制,将扩展类句柄bad赋值给基类句柄blueprint,这样基类句柄指向扩展类对象,后面的代码调用的时候,就直接指向扩展类bad了,改变了蓝图。

l Env.new()和nev.build()区别 Env.new()仅仅new()函数

nev.build()是将各个模块new(),并传达一些参数,通过这些参数将环境的各个模块,连接起来。P213

l $cast 作类型向下转换


System Verilog笔记总结(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:关于成立信访稳定工作领导小组的通知

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: