赞
踩
目录
Q1.定宽数组、动态数组、关联数组、队列各自的特点和使用方式。
Q2.fork...join/fork...join_any/fork...join_none之间的异同
Q3.mailbox、event、semaphore之间的异同
Q4.@(event_handle)和wait(event_handle.triggered)区别
Q18.break、continue和returen的含义用法
Q19.function中return语句执行之后,function里剩下的代码语句还会执行吗
Q23.SVA中and、intersect、or、throughout、$past如何使用
队列:队列结合了链表和数组的优点,可以在一个队列的任何位置进行增加或者删除元素。其通过[$]这样的符号进行申明: int q[$],使用广泛,多应用与scoreboard;
定宽数组:属于静态数组,编译时便已经确定大小。其可以分为压缩定宽数组和非压缩定宽数组:压缩数组是定义在类型后面,名字前面;非压缩数组定义在名字后面。Bit [7:0][3:0] name压缩数组(存储空间连续); bit[7:0] name [3:0]非压缩数组(存储空间非连续);
动态数组:其内存空间在运行时才能够确定,使用前需要用new[]进行空间分配。
关联数组:其主要针对需要超大空间但又不是全部需要所有数据的时候使用,类似于hash,通过一个索引值和一个数据组成: bit [63:0] name[bit[63:0]];索引值必须是唯一的,该部分的重点要学习关联数组的遍历方法。
fork...join:内部 begin end块并行运行,直到所有线程运行完毕才会进入下一个阶段。
fork...join_any:内部 begin end块并行运行,任意一个begin end块运行结束就可以进入下一个阶段。
fork...join_none:内部 begin end块并行运行,无需等待可以直接进入下一个阶段。
mailbox:主要用于两个线程之间的数据通信,通过put函数和 get 函数还有peek函数进行数据的发送和获取。
event:事件主要用于两个线程之间的一个同步运行,通过事件触发和事件等待进行两个线程间的运行同步。使用@(event)或者 wait(event.trigger)进行等待,->进行触发。
semaphore:旗语主要是用于对资源访问的一个交互,通过key的获取和返回实现一个线程对资源的一个访问。使用put和 get函数获取返回key。一次可以多个。
@(event_handle):边沿触发,事件会一直等待,直到触发为止,阻塞型;
wait(event_handle.triggered):电平触发,如果事件在当前已经被触发,则不会引起阻塞,非阻塞型,当event被多次触发时,避免使用wait.
function:至少要有一个输入变量,一个返回值,只能运用于纯粹的数字或者逻辑运算 ;
task:可以内置常用的耗时语句,可能会被运用于需要耗时的信号采样或者驱动场景。
如果要调用function,则使用function和task均可对其调用;而如果要调用task,建议使用task来调用,这是因为如果被调用的task内置有耗时语句,则外部调用它的方法类型必须为task.
FIFO在硬件上是一种地址依次自增的Simple Dual Port RAM,按读数据和写数据工作的时钟域是否相同分为同步FIFO和异步FIFO,其中同步FIFO是指读时钟和写时钟为同步时钟,常用于数据缓存和数据位宽转换;异步FIFO通常情况下是指读时钟和写时钟频率有差异,即由两个异步时钟驱动的FIFO,由于读写操作是独立的,故常用于多比特数据跨时钟域处理。
类主要有三个特性:封装、继承、多态。
封装:通过将一些数据和使用这些数据的方法封装在一个集合里,成为一个类。
继承:允许通过现有类去得到一个新的类,且其可以共享现有类的属性和方法。现有类叫做基类,新类叫做派生类或扩展类。
多态:得到扩展类后,有时我们会使用基类句柄去调用扩展类对象,这时候调用的方法如何准确去判断是想要调用的方法呢?通过对类中方法进行virtual声明,这样当调用基类句柄指向扩展类时,方法会根据对象去识别,调用扩展类的方法,而不是基类中的。而基类和扩展类中方法有着同样的名字,但能够准确调用,叫做多态。
ref参数类型是引用
向子程序传递数组时应尽量使用ref获取最佳性能,如果不希望子程序改变数组的值,可以使用const ref类型
在任务里可以修改变量而且修改结果对调用它的函数随时可见。
使用方法例示:
class Packet;
rand bit [7:0] length;
rand bit [7:0] payload[];
constraint c_valid {length > 0;payload.size() == length;}
constraint c_external;
endclass
constraint Packet::c_external {length == 1;} //外部约束随机化是SV中极其重要的一个知识点,通过设定随机化和相关约束,我们可以自动随机想要的数据。
权重约束 dist: 有两种操作符::=n :/n 第一种表示每一个取值权重都是n,第二种表示每一个取值权重为n/num。
条件约束 if else 和->(case): if else 就是和正常使用一样;->通过前面条件满足后可以触发后面事件的发生。
范围约束inside:inside{[min:max]};范围操作符,也可以直接使用大于小于符号进行,不可以连续使用,如 min<wxm<max 这是错误的
代码覆盖率:是针对RTL设计代码的运行完备度的体现,包括行覆盖率、条件覆盖率、FSM覆盖率、跳转覆盖率、分支覆盖率,只要仿真就可以收集,可以看DUT的哪部分代码没有动,如果有一部分代码一直没动看一下是不是case没有写到。
功能覆盖率:与spec比较来发现,design是否行为正确,需要按verification plan来比较进度。用来衡量哪些设计特征已经被测试程序测试过的一个指标,首要的选择是使用更多的种子来运行现有的测试程序;其次是建立新的约束,只有在确实需要的时候才会求助于定向测试,改进功能覆盖率最简单的方法是仅仅增加仿真时间或者尝试新的随机种子。验证的目的就是确保设计在实际环境中的行为正确。设计规范里详细说明了设备应该如何运行,而验证计划里则列出了相应的功能应该如何激励、验证和测量
断言覆盖率:用于检查几个信号之间的关系,常用在查找错误,主要是检查时序上的错误,测量断言被触发的频繁程度。
因为喜欢,因为热爱,因为行业需要;
立即断言的话就是和时序无关,比如我们在对激励随机化时,我们会使用立即断言,如果随机化出错我们就会触发断言报错。
并发断言的话主要是用来检测时序关系的,由于在很多模块或者总线中,单纯使用覆盖率或者事务check 并不能完全检测多个时序信号之间的关系,但是并发断言却可以使用简洁的语言去监测,除此之外,还可以进行覆盖率检测。
并发断言的用法的话,主要是有三个层次:
第一是布尔表达式,布尔表达式是组成断言的最小单元,断言可以由多个逻辑事件组成,这些逻辑事件可以是简单的布尔表达式.在SVA中,信号或事件可以使用常用的操作符,如:&&, ||, !, ^,&等;
第二个序列sequence编写,sequence是布尔表达式更高一层的单元,一个sequence中可以包含若干个布尔表达式,同时在sequence中可以使用一些新的操作符,如 ## 、重复操作符、序列操作符;
第三个是属性property的编写,property是比sequence更高一层的单元,也是构成断言最常用的模块,其中最重要的性质是可以在property中使用蕴含操作符(|-> |=>);
1、易维护
采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。
2、质量高
在设计时,可重用现有的,在以前的项目的领域中已被测试过的类使系统满足业务需求并具有较高的质量。
3、效率高
在软件开发时,根据设计的需要对现实世界的事物进行抽象,产生类。使用这样的方法解决问题,接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。
4、易扩展
由于继承、封装、多态的特性,自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低。
首先不可能百分百完全完备,即遍历所有信号的组合,这既不经济也不现实。所以只能通过多种验证方法一起验证尽可能减少潜在风险,一般有这些验证流程:ip级验证、子系统级验证、soc级验证,除这些以外,还有upf验证、fpga原型验证等多种手段。前端每走完一个阶段都需要跟设计以及系统一起review验证功能点,测试用例,以及特殊情况下的波形等。
芯片后端也会做一些检查,像sta、formality、DFM、DRC检查等,也会插入一些DFT逻辑供流片回来测试用。流片归来进行测试,有些bug可以软件规避,有些不能规避,只能重新投片。
: =操作符表示值范围内的每一个值的权重是相同的,比如[1:3]:=40,表示1,2,3取到的概率为40/120;
:/操作符表示权重要平均分到值范围内的每一个值,比如[1:3]:/60,表示1,2,3取到的概率为20/60。
rand: 表示随机,比如有56个数,每次的随机概率都是1/56;
randc: 表示周期性随机,比如56个数,第一次随机的概率为1/56,第二次随机的概率为1/55,以此类推。
break 语句结束整个循环。
continue 立即结束本次循环,继续执行下一次循环。
return 语句会终止函数的执行并返回函数的值(如果有返回值的话)。
return之后,function里剩下的语句不能执行,其是终止函数的执行,并返回函数的值。
触发器: 时钟触发,受时钟控制,只有在时钟触发时才采样当前的输入,产生输出。
锁存器: 由电平触发,非同步控制。在使能信号有效时锁存器相当于通路,在使能信号无效时锁存器保持输出状态。触发器由时钟沿触发,同步控制。
锁存器对输入电平敏感,受布线延迟影响较大,很难保证输出没有毛刺产生;触发器则不易产生毛刺。
使用二级触发器进行同步可以极大的避免亚稳态的出现:
至于为什么“打两拍”可以减少亚稳态,我觉得根本原因是在于寄存器是一个双稳态器件,只有0和1两种稳定的状态。在绝大多数时候,第一级Flop的Q端,即使出现亚稳态,也会在一个时钟周期内稳定下来,这样第二级Flop所采到的就是一个稳定的值。“打两拍”就是利用这一点,切断了亚稳态的传播。
setup time是指在时钟有效沿之前,数据输入端信号必须保持稳定的最短时间。
hold time是指在时钟有效沿之后,数据输入端信号必须保持稳定的最短时间。
hold time时序检查确保新数据不会在触发器稳定输出初始数据之前过早到达D端而覆盖其初始数据。
总结为一句话:当前待传输的数据,相对于Capture edge来说,必须早来(setup time)晚走(hold time)。
and:可以按照逻辑与的方式组合两个序列,当两个序列都匹配时,整个属性成功。
示例:
property p;
sequence_a and sequence_b;
endproperty
【注意】sequence_a和sequence_b的检查起始点必须一样,但是两者结束的结束点可以不一样,整个属性的成功点以两个sequence中最后一个成功的sequence的匹配点为属性的成功点。
or:可以按照逻辑或的方式组合两个序列,当两个序列有一个匹配时,整个属性成功。
示例:
property p;
sequence_a or sequence_b;
endproperty
【注意】sequence_a和sequence_b的检查起始点必须一样,但是两者结束的结束点可以不一样,整个属性的成功点取决于最早匹配成功的sequence,只要两个序列有一个匹配成功,整个属性就认为匹配。
intersect两侧的表达式都是sequence,不能是property,使用intersect时需要确保其两侧的sequence必须同时开始,且当两个sequence最终同时匹配时才认为两个序列的intersect匹配,即两个sequence匹配的长度必须相同,intersect操作符和and操作符很相似,但是and没有要求两个序列最终匹配时同时匹配。
在SVA中有时需要某些信号在一个序列的检查过程中一直保持一个状态,此时就可以使用throughout操作符,当这些信号的状态发生不期望的变化时,序列的检查即认为失败。
$past主要用于检查当前表达式前一个时钟周期(如果不指定number_of_ticks,默认指向前一个时钟周期)值是否为真,如果位真则$past返回为真。其格式如下:
$past(expression[,number_of_ticks]);
其中number_of_ticks可以指定检查当前时刻之前number_of_ticks个周期的采样值.
@ (posedge clk) a |-> b 断定clk上升沿后,a事件"开始发生",同时,b事件发生
@ (posedge clk) a |=> b 断定clk上升沿后,a事件开始发生,下一个时钟沿后,b事件开始发生。
根据不同需要,来选择使能哪些约束块,禁止哪些约束块的要求,可以使用内建的constrai nt_mode()函数打开或者关闭多个约束块;rand_mode()函数打开或者关闭随机变量。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。