1. 搭建UVM验证环境基本框架
任务目标
搭建一个最小可运行的UVM验证环境,包含driver、monitor、sequencer、agent、env、test六个核心组件,并驱动一个简单的APB读写序列,验证环境能够正确编译并完成仿真。
环境依赖
需要支持UVM-1.2的仿真器(如VCS、QuestaSim),并已设置好UVM库路径。以下代码基于QuestaSim的uvm_pkg与questa_uvm_pkg,若使用其他仿真器,仅需调整编译命令中的库映射。
完整实现
// 文件: apb_uvm_env.sv
`include "uvm_macros.svh"
import uvm_pkg::*;
// 1. 简单APB事务
class apb_transfer extends uvm_sequence_item;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit write; // 1:写, 0:读
`uvm_object_utils_begin(apb_transfer)
`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 = "apb_transfer");
super.new(name);
endfunction
endclass
// 2. 简单序列:先写后读
class apb_rw_seq extends uvm_sequence #(apb_transfer);
`uvm_object_utils(apb_rw_seq)
function new(string name = "apb_rw_seq");
super.new(name);
endfunction
task body();
apb_transfer t;
// 写操作
t = apb_transfer::type_id::create("write_t");
t.addr = 32'h0000_0004;
t.data = 32'hDEAD_BEEF;
t.write = 1;
start_item(t);
finish_item(t);
// 读操作
t = apb_transfer::type_id::create("read_t");
t.addr = 32'h0000_0004;
t.write = 0;
start_item(t);
finish_item(t);
endtask
endclass
// 3. Driver:将事务驱动到虚拟接口
class apb_driver extends uvm_driver #(apb_transfer);
`uvm_component_utils(apb_driver)
virtual apb_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "virtual interface not found")
endfunction
task run_phase(uvm_phase phase);
apb_transfer req;
vif.psel = 0; vif.penable = 0; vif.pwrite = 0;
forever begin
seq_item_port.get_next_item(req);
@(posedge vif.pclk);
vif.psel = 1;
vif.paddr = req.addr;
vif.pwrite = req.write;
if (req.write) begin
vif.pwdata = req.data;
@(posedge vif.pclk);
vif.penable = 1;
@(posedge vif.pclk);
vif.penable = 0;
end else begin
@(posedge vif.pclk);
vif.penable = 1;
@(posedge vif.pclk);
req.data = vif.prdata;
vif.penable = 0;
end
vif.psel = 0;
seq_item_port.item_done();
end
endtask
endclass
// 4. Monitor:采样总线并发送给scoreboard(此处仅打印)
class apb_monitor extends uvm_monitor;
`uvm_component_utils(apb_monitor)
virtual apb_if vif;
uvm_analysis_port #(apb_transfer) mon_ap;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
mon_ap = new("mon_ap", this);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "virtual interface not found")
endfunction
task run_phase(uvm_phase phase);
apb_transfer t;
forever begin
@(posedge vif.pclk);
if (vif.psel && vif.penable) begin
t = apb_transfer::type_id::create("mon_t");
t.addr = vif.paddr;
t.write = vif.pwrite;
if (vif.pwrite) t.data = vif.pwdata;
else t.data = vif.prdata;
mon_ap.write(t);
`uvm_info("MON", $sformatf("Observed: %s addr=0x%0h data=0x%0h",
t.write ? "WR" : "RD", t.addr, t.data), UVM_MEDIUM)
end
end
endtask
endclass
// 5. Sequencer
class apb_sequencer extends uvm_sequencer #(apb_transfer);
`uvm_component_utils(apb_sequencer)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
// 6. Agent
class apb_agent extends uvm_agent;
`uvm_component_utils(apb_agent)
apb_driver drv;
apb_monitor mon;
apb_sequencer sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
sqr = apb_sequencer::type_id::create("sqr", this);
drv = apb_driver::type_id::create("drv", this);
mon = apb_monitor::type_id::create("mon", this);
endfunction
function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
// 7. Env
class apb_env extends uvm_env;
`uvm_component_utils(apb_env)
apb_agent agent;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
agent = apb_agent::type_id::create("agent", this);
endfunction
endclass
// 8. Test:启动序列
class apb_base_test extends uvm_test;
`uvm_component_utils(apb_base_test)
apb_env env;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
env = apb_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
apb_rw_seq seq;
seq = apb_rw_seq::type_id::create("seq");
phase.raise_objection(this);
seq.start(env.agent.sqr);
#100ns;
phase.drop_objection(this);
endtask
endclass
// 9. 接口与顶层模块
interface apb_if(input bit pclk);
logic presetn;
logic [31:0] paddr;
logic pwrite;
logic psel;
logic penable;
logic [31:0] pwdata;
logic [31:0] prdata;
endinterface
module top;
bit pclk;
always #5 pclk = ~pclk;
apb_if apb_if_inst(pclk);
// 简单APB从设备模型:寄存器文件
reg [31:0] mem [int];
assign apb_if_inst.prdata = (apb_if_inst.pwrite) ? 32'h0 :
(apb_if_inst.paddr == 32'h4) ? mem[32'h4] : 32'h0;
always @(posedge pclk) begin
if (apb_if_inst.psel && apb_if_inst.penable && apb_if_inst.pwrite)
mem[apb_if_inst.paddr] = apb_if_inst.pwdata;
end
initial begin
apb_if_inst.presetn = 0;
#20 apb_if_inst.presetn = 1;
uvm_config_db#(virtual apb_if)::set(null, "uvm_test_top.env.agent.*", "vif", apb_if_inst);
run_test("apb_base_test");
end
endmodule
编译与运行
# QuestaSim/ModelSim
vlib work
vlog +incdir+$UVM_HOME/src $UVM_HOME/src/uvm_pkg.sv $UVM_HOME/src/questa_uvm_pkg.sv apb_uvm_env.sv
vsim -c -do "run -all; quit" work.top
验证方法与通过标准
仿真运行后,观察UVM报告。通过标准为:
- 编译无错误,仿真无fatal;
- monitor打印出两条事务:
WR addr=0x4 data=0xdeadbeef和RD addr=0x4 data=0xdeadbeef; - UVM报告显示
UVM_ERROR : 0,仿真正常退出。
预期输出(由代码逻辑直接推出):
UVM_INFO MON: Observed: WR addr=0x4 data=0xdeadbeef
UVM_INFO MON: Observed: RD addr=0x4 data=0xdeadbeef
UVM_INFO /uvm-1.2/uvm_report_server.svh: Reporting: 0 errors