4. 使用uvm_analysis_port实现零拷贝数据传输
任务目标
在UVM验证环境中,monitor与scoreboard之间默认的analysis port通信会触发事务的深拷贝(`copy()`),当事务包含大数组或复杂对象时,频繁拷贝将显著消耗仿真时间。本案例通过自定义analysis port实现句柄直传(零拷贝),使scoreboard直接引用monitor端创建的事务对象,消除数据复制开销。
实现方案
UVM内建`uvm_analysis_port`的`write()`函数参数为`uvm_sequence_item`,内部会调用`clone()`产生新对象。要实现零拷贝,需绕过该机制:
- 自定义一个参数化analysis port类,其`write()`直接传递原始句柄;
- monitor端通过该port发送事务句柄;
- scoreboard端接收句柄并直接访问其成员,不进行任何复制。
关键完整代码
// 1. 零拷贝analysis port定义
class zero_copy_analysis_port #(type T=int) extends uvm_port_base #(uvm_tlm_if_base #(T,T));
`uvm_object_utils(zero_copy_analysis_port#(T))
function new(string name, uvm_component parent);
super.new(name, parent, UVM_PORT);
endfunction
// 直接传递句柄,不调用clone()
virtual function void write(T t);
uvm_analysis_port #(T)::write(t); // 实际不会走到这里,见下方说明
endfunction
// 真正实现:使用uvm_tlm_fifo的put/get语义或直接调用export
// 更简洁方式:直接使用uvm_analysis_port的export连接,但重写write
endclass
// 2. 实际推荐实现:直接使用标准uvm_analysis_port,但通过配置禁止自动拷贝
// 标准UVM中,analysis_port的write()会调用`write()`任务,默认实现是:
// m_export.write(t); 其中export端会调用`write(t)`,而scoreboard的write()接收的是原句柄
// 关键点:UVM的analysis port本身不强制拷贝!拷贝发生在`uvm_tlm_fifo`或`uvm_analysis_imp`的`write()`中
// 若scoreboard直接实现`write(trans)`且内部不调用copy,则本身就是零拷贝
// 3. 最小零拷贝实现(monitor → scoreboard直连)
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction)
byte data[]; // 大块数据
function new(string name="my_transaction");
super.new(name);
endfunction
endclass
class monitor extends uvm_component;
`uvm_component_utils(monitor)
uvm_analysis_port #(my_transaction) ap;
function new(string name, uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
endfunction
task run_phase(uvm_phase phase);
my_transaction tr;
// 产生事务并发送句柄
tr = my_transaction::type_id::create("tr");
tr.data = new[1024*1024]; // 1MB数据
foreach(tr.data[i]) tr.data[i] = i%256;
ap.write(tr); // 发送句柄,不拷贝
endtask
endclass
class scoreboard extends uvm_component;
`uvm_component_utils(scoreboard)
uvm_analysis_imp #(my_transaction, scoreboard) analysis_export;
function new(string name, uvm_component parent);
super.new(name, parent);
analysis_export = new("analysis_export", this);
endfunction
// 直接接收句柄,不调用copy()
function void write(my_transaction t);
// 直接访问t.data,验证数据完整性
if(t.data[0] == 0 && t.data[1024*1024-1] == (1024*1024-1)%256)
`uvm_info("SCB", "数据校验通过,零拷贝传输成功", UVM_LOW)
else
`uvm_error("SCB", "数据校验失败")
endfunction
endclass
// 4. 连接与测试
class test_base extends uvm_test;
`uvm_component_utils(test_base)
monitor mon;
scoreboard scb;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
mon = monitor::type_id::create("mon", this);
scb = scoreboard::type_id::create("scb", this);
endfunction
function void connect_phase(uvm_phase phase);
mon.ap.connect(scb.analysis_export);
endfunction
endclass
运行与验证
- 环境依赖:需要UVM-1.2或IEEE 1800.2兼容库,使用标准仿真器(VCS、Questa、Xcelium等)。
- 编译运行:
# 假设文件名为zero_copy_test.sv vlog -sv zero_copy_test.sv +incdir+$UVM_HOME/src vsim -c -do "run -all; quit" test_base - 验证方法:观察仿真日志中是否出现
数据校验通过信息。若出现,说明scoreboard正确接收并访问了monitor发送的句柄,未发生拷贝。 - 判定标准:仿真无编译错误,且打印
UVM_INFO SCB 数据校验通过,即证明零拷贝路径生效。
关键说明
UVM标准analysis port本身不强制拷贝——拷贝行为取决于接收端实现。若scoreboard的`write()`直接使用传入句柄而不调用`copy()`或`clone()`,则传输即为零拷贝。本案例通过直接连接`uvm_analysis_port`到`uvm_analysis_imp`,并在接收端避免任何复制操作,实现了最小可复现的零拷贝传输。若要验证拷贝确实未发生,可在`my_transaction`中重载`copy()`并打印调试信息,若零拷贝生效则不会打印。