芯片验证是确保芯片设计正确性的关键环节。随着芯片复杂度的不断提升,验证工作在整个设计流程中所占比重越来越大,已成为芯片成功与否的决定性因素。这一讲我们将从基础概念入手,系统介绍验证的重要性、基本流程与方法、验证层次和评估指标,探讨实际项目流程中可能遇到的挑战与敏捷验证的应对思路,分析当前验证领域面临的困境以及使用高级语言进行验证的价值,并展望芯片验证众包这一未来解决方案。

接下来,你将了解:

  • 芯片验证的基本概念:什么是芯片验证,为什么它对芯片设计至关重要,以及验证不足可能导致的严重后果。
  • 验证流程与敏捷方法:完整的验证流程是如何开展的,敏捷验证的原则与实践,以及从计划到报告的完整验证步骤。
  • 验证层次体系:从单元测试到系统测试的不同验证层次及其特点。
  • 验证质量评估:如何通过功能正确性、代码/功能覆盖率、缺陷密度等关键指标来评估验证质量。
  • 当前验证挑战与高级语言价值:验证面临的工作量、成本、人才等挑战,以及使用高级语言(如 Python)的优势及其对验证的推动作用。
  • 芯片验证众包前景:作为应对挑战的创新方案,众包验证的可行性、技术路线,以及 Picker 等工具在其中的作用。

芯片验证的定义

芯片验证是芯片开发流程中的关键环节,它的目标是确保设计的芯片在功能、性能和功耗等方面都满足预定的规范要求。

在本课程中,我们主要关注的是****功能验证,也就是验证设计的电路逻辑是否满足既定需求,回答的核心问题是:“这个设计真的能按照预期工作吗?”

芯片验证并不等同于芯片测试。验证发生在设计阶段,通过各种方法(如仿真)在芯片制造前发现问题;而测试则是在芯片制造后,通过物理手段检查实际芯片是否工作正常。

想象一下,如果你的手机突然无法计算,或者自动驾驶汽车的导航系统出现了错误,这将是多么可怕的事情!芯片验证正是为了防止这些问题发生。

一旦芯片被制造出来,修改错误的成本将会非常高昂。以下是几个验证不足导致灾难性后果的经典案例:

  • Intel Pentium FDIV Bug (1994): 浮点单元的一个计算错误导致 Intel 不得不召回大量处理器,造成约 4.75 亿美元的损失。
  • AMD Barcelona Bug (2007): TLB 错误导致系统不稳定,AMD 不得不降低处理器频率并推迟产品发布。
  • Intel Sandy Bridge 芯片组缺陷(2011):缺陷源于芯片组电路设计中的问题,SATA 端口可能在某些情况下退化,影响设备性能。根据 CNET - Intel’s Sandy Bridge chipset flaw: The fallout,Intel 预计 2011 年第一季度损失约 3 亿美元的销售收入,并支付 7 亿美元的维修和更换费用,总计约 10 亿美元的损失。

3. 验证流程与敏捷方法

验证与设计的关系

芯片验证并不是设计完成后的一个简单检查步骤,而是与设计过程并行进行的关键活动。

设计团队和验证团队通常从同一份规范出发,但有着不同的实现方式和关注点:

  • 设计团队:开发 DUT,关注功能实现、代码的可综合性和电路效率。
    DUT(Design Under Test)是****待测设备 ,通常是一个模块或子系统。
  • 验证团队:开发验证平台,专注于验证功能的正确实现。

这种设计与验证的分离确保了两个团队能够独立理解规范,从而提高发现潜在错误的概率。

img

完整的验证流程

芯片验证是一个系统工程,确保设计按预期工作。其主要流程概括如下:

  1. 制定验证计划:此阶段是验证工作的起点,旨在明确“验证什么”和“如何验证”。主要任务是定义验证的总体范围、目标、采用的验证方法、所需资源、整理功能点等。
  2. 搭建验证平台:根据验证计划,此阶段专注于构建用于执行测试的验证环境,包含测试输入产生、信号收集和结果检查等组件。平台搭建的初步成功通常以通过基本的“冒烟测试”为标志,证明环境主要功能通路工作正常。
  3. 编写测试用例:在验证平台基础上,此阶段的核心是依据验证计划,编写实现具体的测试用例,用以全面覆盖预定的功能点、边界条件及异常场景。
  4. 收集 Bug 和覆盖率:此阶段将运行已开发的测试用例,识别并记录设计中出现的缺陷,同时收集代码覆盖率和功能覆盖率数据以评估验证的完备程度。这是一个包含调试和分析的迭代过程。
  5. 进行回归测试:在设计代码发生变更(如缺陷修复或功能更新)后,重新运行相关测试用例,确保修改的正确性且未引入新问题。
  6. 撰写验证报告:在验证达到特定节点或结束时,总结整个验证过程、测试结果、缺陷状态、覆盖率达成情况及存在的风险,为项目决策提供依据。

功能点是指芯片设计中需要验证的具体功能或特性,通常从设计规范和要求文档中提取。

测试点是从功能点派生出的具体测试用例或场景,用于确保功能点的每个方面都被彻底测试。

功能点(Functional Point)是验证的“目标”,而测试点(Test Point)是为达成这个目标而采取的“具体行动和步骤”

假设我们正在设计一个芯片,其中包含一个 DMA(Direct Memory Access,直接内存访问)模块。

第 1 步:从设计规范中提取“功能点”

翻开设计规范文档,关于 DMA 的部分有这样一条要求:

  • 功能点 1:DMA 模块必须支持**突发传输(Burst Transfer)**模式。

这是一个很明确的功能点。项目经理和验证负责人在看进度时,会关注“突发传输”这个功能点是否已经验证。

第 2 步:将“功能点”分解为具体的“测试点”

现在,验证工程师拿到这个功能点,他需要思考:“为了证明 DMA 的突发传输功能是完全正确的,我需要测试哪些场景?” 他会从不同维度进行分解:

  • 正常工作场景 (Happy Path)
    • 测试点 1.1:测试突发长度为 4(Burst Length=4)的传输。
    • 测试点 1.2:测试突发长度为 8(Burst Length=8)的传输。
    • 测试点 1.3:测试不同的数据位宽(32 位,64 位)下的突发传输。
  • 边界条件场景 (Boundary Conditions)
    • 测试点 1.4:测试最小突发长度(例如 1)的传输。
    • 测试点 1.5:测试最大突发长度(例如 16)的传输。
    • 测试点 1.6:测试传输的数据总量恰好等于一个突发包的大小。
    • 测试点 1.7:测试源地址或目标地址未对齐(Unaligned)的情况。
  • 异常和错误场景 (Exception/Error Scenarios)
    • 测试点 1.8:在突发传输过程中,外部总线(Bus)返回一个错误信号,看 DMA 是否能正确中止并上报错误。
    • 测试点 1.9:配置一个不支持的突发长度,看 DMA 是否能报错或不工作。
    • 测试点 1.10:在传输过程中,尝试通过软件中止(abort)任务。
  • 组合和压力场景 (Corner Cases)
    • 测试点 1.11:一个突发传输刚结束,立即启动下一个突发传输(背靠背,Back-to-Back)。
    • 测试点 1.12:同时启动多个 DMA 通道进行突发传输,测试仲裁和带宽压力。

现在,您可以看到,为了验证“支持突发传输”这一个功能点,我们派生出了十几个具体的、可操作的测试点。只有当所有这些测试点都成功通过后,我们才能有信心地说,这个功能点已经得到了充分和彻底的验证。

第一阶段:需求理解与规划

  1. 深入研究规范文档

    1. 反复、仔细地阅读功能规范、架构规范。
    2. 在需要时参考详细设计规范,以挖掘和覆盖边界情况。
    3. 目标是全面理解设计意图、功能、接口、性能指标和操作模式。
  2. 跨部门协作与澄清

    1. 与架构师、设计师以及其他验证工程师紧密合作。

    2. 采用多种沟通方式确保理解一致:

      • 串讲: 由需求提出者(如架构师)向验证/设计团队讲解需求。
      • 反串讲: 由验证/设计团队向需求提出者复述他们的理解,以确认无误。
      • 评审: 组织正式的会议,共同审查需求的准确性、完整性和可测试性。
    3. 此阶段的目标是消除歧义,就需求达成共识。

  3. 确定组织方法
    当我们完成上一阶段之后,需要开始制定验证计划。在制定验证计划时,我们需要组织需求和功能点,主要有两种方法:

    • 自下而上
      1. 核心:设计的具体模块或接口出发,强调设计视角。
      2. 优点:易提取具体需求,便于链接到代码覆盖点,适合模块级验证。
      3. 缺点:能产生大量低层需求,不易把握系统全局。
      4. 适用:模块验证、控制逻辑复杂的单元、有详细实现文档时。
    • 自上而下
      1. 核心:系统级的使用场景或数据流出发,强调客户/验证视角。
      2. 优点:能更好地把握系统级功能和性能,可在早期进行。
      3. 缺点:需要清晰的高层规划,用例可能非常多,覆盖点可能偏宏观。
      4. 适用:SoC(片上系统)验证、数据流为主的设计、有清晰架构或用例定义时。
    • 实践中, 常常将两者结合,先用“自上而下”定义整体框架和主要场景,再用“自下而上”细化关键模块或接口的需求 。选择哪种方式取决于项目特点和可用信息。

第二阶段:功能点识别与测试点细化

  1. 识别功能点
    1. 明确需求之后,我们需要识别设计规范和文档中需要验证的内容,例如关键功能、配置组合方式、操作模式、数据流、时序关系、协议规则等。它们构成了需要验证的功能点。
    2. 对功能点进行优先级排序,重点关注高风险、新设计、关键性能或客户要求的部分。
  2. 分解与细化测试点
    1. 将每个功能点进一步分解为具体的、可测量的****测试点覆盖项。这是定义如何衡量功能点是否被覆盖的关键步骤。

    2. 我们可以从不同维拆分测试点,例如:

      • 场景:不同的特权状态,如 RISC-V 中某条指令在 M、S、U 特权模式下的行为,应该是什么样的。
      • 功能:设计的核心操作,如算法计算、数据转换、控制逻辑。
      • 白盒:关注内部实现细节,如状态机状态和跳转、内部计数器边界值、流水线状态等。
      • 接口:模块或芯片与外部的交互,如总线协议时序、握手信号、中断处理。
      • 异常:错误处理、故障注入、超时、边界条件下的行为、非法配置或输入。
      • 复位与初始化:注复位后所有相关逻辑都恢复到预期默认值。
    3. 链接测试点与需求:在验证计划中,把每个需求明确地链接到一个或多个具体的测试点其实现类型。

第三阶段:覆盖实现与迭代

**此阶段,我们通过“**覆盖率”这一量化指标来评定验证的完备性。

在仿真过程中,我们需要了解各个****测试点是否被测试激励有效“命中”。为准确获取并体现这一覆盖情况,我们会针对这些测试点编写专门的覆盖代码,用以在仿真时监测和记录它们的激活状态,最终得到覆盖率信息。

**关于覆盖率的具体技术细节,后续课程将详细介绍,此处不作展开。但我们需要知道的是,**验证是一个持续迭代的过程。这意味着需要分析覆盖率报告,找出“覆盖盲区”(即未被充分测试的部分),并据此指导后续的测试用例开发,如此循环,直至各项覆盖率达到预设的验收目标。

敏捷验证

然而,在实践中,尤其是在追求快速迭代和市场响应的 创业公司或新兴业务 中,情况往往更加复杂。有时,为了快速验证想法或抢占市场先机,芯片开发本身也越来越多地借鉴和实践“敏捷开发”的原则:设计实现可能在规范细节尚未完全冻结时就已经开始,甚至出现“设计先行,规范后补”或者设计与规范频繁迭代的情况。

**在这种快速变化、规范可能不完全成熟的环境下,传统的、依赖稳定规范的验证方法会面临巨大挑战。如果严格等待规范最终确定再开始验证,可能会错失市场窗口;而如果基于不稳定的规范进行验证,则可能需要大量的重复工作。 因此,**敏捷验证的概念应运而生。

敏捷验证强调:

  • 早期介入与持续集成:验证工作尽早开始,与设计紧密迭代,持续集成和测试新功能。
  • 适应性与灵活性:能够快速响应需求和设计的变更,调整验证计划和策略。
  • 风险驱动:优先验证最高风险或最不确定的部分。
  • 紧密协作:设计与验证团队之间需要更频繁、更紧密的沟通与协作。

虽然敏捷验证带来了灵活性,但也对验证团队的技术能力、工具链的自动化程度以及团队协作模式提出了更高的要求。如何在快速迭代和保证质量之间找到平衡点,是许多现代芯片开发团队需要面对的课题。

4. 芯片验证的层次

芯片验证可以按照验证对象的规模分为四个主要层次,从小到大依次为:

  1. 单元测试(Unit Testing,UT):单元测试关注的是最小的功能单元,即单个模块或组件。
  2. 块测试(Block Testing,BT):当多个模块之间存在紧密耦合关系时,单独测试每个模块可能效率不高。块测试将这些相互关联的模块组合在一起进行测试。
  3. 集成测试(Integration Testing,IT):集成测试将多个功能块组合在一起,验证它们能否正确协同工作,通常用于验证子系统级别的功能。
  4. 系统测试(System Testing,ST):系统测试也称为 Top 验证,是将所有子系统组合起来,验证整个芯片系统的功能是否符合预期。

🧩 在实际项目中,验证层次的选择应根据项目规模、团队经验和时间预算灵活调整。对于小型项目,可能只需要 UT 和 ST 两个层次;而对于复杂的 SoC 设计,通常需要所有四个层次的验证。


5. 芯片验证指标

如何知道我们的验证工作做得是否足够充分?这就需要一系列指标来评估验证质量,下面是芯片验证中的一些关键指标:

功能正确性

功能正确性是最基本也是最重要的指标,即芯片是否能正确执行设计规范中定义的所有功能。功能正确性是一个定性指标(无法直接用数值来衡量),通常通过以下方式验证:

  • 正常工作条件下的功能测试。
  • 极端条件下的边界测试。
  • 异常情况下的健壮性测试。

测试覆盖率

测试覆盖率是评估验证进展和验证完整性****最重要的量化指标,主要包括代码覆盖率和功能覆盖率。

代码覆盖率

代码覆盖率是一种****隐式覆盖率指标,用于衡量在仿真过程中设计源代码的执行情况。它通过分析代码结构(如行、语句、分支等)来识别哪些部分在测试中被激活,哪些未被执行。

代码覆盖率包括以下几种常见类型:

  • 翻转覆盖率:跟踪寄存器或连线中每一位从 0 到 1 和从 1 到 0 的翻转情况,用于检查基本连接性。
  • 行覆盖率:记录哪些代码行在模拟中被执行。
  • 语句覆盖率:比行覆盖率更细粒度,跟踪每个语句的执行情况。
  • 分支覆盖率:确保控制结构(如 if、case)的布尔表达式在测试中评估为真和假。
  • 表达式覆盖率:验证表达式中的每个条件独立地评估为真和假。
  • 有限状态机覆盖率:测量状态机的状态访问和状态间转换情况。
优点
  • 自动化生成:代码覆盖率可以由工具自动提取并分析,无需手动定义,易于集成到现有验证流程。
  • 识别未执行代码:帮助发现测试中未覆盖的代码区域,提示需要调整输入激励。
局限性
  • 不保证功能正确性:即使达到 100% 的代码覆盖率,也无法确保设计没有错误或功能符合规范,因为它不涉及功能需求的验证。
  • 缺乏规范关联:无法判断是否测试了规范中定义的所有功能,仅关注代码的结构执行。

功能覆盖率

功能覆盖率是一种****显式覆盖率指标,用于衡量在验证仿真的过程中,设计规范中定义的功能需求是否被测试到。与代码覆盖率不同,功能覆盖率需要手动创建,通常基于设计规范或设计的实现细节完成功能点、测试点的划分后,再编写测试点的触发条件在仿真时采样,然后收集结果得到功能覆盖率。

功能覆盖率主要分为以下两种模型:

  • 覆盖组模型:在特定时间点采样状态值,例如使用 Toffee 的 CovGroup 收集功能点和测试点。
  • 覆盖属性模型:观察事件序列的时间关系,例如使用断言验证总线协议的握手序列或状态机的状态转换。
功能覆盖率的优点
  • 与规范直接关联:能够追踪功能需求的测试进度。
  • 识别未测试功能:帮助发现规范中定义但未被测试的功能。
功能覆盖率的局限性
  • 手动创建:需要工程师根据规范定义覆盖模型,过程复杂且耗时。
  • 可能遗漏功能:如果覆盖模型设计不全面,可能无法覆盖所有功能需求。

🔍 思考:如果功能覆盖率很高,但是代码覆盖率不是很高,说明什么?

两者的关系

代码覆盖率和功能覆盖率****相辅相成,共同提供更全面的验证视图:

  • 代码覆盖率从底层视角检查代码执行的完整性,但无法验证功能是否符合设计意图。例如,100% 的代码覆盖率可能仍遗漏关键功能测试。
  • 功能覆盖率从顶层视角确保规范中的需求被测试,但可能无法发现未实现的代码或冗余代码。
  • 结合使用:通过结合两者,验证团队可以同时识别未执行的代码(通过代码覆盖率)和未测试的功能(通过功能覆盖率),从而更准确地评估验证质量和进度。

小结

代码覆盖率是一种自动化的指标,关注设计实现的执行情况,帮助发现测试中的盲点,但不涉及功能正确性。

功能覆盖率是一种手动的指标,关注设计规范的测试情况,确保功能需求的实现和验证,但依赖于覆盖模型的完备性。

在实际验证中,这两者应协同使用,以实现从代码结构到功能需求的全面覆盖,确保设计的可靠性和规范符合性。通常认为代码覆盖率达到 90% 以上,功能覆盖率达到 100% 时,验证工作才算充分。

**⚖️ ****覆盖率 100% 是否意味着设计没有错误?**答案是否定的。

高覆盖率是必要条件,但不是充分条件。我们无法测试所有可能的输入组合和状态,因此即使达到 100% 的覆盖率,设计中仍可能存在未被发现的错误。这也是为什么验证工作需要综合运用多种方法和技术。

缺陷密度

缺陷密度是指在单位代码量中发现的缺陷数量。随着验证的深入,新发现的缺陷应该逐渐减少,缺陷密度曲线应趋于平稳。如果在项目后期仍然有大量新缺陷被发现,这通常意味着验证工作不够充分。

验证效率与成本

验证效率是指在有限的时间和资源下完成的验证工作量。验证成本则包括人力、设备、时间等各类资源消耗。

提高验证效率、降低验证成本是芯片设计企业的重要目标。

7. 高级语言在芯片验证中的价值

**面对验证复杂性、成本和人才方面的挑战,业界也在不断探索新的方法和工具。其中,****在验证中使用高级编程语言(如 Python、Java、C++ 等)**显示出越来越大的价值,原因在于:

  1. 更广泛的人才基础和生态系统:相比传统的硬件描述/验证语言,高级语言拥有更庞大的开发者社区、更丰富的学习资源和成熟的软件库(生态系统),这有助于降低学习门槛,吸引更多不同背景的人才进入验证领域。学术界也认识到,吸引软件工程师参与硬件领域对于应对后摩尔定律时代挑战的重要性。
  2. **与软件测试实践的共通性:**虽然领域不同,但芯片功能验证与软件测试在目标(发现缺陷)、流程(测试规划、用例编写、Bug 管理)、度量(覆盖率)以及环境(大多基于软件仿真)等方面存在诸多共通之处。高级语言及其测试框架(如 pytest)可以更容易地引入软件工程中的最佳实践(如单元测试、持续集成、自动化测试等),提升验证的效率和规范性。
  3. **提升抽象层次和开发效率:**高级语言通常提供更强的抽象能力和更简洁的语法,使得验证工程师可以更专注于验证逻辑本身,而不是底层的信号交互细节(尽管这需要如 Picker 这样的工具进行桥接),从而可能提高验证环境的开发效率和可维护性。

正是这些优势,使得基于高级语言的验证方法成为降低验证门槛、提高效率、促进验证众包模式发展的关键推动力之一。

💡Picker 正是实现这一目标的关键工具之一,它能够将 RTL 设计代码转换为多种高级语言(如 Python、C++、Java 等)的接口,让开发者可以使用自己熟悉的语言来驱动和验证硬件设计。

芯片验证众包的技术路线

为了推动芯片验证众包的发展,我们提出以下技术路线:

  1. 多语言验证工具:开发如 Picker 等工具,允许验证人员使用自己熟悉的编程语言(如 Python、Java、C++ 或 Go)参与验证,降低入门门槛。
  2. 开放学习资源:提供全面、系统的在线学习材料,让任何人都能自学芯片验证知识。
  3. 真实验证案例:基于开源处理器(如”香山昆明湖”RISC-V 处理器)提供实际验证案例,让学习者能够实践所学。
  4. 众包验证平台:建立专门的众包验证平台,连接芯片设计公司和验证人才,组织验证任务的分发和管理。

img

**🚀 **未来展望:我们的愿景是”打开传统验证模式的黑盒,让所有感兴趣的人可以随时随地的,用自己擅长的编程语言参与芯片验证”。这将极大地扩展验证人才池,降低芯片验证成本,加速芯片创新周期。

9. 小结

在本节课中,我们学习了芯片验证的基础知识,包括:

  • 芯片验证的定义及其在芯片设计中的关键作用。
  • 完整的芯片验证流程,从验证计划到验证报告。
  • 芯片验证的不同层次,从单元测试到系统测试。
  • 评估验证质量的关键指标,特别是覆盖率指标。
  • 当前芯片验证面临的挑战。
  • 芯片验证众包作为未来解决方案的潜力。

芯片验证是确保芯片质量的关键环节,也是芯片设计中最耗时和耗资的部分。掌握验证知识不仅能够帮助你成为一名优秀的验证工程师,还能为推动芯片验证方法的创新做出贡献。

通过参与芯片验证众包,你不仅可以学以致用,还能为半导体产业的发展贡献自己的力量。无论你是大学生、软件开发者还是对硬件感兴趣的爱好者,都可以参与到这一激动人心的领域中来。

下一节课,我们将讲解 Picker 的使用,并尝试用它尝试验证环境的搭建和简单测试用例的编写。

10. 课程后续需要的预备知识

为了后续的学习,您还需要确保学习过以下知识:

Linux 基础

  1. Linux 的基本命令和环境配置
  2. Git 的使用
  3. gcc 和常用的二进制工具:重点是如何源码编译安装

可以看计算机教育中缺失的一课

Python 基础

  1. Python 的安装、环境配置和pip 的使用
  2. Python 基础
  3. 基础的 Python 面向对象编程
  4. Python 协程相关的内容(asyncio

可以看廖雪峰的 Python 教程

  • 4
  • 5
  • 6
  • 7.1~7.4
  • 8.2~8.4
  • 9
  • 10
  • 23.1~23.2

上述内容是本教程的验证开发环境和使用的编程语言。

数字电路基础

学习数字电路是芯片验证的核心基础。它能帮工程师透彻理解芯片设计原理、精准对接规范要求,同时解决时序冲突和信号完整性问题,高效排查逻辑漏洞,并设计出覆盖全场景的测试用例。可以说,数字电路知识贯穿验证全流程,是确保芯片功能可靠的关键底层能力。

这就要求我们掌握:

  1. Verilog 基础
  2. Chisel 基础

Chisel 可以等到后续的果壳 Cache 实战再开始学习,但下一讲的例子我们会用到使用 Verilog 描述的模块。