验证那些事儿【SV&C++ Part 1】

本系列文章从秋招高频问题入手,深度剖析 SV 和 C++,如有错误之处,欢迎批评指正!

联系邮箱:zhangshaopu@ufl.edu

必考题 1:什么是 OOP?三个特性?/ 面向对象编程的优势?

OOP 的全称是 Object-Oriented Programming,也就是面向对象编程;三大特性分别是:封装继承多态

封装指的是:将数据和函数封装在一个 class 中,对外部隐藏细节,仅仅公布一些接口,类似一个黑盒子

  • 好处:提高安全性和模块化程度
  • 对应的语法:publicprotectedlocal 等关键词
    • local:只有该类 可以访问成员,子类和外部无法访问
    • protected:该类和子类 可以访问成员,外部无法访问

继承指的是:一个类能够从另一个类继承属性和方法

  • 好处:提高代码的复用性
  • 对应的语法:extends 等关键词

多态指的是:允许【基类句柄】指向【派生类对象】,并根据【实际指向的对象类型】来调用相应的方法

  • 好处:提高灵活性和扩展性
  • 对应的语法:virtual 等关键词

必考题 2:OOP 为什么要用 virtual?什么时候用 virtual?

virtual 关键字是实现 多态 这一特性的关键,在 SV 中很多地方都需要使用 virtual

首先就是 virtual class,也就是虚类

它常用于定义 类的模板,往往作为 基类 存在。因此,虚类 不能被实例化,只能在 被重载后 实例化

uvm 中有一些显著的虚类,例如 uvm_void  uvm_reg

其次是 virtual interface

virtual interface 是一个 句柄,指向真实的 interface

在 uvm 的设计模式中,一般在 top 定义 interface 的实例;在 driver 和 monitor 里面定义 virtual interface,然后将 top 的 interface 通过 config_db 机制和 virtual interface 联系起来

最后是 virtual task/function

如果子类重写了父类的虚方法,使用父类指针或引用调用该方法时,实际调用的是子类中重写后的版本。这种机制极大地提高了代码的灵活性和扩展性

如果某一 class 会被继承,则用户定义的 task/function 都应该加上 virtual 关键字,以备后续扩展


必考题 3:OOP 的虚函数和纯虚函数的区别?

虚函数 更多强调的是 OOP 的 多态性:通过在父类中实现一个虚函数,并在子类中完成覆盖,则通过父类指针调用该方法时,呈现的是子类的实现;使用的关键词是:virtual

纯虚函数 更多强调的是 OOP 的 继承性:纯虚函数 不会被实现,只是起一个 规范 的作用,表示:继承这个类的时候必须实现这个函数;使用的关键词是:pure virtual


必考题 4:OOP 中的深复制(Deep Copy)和浅复制(Shallow Copy)

深复制浅复制 都指的是:复制一个对象,而不是句柄;它们的区别在于:

  • 深复制会复制原有对象中的 所有成员变量,包括【原有对象中的句柄】指向的内容;深复制的函数往往 需要自己定义
    • 在 UVM 中,可以使用 UVM 提供的 clone copy 实现深复制
    • // copy
      item item_new = new();
      item_new.copy(item_old);
      
      // clone
      item item_new;
      $cast(item_new, item_old.clone());
  • 浅复制同样复制原有对象中的 所有成员变量,但 不包括【原有对象中的句柄】指向的内容
    • 浅复制通过 new 即可实现
    • item item_new;
      item_new = new item_old;


必考题 5:SV 中的四大数据结构?

定宽数组:声明的时候需要 确定数组大小,编译后分配的 内存空间固定,不可改变

定宽数组可以分为 合并数组非合并数组

  • 合并数组 在内存中 连续存储,适用于【逐位 / 逐字节访问】数据的场景
  • 非合并数组 在内存中 按元素存储(每个元素可以是一个复杂的数据类型),适用于【按元素访问】数据的场景
// 一个 8 位宽、16 个元素的定宽数组
logic [7:0] fixed_array [0:15];

// 合并数组特点:在内存中连续存储,适用于需要逐位或逐字节访问数据的场景
logic [7:0] packed_array;
logic [11:0][7:0] packed_array;

// 非合并数组特点:在内存中按元素存储,每个元素可以是一个更复杂的数据类型,适用于需要按元素访问而不是逐位访问的场景
logic unpacked_array[0:7];
logic [11:0] unpacked_array[0:7];
动态数组:声明的时候 不需要确定数组大小,而是在仿真的时候 通过 new 进行空间分配
int dynamic_array[];     // 声明一个动态数组
dynamic_array = new[10]; // 分配 10 个元素的大小

关联数组:使用 索引访问 元素,索引可以是 任何的标量类型

它的结构类似 hash,也是一种 键值对 的数据结构

int associative_array[string]; // 声明一个字符串键的关联数组
associative_array["key1"] = 1;
associative_array["key2"] = 2;

队列:是一种 FIFO 结构,支持 动态增长和缩小

SV 中的队列结合了 链表 的优点,可以 在一个队列的任何位置进行增删

int queue[$];

必考题 6:上述四种数据结构的应用场景?

定宽数组:针对 信号位宽固定 的数据进行建模,例如 AMBA 总线中的控制信号和数据信号以太网包的非 payload 字段 等等

动态数组:不确定数据大小 的情况使用,例如 以太网包的 vlan_id 字段 和 payload 字段

关联数组:适用于 非连续 / 稀疏 访问数据的场景

队列:适用于需要 频繁插入和删除 的情况


必考题 7:SV 中的 function 和 task 的区别?

区别functiontask
输入function 中 至少有一个输入参数task 对输入参数没有限制
输出function 只能通过 return 返回一个值task 没有返回值,通过定义参数列表中的 output 或 inout 返回
时序

function 中不能有任何时序控制语句

它在 仿真 0 时刻 开始执行

task 可以包含时序控制语句

它可以在 非 0 时刻 执行

调用function 只能调用 functiontask 都可以调用

必考题 8:从可综合的角度谈谈 function 和 task?

只要 function 和 task 内部的代码可综合,并且 function 和 task 满足语法范式的要求,就可以综合

可以把 function 看作 组合逻辑的子 module,task 看作 时序逻辑的子 module

但是我一般不在 RTL 中使用 function 和 task,因为不够直观,debug 也很复杂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值