引言
本专栏的博客均与 UVM 的学习相关,学习参考:
【1】UVM Tutorial
【2】张强著,UVM实战 (卷 Ⅰ)
【3】Download UVM (Standard Universal Verification Methodology)
本专栏的学习基本依照 资料【2】的主线,以【1】【3】资料作为参考。特别是【3】是官方的UVM手册,具有很高的可参考性以及权威性。
本文则先搭建一个简单的 UVM 平台,了解其思想和机制。
UVM 平台:Questa Sim - 64 10.6c (Win 10)
验证平台的组成有哪些?
验证的对象是前端设计工程师提交的硬件设计代码(Verilog),验证的目的就是设计各种验证case,观察前端工程师设计的电路是否符合spec里要求的各项指标以及功能。简单的验证平台结构如下图所示,driver 负责将相同的激励同时给到 DUT 和参考模型,monitor 负责接收 DUT的各个响应结果送至计分板将二者的结果进行比对,给出验证结果,pass or fail。
在 UVM 中,引入了 agent 和 sequence 的概念,因此UVM中验证平台的典型框图:
只有 driver 的验证平台
搭建最简单的验证平台
假设待分析的设计为数据转发模块:
Design
module Design(
input clk,
input rstn,
input [7:0] rxd,
input rxval,
output reg tx_en,
output reg [7:0] txd
);
always @ (posedge clk)
begin
if(~rstn)
begin
txd <= 0;
tx_en <= 0;
end
else
begin
tx_en <= rxval;
txd <= rxd;
end
end
endmodule
Driver
class Driver extends uvm_driver;
function new(string name = "Driver",uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task Driver::main_phase(uvm_phase phase);
Top_tb.rxd <= 8'd0;
Top_tb.rx_val <= 1'b0;
while(~Top_tb.rstn)//等待复位完成
begin
@(posedge Top_tb.clk);
end
// 随机数激励
for(int k = 0;k<256;k++)
begin
@(posedge Top_tb.clk);
Top_tb.rxd <= $urandom_range(0,255);
Top_tb.rx_val <= 1'b1;
`uvm_info("Driver",$sformatf("Random data is drived ,data = %0d",Top_tb.rxd),UVM_LOW)
end
// 有效信号拉低
@(posedge Top_tb.clk);
Top_tb.rx_val <= 1'b0;
endtask
对于 `uvm_info 宏的使用,这里建议查阅 UVM 的使用手册,很详细:
Top_tb
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "Driver.sv"
module Top_tb ();
reg clk;
reg rstn;
reg [7:0] rxd;
reg rx_val;
wire tx_en;
wire [7:0] txd;
Design inst_Design (.clk(clk), .rstn(rstn), .rxd(rxd), .rx_val(rx_val), .tx_en(tx_en), .txd(txd));
initial
begin
Driver drv;
drv = new("Driver",null);
drv.main_phase(null);
$finish;
end
initial clk = 0;
always #10 clk = ~clk;
initial
begin
rstn = 1'b0;
#138 rstn = 1'b1;
end
endmodule
在 Questa Sim 中,仅需要对 顶层仿真文件编译,不需要对各个组件编译:
`include "uvm_macros.svh"
将对应的宏定义文件读取进来,这在编译开始之前进行的操作。
import uvm_pkg::*
将整个uvm_pkg 导入验证平台,这样才能在自己的验证平台使用 uvm的各种库。
以上这两个操作在后续的所有验证平台的顶层都应包括进去。
仿真
编译完成后,在脚本处,执行仿真:
vsim -voptargs=+acc work.Top_tb +UVM_NO_RELNOTES
文件加载完成后 ,在脚本处 执行 run 命令,即可看到仿真结果:
加入 factory 机制
factory 机制,使用 uvm_component_utils 宏来实现。此宏会根据类型,自动创建一个实例并且这个类的 main_phase 会被自动调用。
Driver 文件作如下改动:
class Driver extends uvm_driver;
`uvm_component_utils(Driver)
function new(string name = "Driver",uvm_component parent = null);
super.new(name,parent);
`uvm_info("Driver","new is called",UVM_LOW)
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task Driver::main_phase(uvm_phase phase);
`uvm_info("Driver","main_phase is called",UVM_LOW)
Top_tb.rxd <= 8'd0;
Top_tb.rx_val <= 1'b0;
while(~Top_tb.rstn)//等待复位完成
begin
@(posedge Top_tb.clk);
end
// 随机数激励
for(int k = 0;k<256;k++)
begin
@(posedge Top_tb.clk);
Top_tb.rxd <= $urandom_range(0,255);
Top_tb.rx_val <= 1'b1;
`uvm_info("Driver",$sformatf("Random data is drived ,data = %0d",Top_tb.rxd),UVM_LOW)
end
// 有效信号拉低
@(posedge Top_tb.clk);
Top_tb.rx_val <= 1'b0;
endtask
顶层文件 Top_tb 改动后的结果:
一个run_test语句会创建一个my_driver的实例,并且会自动调用my_driver的main_phase。
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "Driver.sv"
module Top_tb ();
reg clk;
reg rstn;
reg [7:0] rxd;
reg rx_val;
wire tx_en;
wire [7:0] txd;
Design Inst_Design (
.clk(clk),
.rstn(rstn),
.rxd(rxd),
.rx_val(rx_val),
.tx_en(tx_en),
.txd(txd)
);
initial
begin
// Driver drv;
// drv = new("Driver",null);
// drv.main_phase(null);
run_test("Driver");
// $finish;
end
initial clk = 0;
always #10 clk = ~clk;
initial
begin
rstn = 1'b0;
#138 rstn = 1'b1;
end
endmodule
上图是 run_test 的使用说明。
编译后执行仿真:
发现仿真结果中,只打印到 main_phased is called,后面的随机数激励部分没有执行。此处引出了 UVM 的objection 机制。
加入objection机制
UVM中通过objection机制来控制验证平台的关闭。在每个phase中,UVM会检查是否有objection被提起 (raise_objection),如果有,那么等待这个objection被撤销(drop_objection)后停止仿真;如果没有,则马上结束当前phase。
可以简单地将drop_objection语句当成是finish函数的替代者,只是在drop_objection语句之前必须先调用 raise_objection语句,raise_objection和drop_objection总是成对出现。
加入 objection 机制的 Driver 代码:
class Driver extends uvm_driver;
`uvm_component_utils(Driver)
function new(string name = "Driver",uvm_component parent = null);
super.new(name,parent);
`uvm_info("Driver","new is called",UVM_LOW)
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task Driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("Driver","main_phase is called",UVM_LOW)
Top_tb.rxd <= 8'd0;
Top_tb.rx_val <= 1'b0;
while(~Top_tb.rstn)//等待复位完成
begin
@(posedge Top_tb.clk);
end
// 随机数激励
for(int k = 0;k<256;k++)
begin
@(posedge Top_tb.clk);
Top_tb.rxd <= $urandom_range(0,255);
Top_tb.rx_val <= 1'b1;
`uvm_info("Driver",$sformatf("Random data is drived ,data = %0d",Top_tb.rxd),UVM_LOW)
end
// 有效信号拉低
@(posedge Top_tb.clk);
Top_tb.rx_val <= 1'b0;
phase.drop_objection(this);
endtask
编译后执行仿真:
raise_objection 语句必须在 main_phase 中第一个消耗仿真时间的语句之前。如$display语句是不消耗仿真时间的,这些语句可 以放在raise_objection之前,但是类似@(posedge top.clk)等语句是要消耗仿真时间的。按照如下的方式使用raise_objection是无法起到作用的。
Driver 类的代码修改:
class Driver extends uvm_driver; `uvm_component_utils(Driver) function new(string name = "Driver",uvm_component parent = null); super.new(name,parent); `uvm_info("Driver","new is called",UVM_LOW) endfunction extern virtual task main_phase(uvm_phase phase); endclass task Driver::main_phase(uvm_phase phase); @(posedge Top_tb.clk); phase.raise_objection(this); `uvm_info("Driver","main_phase is called",UVM_LOW) Top_tb.rxd <= 8'd0; Top_tb.rx_val <= 1'b0; while(~Top_tb.rstn)//等待复位完成 begin @(posedge Top_tb.clk); end // 随机数激励 for(int k = 0;k<256;k++) begin @(posedge Top_tb.clk); Top_tb.rxd <= $urandom_range(0,255); Top_tb.rx_val <= 1'b1; `uvm_info("Driver",$sformatf("Random data is drived ,data = %0d",Top_tb.rxd),UVM_LOW) end // 有效信号拉低 @(posedge Top_tb.clk); Top_tb.rx_val <= 1'b0; phase.drop_objection(this); endtask
仿真结果:
加入 virtual interface
未引入 虚拟接口 时,验证平台的端口索引是通过模块之间层级的绝对路径来索引,比如上面的Driver类内的 Top_tb.rx_val <= 1'b0; 这种接口的索引很不利于验证平台的移植。使用宏定义也可以在一定程度缓解这种问题,但是最根本的解决方案还是要引入虚拟接口。
在类内引入非虚拟接口,在编译时会报语法错误。在模块(module)内可以引入非虚拟接口。虚拟接口在实例化时需要声明 virtual 。
interface 文件:
interface Interface (input clk,input rstn);
logic [7:0] data;
logic val;
endinterface
接口引入的目的就是方便驱动 Driver 和 仿真顶层 Top_tb 之间更友好地(代码可移植性更高)连接。那么二者之间具体如何连接?UVM引进了config_db 机制。在config_db机制中,分为set和get两步操作。所谓set操作,读者可以简单地理解成是“寄信”,而get则相当于是“收信”。可以向驱动器 Driver 寄同类型的不同实例对应的“信”,也可以寄不同类型的“信”。对于收侧也一样。
Driver 中需要引入 build_phase 。与main_phase一样,build_phase也是UVM中内建的一个phase。当UVM启动后,会自动执行 build_phase。build_phase在new函数之后main_phase之前执行。在build_phase中主要通过config_db的 set 和 get 操作来传递一些数据, 以及实例化成员变量等。
Driver 类的改变:
class Driver extends uvm_driver;
virtual Interface Virtual_Driver_if;
`uvm_component_utils(Driver)
function new(string name = "Driver",uvm_component parent = null);
super.new(name,parent);
`uvm_info("Driver","new is called",UVM_LOW)
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("Driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual Interface)::get(this, "", "vif", Virtual_Driver_if))
`uvm_fatal("Driver", "virtual interface must be set for vif!!!")
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task Driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("Driver","main_phase is called",UVM_LOW)
Virtual_Driver_if.data <= 8'd0;
Virtual_Driver_if.val <= 1'b0;
while(~Virtual_Driver_if.rstn)//等待复位完成
begin
@(posedge Virtual_Driver_if.clk);
end
// 随机数激励
for(int k = 0;k<256;k++)
begin
@(posedge Virtual_Driver_if.clk);
Virtual_Driver_if.data <= $urandom_range(0,255);
Virtual_Driver_if.val <= 1'b1;
`uvm_info("Driver",$sformatf("Random data is drived ,data = %0d",Virtual_Driver_if.data),UVM_LOW)
end
// 有效信号拉低
@(posedge Virtual_Driver_if.clk);
Virtual_Driver_if.val <= 1'b0;
phase.drop_objection(this);
endtask
上面的代码用到了 uvm_fatal 。与uvm_info宏不同的是,当它打印第二个参数所示的信息后,会直接调用Verilog的finish函数来结束仿真。uvm_fatal的出现表示验证平台出现了重大问题而无法继续下去,必须停止仿真并做相应的检查。所以对于uvm_fatal来 说,uvm_info中出现的第三个参数的冗余度级别是完全没有意义的,只要是uvm_fatal打印的信息,就一定是非常关键的,所以无需设置第三个参数。
关于 uvm_config_db :
与之有关的函数均为静态函数,所以在调用时要用 :: 操作符。
config_db的 set 和 get 函数都有四个参数,这两个函数的第三个参数必须完全一致。
set函数的第四个参数表示要将哪个interface 通过config_db传递给my_driver,get函数的第四个参数表示把得到的interface传递给哪个my_driver的成员变量。
set函数的第二个参数表示的是路径索引,即在前面介绍uvm_info宏时提及的路径索引。在top_tb中通过run_test创建了一个my_driver的实例,那么 这个实例的名字是什么呢?答案是uvm_test_top:UVM通过run_test语句创建一个名字为uvm_test_top的实例。无论传递给run_test 的参数是什么,创建的实例的名字都为 uvm_test_top。由于set操作的目标是my_driver,所以set函数的第二个参数就是uvm_test_top。set函数的第一个参数null以及get函数的第一和第二个参数可以暂时放在一边,后文会详细说明。
uvm_config_db#(virtual Interface)则是一个参数化的类,其参数就是要寄信的类型,这里是virtual my_if。
手册的具体说明:
Top_tb 的变动:
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "Interface.sv"
`include "Driver.sv"
module Top_tb ();
reg clk;
reg rstn;
// reg [7:0] rxd;
// reg rx_val;
// wire tx_en;
// wire [7:0] txd;
Interface Input_if( clk, rstn);
Interface Output_if( clk, rstn);
Design Inst_Design (
.clk(clk),
.rstn(rstn),
.rxd(Input_if.data),
.rx_val(Input_if.val),
.tx_en(Output_if.data),
.txd(Output_if.val)
);
initial
begin
// Driver drv;
// drv = new("Driver",null);
// drv.main_phase(null);
run_test("Driver");
// $finish;
end
initial clk = 0;
always #10 clk = ~clk;
initial
begin
rstn = 1'b0;
#138 rstn = 1'b1;
end
initial begin
uvm_config_db#(virtual Interface)::set(null, "uvm_test_top", "vif", Input_if);
end
endmodule
编译后执行仿真:
给验证平台加入其他组件
加入 transaction
transaction 的翻译为事务。那么到底什么是事务?这其实是一个很抽象的概念。通俗来讲,可以将 transaction 理解为一次完整的激励。一般驱动器的数据激励都是基于 transaction 进行传输。
此处,参考书以以太网协议的数据包为例,做一个说明。
class Transaction extends uvm_sequence_item;
rand bit [47:0] dmac;
rand bit [47:0] smac;
rand bit [47: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'h0;
endfunction
function void post_randomize();
crc = calc_crc;
endfunction
`uvm_object_utils(Transaction);
function new (string name = "Transaction");
super.new(name);
endfunction
endclass
post_randomize是SystemVerilog中提供的一个函数,当某个类的实例的randomize函数被调用后,post_randomize会紧随其后无条件 地被调用。
Transaction的基类是uvm_sequence_item。在UVM中,所有的 transaction都 要从uvm_sequence_item派生,只有从uvm_sequence_item派生的transaction才可以使用UVM中sequence机制。
这里没有使用 uvm_component_utils 宏来实现factory机制,而是使用了uvm_object_utils。从本质上来说,my_transaction与 my_driver是有区别的,在整个仿真期间,my_driver是一直存在的,my_transaction不同,它有生命周期。它在仿真的某一时间产 生,经过driver驱动,再经过reference model处理,最终由scoreboard比较完成后,其生命周期就结束了。一般来说,这种类都是派 生自uvm_object或者uvm_object的派生类,uvm_sequence_item的祖先就是uvm_object。UVM中具有这种特征的类都要使用 uvm_object_utils宏来实现。
Driver 程序:
class Driver extends uvm_driver;
virtual Interface Virtual_Driver_if;
`uvm_component_utils(Driver)
function new(string name = "Driver",uvm_component parent = null);
super.new(name,parent);
`uvm_info("Driver","new is called",UVM_LOW)
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("Driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual Interface)::get(this, "", "vif", Virtual_Driver_if))
`uvm_fatal("Driver", "virtual interface must be set for vif!!!")
endfunction
extern task main_phase(uvm_phase phase);
extern task drive_one_pkt(Transaction tr);
endclass
task Driver::main_phase(uvm_phase phase);
Transaction tr;
phase.raise_objection(this);
`uvm_info("Driver","main_phase is called",UVM_LOW)
Virtual_Driver_if.data <= 8'd0;
Virtual_Driver_if.val <= 1'b0;
while(~Virtual_Driver_if.rstn)//等待复位完成
begin
@(posedge Virtual_Driver_if.clk);
end
// 随机数激励
for(int k = 0;k<1;k++)
begin
tr = new("tr");
assert(tr.randomize() with{pload.size == 200;});
drive_one_pkt(tr);
`uvm_info("Driver",$sformatf("Randomize successfully ! "),UVM_LOW)
end
// 有效信号拉低
repeat(10) @(posedge Virtual_Driver_if.clk);
phase.drop_objection(this);
endtask
task Driver::drive_one_pkt(Transaction tr);
bit [47:0] tmp_data;
bit [7:0] data_q[$];
`uvm_info("Driver", "Random Package Data Generate Start ...", UVM_LOW);
//push dmac to data_q
tmp_data = tr.dmac;
`uvm_info("Driver",$sformatf("dmac = 0x%0h",tr.dmac),UVM_LOW)
for(int i = 0; i < 6; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push smac to data_q
tmp_data = tr.smac;
`uvm_info("Driver",$sformatf("smac = 0x%0h",tr.smac),UVM_LOW)
for(int i = 0; i < 6; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push ether_type to data_q
tmp_data = tr.ether_type;
`uvm_info("Driver",$sformatf("ether_type = 0x%0h",tr.ether_type),UVM_LOW)
for(int i = 0; i < 2; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push payload to data_q
for(int i = 0; i < tr.pload.size; i++) begin
data_q.push_back(tr.pload[i]);
`uvm_info("Driver",$sformatf("pload[%0d] = 0x%0h",i,tr.pload[i]),UVM_LOW)
end
//push crc to data_q
tmp_data = tr.crc;
`uvm_info("Driver",$sformatf("crc = 0x%0h",tr.crc),UVM_LOW)
for(int i = 0; i < 4; i++)
begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
`uvm_info("Driver", "Random Package Data Generate end ...", UVM_LOW);
`uvm_info("Driver", "begin to drive one pkt", UVM_LOW);
repeat(3) @(posedge Virtual_Driver_if.clk);
while(data_q.size() > 0)
begin
@(posedge Virtual_Driver_if.clk);
Virtual_Driver_if.val <= 1'b1;
Virtual_Driver_if.data <= data_q.pop_front();
end
repeat (10) @(posedge Virtual_Driver_if.clk);
Virtual_Driver_if.val <= 1'b0;
`uvm_info("Driver", "end drive one pkt", UVM_LOW);
endtask
在main_phase中,先使用randomize将tr随机化,之后通过drive_one_pkt任务将tr的内容驱动到DUT的端口上。在drive_one_pkt 中,先将tr中所有的数据压入队列data_q中,之后再将data_q中所有的数据弹出并驱动。将tr中的数据压入队列data_q中的过程相当 于打包成一个byte流的过程。
编译后执行仿真:
加入 environment
environment 就像是一个大的容器内部包含了 计分板、参考模型、监视器、驱动器等组件。在调用 run_test时,传递的参数不再是my_driver,而是这个容器类,即让UVM自动创建这个容器类的实例。在UVM中,这个容器类称为 uvm_env。
class Environment extends uvm_env;
Driver drv;
function new(string name = "env",uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = Driver::type_id::create("drv",this);
endfunction
`uvm_component_utils(Environment)
endclass
所有的 environment 应该派生自uvm_env,且与Driver一样,容器类在仿真中也是一直存在的,使用uvm_component_utils宏来实现 factory的注册。
在my_env的定义中,最让人难以理解的是第14行drv的实例化。这里没有直接调用my_driver的new函数,而是使用了一种古怪 的方式。这种方式就是factory机制带来的独特的实例化方式。只有使用factory机制注册过的类才能使用这种方式实例化;只有使 用这种方式实例化的实例,才能使用后文要讲述的factory机制中最为强大的重载功能。验证平台中的组件在实例化时都应该使用 type_name::type_id::create的方式。
new函数有两个参数,第一个参数是实例的名字,第二个则是parent。由于my_driver在uvm_env中实例化,所以my_driver 的父结点(parent)就是my_env。通过parent的形式,UVM建立起了树形的组织结构。在这种树形的组织结构中,由run_test创建 的实例是树根(这里是my_env),并且树根的名字是固定的,为uvm_test_top,这在前文中已经讲述过;在树根之后会生长出枝 叶(这里只有my_driver),长出枝叶的过程需要在my_env的build_phase中手动实现。无论是树根还是树叶,都必须由 uvm_component或者其派生类继承而来。整棵UVM树的结构如图所示。
当加入了my_env后,整个验证平台中存在两个build_phase,一个是my_env的,一个是my_driver的。那么这两个build_phase按 照何种顺序执行呢?在UVM的树形结构中,build_phase的执行遵照从树根到树叶的顺序,即先执行my_env的build_phase,再执行 my_driver的build_phase。当把整棵树的build_phase都执行完毕后,再执行后面的phase。
my_driver在验证平台中的层次结构发生了变化,它从树根变成了树叶,所以在Top_tb中使用config_db机制传递virtual Interface时,要改变相应的路径;同时,run_test的参数也从Driver变为了Environment:
`timescale 1ns/1ns `include "uvm_macros.svh" import uvm_pkg::*; `include "Interface.sv" `include "Transaction.sv" `include "Driver.sv" `include "Environment.sv" module Top_tb (); reg clk; reg rstn; // reg [7:0] rxd; // reg rx_val; // wire tx_en; // wire [7:0] txd; Interface Input_if( clk, rstn); Interface Output_if( clk, rstn); Design Inst_Design ( .clk(clk), .rstn(rstn), .rxd(Input_if.data), .rx_val(Input_if.val), .tx_en(Output_if.data), .txd(Output_if.val) ); initial begin // Driver drv; // drv = new("Driver",null); // drv.main_phase(null); // run_test("Driver"); run_test("Environment"); // $finish; end initial clk = 0; always #10 clk = ~clk; initial begin rstn = 1'b0; #138 rstn = 1'b1; end initial begin uvm_config_db#(virtual Interface)::set(null, "uvm_test_top.drv", "vif", Input_if); end endmodule
set函数的第二个参数从uvm_test_top变为了uvm_test_top.drv,其中uvm_test_top是UVM自动创建的树根的名字,而drv则是在 my_env的build_phase中实例化drv时传递过去的名字。如果在实例化drv时传递的名字是my_drv,那么set函数的第二个参数中也应该是my_drv
加入 monitor
monitor 存在的意义就是将 DUT 输出的响应结果送入计分板,判断其响应的正确性。
验证平台中实现监测DUT行为的组件是monitor。driver负责把transaction级别的数据转变成DUT的端口级别,并驱动给DUT, monitor的行为与其相对,用于收集DUT的端口数据,并将其转换成transaction交给后续的组件如reference model、scoreboard等处理。
Monitor:
class Monitor extends uvm_monitor;
virtual Interface vif;
`uvm_component_utils(Monitor)
function new (string name = "Monitor",uvm_component parent = null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(virtual Interface)::get(this,"","vif",vif))
`uvm_fatal("Monitor","virtual interface must be set for vif !")
endfunction
extern task main_phase(uvm_phase phase);
extern task collect_one_pkt(Transaction tr);
endclass
task Monitor::main_phase(uvm_phase phase);
Transaction tr;
while(1)
begin
tr = new("tr");
collect_one_pkt(tr);
end
endtask
task Monitor::collect_one_pkt(Transaction tr);
bit[7:0] data_q[$];
int psize;
while(1)
begin
@(posedge vif.clk);
if(vif.val) break;
end
`uvm_info("Monitor","begin to collect one package",UVM_LOW);
while(vif.val)
begin
data_q.push_back(vif.data);
@(posedge vif.clk);
end
`uvm_info("Monitor",$sformatf("collect one package,size=%0d",data_q.size()),UVM_LOW);
// pop out to tr
for (int i=0;i<6;i++)
begin
tr.dmac = {tr.dmac[39:0],data_q.pop_front()};
end
for (int i=0;i<6;i++)
begin
tr.smac = {tr.smac[39:0],data_q.pop_front()};
end
for (int i=0;i<2;i++)
begin
tr.dmac = {tr.dmac[39:0],data_q.pop_front()};
end
psize = data_q.size() - 4;
`uvm_info("Monitor",$sformatf("pload data size=%0d",psize),UVM_LOW);
tr.pload = new[psize];
//pop payload
for(int i = 0; i < psize; i++) begin
tr.pload[i] = data_q.pop_front();
end
//pop crc
for(int i = 0; i < 4; i++) begin
tr.crc = {tr.crc[23:0], data_q.pop_front()};
end
`uvm_info("Monitor", "end collect one package, print it:", UVM_LOW);
tr.Print();
endtask
第一,所有的monitor类应该派生自uvm_monitor;
第二,与driver类似,在my_monitor中也需要有一个virtual Interface;
第三,uvm_monitor在整个仿真中是一直存在的,所以它是一个component,要使用uvm_component_utils宏注册;
第四,由于monitor需要时刻收集数据,永不停歇,所以在main_phase中使用while(1)循环来实现这一目的。
Transaction 部分修改:
class 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'h0;
endfunction
function void post_randomize();
crc = calc_crc;
endfunction
`uvm_object_utils(Transaction);
function new (string name = "Transaction");
super.new(name);
endfunction
function void Print ();
$display("dmac = 0x%0h",dmac);
$display("smac = 0x%0h",smac);
$display("ether type = 0x%0h",ether_type);
for(int i=0;i<pload.size;i++)
$display("pload[%0d] = 0x%0h",i,pload[i]);
$display("crc = 0x%0h",crc);
endfunction
endclass
增加 Print 打印输出函数。
Environment 部分修改:
class Environment extends uvm_env;
Driver drv;
Monitor i_mon,o_mon;
function new(string name = "env",uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = Driver::type_id::create("drv",this);
i_mon=Monitor::type_id::create("i_mon",this);
o_mon=Monitor::type_id::create("o_mon",this);
endfunction
`uvm_component_utils(Environment)
endclass
这里实例化了两个monitor,一个用于监测DUT的输入口,一个用于监测DUT的输出口。DUT的输出口设置一个monitor没有任何疑问,但是在DUT的输入口设置一个monitor有必要吗?由于transaction是由driver产生并输出到DUT的端口上,所以driver可以直接将其交给后面的reference model。在2.1节所示的框图中,也是使用这样的策略。所以是否使用monitor,这个答案仁者见仁,智者见智。这里还是推荐使用monitor,原因是:第一,在一个大型的项目中,driver根据某一协议发送数据,而monitor根据这种协议收集数据,如果driver和monitor由不同人员实现,那么可以大大减少其中任何一方对协议理解的错误;第
二,在后文将会看到,在实现代码重用时,使用monitor是非常有必要的。environment 中树形结构:
Top_tb 部分修改:
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "Interface.sv"
`include "Transaction.sv"
`include "Driver.sv"
`include "Monitor.sv"
`include "Environment.sv"
module Top_tb ();
reg clk;
reg rstn;
// reg [7:0] rxd;
// reg rx_val;
// wire tx_en;
// wire [7:0] txd;
Interface Input_if( clk, rstn);
Interface Output_if( clk, rstn);
Design Inst_Design (
.clk(clk),
.rstn(rstn),
.rxd(Input_if.data),
.rx_val(Input_if.val),
.tx_en(Output_if.val),
.txd(Output_if.data)
);
initial
begin
// Driver drv;
// drv = new("Driver",null);
// drv.main_phase(null);
// run_test("Driver");
run_test("Environment");
// $finish;
end
initial clk = 0;
always #10 clk = ~clk;
initial
begin
rstn = 1'b0;
#138 rstn = 1'b1;
end
initial begin
uvm_config_db#(virtual Interface)::set(null, "uvm_test_top.drv", "vif", Input_if);
uvm_config_db#(virtual Interface)::set(null, "uvm_test_top.i_mon", "vif", Input_if);
uvm_config_db#(virtual Interface)::set(null, "uvm_test_top.o_mon", "vif", Output_if);
end
endmodule
编译后执行仿真:
# ----------------------------------------------------------------
# UVM-1.1d
# (C) 2007-2013 Mentor Graphics Corporation
# (C) 2007-2013 Cadence Design Systems, Inc.
# (C) 2006-2013 Synopsys, Inc.
# (C) 2011-2013 Cypress Semiconductor Corp.
# ----------------------------------------------------------------
#
# *********** IMPORTANT RELEASE NOTES ************
#
# You are using a version of the UVM library that has been compiled
# with `UVM_NO_DEPRECATED undefined.
# See http://www.eda.org/svdb/view.php?id=3313 for more details.
#
# You are using a version of the UVM library that has been compiled
# with `UVM_OBJECT_MUST_HAVE_CONSTRUCTOR undefined.
# See http://www.eda.org/svdb/view.php?id=3770 for more details.
#
# (Specify +UVM_NO_RELNOTES to turn off this notice)
#
# UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(215) @ 0: reporter [Questa UVM] QUESTA_UVM-1.2.3
# UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(217) @ 0: reporter [Questa UVM] questa_uvm::init(+struct)
# UVM_INFO @ 0: reporter [RNTST] Running test Environment...
# UVM_INFO Driver.sv(7) @ 0: uvm_test_top.drv [Driver] new is called
# UVM_INFO Driver.sv(12) @ 0: uvm_test_top.drv [Driver] build_phase is called
# UVM_INFO Driver.sv(24) @ 0: uvm_test_top.drv [Driver] main_phase is called
# UVM_INFO Driver.sv(48) @ 150: uvm_test_top.drv [Driver] Random Package Data Generate Start ...
# UVM_INFO Driver.sv(51) @ 150: uvm_test_top.drv [Driver] dmac = 0xf4bfbad01b03
# UVM_INFO Driver.sv(59) @ 150: uvm_test_top.drv [Driver] smac = 0x26df69099fc0
# UVM_INFO Driver.sv(67) @ 150: uvm_test_top.drv [Driver] ether_type = 0xa67a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[0] = 0x4a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[1] = 0x60
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[2] = 0x23
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[3] = 0x24
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[4] = 0x90
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[5] = 0xaf
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[6] = 0xce
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[7] = 0x23
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[8] = 0xb1
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[9] = 0xd3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[10] = 0x45
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[11] = 0xf
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[12] = 0x60
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[13] = 0x5f
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[14] = 0xee
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[15] = 0x5c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[16] = 0x3e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[17] = 0xee
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[18] = 0x99
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[19] = 0x30
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[20] = 0xbf
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[21] = 0xbb
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[22] = 0x8b
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[23] = 0x92
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[24] = 0x3b
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[25] = 0x53
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[26] = 0x23
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[27] = 0x27
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[28] = 0xc8
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[29] = 0x9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[30] = 0x82
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[31] = 0x9b
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[32] = 0x48
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[33] = 0x76
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[34] = 0xfb
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[35] = 0x32
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[36] = 0xcc
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[37] = 0xcb
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[38] = 0x5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[39] = 0xc2
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[40] = 0xa2
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[41] = 0x6
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[42] = 0x4c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[43] = 0xe9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[44] = 0x1c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[45] = 0x79
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[46] = 0xf4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[47] = 0x65
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[48] = 0xa7
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[49] = 0x8d
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[50] = 0x3d
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[51] = 0xac
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[52] = 0xb9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[53] = 0xe4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[54] = 0x11
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[55] = 0x99
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[56] = 0x44
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[57] = 0xca
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[58] = 0xe5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[59] = 0xa1
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[60] = 0xea
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[61] = 0x11
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[62] = 0xc4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[63] = 0x52
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[64] = 0xc4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[65] = 0x9a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[66] = 0x36
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[67] = 0x15
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[68] = 0x1
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[69] = 0x6f
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[70] = 0x62
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[71] = 0xe1
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[72] = 0x79
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[73] = 0xa9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[74] = 0x75
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[75] = 0x62
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[76] = 0x89
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[77] = 0xda
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[78] = 0x56
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[79] = 0xe8
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[80] = 0x3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[81] = 0xf9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[82] = 0x4f
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[83] = 0x7a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[84] = 0x6d
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[85] = 0x6a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[86] = 0x49
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[87] = 0x3e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[88] = 0xc0
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[89] = 0x6a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[90] = 0xd4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[91] = 0xa5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[92] = 0x86
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[93] = 0xac
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[94] = 0x10
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[95] = 0xce
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[96] = 0xd4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[97] = 0x83
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[98] = 0xbc
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[99] = 0x6e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[100] = 0x36
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[101] = 0xfc
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[102] = 0xf8
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[103] = 0x80
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[104] = 0xd
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[105] = 0x7b
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[106] = 0xde
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[107] = 0xd1
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[108] = 0x7e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[109] = 0x75
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[110] = 0xea
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[111] = 0xe0
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[112] = 0xed
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[113] = 0xbf
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[114] = 0xba
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[115] = 0x2a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[116] = 0x8a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[117] = 0x5b
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[118] = 0x9a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[119] = 0x57
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[120] = 0x17
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[121] = 0x14
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[122] = 0xd0
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[123] = 0x21
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[124] = 0xca
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[125] = 0x6a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[126] = 0x74
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[127] = 0x34
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[128] = 0x58
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[129] = 0xbc
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[130] = 0x94
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[131] = 0x7e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[132] = 0x7c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[133] = 0xb3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[134] = 0xd8
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[135] = 0x8b
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[136] = 0xef
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[137] = 0xb5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[138] = 0x41
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[139] = 0x6c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[140] = 0x2
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[141] = 0x46
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[142] = 0x71
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[143] = 0x1a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[144] = 0x9c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[145] = 0x22
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[146] = 0x27
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[147] = 0x52
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[148] = 0x26
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[149] = 0x90
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[150] = 0x43
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[151] = 0xc3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[152] = 0x1a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[153] = 0x12
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[154] = 0x87
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[155] = 0x71
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[156] = 0xf
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[157] = 0x9c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[158] = 0xd6
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[159] = 0x64
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[160] = 0xac
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[161] = 0x3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[162] = 0xd5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[163] = 0x43
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[164] = 0x33
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[165] = 0xb
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[166] = 0xf3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[167] = 0x4f
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[168] = 0x22
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[169] = 0x9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[170] = 0xe5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[171] = 0x46
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[172] = 0x17
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[173] = 0x39
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[174] = 0x20
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[175] = 0x99
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[176] = 0xd1
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[177] = 0x2a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[178] = 0x9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[179] = 0x87
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[180] = 0xbf
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[181] = 0x69
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[182] = 0x6a
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[183] = 0xcc
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[184] = 0xa8
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[185] = 0xd3
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[186] = 0x4
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[187] = 0xa2
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[188] = 0x81
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[189] = 0x9e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[190] = 0xe5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[191] = 0x29
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[192] = 0x5c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[193] = 0xf9
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[194] = 0xb5
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[195] = 0x50
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[196] = 0x51
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[197] = 0x3c
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[198] = 0x9e
# UVM_INFO Driver.sv(76) @ 150: uvm_test_top.drv [Driver] pload[199] = 0x43
# UVM_INFO Driver.sv(80) @ 150: uvm_test_top.drv [Driver] crc = 0x0
# UVM_INFO Driver.sv(86) @ 150: uvm_test_top.drv [Driver] Random Package Data Generate end ...
# UVM_INFO Driver.sv(88) @ 150: uvm_test_top.drv [Driver] begin to drive one pkt
# UVM_INFO Monitor.sv(39) @ 250: uvm_test_top.i_mon [Monitor] begin to collect one package
# UVM_INFO Monitor.sv(39) @ 270: uvm_test_top.o_mon [Monitor] begin to collect one package
# UVM_INFO Driver.sv(100) @ 4590: uvm_test_top.drv [Driver] end drive one pkt
# UVM_INFO Driver.sv(37) @ 4590: uvm_test_top.drv [Driver] Randomize successfully !
# UVM_INFO Monitor.sv(45) @ 4610: uvm_test_top.i_mon [Monitor] collect one package,size=218
# UVM_INFO Monitor.sv(60) @ 4610: uvm_test_top.i_mon [Monitor] pload data size=200
# UVM_INFO Monitor.sv(70) @ 4610: uvm_test_top.i_mon [Monitor] end collect one package, print it:
# dmac = 0xd0babff47aa6
# smac = 0xc09f0969df26
# ether type = 0x0
# pload[0] = 0x4a
# pload[1] = 0x60
# pload[2] = 0x23
# pload[3] = 0x24
# pload[4] = 0x90
# pload[5] = 0xaf
# pload[6] = 0xce
# pload[7] = 0x23
# pload[8] = 0xb1
# pload[9] = 0xd3
# pload[10] = 0x45
# pload[11] = 0xf
# pload[12] = 0x60
# pload[13] = 0x5f
# pload[14] = 0xee
# pload[15] = 0x5c
# pload[16] = 0x3e
# pload[17] = 0xee
# pload[18] = 0x99
# pload[19] = 0x30
# pload[20] = 0xbf
# pload[21] = 0xbb
# pload[22] = 0x8b
# pload[23] = 0x92
# pload[24] = 0x3b
# pload[25] = 0x53
# pload[26] = 0x23
# pload[27] = 0x27
# pload[28] = 0xc8
# pload[29] = 0x9
# pload[30] = 0x82
# pload[31] = 0x9b
# pload[32] = 0x48
# pload[33] = 0x76
# pload[34] = 0xfb
# pload[35] = 0x32
# pload[36] = 0xcc
# pload[37] = 0xcb
# pload[38] = 0x5
# pload[39] = 0xc2
# pload[40] = 0xa2
# pload[41] = 0x6
# pload[42] = 0x4c
# pload[43] = 0xe9
# pload[44] = 0x1c
# pload[45] = 0x79
# pload[46] = 0xf4
# pload[47] = 0x65
# pload[48] = 0xa7
# pload[49] = 0x8d
# pload[50] = 0x3d
# pload[51] = 0xac
# pload[52] = 0xb9
# pload[53] = 0xe4
# pload[54] = 0x11
# pload[55] = 0x99
# pload[56] = 0x44
# pload[57] = 0xca
# pload[58] = 0xe5
# pload[59] = 0xa1
# pload[60] = 0xea
# pload[61] = 0x11
# pload[62] = 0xc4
# pload[63] = 0x52
# pload[64] = 0xc4
# pload[65] = 0x9a
# pload[66] = 0x36
# pload[67] = 0x15
# pload[68] = 0x1
# pload[69] = 0x6f
# pload[70] = 0x62
# pload[71] = 0xe1
# pload[72] = 0x79
# pload[73] = 0xa9
# pload[74] = 0x75
# pload[75] = 0x62
# pload[76] = 0x89
# pload[77] = 0xda
# pload[78] = 0x56
# pload[79] = 0xe8
# pload[80] = 0x3
# pload[81] = 0xf9
# pload[82] = 0x4f
# pload[83] = 0x7a
# pload[84] = 0x6d
# pload[85] = 0x6a
# pload[86] = 0x49
# pload[87] = 0x3e
# pload[88] = 0xc0
# pload[89] = 0x6a
# pload[90] = 0xd4
# pload[91] = 0xa5
# pload[92] = 0x86
# pload[93] = 0xac
# pload[94] = 0x10
# pload[95] = 0xce
# pload[96] = 0xd4
# pload[97] = 0x83
# pload[98] = 0xbc
# pload[99] = 0x6e
# pload[100] = 0x36
# pload[101] = 0xfc
# pload[102] = 0xf8
# pload[103] = 0x80
# pload[104] = 0xd
# pload[105] = 0x7b
# pload[106] = 0xde
# pload[107] = 0xd1
# pload[108] = 0x7e
# pload[109] = 0x75
# pload[110] = 0xea
# pload[111] = 0xe0
# pload[112] = 0xed
# pload[113] = 0xbf
# pload[114] = 0xba
# pload[115] = 0x2a
# pload[116] = 0x8a
# pload[117] = 0x5b
# pload[118] = 0x9a
# pload[119] = 0x57
# pload[120] = 0x17
# pload[121] = 0x14
# pload[122] = 0xd0
# pload[123] = 0x21
# pload[124] = 0xca
# pload[125] = 0x6a
# pload[126] = 0x74
# pload[127] = 0x34
# pload[128] = 0x58
# pload[129] = 0xbc
# pload[130] = 0x94
# pload[131] = 0x7e
# pload[132] = 0x7c
# pload[133] = 0xb3
# pload[134] = 0xd8
# pload[135] = 0x8b
# pload[136] = 0xef
# pload[137] = 0xb5
# pload[138] = 0x41
# pload[139] = 0x6c
# pload[140] = 0x2
# pload[141] = 0x46
# pload[142] = 0x71
# pload[143] = 0x1a
# pload[144] = 0x9c
# pload[145] = 0x22
# pload[146] = 0x27
# pload[147] = 0x52
# pload[148] = 0x26
# pload[149] = 0x90
# pload[150] = 0x43
# pload[151] = 0xc3
# pload[152] = 0x1a
# pload[153] = 0x12
# pload[154] = 0x87
# pload[155] = 0x71
# pload[156] = 0xf
# pload[157] = 0x9c
# pload[158] = 0xd6
# pload[159] = 0x64
# pload[160] = 0xac
# pload[161] = 0x3
# pload[162] = 0xd5
# pload[163] = 0x43
# pload[164] = 0x33
# pload[165] = 0xb
# pload[166] = 0xf3
# pload[167] = 0x4f
# pload[168] = 0x22
# pload[169] = 0x9
# pload[170] = 0xe5
# pload[171] = 0x46
# pload[172] = 0x17
# pload[173] = 0x39
# pload[174] = 0x20
# pload[175] = 0x99
# pload[176] = 0xd1
# pload[177] = 0x2a
# pload[178] = 0x9
# pload[179] = 0x87
# pload[180] = 0xbf
# pload[181] = 0x69
# pload[182] = 0x6a
# pload[183] = 0xcc
# pload[184] = 0xa8
# pload[185] = 0xd3
# pload[186] = 0x4
# pload[187] = 0xa2
# pload[188] = 0x81
# pload[189] = 0x9e
# pload[190] = 0xe5
# pload[191] = 0x29
# pload[192] = 0x5c
# pload[193] = 0xf9
# pload[194] = 0xb5
# pload[195] = 0x50
# pload[196] = 0x51
# pload[197] = 0x3c
# pload[198] = 0x9e
# pload[199] = 0x43
# crc = 0x0
# UVM_INFO Monitor.sv(45) @ 4630: uvm_test_top.o_mon [Monitor] collect one package,size=218
# UVM_INFO Monitor.sv(60) @ 4630: uvm_test_top.o_mon [Monitor] pload data size=200
# UVM_INFO Monitor.sv(70) @ 4630: uvm_test_top.o_mon [Monitor] end collect one package, print it:
# dmac = 0xd0babff47aa6
# smac = 0xc09f0969df26
# ether type = 0x0
# pload[0] = 0x4a
# pload[1] = 0x60
# pload[2] = 0x23
# pload[3] = 0x24
# pload[4] = 0x90
# pload[5] = 0xaf
# pload[6] = 0xce
# pload[7] = 0x23
# pload[8] = 0xb1
# pload[9] = 0xd3
# pload[10] = 0x45
# pload[11] = 0xf
# pload[12] = 0x60
# pload[13] = 0x5f
# pload[14] = 0xee
# pload[15] = 0x5c
# pload[16] = 0x3e
# pload[17] = 0xee
# pload[18] = 0x99
# pload[19] = 0x30
# pload[20] = 0xbf
# pload[21] = 0xbb
# pload[22] = 0x8b
# pload[23] = 0x92
# pload[24] = 0x3b
# pload[25] = 0x53
# pload[26] = 0x23
# pload[27] = 0x27
# pload[28] = 0xc8
# pload[29] = 0x9
# pload[30] = 0x82
# pload[31] = 0x9b
# pload[32] = 0x48
# pload[33] = 0x76
# pload[34] = 0xfb
# pload[35] = 0x32
# pload[36] = 0xcc
# pload[37] = 0xcb
# pload[38] = 0x5
# pload[39] = 0xc2
# pload[40] = 0xa2
# pload[41] = 0x6
# pload[42] = 0x4c
# pload[43] = 0xe9
# pload[44] = 0x1c
# pload[45] = 0x79
# pload[46] = 0xf4
# pload[47] = 0x65
# pload[48] = 0xa7
# pload[49] = 0x8d
# pload[50] = 0x3d
# pload[51] = 0xac
# pload[52] = 0xb9
# pload[53] = 0xe4
# pload[54] = 0x11
# pload[55] = 0x99
# pload[56] = 0x44
# pload[57] = 0xca
# pload[58] = 0xe5
# pload[59] = 0xa1
# pload[60] = 0xea
# pload[61] = 0x11
# pload[62] = 0xc4
# pload[63] = 0x52
# pload[64] = 0xc4
# pload[65] = 0x9a
# pload[66] = 0x36
# pload[67] = 0x15
# pload[68] = 0x1
# pload[69] = 0x6f
# pload[70] = 0x62
# pload[71] = 0xe1
# pload[72] = 0x79
# pload[73] = 0xa9
# pload[74] = 0x75
# pload[75] = 0x62
# pload[76] = 0x89
# pload[77] = 0xda
# pload[78] = 0x56
# pload[79] = 0xe8
# pload[80] = 0x3
# pload[81] = 0xf9
# pload[82] = 0x4f
# pload[83] = 0x7a
# pload[84] = 0x6d
# pload[85] = 0x6a
# pload[86] = 0x49
# pload[87] = 0x3e
# pload[88] = 0xc0
# pload[89] = 0x6a
# pload[90] = 0xd4
# pload[91] = 0xa5
# pload[92] = 0x86
# pload[93] = 0xac
# pload[94] = 0x10
# pload[95] = 0xce
# pload[96] = 0xd4
# pload[97] = 0x83
# pload[98] = 0xbc
# pload[99] = 0x6e
# pload[100] = 0x36
# pload[101] = 0xfc
# pload[102] = 0xf8
# pload[103] = 0x80
# pload[104] = 0xd
# pload[105] = 0x7b
# pload[106] = 0xde
# pload[107] = 0xd1
# pload[108] = 0x7e
# pload[109] = 0x75
# pload[110] = 0xea
# pload[111] = 0xe0
# pload[112] = 0xed
# pload[113] = 0xbf
# pload[114] = 0xba
# pload[115] = 0x2a
# pload[116] = 0x8a
# pload[117] = 0x5b
# pload[118] = 0x9a
# pload[119] = 0x57
# pload[120] = 0x17
# pload[121] = 0x14
# pload[122] = 0xd0
# pload[123] = 0x21
# pload[124] = 0xca
# pload[125] = 0x6a
# pload[126] = 0x74
# pload[127] = 0x34
# pload[128] = 0x58
# pload[129] = 0xbc
# pload[130] = 0x94
# pload[131] = 0x7e
# pload[132] = 0x7c
# pload[133] = 0xb3
# pload[134] = 0xd8
# pload[135] = 0x8b
# pload[136] = 0xef
# pload[137] = 0xb5
# pload[138] = 0x41
# pload[139] = 0x6c
# pload[140] = 0x2
# pload[141] = 0x46
# pload[142] = 0x71
# pload[143] = 0x1a
# pload[144] = 0x9c
# pload[145] = 0x22
# pload[146] = 0x27
# pload[147] = 0x52
# pload[148] = 0x26
# pload[149] = 0x90
# pload[150] = 0x43
# pload[151] = 0xc3
# pload[152] = 0x1a
# pload[153] = 0x12
# pload[154] = 0x87
# pload[155] = 0x71
# pload[156] = 0xf
# pload[157] = 0x9c
# pload[158] = 0xd6
# pload[159] = 0x64
# pload[160] = 0xac
# pload[161] = 0x3
# pload[162] = 0xd5
# pload[163] = 0x43
# pload[164] = 0x33
# pload[165] = 0xb
# pload[166] = 0xf3
# pload[167] = 0x4f
# pload[168] = 0x22
# pload[169] = 0x9
# pload[170] = 0xe5
# pload[171] = 0x46
# pload[172] = 0x17
# pload[173] = 0x39
# pload[174] = 0x20
# pload[175] = 0x99
# pload[176] = 0xd1
# pload[177] = 0x2a
# pload[178] = 0x9
# pload[179] = 0x87
# pload[180] = 0xbf
# pload[181] = 0x69
# pload[182] = 0x6a
# pload[183] = 0xcc
# pload[184] = 0xa8
# pload[185] = 0xd3
# pload[186] = 0x4
# pload[187] = 0xa2
# pload[188] = 0x81
# pload[189] = 0x9e
# pload[190] = 0xe5
# pload[191] = 0x29
# pload[192] = 0x5c
# pload[193] = 0xf9
# pload[194] = 0xb5
# pload[195] = 0x50
# pload[196] = 0x51
# pload[197] = 0x3c
# pload[198] = 0x9e
# pload[199] = 0x43
# crc = 0x0
#
封装为 agent
driver和monitor之间的联系:两者之间的代码高度相似。其本质是因为二者处理的是同一种协议,在同样一套既定的规则下做着不同的事情。由于二者的这种相似性,UVM中通常将二者封装在一起,成为 一个agent。因此,不同的agent就代表了不同的协议。
class Agent extends uvm_agent;
Driver drv;
Monitor mon;
function new (string name , uvm_component parent);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(Agent)
endclass
function void Agent::build_phase(uvm_phase phase);
super.build_phase(phase);
if (is_active == UVM_ACTIVE)
begin
drv = Driver::type_id::create("drv",this);
end
mon = Monitor::type_id::create("mon",this);
endfunction
function void Agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
所有的agent都要派生自uvm_agent类,且其本身是一个component,应该使用uvm_component_utils 宏来实现factory注册。
is_active是uvm_agent的一 个成员变量,从UVM的源代码中可以找到它的原型如下:
这个枚举变量仅有两个值:UVM_PASSIVE和UVM_ACTIVE。在uvm_agent中,is_active的值默认为UVM_ACTIVE,在这种模 式下,是需要实例化driver的。那么什么是UVM_PASSIVE 模式呢?以本章的DUT为例,如图2-5所示,在输出端口上不需要驱动任 何信号,只需要监测信号。在这种情况下,端口上是只需要monitor的,所以driver可以不用实例化。
在把driver和monitor封装成agent后,在env中需要实例化agent,而不需要直接实例化driver和monitor了:
class Environment extends uvm_env; // Driver drv; // Monitor i_mon,o_mon; Agent i_agt,o_agt; function new(string name = "env",uvm_component parent); super.new(name,parent); endfunction virtual function void build_phase(uvm_phase phase); super.build_phase(phase); i_agt = Agent::type_id::create("i_agt",this); o_agt = Agent::type_id::create("o_agt",this); i_agt.is_active = UVM_ACTIVE; o_agt.is_active = UVM_PASSIVE; endfunction `uvm_component_utils(Environment) endclass
完成i_agt和o_agt的声明后,在my_env的build_phase中对它们进行实例化后,需要指定各自的工作模式是active模式还是passive 模式。现在,整棵UVM树变为了如所示形式。
由于agent的加入,driver和monitor的层次结构改变了,在top_tb中使用config_db设置virtual my_if时要注意改变路径:
在加入了my_agent后,UVM的树形结构越来越清晰。首先,只有uvm_component才能作为树的结点,像my_transaction这种使 用uvm_object_utils宏实现的类是不能作为UVM树的结点的。其次,在my_env的build_phase中,创建i_agt和o_agt的实例是在 build_phase中;在agent中,创建driver和monitor的实例也是在build_phase中。按照前文所述的build_phase的从树根到树叶的执行顺序,可以建立一棵完整的UVM树。UVM要求UVM树最晚在build_phase时段完成,如果在build_phase后的某个phase实例化一个 component:
class Environment extends uvm_env; // Driver drv; // Monitor i_mon,o_mon; Agent i_agt,o_agt; function new(string name = "env",uvm_component parent); super.new(name,parent); endfunction virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // i_agt = Agent::type_id::create("i_agt",this); // o_agt = Agent::type_id::create("o_agt",this); // i_agt.is_active = UVM_ACTIVE; // o_agt.is_active = UVM_PASSIVE; endfunction virtual task main_phase(uvm_phase phase); i_agt = Agent::type_id::create("i_agt",this); o_agt = Agent::type_id::create("o_agt",this); i_agt.is_active = UVM_ACTIVE; o_agt.is_active = UVM_PASSIVE; endtask `uvm_component_utils(Environment) endclass
如上所示,将在my_env的build_phase中的实例化工作移动到main_phase中,UVM会给出如下错误提示:
那么是不是只能在build_phase中执行实例化的动作呢?答案是否定的。其实还可以在new函数中执行实例化的动作。如可以在 my_agent的new函数中实例化driver和monitor:
UVM中约定俗成的还是在build_phase中完成实例化工作。因此,强烈建议仅在build_phase中完成实例化。
加入reference model
reference model用于完成和DUT相同的功能。reference model的输出被scoreboard 接收,用于和DUT的输出相比较。DUT如果很复杂,那么reference model也会相当复杂:文章来源:https://www.toymoban.com/news/detail-564918.html
根据学习进度会更新……文章来源地址https://www.toymoban.com/news/detail-564918.html
到了这里,关于UVM学习——搭建简单的UVM平台的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!