数字ic验证工程师在找工作时,刷笔试面试题必不可少,在面试前做好充足的准备才能抓住更多的机会,今天小编为大家准备了数字ic验证工程师大厂面试常用笔试面试题。
- 下列关于代码覆盖率描述错误的是:CD
A.代码覆盖率包括语句覆盖率
B.代码覆盖率包括条件覆盖率
C.代码覆盖率包括功能覆盖率
D.代码覆盖率到达百分百说明代码 bug 已消除
解析:代码覆盖率和功能覆盖率是独立的两种覆盖率,代码覆盖率100%只能表 明代码经过了充分的执行,但是代码中是否有 bug 以及 bug 是否会被发现,取决于验证环境中的监测点是否监测了关键信号以及对这些信号的判断是否正确。
- 关于亚稳态的描述错误的是:A
A.多用几级寄存器打拍可以消除亚稳态。
B.亚稳态是极不稳定的,理论上来讲处在亚稳态的时间可以无限长。
C.亚稳态稳定到 0 或者 1,是随机的,与输入没有必然的关系。
D.如果数据传输中不满足触发器的建文时间Tsu和保持时间Th,可能产生亚稳态。
解析:亚稳态不能被消除,只能降低其对后级电路的影响。
- Verilog 语言中,下面哪些语句不可被综合:A
A.#delay 语句
B.initial 语句
C.always 语句
D.用 generate 语句
解析:产生的代码所有综合工具都不支持的结构 time,defparam,$finish,fork,join,initial,delays,UDP,wait
- 下列关于 verilog 的描述正确的是:ABCD
A.Y=a+b; 属于阻塞赋值语句,执行该语句时,先计算 a+b 的值,然后更新 y 值,在此过程中,不能运行其他语句
B.Generate, for, function 语句可综合
C.如果 A=1‘b1,B=1`b0,F=A&B|B&A||B,则 F=1’b1
D.如果 A=4‘hb,则^A=1’b1
- 判断电路是否存在竞争冒险的方法有哪些呢?A
A.代数法
B.卡诺图法
C.实验法
D.观察法
- 简述 UVM 的工厂机制
Factory 机制也叫工厂机制,其存在的意义就是为了能够方便的替换 TB 中的实例 或者已注册的类型。一般而言,在搭建完 TB 后,我们如果需要对 TB 进行更改配 置或者相关的类信息,我们可以通过使用 factory 机制进行覆盖,达到替换的效果,从而大大提高 TB 的可重用性和灵活性。
要使用 factory 机制先要进行:
将类注册到 factory 表中
创建对象,使用对应的语句 (type_id::create)
编写相应的类对基类进行覆盖。
- OPP(面向对象)的特性?
封装:通过将一些数据和使用这些数据的方法封装在一个集合里,成为一个类。
继承:允许通过现有类去得到一个新的类,且其可以共享现有类的属性和方法。 现有类叫做基类,新类叫做派生类或扩展类。
多态:得到扩展类后,有时我们会使用基类句柄去调用扩展类对象,这时候调用 的方法如何准确去判断是想要调用的方法呢?通过对类中方法进行 virtual 声明, 这样当调用基类句柄指向扩展类时,方法会根据对象去识别,调用扩展类的方法, 而不是基类中的。而基类和扩展类中方法有着同样的名字,但能够准确调用,叫做多态。
- 阻塞赋值与非阻塞赋值的区别
阻塞赋值的操作符号用等号(=)表示,当前语句的赋值阻塞其他语句的赋值;
非阻塞赋值的操作符号用小于等于号(<=)表示,当前语句赋值不阻塞其他语句 的赋值。
- 动态数组和联合数组的区别?
动态数组:其内存空间在运行时才能够确定,使用前需要用 new[]进行空间分配。
关联数组:其主要针对需要超大空间但又不是全部需要所有数据的时候使用,类 似于 hash,通过一个索引值和一个数据组成: bit [63:0] name[bit[63:0]];索引值必 是唯一的。
关联数组可以用来保存稀疏矩阵的元素。当你对一个非常大的地址空间寻址时, 该数组只为实际写入的元素分配空间,这种实现方法所需要的空间要小得多。
此外,关联数组有其它灵活的应用,在其它软件语言也有类似的数据存储结构, 被称为哈希(Hash)或者词典(Dictionary),可以灵活赋予键值(key)和数值 (value)
- SV 中的 interface 的 clock blocking 的功能
Interface 是一组接口,用于对信号进行一个封装,捆扎起来。如果像 verilog 中 对各个信号进行连接,每一层我们都需要对接口信号进行定义,若信号过多,很 容易出现人为错误,而且后期的可重用性不高。因此使用interface 接口进行连接, 不仅可以简化代码,而且提高可重用性,除此之外,interface 内部提供了其他一 些功能,用于测试平台与 DUT 之间的同步和避免竞争。
Clocking block:在 interface 内部我们可以定义 clocking 块,可以使得信号保持同步, 对于接口的采样和驱动有详细的设置操作,从而避免 TB 与 DUT 的接口竞争,减 少我们由于信号竞争导致的错误。采样提前,驱动落后,保证信号不会出现竞争。
- 建立时间和保持时间违例的解决方法
建立时间违例
时钟路径插入缓冲器;
更换延迟小的触发器;
增加时钟周期;
保持时间违例
优化时钟,让时钟更早到来;
触发器插入缓冲器;
更换延迟大的触发器;
- 亚稳态是什么,怎么消除?
产生原因:
数据传输不满足触发器的建立时间和保持时间
发生场合:
主要发生在异步信号检测,跨时钟域信号传输以及复位电路中;
消除办法:
对异步信号进行同步处理;
采用 fifo 对跨时钟域通信进行数据缓冲设计;
对复位电路采用异步复位,同步释放。
- 怎么编写测试用例?
主要是编写 sequence,然后在 body 里面根据测试功能要求写相应的激励,然后 再通过 ref_model 和 checker 判断功能是否实现?
- 如果有很多测试用例,如何让它们自动执行?
可以写脚本让它们自动执行,例如 makefile…
- fifo 的异步与同步
异步 fifo:读写时钟不同;
同步 fifo:读写时钟相同。
- 验证的思想
验证就是在设计规范的要求下,对已知功能目标下的 DUT 进行检查,然而实际 的使用场景是设计规范无法全面覆盖的,验证工程师只能在有限的资源与时间下 对设计代码进行最大限度的检查以尽可能多地消除流片之后的bug。
- 代码覆盖率、功能覆盖率和断言覆盖率的区别
代码覆盖率——是针对 RTL 设计代码的运行完备度的体现,包括行覆盖率、条件 覆盖率、FSM 覆盖率、跳转覆盖率、分支覆盖率,只要仿真就可以收集,可以看 DUT 的哪部分代码没有动,如果有一部分代码一直没动看一下是不是 case 没有 写到。
功能覆盖率—与 spec 比较来发现,design 是否行为正确,需要按 verification plan 来比较进度。用来衡量哪些设计特征已经被测试程序测试过的一个指标
首要的选择是使用更多的种子来运行现有的测试程序;
其次是建立新的约束,只有在确实需要的时候才会求助于定向测试,改进功能覆 盖率最简单的方法是仅仅增加仿真时间或者尝试新的随机种子。
验证的目的就是确保设计在实际环境中的行为正确。设计规范里详细说明了设备 应该如何运行,而验证计划里则列出了相应的功能应该如何激励、验证和测量
断言覆盖率:用于检查几个信号之间的关系,常用在查找错误,主要是检查时序 上的错误,测量断言被触发的频繁程度。
- sv 里面动态数组、关联数组、队列各自的优缺点,应用场景。
特点:
动态数组:可以在仿真时分配空间或者调整宽度,这样仿真中就可以使用最小的 存储空间。
关联数组:用来保存稀疏矩阵的元素,当一个非常大的地址空间进行寻址时,sv 只对实际写入的元素分配空间,比定宽数组和动态数组所占用的空间要小得对。
队列:结合了链表和数组的优点,可以在队列的任意位置增加或者删除元素,这 类操作在性能上比动态数组小得多,可以通过索引对任意元素进行访问。
应用场景:
动态数组:随机事务不确定位宽大小;
关联数组:需要建立一个超大容量数组,用关联数组来存放稀疏矩阵的元素;
队列:增加元素或者删除元素方便。
- 数据类型怎么转换,静态强制类型转换和动态强制转换有什么区别?
静态转换:转换时指定目标类型,并在需要转换的表达式前加上单引号即可;
动态转换:使用函数 $cast
区别:静态类型转换操作不对转换值进行检查,具有一定的危险性;而动态类型 转换在运行时将进行检查,如果转换失败会产生运行时错误。
20. function 和 task 的区别
21. UVM 的树形结构
22. UVM 验证环境的组成
Sequencer:负责将数据转给 driver
Driver:负责数据的发送;driver 有时钟/时序的概念。
Agent:其实只是简单的把 driver,monitor 和 sequencer 封装在一起。
Agent:对应的是物理接口协议,不同的接口协议对应不同的 agent,一个平台通 常会有多个 agent。
Env:则相当于是一个特大的容器,将所有成员包含进去。
- 定宽数组、动态数组、关联数组、队列各自特点和使用
队列:队列结合了链表和数组的优点,可以在一个队列的任何位置进行增加或者 删除元素;
定宽数组:属于静态数组,编译时便已经确定大小。其可以分为压缩定宽数组和 非压缩定宽数组:压缩数组是定义在类型后面,名字前面;非压缩数组定义在名字 后面。Bit [7:0][3:0] name; bit[7:0] name [3:0];
动态数组:其内存空间在运行时才能够确定,使用前需要用 new[]进行空间分配;
关联数组:其主要针对需要超大空间但又不是全部需要所有数据的时候使用,类 似于 hash,通过一个索引值和一个数据组成,索引值必须是唯一的。
- 多线程 fork join/fork join_any/fork join_none 的用法差异
Fork join:内部 begin end 块并行运行,直到所有线程运行完毕才会进入下一个 阶段。
Fork join_any:内部 begin end 块并行运行,任意一个 begin end 块运行结束就可 以进入下一个阶段。
Fork join_none:内部 begin end 块并行运行,无需等待可以直接进入下一个阶段。
wait fork:会引起调用进程阻塞,直到它的所有子进程结束,一般用来确保所有 子进程(调用进程产生的进程,也即一级子进程)执行都已经结束。
disable fork:用来终止调用进程 的所有活跃进程,以及进程的所有子进程。
- 多线程的同步调度方法
多线程之间同步主要由 mailbox、event、 semaphore 三种进行一个通信交互。
mailbox 邮箱:主要用于两个线程之间的数据通信,通过 put 函数和 get 函数还 有 peek 函数进行数据的发送和获取。
Event:事件主要用于两个线程之间的一个同步运行,通过事件触发和事件等待 进行两个线程间的运行同步。使用@(event)或者 wait(event.trigger)进行等待,-> 进行触发。
Semaphore:旗语主要是用于对资源访问的一个交互,通过 key 的获取和返回实现一个线程对资源的一个访问。使用 put 和 get 函数获取返回 key。一次可以多个。
-
function 和 task 的区别
-
简述在 TB 中使用 interface 和 clocking blocking 的好处
Interface 是一组接口,用于对信号进行一个封装,捆扎起来。如果像 verilog 中 对各个信号进行连接,每一层我们都需要对接口信号进行定义,若信号过多,很 容易出现人为错误,而且后期的可重用性不高。因此使用 interface 接口进行连接, 不仅可以简化代码,而且提高可重用性,除此之外,interface 内部提供了其他一 些功能,用于测试平台与 DUT 之间的同步和避免竞争。
Clocking block 在 interface 内部我们可以定义 clocking 块,可以使得信号保持同步, 对于接口的采样 vrbg 和驱动有详细的设置操作,从而避免 TB 与 DUT 的接口竞争,减少我们由于信号竞争导致的错误。采样提前,驱动落后,保证信号不会出现竞争。
- 描述 soc/ip 验证之间的区别,以及验证二者之间的侧重点
SoC 和 IP 从独立性来看,前者较后者更为独立,往往具备更加完整的功能。SoC会由多个 IP、子系统和其它系统模块构成,从层次来看,IP 是构成 SoC 的重要组成部分。
在验证 SoC 时,首先需要确保其 IP 级别都完成了验证,而在系统级别需要验证各个模块之间的交互和协调情况、集成连线情况,测试用例会更加真实,当然,仿真速度也下降很快,一般需要做门级仿真。
在 IP 级验证时,如果是内部 IP,那么需要就接下来的运用场景(配置情况),展开重点性的验证,如果是向外部提供的 IP,那么需要针对其参数配置展开更为全面细致的验证工作,所以其特点不但是要求验证每一项功能,而且是每一项功能在不同配置下的行为是否是正确的。
- UVM 从哪里启动,接口怎么传递到环境中
在导入 uvm_pkg 文件时,会自动创建 UVM_root 所例化的对象 UVM_top,UVM 顶层的类会提供 run_test()方法充当 UVM 世界的核心角色,通过 UVM_top 调用 run_test()方法.
在环境中输入 run_test 来启动 UVM 验证平台,run_test 语句会创建一个 my_case0的实例,得到正确的 test_name
依次执行 uvm_test 容器中的各个 component 组件中的 phase 机制,按照顺序:build-phase(自顶向下构建 UVM 树)
connet_phase(自低向上连接各个组件)
end_of_elaboration_phase
start_of_simulation_phase
run_phase() objection 机制仿真挂起,通过 start 启动 sequence(每个 sequence都有一个 body 任务。当一个 sequence 启动后,会自动执行 sequence 的 body 任务,等到 sequence 发送完毕则关闭 objection,结束 run_phase()(UVM_objection提供 component 和 sequence 共享的计数器,当所有参与到 objection 机制中的组件都落下 objection 时,计数器 counter 才会清零,才满足 run_phase()退出的条件)
执行后面的 phase
- 接口怎么传递到验证环境中(uvm_config_db)
传递 virtual interface 到环境中;
配置单一变量值,例如 int、string、enum 等;
传递配置对象(config_object)到环境;
传递 virtual interface 到环境中;
a) 虽然 SV 可以通过层次化的 interface 的索引完成传递,但是这种传递方式不利于软件环境的封装和复用。通过使用 uvm_config_db 配置机制来传递接口,可以将接口的传递与获取彻底分离开。
b) 接口传递从硬件世界到 UVM 环境可以通过 uvm_config_db 来实现,在实现过程中应当注意:
c) 接口传递应发生在 run_test()之前。这保证了在进入 build_phase 之前,virtual interface 已经被传递到 uvm_config_db 中。
d) 用户应当把 interface 与 virtual interface 区分开来,在传递过程中的类型应当为 virtual interface,即实际接口的句柄。
配置单一变量值,例如 int、string、enum 等;在各个 test 中,可以在 build_phase阶段对底层组件的各个变量加以配置,进而在环境例化之前完成配置,使得环境可以按照预期运行。
传递配置对象(config_object)到环境;
在 test 配置中,需要配置的参数不只是数量多,可能还分属于不同的组件。对这么多层次的变量做出类似上边的单一变量传递,需要更多的代码,容易出错且不易复用。
如果整合各个组件中的变量,将其放置在一个 uvm_object 中,再对中心化的配置对象进行传递,将有利于整体环境的修改维护,提升代码的复用性。
笔试,面试题的内容过多,可以查看原文,进行下载。
以上就是给大家分享的“数字ic验证工程师经典笔试面试题”,希望对大家有所帮助。