返回题库首页 · 数据类型与数组笔记

先独立作答,再展开参考答案。八股原题沿用原笔记编号;扩展练习为本站根据对应考点编写。

Q1 · wire、reg 和 logic 有什么区别?

八股原题 · Q1 · 题型:问答

考点

  • net 与 variable 的本质区别
  • logic 能否替代 wire
  • 多驱动和 inout 的限制
展开参考答案 · Q1

题目来源(原笔记记录)

  • 平头哥 · 数字IC验证 · 校招 · 一面(wire、reg、logic 三者的区别)
  • 集益威 · 数字IC验证 · 校招 · 一面(logic、reg、wire 三者的区别)
  • 泰凌微 · 数字IC验证 · 实习(logic 与 bit 的区别,logic 额外特性以及和 wire 的区别)
  • 泰凌微 · 数字IC验证 · 校招 · 一面(inout 端口能否用 logic 类型)
  • 面试书《Cracking Digital VLSI Verification Interview》· 第4章 Q18(对应题目:reg、wire 与 logic 的区别)

三类信号类型的核心区别是:wire 指定网络种类,reg 和 logic 指定四态数据类型;裸声明 reg/logic 通常创建变量;logic 能替代 reg,但替代不了需要多驱动的 wire。

第一,wire 是网络类型。 它表示”连接”关系:连续赋值、模块端口、多个驱动源都可以驱动它,它自己不保存过程赋值的结果。

第二,reg 是变量类型。 它在 always、initial 这些过程块里赋值。注意名字叫 reg,不代表综合出来一定是寄存器,也可能综合成普通连线。

第三,裸声明的 logic 通常是四态变量。 平时用它替代 reg,也能由一根连续赋值驱动(单驱动);但它本质是变量,多个驱动源同时驱动会冲突。

类型类别过程赋值连续赋值多驱动解析
wirenet否是是
裸声明 regvariable是SV 中允许单一连续驱动,不能混用过程驱动否
logicvariable是单驱动时可以否

inout 的限制:双向端口通常必须声明成 net 类型(wire);信号确实可能有多个驱动源时也一样,用 wire,不能图省事用 logic。

一句话:wire 管连接和驱动解析,logic 表示四态数据;变量可使用 logic,需要网络解析时使用 wire logic 等网络声明。

表述补充:上文将裸声明 logic 与变量联系起来便于入门;严格说 logic 是四态数据类型,net/variable 是对象种类,例如 wire logic bus; 合法且支持网络解析。表中 reg 的连续赋值限制按传统 Verilog 用法理解;SystemVerilog 对变量单一连续驱动的规则不能仅按 reg/logic 名字区分。


E1 · 双驱动信号应如何声明?

扩展练习 · E1 · 题型:代码改错 · 对应原题:Q1

考点

  • net 与 variable 的本质区别
  • logic 能否替代 wire
  • 多驱动和 inout 的限制

两个连续赋值分别输出 0 和 1,共同驱动 logic bus。应怎样修改?修改后 bus 是什么值?

展开参考答案 · E1

使用网络声明 wire logic bus;(或 wire bus;),由网络类型进行驱动解析。在两个默认等强度驱动分别为 0 和 1 的条件下,解析结果为 X。logic 是四态数据类型,wire 是网络种类;wire logic 正是二者的组合,不能把 logic 一概等同于变量种类。


Q2 · bit 与 logic 有什么区别?什么时候使用二态类型?

八股原题 · Q2 · 题型:问答

考点

  • 二态与四态
  • X/Z 检测
  • 仿真性能与语义正确性的取舍
展开参考答案 · Q2

题目来源(原笔记记录)

  • 泰凌微 · 数字IC验证 · 校招 · 一面(logic 与 bit 的核心区别)
  • 泰凌微 · 数字IC验证 · 实习(logic 与 bit 的区别)
  • 豪威科技 · 数字IC验证 · 校招 · 一面(四值逻辑 vs 二值逻辑:初值问题)
  • 平头哥 · 数字IC验证 · 秋招 · 笔试(bit/int/logic/byte 差异,检测 x 态传播用哪个类型)
  • 面试书《Cracking Digital VLSI Verification Interview》· 第4章 Q19(对应题目:bit 与 logic 数据类型)

bit 是二态,只有 0 和 1;logic 是四态,有 0、1、X、Z。 核心区别是:四态值赋给二态变量时,X/Z 会被转成 0,可能把复位遗漏、多驱动这类问题悄悄掩盖掉。

第一,表示能力。 bit 只存 0 和 1;logic 保留 X 和 Z,能反映硬件的未知态。

第二,使用位置。 连接 DUT、采样值、要检查未知态的信号,用 logic;确定不会出现 X/Z 的抽象数据,比如计数器、配置开关,用 bit,省内存、仿真也更快。

第三,查未知态。 想检查一个向量里有没有 X/Z,用 $isunknown(bus_data),有就报错。

一句话:bit 快但会吞掉 X/Z,logic 能保留硬件未知态;连接 DUT 的信号优先用 logic。


E2 · 二态转换会丢掉什么?

扩展练习 · E2 · 题型:代码阅读 · 对应原题:Q2

考点

  • 二态与四态
  • X/Z 检测
  • 仿真性能与语义正确性的取舍
logic [3:0] a = 4'b10xz;
bit [3:0] b;
initial begin
  b = a;
  $display("%b %b %0d %0d", a, b, $isunknown(a), $isunknown(b));
end

说明输出及未知态检查应放在哪里。

展开参考答案 · E2

输出 10xz 1000 1 0。逐位转换时 X/Z 变为 0,因此应在转换前对四态采样值调用 $isunknown;对 b 检查已经无法发现信息丢失。


Q3 · packed 数组和 unpacked 数组有什么区别?

八股原题 · Q3 · 题型:问答

考点

  • 声明位置与内存布局
  • 整体位运算和逐元素访问
  • 混合数组维度顺序
展开参考答案 · Q3

题目来源(原笔记记录)

  • 合见工软 · 数字IC验证 · 校招 · 一面(packed 数组与 unpacked 数组的核心区别)
  • 面试书《Cracking Digital VLSI Verification Interview》· 第4章 Q26(对应题目:packed 数组与 unpacked 数组)

packed 是连续位向量,unpacked 是元素集合。 分界线在变量名:写在名字左边的是 packed 维度,写在右边的是 unpacked 维度。

第一,packed 维度。 所有 bit 紧密排成一段连续向量,可以整体做算术、位运算、切片和类型转换。

第二,unpacked 维度。 是一组独立元素,适合建模存储器、表格、对象集合。

第三,混合数组。 比如 bit [31:0] mem [0:255],mem[3][7:0] 是先用右边的 unpacked 下标选第 3 个 word,再用左边的 packed 下标选低 8 位。

bit [7:0] packed_data;          // 一个 8 bit 向量
bit       unpacked_data [8];    // 8 个独立 bit 元素
bit [31:0] mem [0:255];         // 256 个 32 bit word

packed 数组必须由可打包的位类型组成,类句柄不能放进去。

一句话:左边 packed 是连续位向量,右边 unpacked 是元素集合;读混合数组先看右边选元素,再看左边选位。


E3 · 混合数组的位宽与索引

扩展练习 · E3 · 题型:代码阅读 · 对应原题:Q3

考点

  • 声明位置与内存布局
  • 整体位运算和逐元素访问
  • 混合数组维度顺序

对 logic [1:0][7:0] lane [0:3];,$bits(lane)、$bits(lane[0])、$bits(lane[0][1]) 分别是多少?如何取第 3 个元素的高字节?

展开参考答案 · E3

分别是 64、16、8 bit。第 3 个元素下标为 2,表达式为 lane[2][1]。最先索引 unpacked 元素,再索引 packed 的高字节。


Q4 · 动态数组、队列和关联数组如何选择?

八股原题 · Q4 · 题型:问答

考点

  • 三类动态容器的分配方式
  • 常用操作和使用场景
展开参考答案 · Q4

题目来源(原笔记记录)

  • 字节跳动 · 数字IC验证 · 校招 · SOC方向(动态数组、关联数组、队列三者的区别)
  • 泰凌微 · 数字IC验证 · 校招 · 一面(动态数组与队列的区别)
  • 知存科技 · 数字IC验证 · 校招(动态数组、队列、关联数组)
  • 泰凌微 · 数字IC验证 · 实习(动态数组、关联数组、队列的区别)
  • 合见工软 · 数字IC验证 · 校招 · 一面(动态数组的使用优势)
  • 面试书《Cracking Digital VLSI Verification Interview》· 第4章 Q21(对应题目:大数组建模:动态数组 vs 关联数组)

三种动态容器怎么选,看三件事:索引连不连续、大小变不变、要不要首尾操作。

第一,动态数组。 用 new[N] 整体分配,长度定了之后适合大量随机访问。

第二,队列。 自动伸缩,push_back、pop_front 很方便,适合 FIFO、频繁首尾插删。

第三,关联数组。 用哪个键才分配空间,适合稀疏地址、ID 映射;常用方法有 exists、first、next、delete。

容器下标大小典型用途
动态数组 a[]连续整数用 new[N] 整体分配,不会自动变长长度偶尔变化、之后大量随机访问
队列 q[$]连续整数自动伸缩FIFO、待比较事务、频繁首尾插删
关联数组 aa[index_t]整数、枚举、字符串等用到哪个键才分配稀疏地址空间、ID 到事务的映射

一句话:频繁变长用队列(自动伸缩),长度偶尔变、之后随机访问用动态数组(new 分配、不会自动变长),稀疏键值映射用关联数组。


E4 · 为三个缓存选择容器

扩展练习 · E4 · 题型:场景分析 · 对应原题:Q4

考点

  • 三类动态容器的分配方式
  • 常用操作和使用场景

为以下数据各选一种容器并解释:固定一笔包的 N 个字节;按到达顺序处理的待比较事务;64 位稀疏地址对应的数据。

展开参考答案 · E4

包体用动态数组,一次按 N 分配;待比较事务用队列,尾入头出;稀疏地址表用 data_t mirror[bit [63:0]] 形式的关联数组,避免分配完整地址空间。容器选择还需服从事务顺序要求,乱序匹配通常需要按 ID 建表。


Q5 · 动态数组如何扩容并保留原数据?

八股原题 · Q5 · 题型:问答

考点

  • new[] 的动态数组语义
  • 旧数组作为复制源
展开参考答案 · Q5

题目来源(原笔记记录)

  • 泰凌微 · 数字IC验证 · 校招 · 一面(动态数组初始化扩容方法)
  • 面试书《Cracking Digital VLSI Verification Interview》· 第4章 Q29(对应题目:动态数组扩容并保留原值)

动态数组扩容想保留旧数据,必须把旧数组作为 new[] 的第二个参数。 只写 a = new[N] 等于重新开一块数组,元素重新初始化为其类型默认值。

int a[] = '{10, 20, 30};
a = new[5](a);
// a[0:2] 保留 10、20、30,新元素使用默认值 0

两个注意点:新长度比旧长度小时,只复制放得下的前几个元素;new[N] 是动态数组专用构造,类对象用 new(),两者不是一回事,别搞混。

一句话:动态数组保留数据扩容写 new[new_size](old_array);省略复制源就相当于重新建一块数组。


E5 · 扩容、缩容与重新分配

扩展练习 · E5 · 题型:代码阅读 · 对应原题:Q5

考点

  • new[] 的动态数组语义
  • 旧数组作为复制源
int a[] = '{10, 20, 30};
initial begin
  a = new[5](a);
  a = new[2](a);
  a = new[4];
end

每步后的内容是什么?

展开参考答案 · E5

依次为 '{10,20,30,0,0}、'{10,20}、'{0,0,0,0}。int 默认值为 0;最后一步没有复制源,因此重新初始化全部元素,而不是保留前两个值。


Q6 · 关联数组适合什么场景?常用方法有哪些?

八股原题 · Q6 · 题型:问答

考点

  • 稀疏存储
  • 键是否存在的安全检查
  • 遍历方法
展开参考答案 · Q6

题目来源(原笔记记录)

  • 字节跳动 · 数字IC验证 · 校招 · 一面(关联数组的使用场景、核心优势与内置函数)
  • 面试书《Cracking Digital VLSI Verification Interview》· 第4章 Q21(对应题目:大数组建模:动态数组 vs 关联数组)

关联数组只为实际用到的键分配空间,特别适合超大但稀疏的地址空间、寄存器镜像、outstanding 表、字符串配置表。

第一,核心优势。 键可以是整数、枚举或字符串,内存按需分配,稀疏的大空间也不会爆内存。

第二,使用习惯。 读取前先 exists()——对不存在的键读取会得到默认值,容易把”没有这一项”和”这一项是零”搞混。

第三,常用方法。 num()、exists()、first()、last()、next()、prev()、delete()。

if (pending.exists(addr))
  pending.delete(addr);

一句话:关联数组是稀疏键值表;最重要的习惯是读取前先 exists(),遍历用 first/next。


E6 · 区分不存在的键和零值

扩展练习 · E6 · 题型:简短编码 · 对应原题:Q6

考点

  • 稀疏存储
  • 键是否存在的安全检查
  • 遍历方法

声明 int pending[int];,写出检查键 7、存在时打印其值并删除的代码。为什么不能只检查 pending[7] == 0?

展开参考答案 · E6
if (pending.exists(7)) begin
  $display("%0d", pending[7]);
  pending.delete(7);
end

不存在的 int 元素读取得到默认值 0,真实存储的值也可以是 0,必须用 exists 区分。