【学习笔记】SystemVerilog 绿皮书100个知识点

一、二章 验证导论和数据类型**

1.随机测试与定向测试:随机测试前期慢,但总时间短,在验证的最后阶段,一般需要用定向测试

2.覆盖率收敛:受约束的随机测试→使用不同种子多次运行→功能覆盖率→识别覆盖盲区→最少的代码修改(添加约束)→受约束的随机测试→使用不同种子多次运行→功能覆盖率→识别覆盖盲区→最少的代码修改(定向测试案例)→功能覆盖率

3.logic类型除了作为变量,还可以被连续赋值、门单元和模块驱动,但只能有一个驱动,故双向总线需要定义成线网类型。

4.格式符%0t和参数 t i m e 可以打印出当前的仿真时间,打印格式在 time可以打印出当前的仿真时间,打印格式在 time可以打印出当前的仿真时间,打印格式在timeformat()子程序中指定

5.缺省值: logic-X; bit/int-0; wire-Z

6.比较操作符 ?: (a)?“b”:“c” a成立则b,否则c

7.动态数组:a[],开始是空的,必须调用new[5]操作符来分配空间。只要数据类型相同,定宽数组和动态数组都可以相互赋值,定宽数组复制到动态数组时会调用new。

8.常量数组和队列初始化:数组a[3]='{1,2,3} 队列a[$]={1,2,3} 不要对队列使用构造函数new[]

9.数组缩减方法:sum product and or xor

10.数组定位方法:返回值是一个队列,min max unique find

11.数组排序:reverse反 sort小到大 rsort大到小 shuffle乱

12.后缀:_t,typedef用户自定义类型; _u,union联合类型; _s,struct自定义类型;_e,enum枚举类型

13.流操作符: << or >>用在赋值表达式的右边,后边带表达式、结构或数组,把其后的数据打包成一个比特流。>>从左到右,<<从右到左。

14.枚举值缺省为从0开始递增的整数。枚举类型缺省类型为int,缺省值为0

15.字符串:getc(N)返回位置N上的字节;toupper返回一个所有字符大写的字符串;tolowerr小写;{}连接字符串;putc(M,C)把字节C写到字符串M位上;函数substr(start,end)提取位置s到e的所有字符。

三章 过程语句与子程序

16.%输出形式: %4d,指定宽度;%d 以十进制格式输出;%b 二进制;%o 八进制;%h 十六进制;%e指数输出实数(real型);%f 十进制数输出实数(real型);%g 以十进制数或者以指数表示方式输出实数;%s 以字符串格式输出;%c ASCII码格式输出;%m 输出层次名;%v 输出线网类型变量的强度;%t 当前时间格式输出

17.任务task和函数function:Verilog 任务可以消耗时间,函数不能;函数不能有时延语句或者阻塞语句,也不能调用任务;函数必须有返回值,而且返回值必须被使用; SV 允许函数调用任务,但只能在fork…join_none语句生成的线程中调用。 注:不消耗时间的任务,从最大灵活性考虑,都应该被定义为void函数。

18.VCS允许不使用void语法的情况下忽略返回值。

19.向子程序传递数组时应尽量使用ref以获取最佳性能,如果不想子程序改变数组的值,可以使用const ref类型。

20.使用-1(或其他越界值)作为缺省值,对于获知调用时是否有指定值,不失为一个好方法。

21.SV中,module和program块中的子程序缺省情况下仍然使用静态存储,如果使用自动存储,需要使用automatic关键词。

22. t i m e 的返回值是按照时间精度要求舍入的整数,不带小数; time的返回值是按照时间精度要求舍入的整数,不带小数; time的返回值是按照时间精度要求舍入的整数,不带小数;realtime返回值是带小数部分的完整整数。

四章 连接设计和测试平台**

23.interface接口信号必须用非阻塞赋值来驱动

24.使用接口必须确保在你的模块和程序块之外声明接口变量; 在接口中不能例化模块,但可以例化其他接口

25.modport结构:modport DUT/MONITOR/TEST, 接口中使用modport结构能够将信号分组并指定方向,modport只需要在模块首部指明,模块例化时不需要,但是若需要多次例化一个模块,分别连接不同的modport,即有不同的接口信号组,则例化模块时需要指明modport。 注:一般在使用接口信号的程序和模块中使用modport名,而不是顶层模块中。

26.使用接口的优劣势;优势:便于设计重用,两个块之间有两个以上的信号连接,并使用特定的协议通讯时,若信号组重复出现,则用虚拟接口;替代模块和程序中的反复声明,减少了错误;增加一个新信号,只需要声明一次,不需要在更高层次的模块声明;modport允许一个模块方便地将接口的一系列信号捆绑到一起,也可以指定方向。 劣势:点对点的连接,modport的信号描述一样冗长;必须同时使用信号名和接口名;若连接的俩模块是不被重用的专用协议,那用接口连接的工作量会比端口连接大;连接俩个不同的接口比较困难,一个bus_if可能包含现有接口的所有信号还新增了信号,需要拆分出独立信号并驱动。

27.驱动logic、wire信号:如果在接口中使用过程赋值语句驱动一个异步信号,那么该信号必须是logic类型(wire连续赋值语句驱动) 只能用assign驱动wire信号 。 但当一个信号有多个元件驱动,需要用wire类型。

28.避免使用#0#1解决时序问题 #0:不可避免的存在多个线程都想在最后执行,会引起不确定性,仿真结果不可测。#1:各模块的时间精度可能不一样,同样引起不确定性。

29.SV时间片 Active,RTL、门级代码、时钟发生器;Observed,执行断言;Reactive,执行测试平台;Postponed,活动都结束后的只读时间段采样信号。Observed和Reactive区域事件可以触发本时钟周期内的Active区域中进一步设计事件。

30.仿真的结束,仅有一个程序块时,完成所有initial块中最后一个语句时仿真结束,即便还有模块或线程在运行。所以,测试结束的时候无需关闭所有的monitor和driver。若存在多个程序块,仿真在最后一个结束时结束。$exit提前中断任何一个程序块 $finish结束仿真.

31.时钟块的默认时序是在#1step延时之后采样输入信号;在#0延时之后驱动输出信号。

32.当从时钟块读取一个信号时,是在时钟沿之前得到采样值,例如postponed区域

33.在时钟块使用modport时,任何同步接口信号都必须加上接口名(arbif)和时钟块名(cb) arbif.cb.request

34.在时钟块当中应使用同步驱动,即“<=”操作符,这是因为信号在赋值后不会立即改变。有效时钟沿驱动,立即传输,若时钟沿之后则需要下一个有效沿。

35.驱动延时,如果想在驱动一个信号前等俩个时钟周期,可以使用“repeat(2) @bus.cb;” 或将时钟周期延时##2. 但后一种方式只能在时钟块里作为驱动信号的前缀来使用,因为需要知道使用哪个时钟来做延时。

36.SV驱动接口中的异步双向信号:使用一个跨模块引用和连续赋值语句;虚接口

37.为什么program中不允许使用always块? program中最后initial块结束仿真就结束了,但是加了always块之后,它将永远不会结束,不得不用$exit来退出。(always块可能从仿真开始就会在每个时钟上升沿执行)

38.0时刻时钟边沿,时钟边沿尽量使用阻塞赋值产生,如果确实需要0时刻产生一个时钟边沿,那么可以使用非阻塞赋值设置初始值。

39.将时钟发生器放在一个模块中可以避免竞争状态。

40.端口列表中的接口必须连接,缺少了必要的modport,编译器不能编译端口。

41.四个输出消息的函数: i n f o ; info; info;warning; e r r o r 和 error和 errorfatal

42.并发断言,可当作一个连续运行的模块,为整个仿真过程检查信号的值,需要在断言内指定一个采样时钟。

43.ref端口,是对变量的引用,值为该变量最后一次赋的值。如果将一个变量连接到多个ref端口,可能产生竞争,因为多个模块的端口都可能更新同一个变量。

44.仿真的结束:仿真在程序块中最后一个initial块结束时结束。真实的情况是当最后一个initial块完成时,隐性的调用 e x i t 以标志程序的结束,所有程序块都退出了, exit以标志程序的结束,所有程序块都退出了, exit以标志程序的结束,所有程序块都退出了,finish函数的隐性调用结束

第五章 面向对象编程基础**

45.声明和创建分开:避免在声明一个句柄的时候调用构造函数,即new函数。这样构造函数在第一条过程语句前就被调用了。此外,如果忘记了使用automatic存储空间,构造函数将在仿真开始时而非进入块的时候被调用。

46.new()仅创建了一个对象,可以使用参数设置对象的数值;new[]建立一个含有多个元素的数组,只需使用一个数值来设置数组的大小。

47.为对象创建一个句柄:通过声明一个句柄来创建一个对象,在一次仿真中一个句柄可以指向很多对象。 Transaction a,b; //声明了俩个句柄 a=new(); // 为第一个transaction对象分配地址

48.对象的回收:SV不能回收一个被句柄引用的对象,如果创建了一个链接表,除非手工设置所有句柄为null,清楚所有句柄,否则SV不会释放对象的空间。如果对象包含有从一个线程派生出来的程序,那么只要该线程仍在运行,这个对象的空间就不会被释放。

49.访问静态变量:句柄.静态变量;类名: :静态变量 ; 静态变量通常在声明时初始化。

50.类的定义-块外方法声明:方法的原型定义(方法名和参数)放在类的内部;方法的程序体(过程代码)放在类的后面定义。 extern function void xxx() function void Transaction: : xxx();

51.类应该在program或module外的package中定义

52.类包含类:通过使用指向对象的句柄,一个类内部可以包含另一个类的实例。

52.调用方法的时候,传递的是对象的句柄而非对象本身;调用一个带标量变量的方法并使用ref关键词,SV传递该标量的地址,所以方法也可以修改标量变量的值

53.对象的复制:a=new b 浅拷贝,句柄a,b指向同一个对象;深拷贝需要调用类所包含的所有对象的copy函数,copy调用了构造函数new(),所以每一个对象都有一个唯一的id,a=copy.b ,句柄a,b指向两个对象。

第六章 随机化**

54.CRT(受约束的随机测试法):由两部分组成,使用随机的数据流为DUT产生输入的测试代码以及伪随机数发生器PRNG的种子seed

55.什么需要随机化:1.器件配置 2.环境配置 3.原始输入数据 4.封装后的输入数据 5.协议异常 6.延时 7.事务状态 8.错误error和违规violation

56.测试平台是查找功能错误,而不是时序错误。测试平台不应该尝试违反建立时间和保持时间的约束,时序分析工具是用来分析时序的。

57.不能在类的构造函数里随机化对象,因为在随机化前可能需要打开或关闭约束、改变权重,甚至添加新的约束。

58.检查随机化(randomize)的结果:randomize()函数为类里所有的rand和randc类型的随机变量赋一个随机值,并保证不违背所有有效的约束。常使用断言检查randomize()的结果。

59.SV什么可以被随机化? 整型变量,即由位组构成的变量。 可以是整数和位矢量,不能随机字符串,或在约束中指向句柄。

60.关系操作符:一个表达式中最多只能使用一个关系操作符(<、<=、==、>=、>); ->操作符,条件约束

61.约束块里只能包含表达式,不能进行赋值等。在类里增加约束,也可以使用randomize() with来增加额外的约束,约束内容也要用{}

62.约束操作符:例如,k inside{[a:b]}; 则可以使用$来代表取值范围里的最大值和最小值; 取反操作符!可以对约束取反:k<a 或 k>b {}用于声明性的代码

63.约束:是声明性代码,是并行的,所有约束表达式同时有效。而程序性代码是自上向下的执行,在约束块中用{ }将多个表达式组织一起,而程序性代码是begin…end关键字。

64.solve…before 引导概率分布,不会改变解的个数,只会改变各个值的概率分布。

65.控制多个约束块:一个类可以包含多个约束块,通过内建的constraint_mode()函数打开或关闭约束

66.随机化常见错误:1.小心使用有符号变量 2.避免复杂的运算,例如乘除法、取模(%),若需要除或乘2的幂次方,使用右移或左移操作,对2的幂次方取模可以用和掩膜的AND布尔操作代替,如果需要进行上述操作,用宽度小于32位的变量可以提高计算性能。

67.伪随机数发生器。Verilog在整个仿真过程中使用一个PRNG,SV每个对象和线程都有一个独立的PRNG和独立的种子,改变一个对象不会影响其他对象获得的随机数。启动一个新对象或线程时,子PRNG的种子由父PRNG产生,所以仿真开始时一个种子可以产生多个随机激励流且相互独立。

第七章 线程以及线程间的通信**

68.停止线程:disable可以停止线程;SV引入disable fork停止从当前线程中衍生出来的所有子线程。为避免无意间停止过多的线程,应该使用fork…join把目标代码包围起来限制disable fork。

69.禁止被多次调用的任务:从某个块禁止该块时要小心,你停止的可能比预期的更多,若该任务被多个线程调用,禁止其中一个将导致全部被禁止

70.事件:Verilog中一个线程总要等待带@的操作符,边沿敏感,所以总是阻塞着,等待事件的变化。其他事件可以通过->操作符来触发事件。SV引入triggered()函数,查询某个事件是否被触发,包括在当前时刻。线程可以等待这个函数的结果,而不是在@操作符上阻塞。

71.可以用电平敏感的wait(e1.triggerred())来替代边沿敏感的阻塞语句@e1.

72.旗语的操作:三种,new方法创建一个带单个或多个钥匙的旗语;get获取一个或多个钥匙;put可以返回一个或多个钥匙;试图获取一个旗语但不希望被阻塞,可以使用try_get()函数,返回1则有足够多钥匙。

73.信箱:是对象,必须调用new函数进行实例化,参数size限制信箱中的条目,不指定或为0则无限大。put任务把数据放到信箱里,get移除任务,peek任务获取对信箱里数据的拷贝而不移除它;信箱满,put阻塞,空,get阻塞。

74.异步线程间使用信箱通信:想让发生器和驱动器一致,需要使用握手信号。

75.使用定容信箱和peek实现线程的同步:生产方创建一个事务并放到信箱里,然后开始阻塞直到事务被消费方处理掉。事务处理完成的标志是事务最终被消费方从信箱里移出。消费方先peek探视,等处理完数据后再get移出数据;若用get可能在处理完成之前生产方又生成新的数据。

76.使用信箱和事件来实现线程的同步:生产方以阻塞的方式来等待消费方完成对信箱条目的处理,例如使用wait(handshake.triggered())或@handshake

77.使用俩个信箱来实现线程的同步:再使用一个信箱把消费方的完成信息发回给生产方。

第八章 面向对象编程的高级技巧指南

78.将类中的子程序定义成虚拟的,这样就可以在扩展类中被重定义。

79.扩展类的构造函数,若基类构造函数有参数,那么扩展类必须有一个构造函数且必须在其第一行调用基类的构造函数。

80.$cast类型向下转换,将一个指向基类的指针转换成一个指向派生类的指针。作为任务来使用的时候,SV会在运行时检查源对象类型,若和目标对象类型不匹配则出一个错误报告,作为函数使用时,SV仍做类型检查,但不会输出错误信息,不兼容返回0,兼容返回非零值。

81.派生类句柄可以赋给基类句柄,基类句柄指向扩展对象。但基类对象拷贝到扩展对象的句柄中时操作会失败,因为有些属性仅存在于扩展类中,基类不具备,会error。(SV会对句柄类型进静态检查)

82.虚方法:virtual,SV根据对象类型而非句柄类型来判断调用什么方法,但若没用virtual修饰符,SV会根据句柄的类型而不是对象的类型调用。

83.抽象类和纯虚方法,SV中允许使用抽象类和纯虚方法来创建一个可以共享的基类。第一种抽象类,可以被扩展但不能被实例化的类没使用virtual关键词定义;第二种是纯虚pure virtual方法,没有实体的方法原型,只能在抽象类中定义。

84.回调,与其预测所有可能的错误、延迟或事务流程的干扰,不如使用回调的方法,驱动器仅需要“回调”一个在顶层测试中定义的子程序。使用回调来为驱动器增加新的功能而不需要编辑Driver类

85.堆栈,入栈push和出栈pop存储和取回数据,在SV中可以为类增加一个数据类型参数,声明类句柄的时候指定类型,如 class Stack #(type T=int) 参数化的堆栈类,类型T在第一行中定义成默认类型int

第九章 功能覆盖率

86.覆盖率分析:1.运行一个带多个种子的测试,可以使用Unix系统时间作为种子,但需要小心批处理系统也许会同时启动多项任务; 2.检查运行通过与否,功能覆盖信息只有在仿真运行成功时才有效,覆盖率数据衡量的是验证计划中有多少项已完成,验证计划是基于设计规范的,所以要定期从头全面衡量功能覆盖率; 3.分析通过多次运行得到的覆盖率,没100%时,若一直在增加,那么继续运行更多的种子,若已经稳定下来,那么应该考虑修改约束,只有当受约束的随机仿真去覆盖特定区域的最后几种情况可能会花太长时间,才有必要考虑编写定向测试。

87.覆盖率:代码覆盖率(行、条件、状态机、翻转、路径) 功能覆盖率 断言覆盖率

88.测量的完备性:功能覆盖率很高但代码覆盖率很低,验证计划不完整,回到硬件的设计规范并更新验证计划;代码覆盖率很高但功能覆盖率很低,代码冗余,查看设计是否实现了所有指定的功能,若功能有了但测试不到,需要形式验证工具来提取设计状态并创建适当的激励。若高代码和功能覆盖率都达成,则需要考虑漏洞率。

89.覆盖组的触发:sample函数和wait或@;如果希望在程序代码中显式地触发覆盖组或不存在可以标识采样时刻的信号或时间,或在一个覆盖组里有多个实例需要独立触发,则用sanple;若想借助已有的事件或信号来触发覆盖组,可以在covergroup定义中使用阻塞语句。

90.使用回调函数进行采样。把功能覆盖率集成到测试平台中,比较好的方法是用回调函数。比使用信箱好,这是因为可能需要多个信箱来收集测试程序中不同点的事务数据,一个信箱要求一个事务处理器来接收事务数据,多信箱会引起多线程间的不平衡。

91.覆盖率:采样值的数目除以域中仓的数目,3bit变量覆盖点的域为0:7,正常情况会除以8个仓。

92.auto_bin_max:指明了自动创建仓的最大数目,缺省值为64.

93.对表达式进行采样。需要及时调整位宽,例如对3bit头长度(0:7)和4bit负载长度(0:15)的加法表达式进行采样,只能得到16个仓,如果实际数据可以达到23个字节(0:22)的话,仓数不够。因为最大长度为23,不是2的幂,自动生成的仓不适用。

94.命名仓使用[]; coverpoint是使用{}围起来的,声明语句。

95.条件覆盖率;关键字iff给覆盖点加条件,最常用于在复位期间关闭覆盖以忽略掉一些杂散的触发。

96.忽略数值。在某些覆盖点上可能始终得不到全部可能值,如3bit变量只存放0~5,若使用自动创建的仓,覆盖率始终不超过75%,对于这个问题有两种解决办法。1.可以明确定义仓来涵盖所有的期望值;2.可以让SV自动创建仓,然后用ignore_bins排除不用来计算功能覆盖率的数值。

97.不合法的仓。有些采样值不仅应该被忽略,当出现的时候还应该报错,illegal_bins对仓进行标识。

98.通用的覆盖组。覆盖组间比较接近,通用的覆盖组的创建:SV不允许把覆盖组的触发参数传递给实例,但可以把覆盖组放到一个类里,然后把出发参数传递给构造函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值