1、明确DUT功能
《UVM》实战中这个DUT的功能比较简单:
DUT要完成的功能
- 在clk的上升沿,且rst不为低电平的时候,将输入的信号直接发送出去,并且输出输入的使能信号;
- 在clk上升沿,如果rst为低电平,复位输出信号和使能信号
DUT对外接口及接口功能
- clk 时钟输入信号
- rst_n 复位输入信号
- rxd 8bit输入数据信号
- rx_dv 输入使能信号
- txd 8bit输出数据信号
- tx_en 输出是能信号
2、测试计划
验证复位功能正常
- 输入:复位信号rst_n为低电平,rxd输入数据,rx_dv输入数据--》输出端输出数据和使能均为0
- 输入:复位信号rst_n为低电平,rxd无输入数据,rx_dv无输入数据--》输出端输出数据和使能均为0
验证数据传输功能正常
- 输入:复位信号rst_n为高电平,rxd无输入数据,rx_dv无输入数据--》输出端无输出数据和使能数据
- 输入:复位信号rst_n为高电平,rxd有输入数据,rx_dv有输入数据--》输出端输出数据和使能数据与输入保持一致
验证状态转换
- 输入数据和使能信号一直有效,复位信号从高电平跳转到低电平--》输出端在复位后输出0
- 输入数据和使能信号一直有效,复位信号从低电平跳转到高电平--》输出端在复位时间输出0,复位结束后恢复输出
接口数据范围测试
- rxd 可以正常发送0-8bit大小的数据;
- rx_dv可以正常发送0/1
3、测试用例
case1
复位信号rst_n为低电平,rxd输入数据8'b1000_0010,rx_dv输入数据1'b1--》输出端输出数据和使能均为0
case2
复位信号rst_n为低电平,rxd无输入数据,rx_dv无输入数据--》输出端输出数据和使能均为0
case3
复位信号rst_n为高电平,rxd无输入数据,rx_dv无输入数据--》输出端无输出数据和使能数据
case4
复位信号rst_n为高电平,rxd有输入数据8'b1010_0010,rx_dv有输入数据1'b0--》输出端输出数据和使能数据与输入保持一致
case5
输入数据8'b1010_0010和使能信号1‘b1一直有效,复位信号从高电平跳转到低电平--》输出端在复位后输出0
case6
输入数据8'b1010_0010和使能信号1‘b1一直有效,复位信号从低电平跳转到高电平--》输出端在复位时间输出0,复位结束后恢复输出
case7
输入数据rxd在0-255之间随机产生,使能信号在0-1之间随机,复位信号为高电平,输出跟随输入变化
4、搭建测试平台
开始一步一步调试UVM测试
测试平台需求
- 产生DUT输入信号
- 监控DUT输出信号
- 和预期比对,判断用例执行成功和失败
测试平台进化
dut的代码:
module dut(clk,
rst_n,
rxd,
rx_dv,
txd,
tx_en);
input clk;
input rst_n;
input[7:0] rxd;
input rx_dv;
output [7:0] txd;
output tx_en;
reg[7:0] txd;
reg tx_en;
always @(posedge clk) begin
if(!rst_n) begin
txd <= 8'b0;
tx_en <= 1'b0;
end
else begin
txd <= rxd;
tx_en <= rx_dv;
end
end
endmodule
top中将DUT和测试信号线进行连接,然后产生clk和rst
test1.0
定义一个专门用于产生激励信号的类,在该类中修改top模块中的输入信号,然后在top中调用这个类中的方法,实现激励信号的生成。
具体代码如下:
top代码如下:
`include "dut_driver.sv"
`include "dut.sv"
module top;
reg clk;
reg rst_n;
reg [7:0] rxd;
reg rx_dv;
wire [7:0] txd;
wire tx_en;
//connect to dut
dut dut_t(
.clk(clk),
.rst_n(rst_n),
.rxd(rxd),
.rx_dv(rx_dv),
.txd(txd),
.tx_en(tx_en)
);
//clk generate
initial begin
clk = 0;
rst_n = 0;
forever begin
#100 clk = ~clk;
end
end
//rst_n generate
initial begin
#1000
rst_n = ~rst_n;
#8000
rst_n = ~rst_n;
#1000
rst_n = ~rst_n;
end
//driver generate signal
initial begin
//实例dut_driver
dut_driver m_drv;
m_drv = new();
m_drv.run();
#1000;
$finish();
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars;
end
endmodule
dut_driver类如下:
综合所有的用例场景:rxd一直存在且变化,rx_dv一直存在且变化,rst_n从不复位到复位,从复位到不复位
/*
*
dut_driver类
产生dut激励信号
修改top中的rxd和rx_dv信号
*/
class dut_driver;
task run;
top.rxd <= 8'b0;
top.rx_dv <= 1'b0;
for(int i = 0; i < 256; i++) begin
@(posedge top.clk);
top.rxd <= $urandom_range(0,255);
top.rx_dv <= $urandom_range(0,1);
end
endtask
endclass
仿真:输出跟随输入变化
test2.0
引入UVM可以自动实例driver,并且自动运行driver中的主函数。
top中引入uvm包,自动执行类调用run_test()
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "dut_driver.sv"
`include "dut.sv"
module top;
reg clk;
reg rst_n;
reg [7:0] rxd;
reg rx_dv;
wire [7:0] txd;
wire tx_en;
//connect to dut
dut dut_t(
.clk(clk),
.rst_n(rst_n),
.rxd(rxd),
.rx_dv(rx_dv),
.txd(txd),
.tx_en(tx_en)
);
//clk generate
initial begin
clk = 0;
rst_n = 0;
forever begin
#100 clk = ~clk;
end
end
//rst_n generate
initial begin
#1000
rst_n = ~rst_n;
#8000
rst_n = ~rst_n;
#1000
rst_n = ~rst_n;
end
//driver generate signal
initial begin
run_test("dut_driver");
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars;
end
endmodule
dut_driver修改:
- 继承uvm_driver
- 注册类到factory
- 添加new,构造uvm树,现在只有driver,他是最高层,parent为null,
- 将主函数移动到main_phase中,这样调用run_test就会自动实例化driver并自动执行mian_phase中的内容;
- main_phase中要添加raise_objection,这样进入main_phase后uvm才知道里面有要执行的代码,否则它就进入直接退出了;最后要drop_objection.
/*
*
dut_driver类
产生dut激励信号
修改top中的rxd和rx_dv信号
*/
class dut_driver extends uvm_driver;
`uvm_component_utils(dut_driver);//注册factory
function new(string name = "dut_driver", uvm_component parent = null);
super.new(name, parent);//用于构建UVM树
`uvm_info("dut_driver", "dut_driver is created!", UVM_LOW);
endfunction
extern virtual task main_phase(uvm_phase phase);//主函数,注册完factory后可以自动运行
endclass
task dut_driver :: main_phase(uvm_phase phase);
phase.raise_objection(this);//需要起一个objection才能继续运行正常的代码,否则直接进来就退出了
top.rxd <= 8'b0;
top.rx_dv <= 1'b0;
for(int i = 0; i < 256; i++) begin
@(posedge top.clk);
top.rxd <= $urandom_range(0,255);
top.rx_dv <= $urandom_range(0,1);
`uvm_info("dut_driver", "driver generate once", UVM_LOW);
end
phase.drop_objection(this);
endtask
test3.0
解耦top和dut_driver,消除dut_driver中信号中的top层次。
- 定义interface接口,使用接口连接dut
- 引入虚拟接口,使用接口消除dut_driver中的top层次
- 在uvm中top和component之间的通信使用config_db来进行配置打通接口
interface:
interface my_if(input clk, input rst_n);
logic [7:0] data;
logic en_data;
endinterface
top中使用接口:
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "my_if.sv"
`include "dut_driver.sv"
`include "dut.sv"
module top;
reg clk;
reg rst_n;
my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);
//connect to dut
dut dut_t(
.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data),
.rx_dv(input_if.en_data),
.txd(output_if.data),
.tx_en(output_if.en_data)
);
//clk generate
initial begin
clk = 0;
rst_n = 0;
forever begin
#100 clk = ~clk;
end
end
//rst_n generate
initial begin
#1000
rst_n = ~rst_n;
#8000
rst_n = ~rst_n;
#1000
rst_n = ~rst_n;
end
initial begin
uvm_config_db #(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);//将input_if传给dut_driver中的指定的vif
end
//driver generate signal
initial begin
run_test("dut_driver");
end
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars;
end
endmodule
driver中引入虚接口
/*
*
dut_driver类
产生dut激励信号
修改top中的rxd和rx_dv信号
*/
class dut_driver extends uvm_driver;
virtual my_if vif;//使用虚接口
`uvm_component_utils(dut_driver);//注册factory
function new(string name = "dut_driver", uvm_component parent = null);
super.new(name, parent);//用于构建UVM树
`uvm_info("dut_driver", "dut_driver is created!", UVM_LOW);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);//主函数,注册完factory后可以自动运行
endclass
function void dut_driver::build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("dut_driver", "config_db connect top-if and driver-if", UVM_LOW);
if(!uvm_config_db #(virtual my_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("dut_driver", "get interface fail");//如果没有获得vif的配置就报错直接退出仿真
end
endfunction
task dut_driver :: main_phase(uvm_phase phase);
phase.raise_objection(this);//需要起一个objection才能继续运行正常的代码,否则直接进来就退出了
vif.data <= 8'b0;
vif.en_data <= 1'b0;
for(int i = 0; i < 256; i++) begin
@(posedge vif.clk);
vif.data <= $urandom_range(0,255);
vif.en_data <= $urandom_range(0,1);
`uvm_info("dut_driver", "driver generate once", UVM_LOW);
end
phase.drop_objection(this);
endtask
test4.0
加入transaction,由transaction生成要传输的数据包,一个transaction类对应一种协议数据包。
- 定义transaction类,定义数据包
- 在driver类中实例化transaction,将transaction发送到接口送出
- 在top中引入transaction
transaction:文章来源:https://www.toymoban.com/news/detail-611126.html
class dut_transaction extends uvm_sequence_item;
rand bit [47:0] dmac;
rand bit [47:0] smac;
rand bit [15:0] ether_type;
rand byte pload[];
rand bit [31:0] crc;
constraint pload_cons{
pload.size >= 46;
pload.size <= 1500;
}
function bit[31:0] calc_crc();
return 32'h0000;
endfunction
function void post_randomize();//这个会在randomize后自动调用
crc = calc_crc;
endfunction
function new(string name = "dut_transaction");
super.new(name);
endfunction
`uvm_object_utils(dut_transaction);//注册transaction的factory
endclass
~
driver中添加transaction并将transaction转换成接口数据驱动出去:文章来源地址https://www.toymoban.com/news/detail-611126.html
/*
*
dut_driver类
产生dut激励信号
修改top中的rxd和rx_dv信号
*/
class dut_driver extends uvm_driver;
virtual my_if vif;//使用虚接口
dut_transaction my_trs;//定义transaction
`uvm_component_utils(dut_driver);//注册factory
function new(string name = "dut_driver", uvm_component parent = null);
super.new(name, parent);//用于构建UVM树
`uvm_info("dut_driver", "dut_driver is created!", UVM_LOW);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);//主函数,注册完factory后可以自动运行
extern virtual task drive_pkg(dut_transaction tr);
endclass
function void dut_driver::build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("dut_driver", "config_db connect top-if and driver-if", UVM_LOW);
if(!uvm_config_db #(virtual my_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("dut_driver", "get interface fail");//如果没有获得vif的配置就报错直接退出仿真
end
endfunction
task dut_driver :: main_phase(uvm_phase phase);
phase.raise_objection(this);//需要起一个objection才能继续运行正常的代码,否则直接进来就退出了
//测试三组
for(int i = 0; i < 3; i++) begin
my_trs = new("my_tr");//实例化transaction
assert(my_trs.randomize());//随机来一个数据包
drive_pkg(my_trs);//将这个数据包送出去
end
phase.drop_objection(this);
endtask
task dut_driver::drive_pkg(d
到了这里,关于【UVM】-- UVM测试平台搭建与调试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!