UVM入门系列(一)----通过uvm_gen脚本快速搭建一般验证平台(上)
前言
文中所有代码已上传,可通过以下链接下载:
代码链接
待测设计:简单的8bit加法器;
仿真工具:vcs+verdi
DUT代码如下:
`timescale 1ns/1ps
module add(
input clk,
input rst,
input [7:0] a,
input [7:0] b,
input cin,
output reg cout,
output reg [7:0] sum
);
always@(posedge clk or negedge rst) begin
if(!rst) begin
cout<=1'b0;
sum<=8'b0;
end
else begin
{cout,sum}<=cin+a+b;
end
end
endmodule
一、uvm_gen脚本
代码生成
首先介绍一下本文使用的uvm_gen脚本,通过使用该脚本会自动生成一个标准的UVM结构框架,针对本文DUT使用该脚本工具会生成如下图所示的基本框架代码。
图中可以看出脚本已经完成了验证平台的部分基础代码,接下来的工作是针对验证方案进行代码的补全。
cr_agent
脚本会自动生成一个cr_agent,用于生成时钟和复位信号(后续文章会对该部分进行进一步描述),本文工程简单起见直接使用该代码,不做改动。
二、代码的补全
*括号内为文件路径
1.interface
interface
1.1.i_agent_if.sv(verf/tb/agent/i)
脚本生成代码如下:
`ifndef I_AGENT_IF_SV
`define I_AGENT_IF_SV
interface i_agent_if();
//
endinterface : i_agent_if
`endif // I_AGENT_IF_SV
加入DUT输入端口,代码完成如下:
`ifndef I_AGENT_IF_SV
`define I_AGENT_IF_SV
interface i_agent_if();
logic [7:0] a;
logic [7:0] b;
logic cin;
endinterface : i_agent_if
`endif // I_AGENT_IF_SV
1.2.o_agent_if.sv(verf/tb/agent/o)
同上操作,加入DUT输出端口:
`ifndef O_AGENT_IF_SV
`define O_AGENT_IF_SV
interface o_agent_if();
logic cout;
logic [7:0] sum;
endinterface : o_agent_if
`endif // O_AGENT_IF_SV
2.i_seq_item.sv(/verf/tb/agent/i)
i_seq_item即为驱动入DUT的数据包。
脚本生成的代码如下
`ifndef I_SEQ_ITEM_SV
`define I_SEQ_ITEM_SV
class i_seq_item extends uvm_sequence_item;
`uvm_object_utils(i_seq_item)
extern function new(string name = "i_seq_item");
endclass : i_seq_item
function i_seq_item::new(string name = "i_seq_item");
super.new(name);
endfunction : new
`endif // I_SEQ_ITEM_SV
添加激励信息和注册域的自动化:
`ifndef I_SEQ_ITEM_SV
`define I_SEQ_ITEM_SV
class i_seq_item extends uvm_sequence_item;
rand bit [7:0] a;
rand bit [7:0] b;
rand bit cin;
`uvm_object_utils_begin(i_seq_item)
`uvm_field_int(a,UVM_DEFAULT)
`uvm_field_int(b,UVM_DEFAULT)
`uvm_field_int(cin,UVM_DEFAULT)
`uvm_object_utils_end
extern function new(string name = "i_seq_item");
endclass : i_seq_item
function i_seq_item::new(string name = "i_seq_item");
super.new(name);
endfunction : new
`endif // I_SEQ_ITEM_SV
3.add_tb.sv
该文件中进行dut的例化,接口的传递。
自动生成的代码如下(后面文章不再贴出原代码)
`timescale 1ns/1ns
module add_tb;
`include "uvm_macros.svh"
import uvm_pkg::*;
import i_agent_pkg::*;
import o_agent_pkg::*;
import cr_agent_pkg::*;
import add_env_pkg::*;
logic sysclock;
logic sysresetn;
cr_if m_cr_if();
assign sysclock = m_cr_if.sysclock;
assign sysresetn = m_cr_if.sysresetn;
i_agent_if m_i_agent_if();
o_agent_if m_o_agent_if();
//
//dut
//
initial
begin
uvm_config_db #(virtual cr_if)::set(null,"\*", "m_cr_if", m_cr_if);
uvm_config_db #(virtual i_agent_if)::set(null,"\*", "m_i_agent_if", m_i_agent_if);
uvm_config_db #(virtual o_agent_if)::set(null,"\*", "m_o_agent_if", m_o_agent_if);
run_test();
end
endmodule
自动生成的代码只需要进行例化的DUT即可。
添加例化:
add dut(
.clk(sysclock),
.rst(sysresetn),
.a(m_i_agent_if.a),
.b(m_i_agent_if.b),
.cin(m_i_agent_if.cin),
.cout(m_o_agent_if.cout),
.sum(m_o_agent_if.sum)
);
添加vcs dump波形代码
initial begin
$fsdbDumpfile("sim.fsdb");
$fsdbDumpvars();
end
4.i_driver(verf/tb/agent/i)
1、例化时钟接口
virtual interface cr_if cvif;
2、get时钟接口(接口从env传递,后面会说明如何在env中向i_driver传递该接口)
if (!uvm_config_db #(virtual cr_if)::get(this, "", "cvif", cvif))
`uvm_error("NOVIF", {"virtual interface must be set for: ",get_full_name(),".cvif"})
3、驱动激励信号
task i_driver::do_drive(i_seq_item req);
vif.a=req.a;
vif.b=req.b;
vif.cin=req.cin;
@(posedge cvif.sysclock);
endtask : do_drive
5.add_env.sv(verf/tb/env)
传递接口至上文所述i_driver,
uvm_config_db#(virtual cr_if)::set(this, "m_i_agent.*", "cvif", m_cr_if);
6.构造sequence
在/verf/testcase/ 新建文件夹seq和test,
6.1 add_seq.sv
在seq文件夹新建add_seq.sv文件。
add_seq继承自自动生成的i_base_seq,具体代码如下:
class add_seq extends i_base_seq;
`uvm_object_utils(add_seq)
uvm_event seq_begin_ev;
extern function new(string name="add_seq");
extern task body();
endclass
function add_seq::new(string name="add_seq");
super.new(name);
endfunction
task add_seq::body();
seq_begin_ev=uvm_event_pool::get_global("seq_begin_ev");
`uvm_info(get_type_name(),"Default sequence starting",UVM_HIGH)
repeat(100) begin
`uvm_do(req);
end
`uvm_info(get_type_name(),"Default sequence end",UVM_HIGH)
endtask
启动该sequence会发送100个随机激励包给driver。
6.2 seq_pkg.sv
seq文件夹新建seq_pkg.sv文件,方便后续扩展seq文件的管理。代码如下:
package seq_pkg;
import uvm_pkg::*;
import i_agent_pkg::*;
`include "uvm_macros.svh"
`include "add_seq.sv"
endpackage
6.3 add_test.sv
test文件夹新建test.sv文件,用于启动激励,控制仿真。
class add_test extends add_test_base;
`uvm_component_utils(add_test)
add_seq tc_seq;
extern function new(string name,uvm_component parent=null);
extern task main_phase(uvm_phase phase);
endclass
function add_test::new(string name,uvm_component parent=null);
super.new(name,parent);
endfunction
task add_test::main_phase(uvm_phase phase);
phase.raise_objection(this);
this.tc_seq=new("tc_seq");
tc_seq.start(m_env.m_vsequencer.m_i_seqer);
#20;
phase.drop_objection(this);
endtask
6.4 test_pkg.sv
test文件夹新建test_pkg.sv,方便管理test文件。
package test_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "add_test_base.sv"
`include "add_test.sv"
endpackage
7 待后续更新
总结:
至此,现在验证平台已经可以通过仿真并查看波形,monitor、reference model 和scoreboard部分的代码会在后续更新继续介绍,后续也会介绍如何使用脚本使用vcs仿真。