jelly_bean
源代码 https://github.com/cluelogic/uvm-tutorial-for-candy-lovers
子弹 transaction
// 首先建立一个transaction,即建立一个数据包
// jelly_bean_transaction.v
// 首先建立一个transaction,即建立一个数据包
class jelly_bean_transaction extends uvm_sequence_item;
// 声明为bit[2:0]的枚举类型变量
typedef enum bit[2:0] { NO_FLAVOR, APPLE, BLUEBERRY, BUBBLE_GUM, CHOCOLATE } flavor_e; // 口味
// 隐式赋值 NO_FLAVOR=1,APPLE=2,...,CHOCOLATE=4
typedef enum bit[1:0] { RED, GREEN, BLUE } color_e; //颜色
typedef enum bit[1:0] { UNKNOWN, YUMMY, YUCKY } taste_e;
rand flavor_e flavor; // 随机化 变量类型(flavor_e) 变量名,将随机化为NO_FLAVOR, ..., CHOCOLATE之一
rand color_e color; // 随机化 变量类型(flavor_e) 变量名,将随机化为RED, GREEN, BLUE之一
rand bit sugar_free; // 无糖
rand bit sour; // 酸的
taste_e taste;
// 约束变量
constraint flavor_color_con {
flavor != NO_FLAVOR; // 口味不能是NO_FLAVOR
flavor == APPLE -> color != BLUE; // 苹果口味,则颜色必须不是蓝色
flavor == BLUEBERRY -> color == BLUE; // 蓝莓口味,则颜色必须是蓝色
}
function new(string name = ""); // 该类new函数
super.new(name); // 执行父类new函数
endfunction: new
// 注册类中定义的变量
// 用field_automation机制(`uvm_object_utils_begin...end)注册object并声明今后对象会用到的操作
`uvm_object_utils_begin(jelly_bean_transaction) // 注册object变量,规定可以使用的函数
`uvm_field_enum(flavor_e, flavor, UVM_ALL_ON) // 枚举型 (变量类型(enum),变量名,标志位)
`uvm_field_enum(color_e, color, UVM_ALL_ON)
`uvm_field_int(sugar_free, UVM_ALL_ON) // 整型 (变量名,标志位)
`uvm_field_int(sour, UVM_ALL_ON)
`uvm_field_enum(taste_e, taste, UVM_ALL_ON)
`uvm_object_utils_end // UVM_ALL_ON 打开所有功能,包括copy, compare, print等
endclass: jelly_bean_transaction
// 通过jelly_bean_transaction类可以扩展到各种类。
// 例如,要创建只有无糖jelly_bean,子类可以定义约束
class sugar_free_jelly_bean_transaction extends jelly_bean_transaction;
`uvm_object_utils(sugar_free_jelly_bean_transaction) // 注册uvm_object类
constraint sugar_free_con { // 定义子类约束
sugar_free == 1;
}
function new(string name = "");
super.new(name);
endfunction: new
endclass: sugar_free_jelly_bean_transaction
field automation 机制:
// 域的自动化宏声明应该在uvm_object或者uvm_componet注册时发生 《芯片验证漫游指南》P276
//即在`uvm_object_utils_begin和`uvm_object_utils_end之间,
//或者在`uvm_component _utils_begin和`uvm_component_utils_end之间
`uvm_object_utils_begin // 用field automation机制注册类
...
`uvm_object_utils_end
为了减少用户负担,UVM 通过 field automation 机制,使用户在注册UVM类的同时也可以声明今后会参与到对象复制、克隆、打印等操作的成员变量。不声明的化,不会自动参与对象复制、克隆、打印等操作。
uvm_object核心方法主要提供与数据操作的相关服务,Copy、Clone、Compare、Print、Pack/Unpack。
// 当使用field_automation机制时,需要使用此宏。
`uvm_object_utils_begin(类名) // 传进某类(即注册object)后,可以使用UVM预定义好的函数
// uvm_field系列宏(变量,标志位)
uvm_field_int(ARG,FLAG) // 注册的字段是整数类型
uvm_field_real(ARG,FLAG) // 注册的字段是实数
uvm_field_enum(T,ARG,FLAG) // 注册的字段是枚举 (枚举类型需要有三个参数:类型+ 变量+ 标志位)
uvm_field_object(ARG,FLAG) // 直接或间接派生自uvm_object的类型
uvm_field_event(ARG,FLAG) // 直接或间接派生自事件
uvm_field_string(ARG,FLAG) // 直接或间接派生自字符串
`uvm_object_utils_end
// 使用field automation宏注册之后,可以直接调用这些函数,而无需自己定义。比如:
extern function void print(); // print函数用于实例中所有字段的打印
extern virtual function uvm_object clone(); // clone函数用于分配内存空间和把某一实例复制到该内存空间中
extern function void copy(uvm_object rhs); // copy函数用于实例的复制
extern function bit compare(uvm_object rhs,uvm_comparer comparer=null); // compare函数用于比较两个实例是否一样
extern function int pack_bytes(ref byte unsigned bytestream[],input uvm_packer packer=null); // pack_bytes函数用于将所有的字段打包成byte流
extern function int unpack_bytes(ref byte unsigned bytestream[],input uvm_packer packer=null); // unpack_bytes函数用于将byte流恢复到某个类的实例中
extern function int pack(ref bit bitstream[],input uvm_packer packer=null); // pack函数用于将所有的字段打包成bit流
extern function int unpack(ref bit bitstream[],input uvm_packer packer=null); // unpack函数用于将bit流恢复到某个类的实例中
extern function int pack_ints(ref int unsigned intstream[],input uvm_packer packer=null); // pack_ints函数用于将所有的字段打包成int流
extern function int unpack_ints(ref int unsigned intstream[],input uvm_packer packer=null); // unpack_ints函数用于将int流恢复到某个类的实例中
弹夹 sequence:
transaction,sequence,sequencer,driver的相互关系参考下图
子弹(transactions),弹夹(sequence )和枪(sequencer)
sequence 就像一个弹夹,里面装了很多"子弹",而这里的"子弹"就是 transaction。弹夹可长可短,只要子弹型号一致(同一种 transaction),就能通过 sequencer 和 driver 把数据驱动给 DUT。在上图中,有三个 sequence 挂载于同一个 sequncer 上(当然也可不同时间挂在其中一个 sequence),通过 driver 进行数据驱动,其中 sequencer 和 driver 是共同使用的。
// one_jelly_bean_sequence.v
// 创建一个单一的jelly_bean
class one_jelly_bean_sequence extends uvm_sequence#(jelly_bean_transaction);
`uvm_object_utils(one_jelly_bean_sequence) // object注册
function new(string name = ""); // 类的构造函数
super.new(name);
endfunction: new
// 随机化配方
task body();
jelly_bean_transaction jb_tx; // 声明jelly_bean_transaction类(sequence_item "子弹") 变量
// 实例化变量jb_tx
// 只有使用factory机制注册过的类才能使用这种方式实例化 type_name::type_id::create(名字,this指针)
jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));
start_item(jb_tx); // 向driver发送请求
assert(jb_tx.randomize());
finish_item(jb_tx); // 等待driver完成当前项
endtask: body
endclass: one_jelly_bean_sequence
// 创建相同风味的多个jelly_bean
class same_flavored_jelly_beans_sequence extends uvm_sequence#(jelly_bean_transaction);
rand int unsigned num_jelly_beans; // 创建数量
constraint num_jelly_beans_con { num_jelly_beans inside { [2:4] }; } //约束创建数量为2~4个
function new(string name = ""); // 构造函数
super.new(name);
endfunction: new
// 随机化配方
task body();
jelly_bean_transaction jb_tx; // 声明jelly_bean_transaction类变量
jelly_bean_transaction::flavor_e jb_flavor; // 声明jelly_bean_transaction类中 口味变量
// 实例化变量jb_tx
jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));
assert(jb_tx.randomize());
// 实例化变量jb_flavor
jb_flavor = jb_tx.flavor;
repeat (num_jelly_beans) begin // 创建多个实例
jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));
start_item(jb_tx);
assert(jb_tx.randomize() with { jb_tx.flavor == jb_flavor; });
// 用randomize()with{ }增加额外的约束,这和在类里增加约束是等效的
finish_item(jb_tx);
end
endtask: body
// 注册类中定义的变量
// 用field_automation机制(`uvm_object_utils_begin...end)注册object并声明今后对象会用到的操作
`uvm_object_utils_begin(same_flavored_jelly_beans_sequence)
`uvm_field_int(num_jelly_beans, UVM_ALL_ON)
`uvm_object_utils_end
endclass: same_flavored_jelly_beans_sequence
// 不同风味创建多个jelly_bean
class gift_boxed_jelly_beans_sequence extends uvm_sequence#(jelly_bean_transaction);
rand int unsigned num_jelly_bean_flavors; // 口味数量
constraint num_jelly_bean_flavors_con { num_jelly_bean_flavors inside { [2:3] }; } //约束口味数量为2~3个
function new(string name = ""); // 构造函数
super.new(name); // jelly_bean_transaction.new 将随机化口味
endfunction: new
task body();
same_flavored_jelly_beans_sequence jb_seq; // 声明类 变量
repeat (num_jelly_bean_flavors) begin // 重复口味数量
jb_seq = same_flavored_jelly_beans_sequence::type_id::create(.name("jb_seq"), .contxt(get_full_name()));
assert(jb_seq.randomize());
jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this));
// parent_sequence如果为null或者不配置,说明sequence没有parent,那么就不会调用parent的pre_do,mid_do和post_do的方法(有parent,也可以设为null)。
end
endtask: body
// 注册该类中创建的变量 num_jelly_bean_flavors
`uvm_object_utils_begin(gift_boxed_jelly_beans_sequence)
`uvm_field_int(num_jelly_bean_flavors, UVM_ALL_ON)
`uvm_object_utils_end
endclass: gift_boxed_jelly_beans_sequence
start_item 里会调用 wait_for_grant 及 parent_seq.pre_do 任务。
finish_item 里会调用 mid_do,send_request,wait_for_item_down 及 post_do 等
/ /start_item ... finish_item 源码
sequencer.wait_for_grant(prior) (task) \ start_item \
parent_seq.pre_do(1) (task) / \
`uvm_do* macros
parent_seq.mid_do(item) (func) \ /
sequencer.send_request(item) (func) \finish_item /
sequencer.wait_for_item_done() (task) /
parent_seq.post_do(item) (func) /
插排 interface:
DUT 与 TB 之间的数据驱动关系都可以使用 interface 这个插排来完成。
在design与testbench之间易产生竞争冒险的情况,例如不满足Tsetup的要求;可以通过提前驱动信号,滞后采样信号(同步)来避免。
时钟控制块中所有的输入或双向信号都在对应的时钟事件发生的时候被采样。同样,时钟控制块中所有输出或双向信号都在对应的时钟事件发生的时候被驱动。default shew会作用于clocking块中所有未明确定义shew的 input/output 信号。如未明确指定default shew,则系统默认的input shew是1 step,output shew 是0。
// 时钟控制块1
clocking ck1 @(posedge clk);
default input #1step output negedge; // (default shew)输入在时钟信号之前的时间步值采样, 输出在clk的下降沿被驱动
input ...; // 使用default shew
output ...; // 使用default shew
endclocking
// 时钟控制块2
clocking ck2 @(clk); // 没有指定沿
default input #1step output negedge; // (default shew)输入在时钟信号之前的时间步值采样, 输出在clk的下降沿被驱动
input ...; // 使用default shew
output ...; // 使用default shew
endclocking
// 时钟控制块3
clocking bus @(posedge clock1);
default input #10ns output #2ns; // clock1上升沿的前10ns来对其进行输入采样,在其事件的后2ns对其进行输出驱动
input data, ready, enable = top.mem1.enable; // 使用default shew // enable指向层次化的信号top.mem1.enable
output negedge ack; // ack信号在clock1的下降沿后2ns被驱动
input #1step addr; // addr在clock1上升沿之前的一个步值时被采样
endclocking
步值 step:
1个步值是一个特殊的时间单位,一个1step的输入时滞使得输入信号在时钟信号之前的时间步值中采样它们的稳定值(也就是在前一个Postponed区域)。与其它代表物理单位的时间单位不同,一个step不能被用来设置或修改时间精度或时间单位。
modport将信号分组,指定方向
// interface.v
// 接口(jelly_bean_if)作用:绑定验证组件和jelly-bean(DUT)
interface jelly_bean_if(input bit clk);
// 类似module声明端口
logic [2:0] flavor;
logic [1:0] color;
logic sugar_free;
logic sour;
logic [1:0] taste;
// 时钟控制块1 (为避免竞争冒险,interface里面使用clocking block模块)
clocking master_cb @ (posedge clk);
default input #1step output #1ns; // (default shew)输入在时钟信号之前的时间步值采样,在其事件的后2ns对其进行输出驱动
input taste;
endclocking: master_cb
// 时钟控制块2
clocking slave_cb @ (posedge clk);
default input #1step output #1ns;
input flavor, color, sugar_free, sour;
output taste;
endclocking: slave_cb
// modport将信号分组,指定方向
// DUT使用异步modport列表(master_mp和slave_mp)
modport master_mp(input clk, taste, output flavor, color, sugar_free, sour); // 信号相对于DUT的输入/出
modport slave_mp(input clk, flavor, color, sugar_free, sour, output taste); // 信号相对于DUT的输入/出
// 而testbench使用同步modport列表(master_sync_mp和slave_sync_mp)
modport master_sync_mp(clocking master_cb); // 调用clocking
modport slave_sync_mp(clocking slave_cb);
endinterface: jelly_bean_if
master_cb时钟块是从主控总线(bus-master)的角度来定义时序的,而slave_cb时钟块是从受控总线(bus-slave)的角度来定义时序的
枪 sequencer
// sequencer.v
typedef uvm_sequencer#(jelly_bean_transaction) jelly_bean_sequencer;
dut
// DUT.v 品尝师jelly_bean_taster
module jelly_bean_taster( jelly_bean_if.slave_mp jb_slave_if ); // 指定jelly_bean_if,以及slave_mp为接口信号选择适当的方向信息
import jelly_bean_pkg::*; // 通过import:: 指定需要导入的内容。如果需要导入所有内容,就需要输入import my_pkg::*
always @ ( posedge jb_slave_if.clk ) begin
if ( jb_slave_if.flavor == jelly_bean_transaction::CHOCOLATE && jb_slave_if.sour ) begin
jb_slave_if.taste <= jelly_bean_transaction::YUCKY;
end
else begin
jb_slave_if.taste <= jelly_bean_transaction::YUMMY;
end
end
endmodule: jelly_bean_taster
driver
Driver和Sequencer之间的握手机制
Driver调用get_next_item()时,Sequencer中的FIFO会Pop出一个transaction送给Driver,Driver将这个transaction的数据驱动给DUT后,需要和Sequence达成握手,告诉它这笔transaction已经使用完了,可以发送下一个transaction了(否则sequence会阻塞在`uvm_do或finish_item等指令上),所以它会调用seq_item_port的item_done()函数。
Driver使用get_next_item()获得item,使用item_done()通知sequence使用完成
注意到item_done其实是一个参数类的函数,参数类型为RSP,这个是Driver和Sequence之间反馈所需要使用的。如果不加入输入参数,sequence就不会收到response。
// get 函数决定了UVM树形结构的具体层次
static function bit get(uvm_component cntxt, string inst_name, string field_name, inout T value)
// uvm_config_db#(T)::get 是收信;uvm_config_db#(T)::set 是寄信
uvm_config_db#(virtual my_if)::get(this, "", "vif", vif);
uvm_component cntxt :UVM树形结构的起始点。可以认为是string inst_name 的相对层次路径。uvm_component cntxt与string inst_name,配合得到UVM树形结构的具体层次。
string field_name :指定层次下的field字段名称。
inout T value :field字段的type类型。
get的返回值是function bit类型(不要看成简单的bit类型),即0或者1。1代表指定层次的inst_name,其对应field_name 的value值,并做好了get function工作。
// driver.v
// driver通过seq_item_port接收jelly_bean_transaction,传给DUT的interface接口
// driver使用driver和DUT之间的interface来驱动信号。该interface存储在uvm_config_db中。
class jelly_bean_driver extends uvm_driver#(jelly_bean_transaction);
`uvm_component_utils(jelly_bean_driver) // component注册
virtual jelly_bean_if jb_vi; // 声明接口
function new(string name, uvm_component parent); // 构造函数new
super.new(name, parent);
endfunction: new
// build_phase:自动获取通过config_db::set 设置的参数
function void build_phase(uvm_phase phase);
super.build_phase(phase); // 使用父类class的build_phase函数
// uvm_config_db存储interface
assert( uvm_config_db#( virtual jelly_bean_if )::
get( .cntxt( this ), .inst_name( "" ), .field_name( "jelly_bean_if" ), .value( jb_vi ) ) );
endfunction: build_phase
task run_phase(uvm_phase phase);
jelly_bean_transaction jb_tx; // 声明transaction 变量
forever begin
@jb_vi.master_cb; // 即@(posedge jb_vi.clk)时钟块事件
jb_vi.master_cb.flavor <= jelly_bean_transaction::NO_FLAVOR; // transaction数据传给DUT的interface接口
// seq_item_port.get_next_item() Sequencer把transaction送给Driver
seq_item_port.get_next_item(jb_tx); // 接收jelly_bean_transaction,然后驱动interface上的信号
@jb_vi.master_cb; // 即@(posedge jb_vi.clk)时钟块事件
jb_vi.master_cb.flavor <= jb_tx.flavor; // transaction数据给接口
jb_vi.master_cb.color <= jb_tx.color;
jb_vi.master_cb.sugar_free <= jb_tx.sugar_free;
jb_vi.master_cb.sour <= jb_tx.sour;
seq_item_port.item_done(); // item_done()通知sequence使用完成
end
endtask: run_phase
endclass: jelly_bean_driver
monitor
UVM中的TLM1端口,第一类是用于driver 和 sequencer连接端口,第二类是用于其他 component 之间连接的端口,如 monitor和 scoreboard。都是继承自uvm_port_base #(uvm_tlm_if_base #(T,T))。
UVM中各种port的连接
1、 port > export ,使用connect建立连接关系:A.connect(B);
2、port > imp(端口优先级:port > export > imp)
3、跨层次传输,eg:agent中的monitor与scoreboard通信
在agent中声明一个ap,但是不例化它,让其指向monitor中的ap。在env中可以直接连接agent的ap到scoreboard的imp。
// 3、跨层次传输,eg:agent中的monitor与scoreboard通信
class monitor extends uvm_monitor;
...
uvm_analysis_port#(transaction) ap; //声明端口
virtual function void build_phase(uvm_phase phase);
this.ap=new("ap",this); //端口实例化
endfunction
task main_phase(uvm_phase phase);
super.mian_phase(phase);
transaction tr;
...
ap.write(tr);
...
endtask
endclass
class agent extends uvm_agent;
...
monitor mon;
uvm_analysis_port#(transaction) ap; //声明端口
...
virtual function void connect_phase(uvm_phase phase);
ap=mon.ap; //1. 调用实例化的mon.ap赋值给为实例化的ap,(相当于连接并实例化)
...
endfucntion
endclass
class scoreboard extends uvm_scoreboard;
...
uvm_analysis_imp #(transaction, scoreboard) analysis_imp;
...
task write(transaction tr);
//do something on tr
endtask
endclass
class environment extends uvm_env;
...
agent agt;
scoreboard sb;
...
virtual function void connect_phase(uvm_phase phase);
agt.ap.connect(sb.analysis_imp); //2. 不同端口类型连接
endfucntion
endclass
// monitor.v
// monitor密切监视jelly_bean_if,并获取信号的值
class jelly_bean_monitor extends uvm_monitor;
`uvm_component_utils(jelly_bean_monitor) // component类注册
uvm_analysis_port#(jelly_bean_transaction) jb_ap; // 声明monitor端口 (jb_mon)
virtual jelly_bean_if jb_vi; // 声明接口
function new(string name, uvm_component parent); // 构造函数new
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
assert( uvm_config_db#( virtual jelly_bean_if )::
get( .cntxt( this ), .inst_name( "" ), .field_name( "jelly_bean_if" ), .value( jb_vi ) ) );
jb_ap = new(.name("jb_ap"), .parent(this)); // 端口实例化
endfunction: build_phase
task run_phase(uvm_phase phase);
forever begin
jelly_bean_transaction jb_tx; // 声明transaction
@jb_vi.slave_cb; // 即@(posedge jb_vi.clk)时钟块事件
if (jb_vi.slave_cb.flavor != jelly_bean_transaction::NO_FLAVOR) begin
jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name())); // 实例化transaction(jb_tx)
jb_tx.flavor = jelly_bean_transaction::flavor_e'(jb_vi.slave_cb.flavor); // DUT接口的输出
jb_tx.color = jelly_bean_transaction::color_e'(jb_vi.slave_cb.color);
jb_tx.sugar_free = jb_vi.slave_cb.sugar_free;
jb_tx.sour = jb_vi.slave_cb.sour;
@jb_vi.master_cb;
jb_tx.taste = jelly_bean_transaction::taste_e'(jb_vi.master_cb.taste); // DUT接口的输入
jb_ap.write(jb_tx);
end
end
endtask: run_phase
endclass: jelly_bean_monitor
agent
sequencer,driver,monitor - 全部例化在在agent中。这些组件在build_phase中创建,创建的组件在connect_phase中连接。由于subscriber没有例化在agent,但是monitor要向subscriber发送trans,所以创建analysis port以与subscriber进行通信。agent和monitor中的analysis port在26行相互连接。然后,agent中的analysis port又和subscriber中的analysis imp在env中连接。
// agent.v
//sequencer,driver,monitor - 全部例化在在agent中。
//这些组件在build_phase中创建,创建的组件在connect_phase中连接
class jelly_bean_agent extends uvm_agent;
`uvm_component_utils(jelly_bean_agent) // component注册
uvm_analysis_port#(jelly_bean_transaction) jb_ap; // agent端口声明
jelly_bean_sequencer jb_seqr; // 声明sequencer,driver,monitor 全部例化在在agent中
jelly_bean_driver jb_drvr;
jelly_bean_monitor jb_mon;
function new(string name, uvm_component parent); // 构造函数
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase); // 例化组件
super.build_phase(phase);
// agent端口jb_ap,sequencer,driver,monitor 组件例化在在agent中
jb_ap = new(.name("jb_ap"), .parent(this));
jb_seqr = jelly_bean_sequencer::type_id::create(.name("jb_seqr"), .parent(this));
jb_drvr = jelly_bean_driver::type_id::create(.name("jb_drvr"), .parent(this));
jb_mon = jelly_bean_monitor::type_id::create(.name("jb_mon"), .parent(this));
endfunction: build_phase
function void connect_phase(uvm_phase phase); // 组件端口连接
super.connect_phase(phase);
jb_drvr.seq_item_port.connect(jb_seqr.seq_item_export); // 实例化的driverd端口jb_drvr.seq_item_port连接sequencer的端口jb_seqr.seq_item_export
jb_mon.jb_ap.connect(jb_ap); // 调用实例化的agent端口jb_ap赋值给实例化monitor的端口 (jb_mon.jb_ap)
endfunction: connect_phase
endclass: jelly_bean_agent
功能覆盖收集器 fc_subscriber
// subscribers.v
//功能覆盖率收集器(jelly_bean_fc_sucbscriber)
class jelly_bean_fc_subscriber extends uvm_subscriber#(jelly_bean_transaction);
`uvm_component_utils(jelly_bean_fc_subscriber) // component注册
jelly_bean_transaction jb_tx; // 声明transaction
// covergroup里包含很多bin(容器),bin里存放要采样的数据或者要传输的值,采到了值就cover到了这个值
covergroup jelly_bean_cg;
flavor_cp: coverpoint jb_tx.flavor;
color_cp: coverpoint jb_tx.color;
sugar_free_cp: coverpoint jb_tx.sugar_free;
sour_cp: coverpoint jb_tx.sour;
cross flavor_cp, color_cp, sugar_free_cp, sour_cp;
endgroup: jelly_bean_cg
function new(string name, uvm_component parent); // 构造函数new
super.new(name, parent);
jelly_bean_cg = new; // 实例化subscriber
endfunction: new
function void write(jelly_bean_transaction t);
jb_tx = t;
jelly_bean_cg.sample(); // transaction.sample() 采样monitor发送的transaction
endfunction: write
endclass: jelly_bean_fc_subscriber
计分板 scoreboard
// scoreboard.v
// scoreboard中也有scoreboard的subscribers,简称sb_subscribers
typedef class jelly_bean_scoreboard;
class jelly_bean_sb_subscriber extends uvm_subscriber#(jelly_bean_transaction);
`uvm_component_utils(jelly_bean_sb_subscriber) // 注册
function new(string name, uvm_component parent); // 构造函数
super.new(name, parent);
endfunction: new
function void write(jelly_bean_transaction t);
jelly_bean_scoreboard jb_sb; // 声明scoreboard
// 把父类句柄复制给子类句柄时使用 $cast(father_tr, child_tr)
$cast( jb_sb, m_parent ); // m_parent指向jelly_bean_scoreboard,指针复制给jb_sb
jb_sb.check_jelly_bean_taste(t);
endfunction: write
endclass: jelly_bean_sb_subscriber
// scoreboard.v
class jelly_bean_scoreboard extends uvm_scoreboard;
`uvm_component_utils(jelly_bean_scoreboard) // 注册
uvm_analysis_export#(jelly_bean_transaction) jb_analysis_export; // 声明scoreboard端口(export)
local jelly_bean_sb_subscriber jb_sb_sub;
function new(string name, uvm_component parent); // 构造函数new
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase); // 例化组件
super.build_phase(phase);
jb_analysis_export = new( .name("jb_analysis_export"), .parent(this)); // 端口实例化
jb_sb_sub = jelly_bean_sb_subscriber::type_id::create(.name("jb_sb_sub"), .parent(this));
endfunction: build_phase
function void connect_phase(uvm_phase phase); // 组件端口连接
super.connect_phase(phase);
jb_analysis_export.connect(jb_sb_sub.analysis_export);
endfunction: connect_phase
virtual function void check_jelly_bean_taste(jelly_bean_transaction jb_tx);
uvm_table_printer p = new; // 采用table格式打印
if (jb_tx.flavor == jelly_bean_transaction::CHOCOLATE && jb_tx.sour) begin
if (jb_tx.taste == jelly_bean_transaction::YUCKY) begin
`uvm_info("jelly_bean_scoreboard",
{ "You have a good sense of taste.\n", jb_tx.sprint(p) }, UVM_LOW);
end else begin
`uvm_error("jelly_bean_scoreboard",
{ "You lost sense of taste!\n", jb_tx.sprint(p) });
end
end else begin
if (jb_tx.taste == jelly_bean_transaction::YUMMY) begin
`uvm_info("jelly_bean_scoreboard",
{ "You have a good sense of taste.\n", jb_tx.sprint(p) }, UVM_LOW);
end else begin
`uvm_error("jelly_bean_scoreboard",
{ "You lost sense of taste!\n", jb_tx.sprint(p) });
end
end
endfunction: check_jelly_bean_taste
endclass: jelly_bean_scoreboard
env
// environment.v
class jelly_bean_env extends uvm_env;
`uvm_component_utils(jelly_bean_env) // 注册
// agent,fc_subscriber,scoreboard将在env中例化
jelly_bean_agent jb_agent; // 声明agent,fc_subscriber,scoreboard
jelly_bean_fc_subscriber jb_fc_sub;
jelly_bean_scoreboard jb_sb;
function new(string name, uvm_component parent); // 构造函数
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase); // 组件例化
super.build_phase(phase);
jb_agent = jelly_bean_agent::type_id::create(.name("jb_agent"), .parent(this));
jb_fc_sub = jelly_bean_fc_subscriber::type_id::create(.name("jb_fc_sub"), .parent(this));
jb_sb = jelly_bean_scoreboard::type_id::create(.name("jb_sb"), .parent(this));
endfunction: build_phase
function void connect_phase(uvm_phase phase); // 组件端口连接
super.connect_phase(phase);
jb_agent.jb_ap.connect(jb_fc_sub.analysis_export); // jb_agent端口->fc_subscriber端口
jb_agent.jb_ap.connect(jb_sb.jb_analysis_export); // jb_agent端口->scoreboard端口
endfunction: connect_phase
endclass: jelly_bean_env
test
// Test.v
class jelly_bean_test extends uvm_test;
`uvm_component_utils(jelly_bean_test) // 注册
jelly_bean_env jb_env; // 声明env
function new(string name, uvm_component parent); // 构造函数new
super.new(name, parent);
endfunction: new
function void build_phase(uvm_phase phase); // 例化组件
super.build_phase(phase);
begin
jelly_bean_configuration jb_cfg; // 声明配置
// 配置实例化
jb_cfg = new;
assert(jb_cfg.randomize());
uvm_config_db#(jelly_bean_configuration)::set // 将配置类注册到config数据库
(.cntxt(this), .inst_name("*"), .field_name("config"), .value(jb_cfg));
// 使用sugar_free_jelly_bean事务,而不是jelly_bean_transaction
jelly_bean_transaction::type_id::set_type_override(sugar_free_jelly_bean_transaction::get_type());
// env实例化
jb_env = jelly_bean_env::type_id::create(.name("jb_env"), .parent(this));
end
endfunction: build_phase
task run_phase(uvm_phase phase);
// sequence实例化
gift_boxed_jelly_beans_sequence jb_seq; // 不同风味创建多个jelly_bean的sequence
phase.raise_objection(.obj(this));
jb_seq = gift_boxed_jelly_beans_sequence::type_id::create(.name("jb_seq"), .contxt(get_full_name()));
assert(jb_seq.randomize());
`uvm_info("jelly_bean_test", { "\n", jb_seq.sprint() }, UVM_LOW)
jb_seq.start(jb_env.jb_agent.jb_seqr); // test中显式启动sequence
#10ns ;
phase.drop_objection(.obj(this));
endtask: run_phase
endclass: jelly_bean_test
configuration
// configuration.v
class jelly_bean_configuration extends uvm_object;
`uvm_object_utils(jelly_bean_configuration) // 注册
function new(string name = ""); // 构造函数new
super.new(name);
endfunction: new
endclass: jelly_bean_configuration
top
// top.v
// 仿真之前,需要编写实例化DUT模块的顶层模块,将jelly_bean_if注册到资源数据库中,并运行测试
// 顶级模块也负责时钟生成
module top;
import uvm_pkg::*;
reg clk; // 时钟
jelly_bean_if jb_slave_if(clk); // 声明接口 绑定时钟
jelly_bean_taster jb_taster(jb_slave_if); // 声明品尝师DUT 绑定接口
initial begin // 产生时钟
clk = 0;
#5ns ;
forever #5ns clk = ! clk;
end
initial begin
assert( uvm_config_db#( virtual jelly_bean_if )::
get( .cntxt( this ), .inst_name( "" ), .field_name( "jelly_bean_if" ), .value( jb_slave_if ) ) );
run_test();
end
endmodule: top
Simulation
UVM_INFO @ 0: reporter [RNTST] Running test jelly_bean_test...
UVM_INFO jb.sv(481) @ 0: uvm_test_top [jelly_bean_test]
-----------------------------------------------------------------------
Name Type Size Value
-----------------------------------------------------------------------
jb_seq gift_boxed_jelly_beans_sequence - @772
num_jelly_bean_flavors integral 32 'h2
req object -
rsp object -
-----------------------------------------------------------------------
UVM_INFO jb.sv(406) @ 40: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
----------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @818
flavor flavor_e 3 CHOCOLATE
color color_e 2 RED
sugar_free integral 1 'h1
sour integral 1 'h1
taste taste_e 2 YUCKY
----------------------------------------------------------------
UVM_INFO jb.sv(406) @ 60: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
----------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @834
flavor flavor_e 3 CHOCOLATE
color color_e 2 GREEN
sugar_free integral 1 'h1
sour integral 1 'h0
taste taste_e 2 YUMMY
----------------------------------------------------------------
UVM_INFO jb.sv(406) @ 80: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @842
flavor flavor_e 3 APPLE
color color_e 2 GREEN
sugar_free integral 1 'h1
sour integral 1 'h1
taste taste_e 2 YUMMY
------------------------------------------------------------
UVM_INFO jb.sv(406) @ 100: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @850
flavor flavor_e 3 APPLE
color color_e 2 RED
sugar_free integral 1 'h1
sour integral 1 'h0
taste taste_e 2 YUMMY
------------------------------------------------------------