3. 编写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对象。