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 — Sequencer — Driver 协作数据流 Sequence 产生事务 randomize() start() 启动 Sequencer 仲裁与转发 FIFO / 优先级 seq_item_export Driver 事务转信号 get_next_item() item_done() 事务请求 授权事务 item_done 完成 虚拟序列(Virtual Sequence)协调层 虚拟序列 协调多接口激励 子序列 A 接口 A 激励 子序列 B 接口 B 激励 图:事务级激励从抽象到具体的完整路径,虚拟序列负责跨接口协调

二、方法流程:从 sequence 到 driver 的完整链路

构建一条可用的激励链路,通常遵循以下步骤:

  1. 定义事务类:继承 uvm_sequence_item,使用 rand 声明随机字段,并通过 `uvm_object_utils 注册。
  2. 定义序列类:继承 uvm_sequence #(my_item),在 body() 任务中通过 `uvm_do 或 start_item/finish_item 产生事务。
  3. 定义驱动类:继承 uvm_driver #(my_item),在 run_phase 中循环调用 get_next_item(),完成信号驱动后调用 item_done()。
  4. 连接与启动:在 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
提示:driver 中必须成对出现 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 内

五、误区与复盘

常见误区一:在 driver 中直接调用 randomize() 产生激励。这破坏了事务级建模的分层原则,导致激励无法在 sequence 层复用和约束。正确做法是 driver 只负责翻译,不负责产生。
常见误区二:虚拟序列中直接使用 `uvm_do 而不指定 sequencer。虚拟序列本身没有对应的 sequencer,必须通过 `uvm_do_on 显式指定目标 sequencer,否则会报错或激励发错接口。

复盘时建议检查三点:一是 driver 是否严格成对调用 get_next_item() 与 item_done();二是虚拟序列是否通过 p_sequencer 正确获取子 sequencer 句柄;三是分层序列的粒度是否合理,避免底层序列承担过多场景逻辑,导致复用性下降。

核心记忆:sequence 管“生成”,sequencer 管“仲裁”,driver 管“翻译”;虚拟序列管“协调”,分层设计管“复用”。四者各司其职,才能构建出既灵活又稳定的 UVM 激励体系。