搭建一个简单的UVM验证平台
转载自 https://zhuanlan.zhihu.com/p/713891980
一. UVM 方法学简介
UVM(Universal Verification Methodology),又称作通用验证方法学。它起源于OVM(Open Verification Methdology),是由Cadence,Mentor和Synopsys联合推出的主流验证方法学;UVM方法学可以帮助我们搭建验证平台、编写测试用例,而它自身提供的基础类库(basic class library)和基本验证结构可以让具有不同程度编程经验的芯片验证工程师们快速构建起一个结构良好可信的验证框架。

二. 基本组件编写和平台搭建
1.平台基本结构
首先,我们将一个非常简单的设计模块作为待验证的DUT,其代码如下:
module simple_design (
input[1:0] sel,
input[7:0] a,
input[7:0] b,
input[7:0] c,
output[31:0] d
);
reg[31:0] d_reg;
always@(*) begin
case(sel)
0: d_reg = a;
1: d_reg = b;
2: d_reg = c;
3: d_reg = (a+b+c);
endcase
end
assign d = d_reg;
endmodule
基于该模块,可规划出验证平台如下,Driver负责将数据传输至接口,进而发送给DUT;Monitor负责从接口采集数据;Env作为更高层级的组件,可容纳Driver和Monitor,并控制Driver和Monitor相互配合(在这里使用了mailbox作为媒介,具体控制方式见组件中的描述);test_top则是整个UVM树形结构的顶层,对应我们的仿真用例。

2.m_transaction
在验证环境中,一般需要对数据流进行事务级的建模,因此我们可编写m_transaction的类,将需要发送的激励进行封装,并加以约束,代码如下:
//file_name: m_transcation.sv
class m_transcation extends uvm_object;
rand bit[1:0] tr_sel;
rand bit[7:0] tr_a;
rand bit[7:0] tr_b;
rand bit[7:0] tr_c;
`uvm_object_utils(m_transcation)
constraint sel_c {
tr_sel dist{3:/40, [0:2]:/60};
}
endclass
3.m_ctrl_if
由于Driver和Monitor中都需要对interface中的信号进行操作,因此我们在实现编写Driver和Monitor之前,需编写interface文件,代码如下:
//file_name: m_ctrl_if.sv
interface m_ctrl_if(input logic clk, rst);
logic[1:0] sel;
logic[7:0] a;
logic[7:0] b;
logic[7:0] c;
logic[31:0] d;
endinterface
4.m_driver
Driver组件中,将transaction进行随机化并驱动至接口,由于DUT所需的数据并不复杂,本平台中的Driver承担了产生激励的功能,后续可适当进行优化;
除了需要发送激励,Driver还应与Monitor进行通信,告知其何时进行采集,因此我们使用了mailbox完成flag的交互(mailbox是一种在进程之间交换消息的机制,数据可以通过一个进程发送到mailbox,然后由另一个进程获取,所传输的数据可以是任何有效的SystemVerilog数据类型以及class),mailbox的主要方法如下,我们在Driver中使用了带阻塞的put方法。

Driver的完整代码如下:
//file_name: m_driver.sv
class m_driver extends uvm_driver;
int flag;
int set_times;
m_transcation m_tr;
mailbox#(int) flag_fifo;
virtual m_ctrl_if m_if;
`uvm_component_utils(m_driver)
function new(string name, uvm_component parent);
super.new(name, parent);
m_tr = new();
flag_fifo = new();
flag = 0;
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(virtual m_ctrl_if)::get(this, "", "m_if", m_if);
uvm_config_db#(int)::get(this, "", "set_times", set_times);
endfunction
function void connect_phase(uvm_phase phase);
super.contect_phase(phase);
endfunction
task main_phase(uvm_phase phase);
int times;
super.main_phase();
phase.raise_objection(this);
times = set_times;
for(int i=0; i<times; i++) begin
#60ns;
`uvm_info(get_type_name(),$sformatf("The times[%0d] send pkt",i),UVM_NONE)
send_one_pkt();
flag = flag + 1;
end
phase.drop_objection(this);
endtask
task set_flag(mailbox#(int) flag_fifo);
this.flag_fifo = flag_fifo;
this.flag_fifo.put(flag);
endtask
task send_one_pkt();
m_tr.randomize();
`uvm_info(get_type_name(),$sformatf("Send pkt as follow::: \
\n tr_sel ---%0d \
\n tr_a ---%0d \
\n tr_b ---%0d \
\n tr_c ---%0d",
m_tr.tr_sel
