4. UVM序列生成与驱动

任务目标

在UVM验证环境中,定义复位序列与读写序列,通过sequencer将sequence item驱动至driver,实现不同激励的生成与发送。本案例聚焦sequence机制的最小可运行闭环,不涉及寄存器模型或协议细节。

环境依赖

需要UVM-1.2库(或等效实现),仿真器支持SystemVerilog(如VCS、QuestaSim)。以下代码为单个SV文件,可直接编译运行。

关键完整代码

// file: seq_drive_demo.sv
`include "uvm_macros.svh"
import uvm_pkg::*;

// 1. 定义sequence item(激励数据单元)
class my_item extends uvm_sequence_item;
    rand bit [7:0] addr;
    rand bit [7:0] data;
    rand bit      write; // 1=写, 0=读
    `uvm_object_utils_begin(my_item)
        `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_item");
        super.new(name);
    endfunction
endclass

// 2. 定义复位序列(只产生一个复位item)
class reset_seq extends uvm_sequence #(my_item);
    `uvm_object_utils(reset_seq)
    function new(string name = "reset_seq");
        super.new(name);
    endfunction
    task body();
        my_item item = my_item::type_id::create("item");
        start_item(item);
        item.addr = 0;
        item.data = 0;
        item.write = 0; // 复位读操作
        finish_item(item);
    endtask
endclass

// 3. 定义读写序列(产生多个随机读写item)
class rw_seq extends uvm_sequence #(my_item);
    `uvm_object_utils(rw_seq)
    function new(string name = "rw_seq");
        super.new(name);
    endfunction
    task body();
        repeat (5) begin
            my_item item = my_item::type_id::create("item");
            start_item(item);
            if (!item.randomize())
                `uvm_fatal(get_type_name(), "randomize failed")
            finish_item(item);
        end
    endtask
endclass

// 4. 定义driver(接收item并打印)
class my_driver extends uvm_driver #(my_item);
    `uvm_component_utils(my_driver)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    task run_phase(uvm_phase phase);
        my_item item;
        forever begin
            seq_item_port.get_next_item(item);
            // 模拟驱动到DUT接口(此处仅打印)
            $display("[DRV] %s addr=%0h data=%0h",
                     item.write ? "WRITE" : "READ", item.addr, item.data);
            seq_item_port.item_done();
        end
    endtask
endclass

// 5. 定义测试层:启动sequence并连接
class test_base extends uvm_test;
    `uvm_component_utils(test_base)
    my_driver drv;
    uvm_sequencer #(my_item) sqr;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        drv = my_driver::type_id::create("drv", this);
        sqr = uvm_sequencer #(my_item)::type_id::create("sqr", this);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        drv.seq_item_port.connect(sqr.seq_item_export);
    endfunction

    task run_phase(uvm_phase phase);
        reset_seq rseq;
        rw_seq    wseq;
        phase.raise_objection(this);
        // 先执行复位序列
        rseq = reset_seq::type_id::create("rseq");
        rseq.start(sqr);
        // 再执行读写序列
        wseq = rw_seq::type_id::create("wseq");
        wseq.start(sqr);
        #10ns;
        phase.drop_objection(this);
    endtask
endclass

// 6. 顶层模块
module top;
    initial begin
        run_test("test_base");
    end
endmodule

运行方式

# 使用VCS编译运行
vcs -sverilog +incdir+$UVM_HOME/src seq_drive_demo.sv $UVM_HOME/src/uvm.sv -R -l run.log

# 或使用QuestaSim
vlog -sv seq_drive_demo.sv +incdir+$UVM_HOME/src $UVM_HOME/src/uvm.sv
vsim -c -do "run -all; quit" test_base

验证方法与判定标准

运行仿真后,观察终端输出。判定通过条件:

  • 首先出现1行[DRV] READ addr=0 data=0(来自复位序列);
  • 随后出现5行[DRV] WRITE/READ addr=随机值 data=随机值(来自读写序列);
  • 仿真无UVM_ERROR或UVM_FATAL,正常退出。

预期输出示例(地址与数据为随机值,仅示意格式):

[DRV] READ addr=0 data=0
[DRV] WRITE addr=3c data=a5
[DRV] READ addr=7f data=12
[DRV] WRITE addr=1a data=ff
[DRV] READ addr=55 data=00
[DRV] WRITE addr=e2 data=8d

常见问题

  • 编译报uvm_pkg找不到:确认UVM_HOME环境变量已设置,且编译命令中包含+incdir+$UVM_HOME/src。
  • 没有输出:检查run_test参数与test类名一致(此处为"test_base"),且顶层模块中已调用run_test。
  • objection机制:run_phase中必须raise/drop objection,否则仿真可能在sequence执行前结束。