UVM——sequence & item

此处sequence泛指uvm_sequence_item类,item泛指uvm_sequence_item类。
sequence用来实现激励生成和场景控制;item实现对激励所需要的具体数据和控制要求。

sequence item介绍

item基于uvm_object类,具备UVM核心基类所必要的数据操作方法,如copy(),clone(),compare(),record()等。通常将它们划分为以下类:
- 控制类。如总线协议上的读写类型、数据长度、传送模式等。
- 负载类。一般指数据总线上的数据包。
- 配置类。用来控制driver的驱动行为,例如命令driver的发送间隔或者有无错误插入。
- 调试类。用来标记一些额外信息方便调试,例如该对象的实例序号、创建时间、被driver解析的时间始末等。

//实例
class bus_trans extends uvm_sequence item;
	rand bit write;
	rand int data;
	rand int addr;
	rand int delay;
	static int id_num;
	`uvm_object_utils_begin(bus trans)
		`uvm_field_int...
	`uvm_object_utils_end
	...
endclass

class test1 extends uvm_test;
	`uvm_component_utils(test1)
	...
	task run_phase(uvm_phase phase);
		bus_trans t1, t2;
		phase.raise_objection(phase);
		#100ns;
		t1 = new("t1");
		t1.print;
		#200ns;
		t2 = new("t2");		//也可使用create进行例化,好处:可以实现override方法;可以建立层次关系。
		void`(t2.randomize());
		t2.print();
		phase.drop_objection(phase);
	endtask
endclass

item使用时的特点:
- 如果数据域需要用来做驱动,呢么用户应考虑定义为染得类型,同时按照驱动协议给出合适的constraint。
- 由于item本身的数据属性,为了充分利用UVM域声明的特性,建议将必要的数据成员都通过·uvm_field_xxx宏来声明,以便日后uvm_object的基本数据方法自动实现,例如上面的print()函数。
- t1没有被随机化而t2被随机化了,这种差别在item通往sequencer之前是很明显的。UVM要求item的创建和随机化都发生在sequence的body()任务中,而不是在sequencer或者driver中。
- 按照item对象的生命周期来区分,它的生命应该开始于sequence的body()方法,而后经历了随机化并穿越sequencer最终达到driver,直到被driver消化之后,他的生命一般来讲才结束。要注意在使用时,不能直接操作item对象,直接修改其中的数据,或者将它的句柄发送给其他组件使用,这会无形中修改item的数据,或者延长一个item对象的寿命。正确的方法是合理利用copy()和clone()等数据方法。

item与sequence的关系

一个sequence可以包含一些有序组织起来的item实例,考虑到item在创建后需要被随机化,sequence在声明时也需要预留一些可供外部随机化的变量,这些随机变量一部分是用来通过层级传递约束来最终控制item对象的随机变量,一部分是用老对item对象之间加以组织和时序控制的。通常将sequence分为以下几类:
- 扁平类(flat sequence):这一类往往只用来阻止更细小的粒度,即item实例构成的组织。
- 层次类(hierarchical sequence):这一类是由更高层的sequence用来组织底层的sequence,进而让这些sequence或者按照顺序的方式,或者按照并行的方式,挂载到同一个sequencer上。
- 虚拟类(virtual sequence):这一类则是最终控制整个测试场景的方式,鉴于整个环境中往往存在不同种类的sequencer和与其对应的sequence,我们需要一个虚拟的sequence来协调顶层的测试场景。之所以称这个方式为virtual sequence,是因为该序列本身并不会固定挂载于某一种sequencer类型上,而是将其内部不同类型sequence最终挂载到不同的目标sequencer上。

flat sequence

一个flat sequence往往由细小的sequence item群落构成,在此之上sequence还有更多的信息来完备它需要实现的激励场景。一般对于flat sequence而言,它包含的信息有:
- sequence item以及相关的constraint用来关联生成的item之间的关系,从而完善出一个flat sequence的时序形态。
- 除了限制sequence item的内容,各个item之间的时序信息也需要由flat sequence给定,例如何时生成下一个item并且发送至driver。
- 对于需要与driver握手的情况(例如读操作),或者等待monitor事件从而做出反应(例如slave的memory response数据响应操作),都需要sequence在收到另一侧组件的状态后,再决定下一步操作,即响应具体事件从而创建对应的item并且发送出去。

//实例1
class bus_trans extends uvm_sequence_item;
	rand bit write;
	rand int data;
	rand int addr;
	rand int delay;
	static int id_num;
	`uvm_object_utils_begin(bus_trans)
		`uvm_field_int...
	`uvm_object_utils_end
	...	
endclass

class flat_seq extends uvm_sequence;
	rand int length;
	rand int addr;
	rand int data[];
	rand bit writr;
	rand int delay;
	constraint cstr {
		data,size() == length;
		foreach(data[i]) soft data[i] == i;
		soft addr == 'h100;
		soft write == 1;
		delay inside {[1:5]};
	}
	`uvm_object_utils(flat_seq)
	...
	task body();
		bus_trans tmp;
		foreach(data[i]) begin
			tmp = new();
			tmp.randomize() with {data == local::data[i];
								addr == local::addr + i << 2;
								write == local::write;
								delay == local::delay;};
			tmp.print();
		end
	endtask
endclass

class test1 extends uvm_test:
	`uvm_component_utils(test1)
	...
	task run_phase(uvm_phase phase);
		flat_seq seq;
		phase.raise_objection(phase);
		seq = new();
		seq.randomize() with {addr == 'h200; length == 3;};
		seq.body();
		phase.drop_objection(phase);
	endtask
endclass

UVM——sequence & item_第1张图片

在上例中,没有使用sequence的宏或其他发送item的宏来实现sequence/item与sequencer之间的传送,而是用更直白的方式来描述层次关系。flat_seq类可以看作是一个更长的数据包,数据包的具体内容、长度、地址等信息都包含在flat_seq中。在生成item过程中,通过将自身随机变量作为constraint内容来限定item随机变量,这是flat sequence的大致处理方法。
上例中没有给出’uvm/'uvm_do_with/'uvm_create等宏是为了首先认清sequence与item之间的关系,因此该例也只给出在flat_seq::body()任务中创建和随机化item,而忽略了发送item。实际上bus_trans理应容纳更多的时序内容,而不应该只作为一次数据传输。作为数据传送的最小粒度,可以将它扩展到更大的数据和时间范围,从而减小数据通信和处理的成本,提高整体运行效率。

//实例2
class bus_trans extends uvm_sequence_item;		//与例1相比具有更大的颗粒度,将flat_seq的部分变量放到了bus_trans中
	rand bit write;
	rand int data[];
	rand int length;
	rand int addr;
	rand int delay;
	static int id_num;
	constraint cstr {
		data.siae() == length;
		foreach(data[i]) soft data[i] == i;
		soft addr == 'h100;
		soft write == 1;
		delay inside {[1:5]};
	}
	`uvm_object_utils_begin(bus_trans)
		`uvm_field_int...
	`uvm_object_utils_end
	...	
endclass

class flat_seq extends uvm_sequence;
	rand int length;
	rand int addr;
	`uvm_object_utils(flat_seq)
	...
	task body();
		bus_trans tmp;
		tmp = new();
		tmp.randomize() with {length == local::length;
								addr == local::addr;};
		tmp.print();
	endtask
endclass

class test1 extends uvm_test:
	`uvm_component_utils(test1)
	...
	task run_phase(uvm_phase phase);
		flat_seq seq;
		phase.raise_objection(phase);
		seq = new();
		seq.randomize() with {addr == 'h200; length == 3;};
		seq.body();
		phase.drop_objection(phase);
	endtask
endclass

UVM——sequence & item_第2张图片
我们可以将一段完整发生在数据传输中的、更长的数据都收编在一个bus_trans类中,提高这个item粒度的抽象层次,让上层的flat sequence在使用中更加方便。flat_seq类不在操作本不属于自己的数据内容,而只应该考虑这个数据包的长度、地址等信息,因为扩充随机数据的责任一般由item负责就够了。

hierarchical sequence

hierarchical sequence区别于flat sequence的地方在于,它可以使用其他sequence和item,能创建更丰富的激励场景。通过层次嵌套关系,可以让hierarchical sequence使用其他hierarchical sequence、flat sequence和sequence item,这意味着,如果低层的sequence item和flat sequence的粒度得当,那么用户就可以充分复用这些sequence/item来构成形式更加多样的hierarchical sequence。

//实例
class hier_seq extends uvm_sequence;
	`uvm_object_utils(hier_seq)
	function new(string name = "hier_seq");
		super.new(name);
	endfunction
	task body();
		bus_trans t1, t2;
		flat_seq s1, s2;
		`uvm_do_with(t1, {length == 2;})
		fork
			`uvm_do_with(s1, {length == 5;})
			`uvm_do_with(s2, {length == 8;})
		join
		`uvm_do_with(t2, {length == 3;})
	endtask
endclass

hier_seq::body()来看,它包含bus_trans t1,t2和flat_seqs1,s2,而它的层次关系就体现在对于各个sequence/item的协调上。上例中使用了’uvm_do_with宏,该宏完成了三个步骤:
- sequence/item的创建
- sequence/item的随机化
- sequence/item的传送
所谓的sequence复用就是使用’uvm_do_with宏,通过高层的sequence来嵌套底层的sequence/item,最后创建期望的场景。
在上例中既有串行的激励关系,也有并行的激励关系,而在更复杂的场景中,用户还可以考虑加入事件同步,或者一定的延迟关系来构成sequence/item之间的时序关系。

你可能感兴趣的:(UVM,其他,网络,java,开发语言)