SystemVerilog基本语法总结(中)

Systemverilog 语法总结(中)

上一个博客分享了SV基本的概念,这一博客继续分享,等下一个博客分享一个公司的验证的笔试题目。

l 事件

背景:

Verilog中当一个线程在一个事件上发生阻塞的同时,正好另一个线程触发了这个事件,则竞争就出现了。如果触发线程先于阻塞线程,则触发无效(触发是一个零宽度的脉冲)。

解决方法:

Systemverilog 引入了triggered()函数,用于检测某个事件是否已被触发过,包括正在触发。线程可以等待这个结果,而不用在@操作符上阻塞。

例子:

event e1,e2;

Initial begin

->e1;

@e2;

end

Initial begin

->e2;

@e1;

end

上面的代码,假设先执行第一个块,再执行第二个块。第一个块会阻塞在@e2(阻塞先执行),直到e2触发,再运行(触发后执行);在执行第二个块时,会阻塞在@e1,但是e1已经触发(触发先执行,阻塞后执行,触发是个零宽度的脉冲,会错过第一个事件而锁住)

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

l 等待多个事件

最好的办法是:采用线程计数器来等待多个线程。

l 旗语

get()可以获取一个或多个钥匙,put()可以返回一个或多个钥匙。try_get()获取一个旗语而不被阻塞。

l 信箱

背景:如何在两个线程中传递信息?考虑发生器需要创建很多事物并传递给驱动器的情况。

问题:如果使用发生器的线程去调用驱动器的任务。这样,发生器需要知道驱动器的层次化路径(类的层次化),降低了代码的可重用性;还迫使发生器和驱动器同一速率运行,当一个发生器需控制多个驱动器时会发生同步问题。

解决办法:把驱动器和发生器当成各个处理事物的对象,之间通过信道交换数据。信道允许驱动器和发生器异步操作;引入问题:你可能倾向于仅仅使用一个共享的数据或队列,但这样,编写实现线程间的读写和阻塞代码会很困难。解决办法:可以使用systemverilog中的信箱。把信箱看出一个具有源端和收端的FIFO.

操作:

1)信箱的容量可以指定,new(size),size限制信箱中的条目,size为0,或没指定,则信箱是无限大。

2)put()放数据,get()可以移出数据。peek()可以获取信箱中数据的copy而不移出。

3)信箱中可以放句柄,而不是对象。

漏洞:在循环外只创建一个对象,然后使用循环对对象随机化,信箱中是句柄,最终得到的是一个含有多个句柄的信箱,多个句柄都指向同一个对象。

解决办法:在循环中,创建多个对象。

l 异步线程间使用信箱

背景:

很多情况下,由信箱连接的两个线程应该步调一致,这样生产方 才不至于跑到消费方前。 好处:最好层的generator需要等待低层的数据发完后才能结束。测试平台能精确知道所有激励发出去的时间。

两种情况

两个线程同步,需要额外的握手信号。否则,出现生产方运行到结束,消费方还启动。

信箱容量为1,两个线程同步

因阻塞,连个线程不需要握手信箱

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 如果触发事件,先于等待事件。会等不到事件,因为(事件触发,是一个零宽度的脉冲)

OOP的高级编程技巧

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)OOP规则指出:基类的句柄,也可以指向扩展类的对象。(好好体会)

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()和env.build()区别

env.new()仅仅new()函数

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

l $cast 作类型向下转换

背景:基类句柄可以指向扩展类对象,不需要额外的代码; 扩展类句柄指向基类对象,一般情况下会出错,但有时候是可以的,前提是基类句柄指向了它的一个扩展类对象。

作用:扩展类句柄指向基类对象时,使用$cast()函数。在非法的情况下,不会编译报错,会返回了一个0.

$cast做任务使用时,systemverilog会在运行时,检查源对象类型和目的对象类型不匹配,会报错;

cast做函数使用时,运行时,仍做类型检查,在不匹配时,不会报错,cast 做函数使用时,运行时,仍做类型检查,在不匹配时,不会报错,cast做函数使用时,运行时,仍做类型检查,在不匹配时,不会报错,函数返回0.
前面所述:基类句柄可以指向任何它的扩展类的对象、

1) 基类句柄指向扩展类对象——出现情况:修改蓝图,不改过多代码,增加功能

Transaction tr; //基类句柄

BadTr bad; //扩展类句柄

Bad = new();

Tr = bad; // 基类句柄指向扩展类对象

tr.display; //掉用的是扩展类的方法

2) 扩展类句柄指向基类对象——出现情况:基类virtual 方法copy函数,它的继承类中copy函数

将基类句柄赋值给扩展类句柄,使扩展类句柄指向基类对象,一般编译器会出错,不能运行,所以非常小心;只有基类句柄指向扩展类对象时,再将扩展类句柄指向基类对象时,不出错。为了检测基类句柄是否指向了扩展对象,并且不让编译器报错,可以使用$cast()函数检测。

当把扩展类句柄指向基类对象时,发生什么?

Tr= new();

Bad = tr; //扩展类句柄指向基类句柄

上述会发生错误,编译不会被通过。因为有些属性在基类中不存在;但是扩展类句柄指向基类句柄不总是非法的(见下面代码,是可以的),当基类句柄指向一个扩展类对象时是允许的。

Transcation tr;

BadTr bad,bad2;

Bad= new();

Tr = bad;          //基类句柄指向扩展类对象

$cast(bad2,tr);      //扩展类句柄指向基类对象

if(!$cast(bad2,tr);

$display(“cannot assign tr to bad2”);

$display(bad2.bad_crc);

l 句柄类型和对象类型差异(书中翻译的不准,type of handdle 和 object)

个人理解:

Transaction tr; 句柄tr类型是transaction

句柄类型:关键字

对象类型:类中成员的类型差异

l 虚方法和多态

多态:多个程序使用一个共同的名字的现象。(虚方法的重写与实现)

多态解决问题:计算机建构面临的一个问题。让物理内存很小的情况下,让处理器能够对很大的地址空间寻址。针对这个问题引入了虚拟内存。

虚拟方法继承劣势:

基类使用了虚拟方法,扩展类也必须使用相同的“签名”,扩展类中虚拟子程序不能增加或删除参数,这意味着必须提前做好规划。

l 对象复制

1) 因为是virtual 函数,扩展类中copy方法也必须是transaction型的,

但是要copy的是badtr类型的,所以要new一个bad

带有copy 的事物基类。

class transaction ;

rand bit[31:0] src,dst,data[8];

bit[31:0] crc;

virtual function transaction copy ();

copy   = new();

copy.src = src;

copy.dst = dst;

copy.data = data;

copy.crc  = crc;

endfunction

endclass

带有copy的扩展类

calss badtr extends transaction

rand bit bad_crc;

virtual function badtr copy(); //错误

virtual function transaction copy();

Badtr bad;

Bad = new();

Bad.src = src;

bad.dst = dst;

bad.data = data;

bad.crc = crc;

Bad.bad_crc = bad_crc;

return bad;

endfunction

endclass

2)优化途径一,创建一个独立的函数copy_data,这样每个类只负责copy其局部变量,即扩展类中的copy函数用super.copy_data(tr),代替了基类中变量的复制。代码的重用性提高。P8.22

$cast(bad,tr); //扩展类句柄指向基类句柄

使用的情况: 因为virtual 函数,在继承中,虚拟函数必须和基类中名称和参数也一致。这样扩展类中copy_data函数参数仍然是transaction类型的tr,这样出现了参数是基类句柄,但是copy_data函数内要作的确实扩展类的成员,就要将基类句柄参数赋值给扩展类句柄,

要将扩展类badtr类型的数据返回,所以必须用$cast(bad,tr)。

优化途径二,最好的。前面的copy子程序都会创建一个新对象,改进的一种方法就是指定复制对象的存放地址。

virtual function transaction copy(transaction to =null);

if(to == null)

copy = new();

else

copy = to;

copy_data(copy);

endfunction

l 抽象类和纯虚方法

背景:验证的目标之一是创建多个项目共享的代码。

目的:systemverilog 有两种方法创建共享的基类:抽象类和纯虚方法

Virtual class (抽象类):可以被扩展但是不能被直接例化。

Pure virtual function(纯虚方法):没有实体的方法原型,相当于一个声明。

1)  由抽象类扩展而来的类,只有在所以的虚拟方法都有实体的时候才能被例化。

2)  纯虚方法只能在抽象类中定义。

3) 抽象类中,纯虚方法是没实体的,非纯虚方法最好也不写实体。

l 回调

背景:测试平台目的:创建一个不做任何修改就能在所有测试中使用的验证环境。要做到这点的关键是测试平台使用钩子,(什么是钩子?)钩子作用,在不修改原始类的情况下注入新的代码。采用virtual 方法,也可以在扩展类中覆盖基类方法,但是需要重复原方法的所有代码,并且它的修改将传播到它的所有扩展类中。

作用:回调就是一个钩子,在不修改原始类的情况下注入新的代码。

实现:回调任务在顶层中创建,在最低级即驱动器中调用。这样驱动器不需要知道测试的任何信息,它只需要使用一个可以在测试中扩展的通用类。

1) 使用回调注入干扰

回调的一个常见用法就是注入干扰,例如引入一个错误或者延迟。下面测试平台使用回调对象,随机地丢弃数据包。

扩展类是如何作用的?在扩展的回调类中注入错误,如何在驱动器中作用的?

关键是数据队列的作用,驱动器中使用了,回调基类的数据队列

回调基类是抽象类,在扩展的回调类中加入错误注入,而drive驱动类中,是回调基类的数据队列,在环境中将扩展类句柄让入驱动器类,回调基类的数据队列中。

begin // Create error injection callback

Driver_cbs_drop dcd = new();

env.drv.cbs.push_back(dcd); // Put into driver

end

与前面扩展类作用的差异?

前面代码,要使扩展类中增加代码,需要使基类句柄指向扩展类句柄。

l 驱动器类

下面的代码如何解释

2)回调也可以想scoreboard 发送数据或收集功能覆盖率。

优点:

你可能想过将scoreboard和功能覆盖数据组置于一个事物处理器中,通过邮箱连接到测试平台中,这是一种笨拙的方法。原因如下:测试平台组件几乎都是被动和异步的,组件只有在测试平台给他数据的时候才被唤醒,而且不会主动地向下游事物处理器传递信息。

麻烦:

a)这样一个需要同时监视多个邮箱的事物处理器复杂了;

b)你可能在多个地方采集数据,但是事物处理器设计用来处理单个数据源回调

原文地址:https://www.cnblogs.com/zhangxianhe/p/11797626.html

时间: 2024-10-17 15:20:44

SystemVerilog基本语法总结(中)的相关文章

Apple Swift 中文教程 快速参考 基本语法 更新中...

总的来说,语法有java的味道,也有python的味道,还有swift自己的味道. 有些语法还是挺不伦不类的,不太好理解,即使你有几年的java或python经验,也不见得有些语法你能很轻松的看明白. 有些语法特性很好,很个性,但有些语法个人感觉乱,特性多,注意点多,还不太好理解. 慢慢学习吧... ================================================================= 值类型 Int Double Bool String 用双引号"&

.Vue 文件 ES6 语法 webstorm 中的一个识别Bug

webstorm 2017 版本中即使安装了vue template file 设置了 js 语言为 es6 语法仍旧会出现识别不了划线的情况,苦寻很久,最后解决方式如下 <script type="text-ecmascript-6"> export default{ data(){ return{ msg: '' } }, components:{ } } </script> 添加  type 类型 指明为: text-ecmascript-6  亲测有效.

详解es6 class语法糖中constructor方法和super的作用

大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScript 6中引入了类的特性.本文将详细介绍ES6中的类,ES6 的 class 属于一种“语法糖”,所以只是写法更加优雅,更加像面对对象的编程,其思想和 ES5 是一致的. function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toSt

SystemVerilog基本语法总结(下)

2018年IC设计企业笔试题解析-(验证方向) 1.请简述:定宽数组,动态数组,关联数组,队列四种数据类型的各自特点.解析:(1)定宽数组:其宽度在声明的时候就指定了,故其宽度在编译时就确定了.(2)动态数组:可以在仿真时分配空间或者调整宽度,这样在仿真中就可以使用最小的存储量.在声明时,其下标为空[ ],使用new[ ]操作符来分配空间.(3)关联数组:SystemVerilog提供关联数组来保存稀疏矩阵的元素.一般用在对非常大的空间进行寻址,当对一个非常大的地址空间进行寻址时,SystemV

shell语法—shell 中特殊的变量名

现在我们新建一个demo1.sh 脚本文件,代码如图 chomd +x demo1.sh   给脚本执行权限. 我们在命令行输入 让我们来看下执行的结果 大家可以自己写一个脚本执行 试试,有什么问题,欢迎大家指出,让我们共同进步!

systemverilog中module与program的区别

我们知道,verilog语法标准中是没有program的,program是systemverilog语法标准新增的内容. 那么,为什么要新增一个program呢?主要考量是基于电路的竞争与冒险. 为避免仿真和设计竞争问题(race condition),systemverilog中引入了program的概念. 所有与设计相关的线程在module中执行,而与验证有关的线程则在program中运行.在仿真过程中,这两种线程运行在不同的时间步(time step),从而解决了竞争问题. module与

C# 7.0中可能出现的语法

今天在MSDN上看到的微软关于微软关于C# 7.0特性的Work List,主要特性如下: Tuple增强 Tuple的可读性一直不是很好, 很多时候宁愿新写一个类也不使用Item1, Item2这种不知何物的属性: ????public Tuple<int, int> Tally(IEnumerable<int> values) { ... }????var t = Tally(myValues);????Console.WriteLine($"Sum: {t.Item

Verilog与SystemVerilog编程陷阱:如何避免101个常犯的编码错误

这篇是计算机类的优质预售推荐>>>><Verilog与SystemVerilog编程陷阱:如何避免101个常犯的编码错误> 编辑推荐 纠错式学习,从"陷阱"中学习编程,加深对语言本身的理解. 逆向式学习,从错误中学习避免错误的方法,让读者写出更好的代码. 案例式学习,将101个"陷阱"分类汇编,以针对性案例引导读者掌握编程要点. 译者序 译者序 随着电子设计自动化(Electronic Design Automation,EDA)

【Java学习笔记之三】java中的变量和常量

变量和常量 在程序中存在大量的数据来代表程序的状态,其中有些数据在程序的运行过程中值会发生改变,有些数据在程序运行过程中值不能发生改变,这些数据在程序中分别被叫做变量和常量. 在实际的程序中,可以根据数据在程序运行中是否发生改变,来选择应该是使用变量代表还是常量代表. 变量 变量代表程序的状态.程序通过改变变量的值来改变整个程序的状态,或者说得更大一些,也就是实现程序的功能逻辑.     为了方便的引用变量的值,在程序中需要为变量设定一个名称,这就是变量名.     由于Java语言是一种强类型