每日一问

本文

主要记录SV与UVM的一些知识点,每日一问,每日一记。

版本 说明
0.1 初版发布

写在前头

本文非原创,内容来自路科验证,整理成文并持续更新。

每日一问

在一些头文件(.svh) 中, 会有typedef class X, 这是什么意思?

  • 回答: 首先这需要与常见的typedef enum/struct…x_t的类型定义区别;真正的类的定义依然是利用“Xextends…”的方式实现, 而这里则是为了编译时能够让编译器识别class X采取的“占坑”方法, 即先“声明”类X的存在,并且在后续将X的定义字段“Xextends…”进行补充编译。
  • 关键词: typedef class
  • 避坑指南: typedef class常常用来解决两个类之间的互相引用问题,即在A中使用B,且在B中使用A。

在enum/struct类型声明时, 添加typedef与否的差别是什么?

  • 回答: 如果不添加typedef, 例如enum(NO, YES) bool,那么bool为枚举类型“变量”, 而“enum(NO, YES) ” 按照“匿名类型”(implicit type)来理解;如果添加typedef, 例如typedef enum(NO, YES) bool_t, 那么bool_t为枚举类型, 即通过typedef将匿名类型“显式”(explicitly) 定义为bool_t, 并且接下来可重复利用bool_t来声明多个变量。
  • 关键词: enum,struct, typedef
  • 避坑指南: 对于重复使用的enum/struct定义, 默认添加typedef先定义类型, 再利用该类型声明变量。

Interface的mod port和clocking block如何使用?

  • 回答: 这两个概念得需要独立看待。Mod port是将interface中的信号列表按类分簇(grouping),便于模块之间、模块与TB之间的连线管理; clocking block并非interface的专属产物, 不过多见于interface使用它进行数据信号采样或者驱动, 继而有效避免delta cycle问题,并通过波形上的可见延迟帮助理解仿真时序。
  • 关键词: interface,mod port, clocking block
  • 避坑指南: mod port和clocking在连接时方向不要混淆,在它们中只需对若干已声明信号再次声明方向即可。

Module中的Initial额always执行先后顺序是否与它们的代码位置有关?

  • 回答: Module中的initial和always执行先后顺序是always在综合逻辑中, 以及initial在初始化激励序列中,彼此过程块之间都是“并行”的,这是从模拟硬件执行角度来理解“并行”;同时,仿真器即便要处理这些过程块语句,也需要从软件语句执行着手,这意味着同一个仿真时刻执行的并行语句,也需要由仿真器安排它们的执行顺序,这是从仿真软件执行角度来理解“顺序”。
  • 关键词: 执行顺序, initial, always
  • 避坑指南: 不对多个initial执行顺序做假设。如果要按照顺序执行, 可将逻辑放置在同一个initial块, 或者使用event实现线程间同步。

组合型数组和非组合型数组如何做赋值?

  • 回答: 组合型数组之间赋值时可以将不同维度、不同元素数量的数组直接做赋值(注意位宽默认补全或者截取);非组合型数组之间赋值有严格要求必须相同维度且各维度元素数量须相等,可直接赋值,但如果不满足该条件则只能对数组中的元素做逐一赋值。
  • 关键词: 组合型,非组合型,赋值
  • 避坑指南: 组合型数组可直接赋值;非组合型数组直接赋值要求繁多,逐一赋值最安全。

类中的成员变量在声明时初始化或者在new函数中初始化是否有区别?

  • 回答: 从实现结果来看都可以对成员变量在对象创建时完成初始化,但如果两个动作均发生了,那么应该注意变量声明初始化执行在前(空间开辟动作),而new函数对成员变量初始化在后(构建函数体内部对成员变量做初始化操作)
  • 关键词: 变量声明,构建函数,初始化
  • 避坑指南: 搞清楚两者的执行顺序,按照代码习惯只选择一种即可。

组合型(packed)数组和非组合型数组(unpacked)怎么区分?

  • 回答: 数组维度声明在数组变量名左侧的为组合型数组,例如byte[3:0] [1:0] array 1为组合型4*2二维数组。数组维度声明在数组变量名右侧的为非组合型数组,例如bytearray 2[5:0] [7:0] 为非组合型6*8二维数组。混合型数组byte[3:0] [1:O] array 3[5:O] [7:0] 为6*8*4*2的四维混合数组。
  • 关键词: 组合型,非组合型
  • 避坑指南: 数组维度从高到低是先看数组名右侧(从左到右),再看数组名左侧(人左到右)o

include和import的差别在哪里?

  • 回答: SV常会用’include将多个文件“平铺”(flatten) 置于某个域中(scope) , 这个域可能是package/ module/ interface等, 简单理解`include就是将对应文本的

内容“平铺”到当前域的字段中。import则是从包(package) 中引用某些需要的数据类型,例如class/parameter/enum到当前域中, 以帮助编译器能够识别被亏用的类型。

  • 关键词: `include, import
  • 避坑指南: `include部分会由编译器编译, import部分会由编译器从库中查找导出。

在哪里应该使用`include?

  • 回答: `include使用的地方多见于在包(package) 文件中将多个其它类文件“平铺”于其中,从而在编译时能够将多个文件中定义的类置于这个包中,形成一种逻辑上的包含关系; 同时, 在module文件也可能会使用`include, 使其“平铺”一些宏定义文件或者接口文件。
  • 关键词: include
  • 避坑指南: 一个包通过include包含多个文件, 在编译该包后, 不再需要对被\include文件做额外编译。

module和interface之间可以相互例化吗?

  • 回答: module可以例化module, 也可以例化interface;interface可以例化interface, 但是不可以例化module。就硬件实现逻辑而言, module之间的嵌套、interface之间的嵌套以及module嵌套interface都符合设计理念, 而interface不需要(也不应该) 例化module。
  • 关键词: module, interface
  • 避坑指南: interface可以例化interface, 但无法例化module。

方法中的参数的默认方向如何辨别?

  • 回答: 关注于系统验证思想和前沿验证资讯,为IC从业人员提供技术食粮。对于function/task, 如果其参数的方向未声明, 那么它的方向为input方向, 如果声明了方向, 那么该参数以及其后续的参数方向均相同。例如function void foo(A, B, output C, D) 中, A和B由于为声明方向, 为input, C和D为声明后的方向,即output。
  • 关键词: 参数方向,默认方向
  • 避坑指南: 对每一个参数都应该声明方向。

return的使用场景有哪些?

  • 回答: return可在function和task中使用。在返回值为void的function, 或者task中调用return, 即会立即退出该方法; 如果function返回值非void, 那么在退出function的同时还会返回数值。
  • 关键词: return, function, task
  • 避坑指南: 在task中也可以使用return立即退出。

task与function的联系差另在哪里?

  • 回答: 二者均可在module、interface、package和class中定义, 并实现一些功能。function执行须即刻返回,无法内置阻塞等待语句,在声明时需指定返回值(包括void) 。task执行无须即刻返回, 可以内置阻塞等待语句(wait、#、@) , 返回数值只能依靠参数列表中的参数。
  • 关键词: task, function
  • 避坑指南: function可以调用function, 但无法调用task; task既可以调用task, 也可以调用function。

{}操作符号的使用场景有哪些?

  • 回答: 第一,可作为并置运算符“串联”其一些向量或者字符串,例如{a,b}; 第二,可作为复制运算符,例如{4{w}}中内嵌的一对{}; 第三,对组合型数组赋值时,可采用{},对非组合型数组赋值时,可采用’{} (注意单撇号); 第四,在描述覆盖点和仓时使用{}; 第五,在定义enum/struct时, 使用{} 。
  • 关键词: 花括号,()
  • 避坑指南: SV中应该使用begin…end的字段切勿使用{}去替代。

方法中的参数如果具有默认值该如何使用?

  • 回答: 方法中参数默认值是实用方式(不建议使用参数默认方向和默认数值类型)。在调用方法并传递参数过程中可按位置或按名称传递参数。在更常见的按位置传递参数过程中,如果某个参数可采用其默认值,那么需要为它留好“空的位置”,除非该默认参数的位置在参数列表的最后。
  • 关键词: 参数默认值,参数传递
  • 避坑指南: 将所有带有默认值的参数声明均放在参数列表的最后,便于外部调用方法时的参数省略。

方法中形式参数的方向描述符inout和ref的差别?

  • 回答: inout方向会在方法调用中完成入口处由外部变量到形式参数的拷贝,以及在方法退出时,由形式参数到外部变量的拷贝, 即一共2次值的拷贝; ref则是将外部变量本身传递进入,即不再发生形式参数的值拷贝过程, ref也可理解为指针(reference) 。
  • 关键词: 形式参数方向, in out, ref
  • 避坑指南: 如果要对某个外部变量进行持续“跟踪”那么应该使用ref方向描述符, 并且在task中对其跟踪。

package中可以定义什么类型?

  • 回答: package中可以定义数据变量、方法(task/function) 、DPI方法声明、class、parameter、cover group和property(断言属性) 等。
  • 关键词: package
  • 避坑指南: package中不可以包含(或编译)module和interface哦~

持续更新