4. 事务级建模与序列机制
在 UVM 验证平台中,事务级建模是连接激励意图与引脚级时序的桥梁。验证工程师不再直接操作信号,而是描述“要发什么事务”,由 sequence、sequencer、driver 三者协作完成从抽象事务到具体信号的转换。理解这三者的职责边界与握手协议,是构建可复用、可扩展验证平台的核心能力。
一、核心原理:三者的职责与握手协议
UVM 将激励产生过程拆分为三个角色:sequence 负责“生成什么事务”,sequencer 负责“仲裁与转发事务”,driver 负责“把事务翻译成信号”。三者通过 uvm_sequence_item 和 uvm_sequence 基类协作,形成标准的 pull 模式数据流。
典型握手流程如下:sequence 调用 start() 启动,通过 uvm_sequence_item 的 randomize() 产生随机事务;sequencer 使用仲裁机制(如 FIFO、优先级)决定哪个 sequence 的事务被发送;driver 通过 seq_item_port.get_next_item() 获取事务,完成引脚级驱动后调用 item_done() 通知 sequencer 可以继续。这一“请求—授权—完成”的闭环,保证了事务不会丢失或重复。
二、方法流程:从 sequence 到 driver 的完整链路
构建一条可用的激励链路,通常遵循以下步骤:
- 定义事务类:继承
uvm_sequence_item,使用rand声明随机字段,并通过`uvm_object_utils注册。 - 定义序列类:继承
uvm_sequence #(my_item),在body()任务中通过`uvm_do或start_item/finish_item产生事务。 - 定义驱动类:继承
uvm_driver #(my_item),在run_phase中循环调用get_next_item(),完成信号驱动后调用item_done()。 - 连接与启动:在 agent 中连接
driver.seq_item_port与sequencer.seq_item_export,在 test 中通过sequence.start(sequencer)启动。
class my_driver extends uvm_driver #(my_item);
`uvm_component_utils(my_driver)
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive_pins(req); // 事务转信号
seq_item_port.item_done(); // 通知 sequencer
end
endtask
endclass
get_next_item() 与 item_done(),否则 sequencer 会一直等待,导致仿真挂起。若需要提前返回响应,可使用 put_response() 配合 get_response()。
三、虚拟序列与分层序列设计
当验证平台包含多个接口或多个 agent 时,单一 sequence 无法协调跨接口的激励时序。此时需要引入虚拟序列(virtual sequence)。虚拟序列本身不直接产生事务,而是持有多个 sequencer 的句柄,通过启动子序列来协调不同接口的激励。
分层序列设计通常分为三层:
- 底层序列:面向单个接口,产生具体事务,如
axi_write_seq、apb_read_seq。 - 中层序列:组合底层序列,完成一个较完整的场景,如“配置寄存器后发起 DMA 传输”。
- 顶层虚拟序列:协调多个中层序列,模拟 SoC 级并发场景,如“CPU 配置 + DMA 搬运 + 中断响应”。
虚拟序列的典型写法如下:
class soc_virtual_seq extends uvm_sequence;
`uvm_object_utils(soc_virtual_seq)
`uvm_declare_p_sequencer(soc_virtual_sequencer)
task body();
axi_config_seq axi_seq;
dma_transfer_seq dma_seq;
irq_check_seq irq_seq;
fork
`uvm_do_on(axi_seq, p_sequencer.axi_sqr)
`uvm_do_on(dma_seq, p_sequencer.dma_sqr)
join
`uvm_do_on(irq_seq, p_sequencer.irq_sqr)
endtask
endclass
四、案例场景:SoC 级多接口协同激励
假设一个 SoC 验证场景:CPU 通过 AXI 总线配置 DMA 控制器,DMA 从内存搬运数据到外设,完成后产生中断。该场景涉及 AXI、DMA、IRQ 三个接口。若用三个独立 sequence 分别启动,很难保证“配置完成后再启动 DMA”的时序关系。
采用虚拟序列后,可以在 body() 中先启动 AXI 配置序列,等待其完成,再启动 DMA 传输序列,最后启动中断检查序列。通过 fork...join 或 fork...join_none 控制并发与顺序,精确复现真实场景的时序依赖。
| 层级 | 典型类名 | 职责 | 运行位置 |
|---|---|---|---|
| 底层序列 | axi_write_seq | 产生单接口事务 | agent 内 sequencer |
| 中层序列 | dma_config_seq | 组合多个底层序列 | agent 或 env 级 sequencer |
| 虚拟序列 | soc_virtual_seq | 协调多接口并发 | virtual sequencer |
| Driver | axi_driver | 事务转引脚信号 | agent 内 |
五、误区与复盘
randomize() 产生激励。这破坏了事务级建模的分层原则,导致激励无法在 sequence 层复用和约束。正确做法是 driver 只负责翻译,不负责产生。
`uvm_do 而不指定 sequencer。虚拟序列本身没有对应的 sequencer,必须通过 `uvm_do_on 显式指定目标 sequencer,否则会报错或激励发错接口。
复盘时建议检查三点:一是 driver 是否严格成对调用 get_next_item() 与 item_done();二是虚拟序列是否通过 p_sequencer 正确获取子 sequencer 句柄;三是分层序列的粒度是否合理,避免底层序列承担过多场景逻辑,导致复用性下降。