4. 测试用例管理与标签体系

当验证环境从几十个用例扩展到几千个用例时,回归平台面临的核心问题不再是“能不能跑”,而是“该跑哪些、先跑哪些、跑完怎么判断”。vPlan 作为 Cadence 验证管理平台中的规划与用例组织载体,把测试意图、用例实体、覆盖目标与执行结果串成一条可追溯的链路。本节围绕这条链路,讲清用例如何组织、标签如何分类、优先级如何划分,以及回归子集如何被选择出来。

一、vPlan 中的用例组织模型

vPlan 的基本组织单元是 vPlan 节点,它既可以是一个功能点(feature),也可以是一个测试场景(test scenario),还可以是一个具体的测试用例(test case)。节点之间通过父子关系形成树状结构,典型层次为:验证计划 → 功能域 → 功能点 → 测试场景 → 具体用例。每个用例节点通常绑定以下关键属性:

  • 用例 ID:全局唯一标识,例如 vplan.dma.ch0.burst_len_max,便于脚本与报告引用。
  • 关联覆盖点:指向 vCover 中的 coverpoint 或 cross,用于判断该用例是否真正命中目标。
  • 执行入口:指向仿真命令、UVM test 名称或脚本路径,例如 +UVM_TESTNAME=dma_burst_max_test。
  • 标签集合:用于分类、筛选和回归子集选择。
  • 优先级:决定在有限机时下谁先跑。

这种树状结构的关键价值在于可追溯性:当某个覆盖点未命中时,可以沿树向上找到对应的功能点,再向下定位到具体用例;当某个用例失败时,也能快速判断它影响的是哪个功能域。实际项目中,建议把“功能点”和“用例”分开建模,功能点描述“要验证什么”,用例描述“怎么验证”,避免一个节点既当需求又当实现。

二、标签分类体系的设计

标签是回归子集选择的“索引”。如果标签设计得随意,后续筛选就会变成字符串匹配的泥潭。推荐按以下维度建立标签分类,每个维度使用统一前缀,避免语义重叠:

维度前缀示例取值示例用途
功能域feat_feat_dma、feat_pcie、feat_ddr按模块筛选回归范围
测试类型type_type_smoke、type_func、type_stress、type_error区分冒烟、功能、压力、异常
覆盖目标cov_cov_basic、cov_corner、cov_cross关联覆盖收敛阶段
接口/协议if_if_axi、if_apb、if_uart按总线或协议筛选
运行特征run_run_fast、run_long、run_seed_dep控制机时与随机种子
状态state_state_new、state_stable、state_known_fail标记用例成熟度

标签命名应遵循“前缀_小写_下划线”的约定,例如 feat_dma、type_stress。一个用例可以同时拥有多个标签,例如 feat_dma + type_stress + run_long + state_stable。这样在回归脚本中就可以用集合运算表达筛选条件,例如“所有 DMA 功能域中,类型为 stress 且运行特征为 long 的稳定用例”。

标签不是越多越好。建议每个维度控制在 5~15 个取值,超过这个范围说明维度划分过细,应该拆成两个维度或改用属性字段。

三、优先级划分策略

优先级决定回归顺序。常见的做法是把优先级与“失败影响面”和“执行成本”挂钩,而不是凭感觉标 P0/P1/P2。推荐使用如下判定逻辑:

  • P0(冒烟级):覆盖基本通路、复位、时钟、寄存器读写。失败意味着环境不可用,必须最先跑,通常控制在 20~50 条以内。
  • P1(核心功能):覆盖主要功能点和关键交叉覆盖。每个功能域至少有一条 P1,用于日常回归。
  • P2(边界与异常):覆盖 corner case、错误注入、协议违例。通常在 P0/P1 通过后执行。
  • P3(长尾与压力):长时间压力、随机种子敏感、性能相关。用于 nightly 或 weekly 回归。

优先级划分后,还需要与标签联动。例如 type_smoke 通常对应 P0,type_stress 通常对应 P3。但两者不是硬绑定:一个 DMA 的 smoke 用例可能是 P0,而一个 PCIe 的 smoke 用例如果依赖外部模型,可能只能算 P1。因此建议在 vPlan 中同时维护 priority 字段和 type_ 标签,用脚本做一致性检查。

四、回归子集选择策略

回归子集选择的目标是:在给定机时预算内,最大化覆盖收益。实际工程中常用以下四种策略,可以组合使用:

  1. 基于标签的静态筛选:用标签表达式直接圈定范围,例如 feat_dma AND type_func AND NOT state_known_fail。适合日常回归和定向验证。
  2. 基于覆盖缺口的动态补充:从 vCover 中读取未命中覆盖点,反向查找关联用例,把这些用例加入本次回归。适合覆盖收敛阶段。
  3. 基于变更影响面的增量选择:根据 RTL 或验证环境的变更文件,映射到受影响的功能域,只跑相关标签的用例。适合每日增量回归。
  4. 基于历史失败率的加权排序:统计最近 N 次回归中各用例的失败次数,失败率高的优先跑。适合在机时紧张时快速暴露问题。

下面这张图展示了从 vPlan 用例库到回归子集的选择链路,以及标签、优先级、覆盖缺口和历史失败率四个输入如何共同作用:

vPlan 用例库 → 回归子集选择链路 vPlan 用例库 功能域 / 功能点 / 场景 用例 ID + 执行入口 标签集合 + 优先级 关联覆盖点 标签筛选 feat_ / type_ / run_ 优先级排序 P0 → P1 → P2 → P3 覆盖缺口 vCover 未命中点 历史失败率 最近 N 次回归统计 回归子集 选择引擎 集合运算 + 排序 机时预算约束 冒烟回归 P0 + smoke 日常回归 P0+P1 + 变更域 夜间回归 P0~P3 + 覆盖缺口 筛选表达式示例(伪代码) smoke_set = vplan.filter("type_smoke AND priority=P0") daily_set = vplan.filter("feat_dma OR feat_pcie") AND priority in [P0,P1] gap_set = vcover.uncovered().map_to_cases() nightly_set = (daily_set ∪ gap_set ∪ stress_set) - known_fail_set

图中四个输入并非每次都要全部启用。冒烟回归通常只用“标签筛选 + 优先级排序”;日常回归在此基础上加入“变更影响面”;夜间回归才把“覆盖缺口”和“历史失败率”纳入。选择引擎的本质是集合运算加排序,最终输出一个有序的用例列表,交给回归平台调度执行。

五、一个可落地的筛选表达式示例

假设当前处于 DMA 模块的覆盖收敛阶段,机时预算为 4 小时,已知单条用例平均耗时 3 分钟,则最多可跑约 80 条。可以按以下顺序构造回归集:

# 第一步:圈定 DMA 功能域中所有稳定用例
base = vplan.filter("feat_dma AND NOT state_known_fail")

# 第二步:优先取 P0 和 P1
high = base.filter("priority in [P0, P1]")

# 第三步:从 vCover 中找出 DMA 相关未命中覆盖点,反查用例
gap = vcover.uncovered("dma.*").map_to_cases()

# 第四步:合并去重,按优先级和历史失败率排序
regression = (high ∪ gap).unique().sort_by(
    priority_asc,
    fail_rate_desc
)

# 第五步:按 4 小时预算截断
final = regression.take_within_budget(minutes=240, avg_case_min=3)

执行后,回归平台会输出一份用例清单和预计机时。如果 final 的数量远小于 80,说明筛选条件过严,可以放宽 state_ 标签或加入 P2;如果远超 80,则需要提高优先级门槛或缩短覆盖缺口反查的范围。这个“预算—筛选—截断”的闭环,就是回归子集选择从策略落到执行的关键。

不要把所有用例都标成 P0。P0 一旦膨胀,冒烟回归就会退化成全量回归,失去快速反馈的意义。建议定期审查 P0 集合,确保其规模稳定在可接受范围内。

最后需要强调的是,标签体系和优先级不是一次设计完就固定不变的。随着验证阶段推进,state_new 的用例会逐步转为 state_stable,覆盖缺口会从 basic 转向 corner 和 cross,回归子集的构成也应随之调整。把 vPlan 用例库、标签、优先级和覆盖数据放在同一套可查询的模型里,回归平台才能真正做到“按需选、按序跑、按结果判”。