本文主要是介绍2023.7.25 UVM学习和环境搭建,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1.验证平台的组成
2. UVM验证平台搭建
1)UVM中driver的搭建
2)增加factory机制
3)增加object机制
4)增加virtual interface
1.验证平台的组成
验证平台通常由DUT(Design Under Test)、driver、monitor、reference model、scoredboard组成。
driver:用于生成各种激励,施加给DUT,验证其功能。
monitor:用于收集DUT的输出,传递给scoredboard。
scoreboard:给出判断功能正确的标准。
reference model:验证平台同样进行正确的DUT过程,提供正确的值。同样由driver施加激励。
在UVM中,引入了agent和sequence,因此实际的验证平台如上所示。
2. UVM验证平台搭建
以下列的DUT为例。
module dut(clk,rxd,rx_dv,txd,tx_en);
input clk;tx_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 @(pospedge clk) beginif(!rst_n) begintxd <= 8'b0;tx_en <= 1'b0;endelse begintxd <= rxd;tx_en <= rx_dv;end
end
endmodule
该DUT通过rxd接收数据,通过txd发送出去。rx_dv是接受的数据有效指示,tx_en是发送的数据有效指示。
1)UVM中driver的搭建
验证平台中的所有组件都派生自UVM中的类。简单的driver如下所示。wdd
class my_driver extends uvm_driver;function new(string name = "my_driver", uvm_component parent = null);super.new(name,parent);endfunction//派生自uvm_component中的类的new函数需要传入两个参数,string类型的name和parent,uvm_driver派生自uvm_component,所以也需要该参数。extern virtual task main_phase(uvm_phase phase);//UVM由phase来管理验证平台的运行,phase统一以xxxx_phase命名,需要传入uvm_phase类型的phase参数,driver中的行为均在main_phase中实现。
endclasstask my_driver::main_phase(uvm_phase phase);top_tb.rxd <= 8'b0;top_tb.rx_dv <= 1'b0;while(!top_tb.rst_n)@(posedge top_tb.clk);for(int i = 0; i < 256; i++) begin@(posedge top_tb.clk);top_tb.rxd <= $urandom(0, 255);top_tb.rx_dv <= 1'b1;`uvm_info("my_driver", "data is drived", UVM_LOW)end@(posedge top_tb.clk);top_tb.rx_dv <= 1'b0;
endtask
有了验证平台,接下来是顶层的调用。
`timescale 1ns/1ps
`include "uvm_macros.svh" //uvm中的文件,包含众多的宏定义,只需要包含一次import uvm_pkg::*; //只有导入了该库,才能识别uvm_driver等类名
`include "my_driver.sv"module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
reg[7:0] txd;
reg tx_en;dut my_dut(...);//正常的端口连接initial beginmy_driver drv;drv = new("drv", null);drv.main_phase(null);$finish;
endinitial beginclk = 0;forever begin#100 clk = ~clk;end
endinitial beginrst_n = 0;#1000;rst_n = 1'b1;
end
endmodule
仿真时的vcs命令中-f file.f中不需要再包含my_driver.sv,因为已经将其include进来,包含该sv文件会报错。同时需要添加-ntb_opts uvm命令将uvm库导入编译器。
vcs -full64 -LDFLAGS -wl,-no-as-needed -V -R +v2k -sverilog -debug_all -f file.f \
-ntb_opts uvm -timescale=1ns/1ps
2)增加factory机制
上述例子中虽然基于UVM平台,但实际上仅仅使用SV依然可以实现。UVM提供了类的实例化机制——factory。factory机制被集成在了一个宏中:uvm_component_utils。通过使用该机制,将该类登记在了UVM内部。如下所示:
class my_driver extends uvm_driver;`uvm_component_utils(my_driver); //增加factory宏机制function new(string name = "my_driver", uvm_component parent = null);super.new(name,parent);endfunctionextern virtual task main_phase(uvm_phase phase);
endclasstask my_driver::main_phase(uvm_phase phase);`uvm_info("my_driver", "main_phase is called", UVM_LOW);top_tb.rxd <= 8'b0;top_tb.rx_dv <= 1'b0;while(!top_tb.rst_n)@(posedge top_tb.clk);for(int i = 0; i < 256; i++) begin@(posedge top_tb.clk);top_tb.rxd <= $urandom(0, 255);top_tb.rx_dv <= 1'b1;`uvm_info("my_driver", "data is drived", UVM_LOW);end@(posedge top_tb.clk);top_tb.rx_dv <= 1'b0;
endtask
同时,顶层的tb文件不需要实例化该类。只需要使用run_tes()语句即可。
initial begin
// my_driver drv;
// drv = new("drv", null);
// drv.main_phase(null);
// $finish;run_test("my_driver");
end
执行该代码,会打印如下内容:
new is called
main_phase is called
uvm_component_utils根据类名可以实例化一个类。并自动调用main_phase任务。所有派生自uvm_component及其派生类都应该使用uvm_component_utils宏注册。但是该任务时不正确的。因为缺少object机制,会导致程序被提前终止。
3)增加object机制
object可以简单理解为finish函数的替代者。raise_objection和drop_objection总是成对出现。UVM通过objection机制控制验证平台的开启和关闭。raise_objection必须在main_phase中第一个消耗仿真时间的语句之前被提起。如下所示。
class my_driver extends uvm_driver;`uvm_component_utils(my_driver); function new(string name = "my_driver", uvm_component parent = null);super.new(name,parent);endfunctionextern virtual task main_phase(uvm_phase phase);
endclasstask my_driver::main_phase(uvm_phase phase);phase.raise_objection(this); //提起objection`uvm_info("my_driver", "main_phase is called", UVM_LOW);top_tb.rxd <= 8'b0;top_tb.rx_dv <= 1'b0;while(!top_tb.rst_n)@(posedge top_tb.clk);for(int i = 0; i < 256; i++) begin@(posedge top_tb.clk);top_tb.rxd <= $urandom(0, 255);top_tb.rx_dv <= 1'b1;`uvm_info("my_driver", "data is drived", UVM_LOW);end@(posedge top_tb.clk);top_tb.rx_dv <= 1'b0;phase.drop_objection(this); //撤销objection
endtask
4)增加virtual interface
上面的例程中对于顶层的信号均采用绝对路径的方式赋值,如top_tb.rx_dv <= 1'b1。使用绝对路径大大减弱了验证平台的可移植性。例如clk信号的层次从top_tb变成了top.clk_inst.clk,那么所有带有clk的语句都需要大量的修改。
通过interface可以定义接口,在DUT和验证平台直接搭起一座桥梁。如下所示:
interface my_if(input clk, input rst_n);logic[7:0] data;logic valid;
endinterface
在定义了接口之后,top_tb中的实例化可以变为:
`timescale 1ns/1ps
`include "uvm_macros.svh" import uvm_pkg::*;
`include "my_driver.sv"module top_tb;
reg clk;
reg rst_n;my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);dut my_dut(.clk(clk),.rst_n(rst_n),.rxd(input_if.data),.rx_dv(input_if.valid),.txd(output_if.data),.tx_en(output_if.valid));initial beginrun_test("my_driver");
endinitial beginclk = 0;forever begin#100 clk = ~clk;end
endinitial beginrst_n = 0;#1000;rst_n = 1'b1;
end
endmodule
需要注意的是,类中不能使用上述方式声明一个interface,只有在module、program中才可以,在类中声明可以使用virtual interface:
virtual my_if vif;
在声明了vif后,就可以在main_phase中使用如下方式驱动信号:
task my_driver::main_phase(uvm_phase phase);phase.raise_objection(this); //提起objection`uvm_info("my_driver", "main_phase is called", UVM_LOW);vif.rxd <= 8'b0;vif.valid <= 1'b0;while(!vif.rst_n)@(posedge vif.clk);for(int i = 0; i < 256; i++) begin@(posedge vif.clk);vif.rx <= $urandom(0, 255);vif.valid <= 1'b1;`uvm_info("my_driver", "data is drived", UVM_LOW);end@(posedge vif.clk);vif.valid <= 1'b0;phase.drop_objection(this); //撤销objection
endtask
这样虽然消除了代码中的绝对路径,但是如何把顶层代码top_tb中的input_if和my_driver中的vif对应起来呢?由于UVM通过run_test建立的my_driver实例脱离了层次结构,建立了新的层次结构。所以对于这种脱离了top_tb的层次结构,同时又要在top_tb中对其进行某些操作的实例,可以使用UVM中的config_db机制。config_db分两步,set和get,set设置传送的内容,get设置接收的内容。
在top_tb中增加如下代码:
initial beginuvm_config_db# (virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end
set函数中的第二个参数表示路径的索引,即实例化的类所在的层次,可以通过在类中增加如下语句查看:
$display("the full name of current component is %s", get_full_name());
run_test创建的my_driver的实例为uvm_test_top。set函数的第三个参数是一个标识,需要和get中的第三个参数完全一样。第四个参数表示要将哪个interface通过config_tb传递给my_driver。
在my_driver中,可以引入build_phase函数来规范配置。和main_phase一样,build_phase也是uvm中的内建phase。uvm启动后会自动执行,顺序在new之后,main之前。build_phase中主要通过config_db的set和get操作传递一些数据,以及实例化成员变量等。
virtual function void biuld_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("my_driver", "build_phase is called", UVM_LOW);if(!uvm_config_db# (virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfunciton
最终的my_driver如下:
class my_driver extends uvm_driver;virtual my_if vif;`uvm_component_utils(my_driver); function new(string name = "my_driver", uvm_component parent = null);super.new(name,parent);endfunctionextern virtual task main_phase(uvm_phase phase);extern virtual function void build_phase(uvm_phase phase);
endclassfunction void build_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("my_driver", "build_phase is called", UVM_LOW);if(!uvm_config_db# (virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfuncitontask my_driver::main_phase(uvm_phase phase);phase.raise_objection(this); //提起objection`uvm_info("my_driver", "main_phase is called", UVM_LOW);vif.rxd <= 8'b0;vif.valid <= 1'b0;while(!vif.rst_n)@(posedge vif.clk);for(int i = 0; i < 256; i++) begin@(posedge vif.clk);vif.rx <= $urandom(0, 255);vif.valid <= 1'b1;`uvm_info("my_driver", "data is drived", UVM_LOW);end@(posedge vif.clk);vif.valid <= 1'b0;phase.drop_objection(this); //撤销objection
endtask
get函数的第四个参数表示把得到的interface传递给哪个my_driver的成员变量。
uvm_config_db# (virtual my_if)是一个参数化的类,其参数就是要寄信的类型。使用::是因为set和get都是静态函数。假如要像my_driver的var变量传递一个int类型的数据,那么可以使用如下方式:
initial beginuvm_config# (int)::set(null, "uvm_test_top", "var", 100);
end
my_driver中是如下方式
class my_driver extends uvm_driver;int var;virtual function void build_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("my_driver", "build_phase is called", UVM_LOW);if(!uvm_config_db# (virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_driver", "virtual interface must be set for vif!!")if(!uvm_config_db# (int)::get(this, "", "var", var))`uvm_fatal("my_driver", "var must be set!!")endfuncion
endclass
如果需要传递多个值,如下所示:
initial beginif(!uvm_config_db# (virtual my_if)::set(null, "uvm_test_top", "vif", input_if))if(!uvm_config_db# (virtual my_if)::set(null, "uvm_test_top", "vif2", output_if))
end
virtual my_if vif;
virtual my_if vif2;
virtual function void build_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("my_driver", "build_phase is called", UVM_LOW);if(!uvm_config_db# (virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")if(!uvm_config_db# (virtual my_if)::get(this, "", "vif2", vif2))`uvm_fatal("my_driver", "virtual interface must be set for vif2!!!")
endfunciton
这篇关于2023.7.25 UVM学习和环境搭建的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!