UVM jelly_bean

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
------------------------------------------------------------


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值