域的自动化—UVM

👉最近在写代码时,常常忽略了field automation机制带来的便利性,这里做一个全面的总结。下面我将分为 object 和 component 类型的对象中做域的自动化。首先,常用的宏包括:

  • uvm_field_int(arg,FLAG) ,需要传递:变量+ 标志位
  • uvm_field_enum(T,arg,FLAG) ,需要传递:类型+ 变量+ 标志位
  • uvm_field_object(arg,FLAG)
  • uvm_field_string(arg,FLAG)
  • uvm_field_array_int(arg,FLAG)
  • uvm_field_queue_int(arg,FLAG)

一、Object 类型

首先注册的方式,如下:

	`uvm_object_utils_begin(my_transaction)
		`uvm_field_int(data,UVM_ALL_ON )
		...
	`uvm_object_utils_end

对于 bject 类,将类中的变量注册后,可以使用UVM预定义好的函数,如下:

函数名描述
copy用于实例的复制 ,如B.copy(A),即将A的内容复制给B
clone克隆一个对象,如果对象不存在则创建一个新对象
print打印内容,如B.print( ),即打印 B 对象中所有在域中注册的变量、数组等
pack_bytes将在域中注册的变量和数组等打包成byte流,输出到一个动态数组中
unpack_bytes传入一个动态数组,将所有内容根据域中注册的顺序,转换成类中的字段
pack打包成bit流
unpack将bit流注意恢复到类中

常使用的标志位,包括:

标志描述
UVM_COPY / UVM_NOCOPY打开/关闭 复制
UVM_PACK /UVM_NOPACK打开/关闭 byte流
UVM_ALL_ON所有功能都打开
UVM_PRINT / UVM_NOPRINT打开 / 关闭打印功能

1. 打包成byte流

这个是我用的最多的一个操作,比如我通过发一个以太网的包,数据需要一byte一byte的打到总线的RXD线上,那么你需要怎么造包?又怎么打包和采包?transaction如下:

`ifndef GMII_RX_TRANSACTION_SV
`define GMII_RX_TRANSACTION_SV

typedef enum { RANDOM,INCR,FIXED } payload_kind
class gmii_rx_transaction extends uvm_sequence_item;
	rand bit [2:0]  spa;
	rand bit [47:0] dmac;
	rand bit [47:0] smac;
	rand bit [15:0] ether_type;
	rand bit [7:0]  payload[];
	
	//用于组合 payload
	rand int        pkt_len; 
	rand bit [7:0]  first_data; 
	rand payload_kind kind;  
	
	`uvm_object_utils_begin(gmii_rx_transaction)
		`uvm_field_int(sap,UVM_ALL_ON | UVM_NOPACK)
		`uvm_field_int(dmac,UVM_ALL_ON)
		`uvm_field_int(smac,UVM_ALL_ON)
		`uvm_field_int(ether_type,UVM_ALL_ON)
		`uvm_field_array_int(payload,UVM_ALL_ON)
		`uvm_field_int(pkt_len,UVM_ALL_ON | UVM_NOPACK) 
		`uvm_field_int(first_data,UVM_ALL_ON | UVM_NOPACK)
		`uvm_field_enum(payload_kind,kind,UVM_ALL_ON | UVM_NOPACK)  		
	`uvm_object_utils_end
	...
endclass

`endif

如果我才用打包成byte流的方式,那么在driver和monitor上,就比较容易实现,driver 中不需要挨个向总线上驱动dmac、smac、ether_type、payload…,如下:

class gmii_rx_driver extends uvm_driver#(gmii_rx_transaction);
	byte unsigned data_a[];  #用于保存 byte 流
	...
endclass

task gmii_rx_driver::run()
	...
	seq_item_port.get_next_item(req);
	req.pack_bytes(data_a);  #将byte流数据保存到data_a动态数组中
	foreach( data_a[i] )begin
		vif.drv_cb.RXD <= data_a[i];  //将数据按byte输出到总线上
	end
	...
endtask

byte流是怎么打入到总线的?

下面是发包的内容:在这里插入图片描述
打包成byte流后,数组中的存储状态,如下:在这里插入图片描述
综上,我们可以知道,pack_bytes( )时是按由高到低的顺序排列。举个例子,也就是假如你有一个[47:0] data ,data = 48‘h 55 44 33 22 11 00 ,当你发到总线上的,是先发 55 ,再发44 ,依次发到00。

同理,monitor的实现也会更加简洁,如下:

class gmii_rx_monitor extends uvm_monitor#(gmii_rx_transaction);
	gmii_rx_transaction trans;
	byte unsigned data_q[$]; //用于保存从总线上读取的数据
	byte unsigned data_a[];  //用于转化 byte 流
	...
endclass

task gmii_rx_driver::run();
	@(posedge vif.rstn);
	trans = new("trans");
	while(vif.mon_cb.RX_DV )begin
		data_q.push_back(vif.mon_cb.RXD); //读取总线上的数据
		@(posedge vif.clk);
	end
	data_a = new[data_q.size()];
	for(int i = 0;i <data_q.size( ) ; i++ )begin
		data_a[i] = data_q.pop_front(); //将数据存放在动态数组中
	end
	trans.payload = new[data_a.size() - 14];
	trans.pkt_len = trans.unpack_bytes(data_a) / 8; //将data_a中byte流转换成transaction中的各个字段	
endtask

注意:

  • byte流必须要用动态数组来转换和保存;
  • byte流会根据field automation中域定义的顺序来打包和解包byte流;

为了更加明显的感受到byte流的便利,让大家看一下我之前写的很蠢的办法,惨痛教训。
monitor中的采包,如下:
在这里插入图片描述
transaction中的封包,如下:
在这里插入图片描述
说实话,我最近被这个 field_automation 中的pack_bytes 坑死了,因为经过UVM的封装,灵活性就大大降低了,在使用过程中也会有诸多不便(痛苦面具,这里就不细说了)。然后我将代码改了一版,采用SV中的流操作符(>> 或 <<)进行 pack 和 unpack,极大地提高了代码的灵活性,可以对任意长度的pkg_len的包进行组合。实现如下:
首先driver发包时,将transaction中的数据打包成byte流,保存在一个stream_q队列中:
在这里插入图片描述
其次monitor 收包时,将byte流还原到一个结构体中,再还原到对应的各个栏位中:
在这里插入图片描述在这里插入图片描述

2. 宏与if相结合

这是白皮书总结的用法,我觉得挺实用,在这里也进行一下总结。可以先在transaction中预留一个使能位,如果需要打开这个使能,可以再随机时指定这个使能位的值,选择性的打开或关闭功能。如下:

typedef enum { RANDOM,INCR,FIXED } payload_kind
class gmii_rx_transaction extends uvm_sequence_item;
	rand bit [2:0]  spa;
	rand bit [47:0] dmac;
	rand bit [47:0] smac;
	rand bit [15:0] ether_type;
	rand bit [7:0]  payload[];
	
	//用于组合 payload
	rand int        pkt_len; 
	rand bit [7:0]  first_data; 
	rand payload_kind kind; 

	//控制使能vlan帧
	rand  bit       is_vlan;
	rand  bit[15:0] vlan_info1;
	rand  bit[2:0]  vlan_info2;
	
	`uvm_object_utils_begin(gmii_rx_transaction)
		`uvm_field_int(sap,UVM_ALL_ON | UVM_NOPACK)
		`uvm_field_int(dmac,UVM_ALL_ON)
		`uvm_field_int(smac,UVM_ALL_ON)
		
		if(is_vlan)begin
			`uvm_field_int(vlan_info1,UVM_ALL_ON)
			`uvm_field_int(vlan_info2,UVM_ALL_ON)
		end
		
		`uvm_field_int(ether_type,UVM_ALL_ON)
		`uvm_field_array_int(payload,UVM_ALL_ON)
		`uvm_field_int(pkt_len,UVM_ALL_ON | UVM_NOPACK) 
		`uvm_field_int(first_data,UVM_ALL_ON | UVM_NOPACK)
		`uvm_field_enum(payload_kind,kind,UVM_ALL_ON | UVM_NOPACK)  		
	`uvm_object_utils_end
	...
endclass

二、Component类

首先注册的方式,如下:

	`uvm_component_utils_begin(my_component)
		`uvm_field_int(data,UVM_ALL_ON )
		...
	`uvm_component_utils_end

1. 从config_db中自动get值

在component中使用域的自动化的目的与Objec类有很大区别,其很重要的一个功能在于:经过component类中域的自动化注册后,并且在build_phase中调用了super.build_phase(phase),就可以省略config_db#(T)::get( )获取到变量。如下:

class gmii_rx_driver extends uvm_monitor#(gmii_rx_transaction);
	bit mon_enable;
	`uvm_component_utils_begin(my_component)
		`uvm_field_int(mon_enable,UVM_ALL_ON )
	`uvm_component_utils_end
	
	function new(string name = "gmii_rx_driver",uvm_component patrent = null);
		super.new(name,parent);
		mon_enable = 1;
	endfunction

	virtual function void build( );
		super.build( );
		//uvm_config_db#(int)::get(this,"","mon_enable",mon_enable);  //这段话可以省略
		`uvm_info("gmii_rx_monitor",$sformatf("mon_enable is %0d",mon_enable),UVM_HIGH)
	endfunction

	virtual task run( );
		if(mon_enable)begin
			mon_trans( );
		end
	endtask
endclass

此时monitor中的mon_enable已经在通过域自动化注册,此时如果从顶层set,在monitor就不需要get了。如下:

class base_test extends uvm_test;
	...
	virtual function void build( );
		super.build( );
		//向monitor 发送mon_enable变量
		uvm_config_db#(int)::set(this,"env.agt.mon","mon_enable",0);  
	endfunction
	...
endclass

2.与域的自动化相关函数

,首先component类是在UVM的树形结构中,所以相应地函数上有所区别。如下:

  • clone无法使用,因为parent参数没办法指定;
  • copy可以使用,但要注意在相同parent下的实例名不能相同;
  • 其他的区别不大。
  • 10
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小verifier

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值