3. 编写RISC-V处理器接口UVM agent

编写RISC-V处理器接口UVM agent 学习卡片

任务目标

为RISC-V处理器验证环境搭建一个可复用的UVM agent,将driver、sequencer、monitor封装为独立组件,并定义与处理器时钟、复位、指令总线(IBUS)和数据总线(DBUS)交互的接口协议。该agent通过虚拟接口(virtual interface)驱动和采样处理器总线信号,供上层testbench直接例化使用。

环境依赖

需要UVM-1.2库(或SystemVerilog IEEE 1800.2兼容实现),以及支持UVM的仿真器(VCS、Questa、Xcelium等)。处理器接口信号定义在接口模块中,agent通过config_db传递的virtual interface与DUT连接。

关键完整代码

// 文件: riscv_if.sv - 处理器接口定义
interface riscv_if(input logic clk);
    logic          rst_n;
    logic [31:0]   ibus_addr;
    logic [31:0]   ibus_rdata;
    logic          ibus_req;
    logic          ibus_gnt;
    logic [31:0]   dbus_addr;
    logic [31:0]   dbus_wdata;
    logic [31:0]   dbus_rdata;
    logic          dbus_we;
    logic          dbus_req;
    logic          dbus_gnt;
    logic [3:0]    dbus_be;
endinterface

// 文件: riscv_agent_pkg.sv - agent组件包
package riscv_agent_pkg;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    // 事务对象:描述一次总线操作
    class riscv_transaction extends uvm_sequence_item;
        rand bit [31:0] addr;
        rand bit [31:0] data;
        rand bit        is_write;
        rand bit [3:0]  be;
        rand bit        use_ibus;  // 1=指令总线, 0=数据总线
        `uvm_object_utils_begin(riscv_transaction)
            `uvm_field_int(addr, UVM_ALL_ON)
            `uvm_field_int(data, UVM_ALL_ON)
            `uvm_field_int(is_write, UVM_ALL_ON)
            `uvm_field_int(be, UVM_ALL_ON)
            `uvm_field_int(use_ibus, UVM_ALL_ON)
        `uvm_object_utils_end
        function new(string name = "riscv_transaction");
            super.new(name);
        endfunction
    endclass

    // 驱动:将事务驱动到处理器总线
    class riscv_driver extends uvm_driver #(riscv_transaction);
        virtual riscv_if vif;
        `uvm_component_utils(riscv_driver)
        function new(string name, uvm_component parent);
            super.new(name, parent);
        endfunction
        function void build_phase(uvm_phase phase);
            if (!uvm_config_db#(virtual riscv_if)::get(this, "", "vif", vif))
                `uvm_fatal("NOVIF", "virtual interface not found")
        endfunction
        task run_phase(uvm_phase phase);
            riscv_transaction req;
            @(posedge vif.clk);
            vif.rst_n = 1;
            forever begin
                seq_item_port.get_next_item(req);
                @(posedge vif.clk);
                if (req.use_ibus) begin
                    vif.ibus_addr = req.addr;
                    vif.ibus_req  = 1;
                    @(posedge vif.clk);
                    vif.ibus_req  = 0;
                end else begin
                    vif.dbus_addr = req.addr;
                    vif.dbus_be   = req.be;
                    if (req.is_write) begin
                        vif.dbus_wdata = req.data;
                        vif.dbus_we    = 1;
                    end else
                        vif.dbus_we    = 0;
                    vif.dbus_req   = 1;
                    @(posedge vif.clk);
                    vif.dbus_req   = 0;
                    vif.dbus_we    = 0;
                end
                seq_item_port.item_done();
            end
        endtask
    endclass

    // 监视器:采样总线活动并发送给分析端口
    class riscv_monitor extends uvm_monitor;
        virtual riscv_if vif;
        uvm_analysis_port #(riscv_transaction) ap;
        `uvm_component_utils(riscv_monitor)
        function new(string name, uvm_component parent);
            super.new(name, parent);
        endfunction
        function void build_phase(uvm_phase phase);
            ap = new("ap", this);
            if (!uvm_config_db#(virtual riscv_if)::get(this, "", "vif", vif))
                `uvm_fatal("NOVIF", "virtual interface not found")
        endfunction
        task run_phase(uvm_phase phase);
            riscv_transaction tr;
            forever begin
                @(posedge vif.clk);
                if (vif.ibus_req) begin
                    tr = riscv_transaction::type_id::create("tr");
                    tr.addr     = vif.ibus_addr;
                    tr.use_ibus = 1;
                    ap.write(tr);
                end
                if (vif.dbus_req) begin
                    tr = riscv_transaction::type_id::create("tr");
                    tr.addr     = vif.dbus_addr;
                    tr.data     = vif.dbus_we ? vif.dbus_wdata : vif.dbus_rdata;
                    tr.is_write = vif.dbus_we;
                    tr.be       = vif.dbus_be;
                    tr.use_ibus = 0;
                    ap.write(tr);
                end
            end
        endtask
    endclass

    // Agent:封装sequencer、driver、monitor
    class riscv_agent extends uvm_agent;
        riscv_driver    drv;
        riscv_monitor   mon;
        uvm_sequencer #(riscv_transaction) seqr;
        `uvm_component_utils(riscv_agent)
        function new(string name, uvm_component parent);
            super.new(name, parent);
        endfunction
        function void build_phase(uvm_phase phase);
            seqr = uvm_sequencer#(riscv_transaction)::type_id::create("seqr", this);
            drv  = riscv_driver::type_id::create("drv", this);
            mon  = riscv_monitor::type_id::create("mon", this);
        endfunction
        function void connect_phase(uvm_phase phase);
            drv.seq_item_port.connect(seqr.seq_item_export);
        endfunction
    endclass
endpackage

运行与验证

集成方式:在testbench顶层创建riscv_if实例,通过uvm_config_db将virtual interface设置到agent的driver和monitor中。在测试类中通过uvm_config_db#(virtual riscv_if)::set(null, "*.drv", "vif", vif)和set(null, "*.mon", "vif", vif)完成连接。

编译命令(Questa示例):

vlog -sv riscv_if.sv riscv_agent_pkg.sv
vsim -c -do "run -all; quit" top_tb

验证方法:编写一个sequence向sequencer发送写事务(use_ibus=0, is_write=1),在monitor的分析端口订阅事务,检查采样到的地址和数据与发送值一致。通过条件:monitor捕获的事务字段与sequence发送的事务完全匹配,且仿真无UVM error或fatal。

预期输出:当发送地址0x1000、数据0xDEADBEEF的写事务时,monitor分析端口应输出包含相同地址和数据的riscv_transaction对象。