第二章:数字电路基础回顾——数制与编码、组合逻辑、时序逻辑、状态机

各位同学,欢迎来到第二章。说实话,很多刚入行的验证工程师,总觉得数字电路基础是“做设计的人该操心的事”。我当年也这么想,直到有一次在验证一个复杂的握手协议时,因为对状态机跳转条件理解有偏差,白白浪费了两周时间。嗯,从那以后我明白了——验证工程师不懂电路基础,就像厨师不认识盐和糖。

这一章,我们快速过一遍数字电路的四大基石。别担心,我不会讲得太深,但我会告诉你哪些坑我替你踩过了。

2.1 数制与编码——验证工程师的“度量衡”

验证工作里,你天天跟数据打交道。数制和编码,说白了就是数据的“语言”。

2.1.1 常用数制

  • 二进制(Binary):电路唯一认识的语言。0和1,简单粗暴。
  • 十进制(Decimal):我们人类习惯的,但电路不认识。
  • 十六进制(Hex):验证中最常用的“缩写”。4位二进制对应1位十六进制,写testbench时你绝对离不开它。

我个人习惯:在写验证代码时,所有常量尽量用十六进制表示。比如 8'hA58'b10100101 好读得多,也不容易数错位数。

2.1.2 编码方式

编码就是把信息转换成二进制。验证中你至少得懂这几种:

编码类型 说明 验证中的坑
原码/反码/补码 有符号数的表示方式 减法运算时,补码最容易出错。我曾经在验证一个ALU时,忘了把负数扩展成正确位宽,结果仿真结果全错。
格雷码(Gray Code) 相邻数值只有1位不同 跨时钟域传递计数器时,格雷码是神器。但注意:格雷码不能直接做算术运算。
BCD码 用4位二进制表示1位十进制 显示驱动芯片常用。验证时记得检查“无效状态”(比如1010~1111)的处理。
奇偶校验码 增加1位校验位,检测单比特错误 只能检错,不能纠错。别指望它对付多比特翻转。

小技巧:写testbench时,用 $signed()$unsigned() 系统函数来显式控制符号位,能避免很多隐式转换的bug。

2.2 组合逻辑电路——没有记忆的“直性子”

组合逻辑,输出只取决于当前输入。它没有记忆,像个直性子的人——你给它什么,它立刻给你什么。

2.2.1 基本门电路

与、或、非、与非、或非、异或、同或。这些是数字电路的“原子”。你想想看,再复杂的芯片,底层都是这些门搭起来的。

2.2.2 常用组合逻辑模块

  • 多路选择器(MUX):从多个输入中选一个。验证时注意“选择信号”的优先级和毛刺。
  • 译码器/编码器:地址译码、指令译码都用它。我曾经在验证一个3-8译码器时,发现使能信号没处理好,导致多个输出同时有效——这在设计中是致命的。
  • 加法器:半加器、全加器、超前进位加法器。验证加法器时,别忘了检查进位链的延迟。
  • 比较器:大于、小于、等于。注意无符号数和有符号数的比较方式不同。

避坑指南:我曾经在验证一个组合逻辑时,发现仿真结果和预期总差一点。查了三天,最后发现是代码里用了 if-else 但漏掉了 else 分支,导致综合出了锁存器(Latch)。记住:组合逻辑一定要覆盖所有输入条件,否则综合工具会“帮你”加个锁存器,时序就全乱了。

2.3 时序逻辑电路——有记忆的“老江湖”

时序逻辑,输出不仅取决于当前输入,还取决于之前的状态。它有记忆,像个老江湖——它会记住你之前做过什么。

2.3.1 触发器(Flip-Flop)

D触发器是最基本的时序单元。时钟上升沿采样数据,然后保持到下一个时钟沿。验证时,你主要关注三点:

  • 建立时间(Setup Time):数据必须在时钟沿之前稳定。
  • 保持时间(Hold Time):数据必须在时钟沿之后保持稳定。
  • 复位行为:同步复位还是异步复位?异步复位容易产生亚稳态,我建议尽量用同步复位。

2.3.2 寄存器与移位寄存器

寄存器就是一组D触发器。移位寄存器在串并转换、流水线设计中很常见。验证时,记得检查移位方向(左移/右移)和移位位数是否正确。

2.3.3 计数器

计数器是时序逻辑的“活字典”。二进制计数器、格雷码计数器、约翰逊计数器……验证计数器时,我最常犯的错是“初始值”没设对,导致计数结果整体偏移。

我的经验:验证时序逻辑时,一定要在仿真波形里同时观察时钟、复位、数据输入和数据输出。很多时候,问题就出在“时序错位”上——比如数据在时钟上升沿变化,但采样点却在下降沿。

2.4 状态机基础——数字电路的“大脑”

状态机(FSM),是数字电路里最核心的控制逻辑。说白了,它就是告诉电路“现在该干什么,下一步该干什么”。

2.4.1 状态机的两种类型

类型 特点 验证要点
Moore型 输出只取决于当前状态 输出比状态变化晚一个时钟周期,注意时序对齐
Mealy型 输出取决于当前状态和输入 输入变化可能立即影响输出,容易产生毛刺

2.4.2 状态机的三要素

  • 状态寄存器:记住当前状态。
  • 次态逻辑:根据当前状态和输入,计算下一个状态。
  • 输出逻辑:根据当前状态(或加上输入),产生输出。

2.4.3 验证状态机的常见陷阱

我验证过不下20个状态机,踩过的坑可以写本书了。这里说三个最常见的:

  1. 状态编码没选对:二进制编码省寄存器,但容易产生毛刺;独热码(One-Hot)抗毛刺,但费寄存器。我建议:状态数少于8个用独热码,多于16个用二进制码。
  2. 漏掉了非法状态:状态机如果进入了一个未定义的状态,会怎么样?很多设计直接“死机”了。一定要加 default 分支,让状态机回到安全状态。
  3. 输出条件不完整:Mealy型状态机的输出,很容易漏掉某些输入组合。验证时,用随机激励配合覆盖率分析,能有效发现这类问题。

实用建议:写状态机时,我习惯把状态定义、次态逻辑、输出逻辑分成三个 always 块来写。这样代码清晰,验证时也容易定位问题。另外,仿真时用 $display 打印状态变化,比看波形快得多。

本章小结

数字电路基础,是验证工程师的“内功”。数制与编码让你读懂数据,组合逻辑让你理解运算,时序逻辑让你把握节奏,状态机让你掌控流程。这四块内容,你越熟练,验证时就越能快速定位问题。

下一章,我们会进入硬件描述语言(Verilog/VHDL)的世界。到时候,我会教你如何用代码把这些电路“描述”出来。嗯,那才是验证工程师真正的战场。


公众号:蓝海资料掘金营,微信deep3321