目录
1.定义dut
由于是demo,这里简单的写了一个dut,其实就是一个计数器。代码如下:
`timescale 1ns/1ps
module counter(
input clk,
input rst_n,
input cnt_en,
output reg [7:0] out
);
always @(posedge clk or negedge rst_n)
begin
if(~rst_n) begin
out<=8'b0;
end
else if(cnt_en) begin
if(out != 10) begin
out<=out + 1'b1;
end
else begin
out<=8'b0;
end
end
else begin
out<=8'b0;
end
end
endmodule
代码完成后,我们编写相关的dut_tb去测试相关功能是否符合。
module counter_tb();
reg clk;
reg rst_n;
reg cnt_en;
wire [6:0] out;
counter t_cnt(.clk(clk), .rst_n(rst_n), .cnt_en(cnt_en), .out(out));
initial
begin
clk = 1;
rst_n = 0;
cnt_en = 0;
#10 rst_n = 1;
cnt_en = 1;
#100 cnt_en = 0;
#200 cnt_en = 1;
#1000 cnt_en = 0;
end
always #10 clk = ~clk;
endmodule
仿真后查看波形。
至此,我们的dut准备完毕。
2.验证功能
2.1 验证计划
- 定义uvm_sequence_item,用于生成相应的激励。并通过driver发送给dut。
- driver接收到item后,通过virtual interface发送给dut。
- monitor捕获 dut的输入和输出的值,并发送到scoreboard。
- scoreboard主要负责根据从monitor接收的输入和输出值检查设计的功能正确性。
2.2 定义virtual interface
根据dut分析,定义相关interface如下:
- 以时钟clk为输入
- 有1位的rst_n,cnt_en和8位的data。
`ifndef COUNTER_IF__SV
`define COUNTER_IF__SV
interface counter_if(input clk);
logic rst_n;
logic cnt_en;
logic [7:0] data;
endinterface
`endif
2.3 sequence_item
`ifndef COUNTER_ITEM__SV
`define COUNTER_ITEM__SV
class counter_item extends uvm_sequence_item;
`uvm_object_utils(counter_item)
rand bit count_en;
rand bit rst_n;
logic[7:0] counter_out;
//将内容转成字符串,方便调试打印信息
virtual function string convert2str();
return $sformatf("count_en %d,rst_n %d, counter_out %d", count_en, rst_n, counter_out);
endfunction
function new(string name="counter_item");
super.new(name);
endfunction
//限定约束
// constraint c1 { count_en dist {0:/10, 1:/90};}
constraint c1 { count_en == 1;}
constraint c2 {rst_n == 1;}
endclass
`endif
2.4 sequence
`ifndef COUNTER_SEQUENCE__SV
`define COUNTER_SEQUENCE__SV
class counter_item_seq extends uvm_sequence;
`uvm_object_utils(counter_item_seq)
function new(string name = "counter_item_seq");
super.new(name);
endfunction
rand int num;
//约束发送激励的数量
constraint c1 {soft num inside {[10:50]};}
virtual task body();
for (int i = 0; i < num; i++) begin
counter_item item = counter_item::type_id::create("counter_item");
start_item(item);
item.randomize();
`uvm_info("SEQ", $sformatf("generate new item %s", item.convert2str()), UVM_HIGH);
finish_item(item);
end
`uvm_info("SEQ", $sformatf("Done generation of %d items.", num), UVM_LOW);
endtask
endclass
`endif
2.5 driver
`ifndef COUNTER_DRIVER__SV
`define COUNTER_DRIVER__SV
class counter_driver extends uvm_driver #(counter_item);
`uvm_component_utils(counter_driver)
function new(string name="counter_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
//连接的interface
virtual counter_if vif;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
//通过uvm_config_db获取到相应的vif
if (!uvm_config_db#(virtual counter_if)::get(this, "", "counter_vif", vif))
`uvm_fatal("DRV", "could not get vif")
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
counter_item item;
`uvm_info("DRV", $sformatf("wait for item from sequencer"), UVM_HIGH)
//获取item;发送;通知完成(可以获取下一个item)
seq_item_port.get_next_item(item);
drive_item(item);
seq_item_port.item_done();
end
endtask
virtual task drive_item(counter_item item);
@(vif.clk);
vif.cnt_en <= item.count_en;
vif.rst_n <= item.rst_n;
endtask
endclass
`endif
2.6 monitor
`ifndef COUNTER_MONITOR__SV
`define COUNTER_MONITOR__SV
class counter_monitor extends uvm_monitor;
`uvm_component_utils(counter_monitor)
function new(string name="counter_monitor", uvm_component parent=null);
super.new(name, parent);
endfunction
//定义uvm_analysis_port
uvm_analysis_port #(counter_item) mon_analysis_port;
//需要连接的interface
virtual counter_if vif;
virtual function void build_phase(uvm_phase phase);
//通过uvm_config_db获取到interface句柄
if(!uvm_config_db#(virtual counter_if)::get(this, "", "counter_vif", vif))
`uvm_fatal("MON", "Could not get vif")
//创建analysis port
mon_analysis_port = new("mon_analysis_port", this);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
@(posedge vif.clk);
if(vif.cnt_en)begin
//从interface获取相应的输出
counter_item item = counter_item::type_id::create("item");
item.count_en = vif.cnt_en;
item.counter_out = vif.data;
//发送给scoreboard
mon_analysis_port.write(item);
//`uvm_info("MON", $sformatf("Saw item %s", item.convert2str()), UVM_HIGH);
end
end
endtask
endclass
`endif
2.7 scoreboard
`ifndef COUNTER_SCOREBOARD__SV
`define COUNTER_SCOREBOARD__SV
class counter_scoreboard extends uvm_scoreboard;
`uvm_component_utils(counter_scoreboard)
function new(string name="counter_scoreboard", uvm_component parent=null);
super.new(name, parent);
endfunction
//期望输出
bit[7:0] exp_cout = 8'b0;
//定义analysis port的接收端
uvm_analysis_imp#(counter_item, counter_scoreboard) m_analysis_imp;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
//创建
m_analysis_imp = new("m_analysis_imp", this);
// if(!uvm_config_db#)
endfunction
virtual function write(counter_item item);
if(item.counter_out != exp_cout)begin
`uvm_error("SCBD", $sformatf("ERROR! out %d exp %d", item.counter_out, exp_cout))
end
else begin
`uvm_info("SCBD", $sformatf("PASS ! out %d exp %d", item.counter_out, exp_cout), UVM_LOW)
end
if(item.count_en == 0) begin
exp_cout = 8'b0;
end
else begin
if(exp_cout != 10) begin
exp_cout ++;
end
else begin
exp_cout = 0;
end
end
endfunction
endclass
`endif
2.8 agent
`ifndef COUNTER_AGENT__SV
`define COUNTER_AGENT__SV
class counter_agent extends uvm_agent;
`uvm_component_utils(counter_agent)
function new(string name="counter_agent", uvm_component parent=null);
super.new(name, parent);
endfunction
//agent内部包含了sequencer,driver,mon;
uvm_sequencer #(counter_item) sqr;
counter_driver drv;
counter_monitor mon;
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
endclass
function void counter_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
sqr = uvm_sequencer #(counter_item)::type_id::create("sqr", this);
drv = counter_driver::type_id::create("drv", this);
mon = counter_monitor::type_id::create("mon", this);
endfunction
function void counter_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
//将sequencer的seq_tem_export和driver的seq_item_port连接记起来,用于发送激励
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
`endif
2.9 env
`ifndef COUNTER_ENV__SV
`define COUNTER_ENV__SV
class counter_env extends uvm_env;
`uvm_component_utils(counter_env)
function new(string name = "counter_env", uvm_component parent);
super.new(name, parent);
endfunction
//整个验证环境,包含agent和scoreboard。
//由于agent包含sequencer,driver,monitor,实际是将他们包含进来。
counter_agent agt;
counter_scoreboard scb;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
agt = counter_agent::type_id::create("agt", this);
scb = counter_scoreboard::type_id::create("scb", this);
endfunction
extern virtual function void connect_phase(uvm_phase phase);
endclass
function void counter_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
//将monitor的analysis_port和scoreboard的analysis_port接收端连接
agt.mon.mon_analysis_port.connect(scb.m_analysis_imp);
endfunction
`endif
2.10 test
`ifndef BASE_TEST__SV
`define BASE_TEST__SV
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
//test包含了env和sequence以及相应的interface
counter_env env;
counter_item_seq seq;
virtual counter_if vif;
extern virtual function void build_phase(uvm_phase phase);
//run_phase时执行相应的激励
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
seq.start(env.agt.sqr);
#1000;
phase.drop_objection(this);
endtask
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = counter_env::type_id::create("env", this);
//通过uvm_config_db获取interface
if (!uvm_config_db#(virtual counter_if)::get(this, "", "counter_vif", vif)) begin
`uvm_fatal("TEST", "Did not get vif")
end
//通过uvm_config_db注册interface
uvm_config_db#(virtual counter_if)::set(this, "env.agt.*", "couter_vif", vif);
seq = counter_item_seq::type_id::create("seq");
seq.randomize();
endfunction
`endif
`ifndef COUNTER_CASE0__SV
`define COUNTER_CASE0__SV
class counter_case0 extends base_test;
function new(string name = "counter_case0", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(counter_case0)
endclass
function void counter_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
seq.randomize();
endfunction
`endif
2.11 top
`ifndef TOP__SV
`define TOP__SV
import uvm_pkg::*;
//包含相关文件
`include "dut.v"
`include "counter_item.sv"
`include "counter_if.sv"
`include "counter_driver.sv"
`include "counter_monitor.sv"
`include "counter_scoreboard.sv"
`include "counter_agent.sv"
`include "counter_env.sv"
`include "counter_sequence.sv"
`include "base_test.sv"
`include "counter_case0.sv"
module tb;
//时钟信号
reg clk;
always #10 clk = ~clk;
//使用clk创建相应的interface
counter_if cif(clk);
//实例化counter,通过interface连接
counter cnt(.clk(clk), .rst_n(cif.rst_n), .cnt_en(cif.cnt_en), .out(cif.data));
initial begin
clk <= 0;
//通过uvm_config_db注册interface
uvm_config_db#(virtual counter_if)::set(null, "", "counter_vif", cif);//uvm_test_top.*
//运行相应case
run_test("counter_case0");
end
endmodule
`endif
2.12 运行
vcs -debug -full64 -sverilog -timescale=1ns/1ns -ntb_opts uvm-1.1 +incdir+. top.sv -l comp.log
./simv
3.总结
本文只是给出demo的一些说明,具体其他的相关概念会在其他文章进行说明。
相关代码:https://github.com/zljuft/verify