UVM sequence/driver/scoreboard 实战:一笔事务如何走到引脚再被比对
更新时间:2026-09-14。本文是「集成电路大类 · 芯片设计 · 数字验证」实操序列第六篇。上一篇 UVM 组件与 phase 搭好了静态结构,这一篇追踪一笔事务的完整生命:它在 sequence 里被随机出来,经 sequencer 到 driver 拍成波形,monitor 旁路抓回,最后在 scoreboard 和参考模型比对。
本文要回答的问题
- sequence、sequencer、driver 三者到底谁在发激励,握手机制是什么?
- driver 怎么按时钟把一笔事务拍成带反压的 valid/ready?
- monitor 抓回的事务怎么和期望值对?乱序完成怎么比?
- 报错怎么定位到「第一个错」,而不是被刷屏?
一、全局数据流
记住方向:激励侧是 sequence→sequencer→driver(主动);观测侧是 monitor 单独抓 DUT 真实引脚(被动)。两侧在 scoreboard 汇合,互不引用。
二、sequence_item 与 sequence
uvm_sequence_item 是一笔事务,字段加 rand 和约束(写法就是 CRV 那一套):
class fifo_item extends uvm_sequence_item;
rand bit [7:0] data;
rand bit is_write;
`uvm_object_utils_begin(fifo_item) ... `uvm_object_utils_end
endclass
class fifo_seq extends uvm_sequence #(fifo_item);
task body();
repeat (50) begin
req = fifo_item::type_id::create("req");
start_item(req); // 请求 sequencer 仲裁
if (!req.randomize()) `uvm_error("RND","fail")
finish_item(req); // 交给 driver,driver 完成才返回
end
endtask
endclassstart_item等 sequencer 授权、driver 准备好接收;- 在 start/finish 之间随机化(此时可根据 driver 状态加约束);
finish_item阻塞,直到 driver 对这笔调用item_done表示「已拍到接口」——所以 sequence 天然与接口吞吐同步,不会一股脑塞爆。
三、driver:把事务拍成带反压的波形
driver 在 run_phase 里是个死循环,通过 sequencer 的 seq_item_port 取事务:
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // 取下一笔(阻塞)
drive(req); // 按时钟拍波形
seq_item_port.item_done(); // 告诉 sequencer 这笔完成
end
endtask
task drive(fifo_item req);
if (req.is_write) begin
@(posedge vif.clk);
vif.cb.wr_en <= 1; vif.cb.wdata <= req.data;
do @(posedge vif.clk); // 若 full 则保持,等反压解除
while (vif.cb.full);
vif.cb.wr_en <= 0;
end
endtask要点:
- driver 必须尊重 ready/full 反压:valid 拉起后若 ready 没来,要保持数据稳定直到真正握手,不能假设一拍就成功(协议契约见 SVA);
- 一次
get_next_item配一次item_done,多调/漏调会让 sequencer 挂死; - driver 只做「事务→引脚」的机械翻译,不判断对错、不随机,逻辑越呆越好。
四、monitor:把引脚还原成事务
monitor 在 run_phase 里独立循环,只在检测到真正握手(valid & ready 同拍)时采样,打包成 item 经 analysis_port 广播:
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
if (vif.cb.rd_en && !vif.cb.empty) begin
fifo_item tx = fifo_item::type_id::create("tx");
tx.is_write = 0; tx.data = vif.cb.rdata;
ap.write(tx); // 广播给 scoreboard/coverage
end
end
endtask只在握手成立那拍采、采的是真实引脚——这保证功能覆盖率统计的是 DUT 实际行为而非「我发过什么」(见 覆盖率)。
五、reference model 与 scoreboard
scoreboard 本身不产生预期,它只做「比对与记账」。期望值来源有两种:
- predictor 模式:用独立的参考模型(行为模型/算法/队列),喂同样的输入事务,产生期望输出;
- 简单情况:在 scoreboard 内用队列/关联数组直接预测(如 FIFO 用一个 SystemVerilog 队列,就是 第一篇 自检 TB 的升级版)。
// 收到输入事务:预测期望值入队
function void write_exp(fifo_item tx);
if (tx.is_write) exp_q.push_back(tx.data);
endfunction
// 收到 monitor 的真实输出:出队比对
function void write_act(fifo_item tx);
if (exp_q.size()==0) `uvm_error("SCB","unexpected output")
else begin
byte exp = exp_q.pop_front();
if (exp !== tx.data)
`uvm_error("SCB", $sformatf("MISMATCH exp=%0h got=%0h", exp, tx.data))
end
endfunction
function void check_phase(uvm_phase phase);
if (exp_q.size()!=0) `uvm_error("SCB","expected outputs never observed")
endfunction顺序 vs 乱序:FIFO、AXI 读通道这类可能乱序返回的 DUT,不能简单 FIFO 出队,要用「以 ID/key 索引的关联数组」保存期望,按实际返回的 ID 取对应期望比对;还要检查有没有重复响应、漏响应。结束时在 check_phase 确认期望队列清空,防止「少返回了一笔却没报错」。
六、如何定位第一个错
- 让 scoreboard 在第一次语义不一致就打出:仿真时间、事务关键字段、期望值、实际值、来自哪个 monitor;
- 配合
set_report_severity_action可在首个 UVM_ERROR 处停仿真,直接看波形那一点; - 不要等到几千笔后 dump 全表——错误一旦向后传播,后面的 mismatch 都是连锁噪声;
- 报错信息要能回答「哪笔输入、哪个字段、期望什么、实际什么」。
七、检查清单
- [ ] item 在 start_item/finish_item 之间随机化,检查 randomize 返回;
- [ ] driver 一次 get_next_item 配一次 item_done,尊重 ready/full 反压并保持稳定;
- [ ] driver 不含判断/随机,monitor 不驱动、只在握手拍采样;
- [ ] monitor 经 analysis_port 同时喂 scoreboard 与 coverage;
- [ ] 参考模型独立于 RTL 实现,不抄 RTL;
- [ ] 乱序 DUT 用 key 索引期望,并查重/查漏;
- [ ] check_phase 校验期望队列清空;
- [ ] 首个错误即带时间和期望/实际,可立即定位。
相关与延伸
组件静态结构见 UVM 组件与 phase;item 的约束随机见 CRV,monitor 采样喂覆盖率见 代码与功能覆盖率;不靠随机、用数学穷尽证明接口正确性的路线见 形式验证入门。
总结
一笔 UVM 事务的生命是:sequence 在 start/finish_item 之间把带约束的 item 经 sequencer 仲裁交给 driver,driver 在 run_phase 用 get_next_item/item_done 握手、按真实时钟把它拍成尊重 ready/full 反压的 valid/ready 波形;被动 monitor 只在握手成立那拍把引脚还原成事务,经 analysis_port 广播;独立参考模型预测期望,scoreboard 用队列(顺序)或按 ID 的关联数组(乱序)与真实输出逐笔比对、查重查漏并在 check_phase 确认期望清空,在第一个语义错误处打出时间/期望/实际定位问题——激励侧主动发、观测侧被动收、两侧在 scoreboard 汇合,这就是可复用 UVM 平台的数据闭环。