UVM验证平台搭建实战:从driver到scoreboard的完整实现流程
扫描二维码
随时随地手机看文章
在SoC/ASIC验证中,UVM(Universal Verification Methodology) 已成为芯片流片前的“最后防线”。很多初学者卡在“组件怎么连”、“sequence怎么跑”。本文将基于SystemVerilog,手把手带你走完从driver到scoreboard的最小闭环验证流程。
一、UVM验证平台核心架构(先懂“骨架”)
一个标准的UVM平台遵循严格的层次结构,数据流自上而下:
1. Sequencer:生成高层次的transaction(事务)。
2. Driver:从Sequencer“拉”取transaction,并驱动到DUT(被测设计) 的接口上(转换成信号)。
3. Monitor:被动监视接口信号,将信号采集并还原成transaction。
4. Scoreboard:从Monitor拿到实际transaction,与自己预测的期望值进行比对。
核心原则:Driver是“发号施令者”,Monitor是“情报员”,Scoreboard是“裁判”。
二、实战第一步:定义Transaction(数据包)
Transaction是所有组件传递的“标准信封”。假设我们要验证一个简单的APB总线写操作。
// my_transaction.sv
class my_transaction extends uvm_sequence_item;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit write; // 1=写, 0=读
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(write, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
关键点:必须继承uvm_sequence_item,并使用uvm_object_utils宏注册,否则UVM工厂机制无法识别。
三、Driver:把“指令”变成“电信号”
Driver负责与DUT接口“硬碰硬”。它需要virtual interface来访问DUT的信号。
1. 接口定义(Interface)
// my_if.sv
interface my_if (input logic clk);
logic [31:0] paddr;
logic [31:0] pwdata;
logic pwrite;
logic psel;
logic penable;
// ... 其他信号
endinterface
2. Driver实现
// my_driver.sv
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual my_if vif; // 关键:虚拟接口句柄
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // 从sequencer取transaction
drive_one_pkt(req); // 驱动到接口
seq_item_port.item_done();
end
endtask
task drive_one_pkt(my_transaction tr);
@(posedge vif.clk);
vif.psel <= 1'b1;
vif.pwrite <= tr.write;
vif.paddr <= tr.addr;
if (tr.write) vif.pwdata <= tr.data;
@(posedge vif.clk);
vif.penable <= 1'b1;
@(posedge vif.clk);
vif.psel <= 1'b0;
vif.penable <= 1'b0;
endtask
endclass
避坑指南:seq_item_port.get_next_item()是阻塞调用,Driver会一直等到Sequencer有数据才往下跑。
四、Monitor:从信号中“破案”
Monitor与Driver类似,但它不驱动信号,只采样。它的产出是analysis_port。
// my_monitor.sv
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual my_if vif;
uvm_analysis_port #(my_transaction) ap; // 广播端口
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new("ap", this);
endfunction
task run_phase(uvm_phase phase);
forever begin
my_transaction tr = my_transaction::type_id::create("tr");
@(posedge vif.clk iff vif.psel && vif.penable);
tr.addr = vif.paddr;
tr.data = vif.pwdata;
tr.write = vif.pwrite;
ap.write(tr); // 广播给所有订阅者(如scoreboard)
end
endtask
endclass
五、Scoreboard:最终的“法官”
Scoreboard负责预测与比对。它订阅Monitor的数据,同时可能直接访问DUT的寄存器模型或参考模型。
// my_scoreboard.sv
class my_scoreboard extends uvm_scoreboard;
`uvm_component_utils(my_scoreboard)
uvm_analysis_imp #(my_transaction, my_scoreboard) analysis_export;
bit [31:0] ref_mem [32'h0000:32'hFFFF]; // 参考模型(内存)
function void build_phase(uvm_phase phase);
analysis_export = new("analysis_export", this);
endfunction
// 必须实现的write函数
function void write(my_transaction tr);
if (tr.write) begin // 写操作
ref_mem[tr.addr] = tr.data; // 更新参考模型
end else begin // 读操作
if (ref_mem[tr.addr] != tr.data) begin // 关键:比对
`uvm_error("SCB_ERR",
$sformatf("Addr=0x%0h, Exp=0x%0h, Got=0x%0h",
tr.addr, ref_mem[tr.addr], tr.data))
end
end
endfunction
endclass
核心逻辑:analysis_export收到Monitor发来的transaction后,调用write()函数。在这里进行期望值(ref_mem) 与实际值(tr.data) 的比较。
六、Env与Test:把积木拼起来
1. Environment(胶水层)
class my_env extends uvm_env;
my_driver drv;
my_monitor mon;
my_scoreboard scb;
uvm_agent agent; // 通常用agent封装drv/mon
// ... 省略agent定义 ...
function void build_phase(uvm_phase phase);
drv = my_driver::type_id::create("drv", this);
mon = my_monitor::type_id::create("mon", this);
scb = my_scoreboard::type_id::create("scb", this);
endfunction
function void connect_phase(uvm_phase phase);
mon.ap.connect(scb.analysis_export); // 关键连接!
endfunction
endclass
2. Test与Sequence
class base_test extends uvm_test;
my_env env;
`uvm_component_utils(base_test)
// ... build_phase创建env ...
task run_phase(uvm_phase phase);
my_sequence seq = my_sequence::type_id::create("seq");
phase.raise_objection(this);
seq.start(env.agent.sequencer); // 启动sequence
phase.drop_objection(this);
endtask
endclass
七、调试与避坑指南
1. Factory Override失效:确保create()使用的是type_id::create(),且字符串名字匹配。
2. Driver没反应:检查Test中是否raise_objection(),如果没有,仿真会立刻结束。
3. Scoreboard收不到数据:99%是connect_phase没连对,确认monitor.ap.connect(scoreboard.analysis_export)。
4. 看波形调试:在波形窗口中,优先看Driver的vif信号,确认激励是否正确发出;再看Scoreboard内部的ref_mem和比对结果。
八、结语
搭建UVM平台的核心是“数据流”思维:Sequence → Sequencer → Driver → DUT → Monitor → Scoreboard。掌握了从Driver的信号驱动到Scoreboard的数据比对,你就打通了UVM验证的“任督二脉”。下一步,可以进阶学习寄存器模型(RAL) 与覆盖率(Coverage)。





