在复杂SoC验证场景中,验证工程师常常面临多接口、多协议协同验证的挑战。就像繁忙的十字路口需要交通警察指挥车辆有序通行,验证环境也需要一个"交通指挥官"来协调各路激励。这正是UVM中virtual sequence和virtual sequencer的核心价值所在。
想象一下这样的场景:你需要验证一个集成AHB总线、APB寄存器和中断控制器的模块。如果让这些接口的sequence"各自为战",不仅难以构建复杂的验证场景,还可能因为激励冲突导致验证效率低下。而通过virtual sequence/sequencer的协同调度,我们可以像交响乐团指挥一样,精确控制每个"乐器"(子sequence)的启动时机和演奏节奏,创造出和谐统一的"验证交响曲"。
在UVM验证架构中,virtual sequence和virtual sequencer扮演着独特的角色:
Virtual sequencer:不直接连接driver,而是作为物理sequencer的容器。它通过持有多个子sequencer的句柄,实现对不同接口sequence的间接控制。
Virtual sequence:不产生具体的transaction,而是通过控制挂载在virtual sequencer上的子sequence,实现多路激励的协同调度。
systemverilog复制class soc_virtual_sequencer extends uvm_sequencer;
ahb_sequencer ahb_sqr;
apb_sequencer apb_sqr;
int_sequencer int_sqr;
// ...其他接口sequencer
endclass
| 特性 | 传统sequence | Virtual sequence |
|---|---|---|
| 产生transaction | 是 | 否 |
| 连接driver | 是 | 否 |
| 调度其他sequence | 否 | 是 |
| 适用场景 | 单一接口激励生成 | 多接口协同验证 |
提示:virtual sequence本质上是一个"元调度器",它通过协调多个子sequence的执行顺序和同步关系,构建复杂的验证场景。
让我们通过一个AHB+APB+中断控制器的验证案例,展示如何构建完整的virtual sequencer架构:
systemverilog复制class soc_env extends uvm_env;
soc_virtual_sequencer virt_sqr;
ahb_agent ahb_agt;
apb_agent apb_agt;
int_agent int_agt;
function void connect_phase(uvm_phase phase);
virt_sqr.ahb_sqr = ahb_agt.sequencer;
virt_sqr.apb_sqr = apb_agt.sequencer;
virt_sqr.int_sqr = int_agt.sequencer;
endfunction
endclass
一个典型的AHB+APB协同验证virtual sequence可能包含以下步骤:
systemverilog复制class ahb_apb_virtual_seq extends uvm_sequence;
`uvm_declare_p_sequencer(soc_virtual_sequencer)
task body();
apb_config_seq apb_cfg = apb_config_seq::type_id::create("apb_cfg");
ahb_data_seq ahb_dat = ahb_data_seq::type_id::create("ahb_dat");
int_wait_seq int_wt = int_wait_seq::type_id::create("int_wt");
// 步骤1:APB配置
apb_cfg.start(p_sequencer.apb_sqr);
// 步骤2:AHB数据传输
fork
ahb_dat.start(p_sequencer.ahb_sqr);
join_none
// 步骤3:等待中断
int_wt.start(p_sequencer.int_sqr);
// 步骤4:APB状态读取
apb_cfg.read_status(p_sequencer.apb_sqr);
endtask
endclass
start方法的第三个参数设置sequence优先级fork/join或fork/join_none实现多sequence并行systemverilog复制// 并行执行示例
fork
begin : ahb_block
ahb_seq1.start(p_sequencer.ahb_sqr, null, 100);
ahb_seq2.start(p_sequencer.ahb_sqr, null, 200);
end
begin : apb_block
apb_seq.start(p_sequencer.apb_sqr);
end
join
考虑一个DMA控制器的验证场景,需要协调以下操作:
systemverilog复制class dma_virtual_seq extends uvm_sequence;
`uvm_declare_p_sequencer(soc_virtual_sequencer)
task body();
// 1. APB配置
apb_dma_config();
// 2. 启动AHB传输
fork
begin
ahb_dma_transfer();
`uvm_info("DMA", "AHB transfer completed", UVM_MEDIUM)
end
join_none
// 3. 等待中断并验证
wait_for_interrupt();
verify_data_consistency();
endtask
// 各子任务实现...
endclass
当virtual sequence调度出现问题时,可以采取以下调试方法:
uvm_info日志注意:在复杂virtual sequence中,建议采用分阶段验证策略,先验证各子sequence的独立性,再逐步构建完整的协同场景。
systemverilog复制// 参数化示例
class param_virtual_seq extends uvm_sequence;
int burst_len = 10;
task body();
uvm_config_db#(int)::get(null, get_full_name(), "burst_len", burst_len);
// 使用burst_len控制传输长度...
endtask
endclass
在实际项目中,我发现最有效的virtual sequence设计往往遵循"简单即美"的原则。与其构建一个庞大复杂的virtual sequence,不如将其拆分为多个逻辑独立的子virtual sequence,通过更高层的测试逻辑进行组合。这种模块化设计不仅便于调试,还能显著提高代码复用率。