如何验证自定义RISC-V指令的功能?

解读

面试官抛出这道题,核心想考察三件事:

  1. 你是否真正理解 RISC-V 指令生命周期(取指-译码-执行-写回-异常/中断)以及自定义指令如何“无缝”插入流水线;
  2. 能否把“指令功能”拆成可量化、可覆盖的验证场景,而不是拍脑袋写几个定向用例;
  3. 在国产 EDA 受限、项目周期紧、人力少的国内真实场景下,如何用最经济的验证方法学(SystemVerilog/UVM + 形式 + 后仿 + FPGA 原型)把风险压到零,达到 sign-off。
    因此,回答必须体现“场景拆解 → 覆盖率闭环 → 国产工具链落地 → 交付物”四段式思维,避免空谈 UVM 语法。

知识点

  1. RISC-V 指令格式:R/I/S/B/U/J 六大类型,custom-0 / custom-1 / custom-2 / custom-3 四组 opcode 空间,funct3/funct7 可扩展。
  2. 伪指令与宏定义:在 riscv-tests/riscv-pk 里用 .word 拼接 32 位机器码,绕过汇编器对“未知指令”报错。
  3. 黄金模型:Spike 支持通过 riscv_extensions 注册自定义指令的回调函数,ISS 与 RTL 同步比对;国内项目若担心合规,可用开源 NEMU 或自研指令级参考模型。
  4. 覆盖率模型:
    • 指令级:opcode、funct3、funct7、rs1/rd 边界、立即数符号扩展;
    • 微架构级:流水线冲突、旁路前递、multi-cycle 自定义指令与标准指令混排、异常指令边界对齐;
    • 系统级:与中断、debug 模块、PMP 保护窗口的交叉。
  5. 形式验证:对“自定义指令译码信号唯一性”和“写回使能互锁”做 assertion,避免 opcode 重叠或死锁;国产工具“形式化验证平台(FVP)”已支持 SVA + 约束求解。
  6. 后仿与功耗:自定义指令若带 DSP 乘加,需在后注反标(SDF)下跑门级仿真,确认关键路径 105 °C、0.72 V 下无 glitch;同时用 PTA 向量生成真实翻转率,供功耗 sign-off。
  7. FPGA 原型:在 Xilinx KU115 或国产复旦微 JFM7K325T 上跑 Linux + busybox,通过 riscv-pk 动态加载 ko,把自定义指令当“加速器”调用,跑 24 小时老化,观察内存一致性。
  8. 交付物:验证计划 V1.3、覆盖率报告(代码 100 %、功能 100 %、断言 100 %、漏洞清零报告)、后仿日志、形式验证空反例报告、FPGA 老化 72 小时无异常截图,最终由项目负责人签字确认达到 tape-out 标准。

答案

第一步,把“功能”拆成 5 条可测属性:
A. 译码正确——opcode、funct3/7 命中后,仅自定义指令译码信号拉高;
B. 操作数正确——rs1、rs2、立即数符号扩展与 spec 一致;
C. 运算正确——ALU 或自定义加速器输出与黄金模型相比误差为 0;
D. 写回正确——结果在 WB 阶段正确写入 rd,且旁路逻辑对后续指令立即可见;
E. 异常正确——遇到非法立即数或特权级不足时,能触发 Illegal Instruction 异常,且 mtval 保存完整指令码。

第二步,搭建 UVM 环境:

  • 在 env 中例化 Spike 的 DPI-C 接口,每次 drv.send_insn() 后,自动调用 spike_step() 拿到寄存器快照;
  • uvm_reg_predictor 监听 RTL 寄存器写总线,实现 0 时刻比较,避免日志后处理;
  • 把 5 条属性转成 15 条 SVA:例如 assert property (@(posedge clk) decode_valid |-> !$isunknown(rd_data)) 覆盖写回 X 态;
  • riscv-dv 二次开发,在 custom_insn.yaml 里填入新指令格式,随机生成 1 万条指令序列,保证与标准指令 1:3 混排;
  • 覆盖率收集:指令类型交叉寄存器索引、流水线冲突类型,再用 covergroup 采样,目标 100 % 交叉。

第三步,形式验证:

  • 对译码唯一性写 assert never (decode_custom0 && decode_custom1),用国产形式工具跑 4 小时,无反例即通过;
  • 对多周期自定义指令写“握手协议”断言:req-ack 无死锁,工具在 20 分钟内给出空反例。

第四步,门级仿真:

  • 用 SDF 反标,跑典型 200 MHz 场景,setup/hold 违例必须清零;
  • 用 VCD 生成 saif,供 PowerArtist 做动态功耗,若超过 5 % 裕量,反馈设计加 clock gate。

第五步,FPGA 原型:

  • 在国产 JFM7K325T 上跑 Debian 镜像,通过内核模块把自定义指令封装成 ioctl,连续跑 72 小时 CoreMark 与内存压力,观察是否出现数据不一致或非法指令异常;
  • 用逻辑分析仪抓 AXI 总线,确认无协议违例。

最终交付:

  • 代码覆盖率 100 %、功能覆盖率 100 %、断言覆盖率 100 %;
  • 漏洞清零,高风险缺陷全部修复并回归通过;
  • 形式验证与后仿无违例,FPGA 老化无异常,项目负责人签字确认,达到 sign-off 标准。

拓展思考

  1. 如果自定义指令是“多周期、带反馈”的矩阵乘加速器,如何验证它与 Cache 一致性协议的交互?
    提示:在 UVM 环境中插入 Cache 模型,监听自定义指令的 memory fence 语义,用 SystemVerilog 构造“写后读、读后写、写后写”三条一致性用例,并用形式工具证明无 WAR 反例。

  2. 当项目使用国产 14 nm 工艺、库文件不完整时,如何在没有 PrimeTime 的情况下做时序 sign-off?
    提示:用开源 OpenSTA + 自写 Tcl 脚本,把.lib 转成 db,再跑 corner 分析;同时把关键路径灌到 FPGA 原型,跑 1.2× 最高频率做“过压超频”实验,用实测结果反标时序裕量。

  3. 若自定义指令需要在未来通过 JTAG 在线升级微码,如何验证“热升级”不破坏现场寄存器?
    提示:在验证环境里模拟 JTAG 接口随机插入升级序列,用 SVA 检查 upgrade_done 信号拉高后,下一条自定义指令结果仍与黄金模型一致;同时用形式验证证明升级期间流水线刷新无死锁。