Sequencer和Sequence

一、sequence和item发送实例

class bus_trans extends uvm_sequence_item;
	rand int data;
	`uvm_object_utils_begin(bus_trans)
		`uvm_field_int(data, UVM_ALL_ON)
	`uvm_object_utils_end
	...
endclass

class child_seq extends uvm_sequence;
	`uvm_object_utils(child_seq);
	...

	task body();
		uvm_sequence_item tmp;
		bus_trans req, rsp;
		tmp = create_item(bus_trans::get_type(), m_sequencer, "req");
		void'($cast(req, tmp));
		start_item(req);
		req.randomize with {data == 10;};
		finish_item(req);
	endtask
endclass

class top_seq extends uvm_sequence;
	`uvm_object_utils(top_seq)
	...
	
	task body();
		uvm_sequence_item tmp;
		child_seq cseq;
		bus_trans req;
		//create child sequence and items
		cseq = child_seq::type_id::create("cseq");
		tmp = create_item(bus_trans.get_type(), m_sequencer, "req");
		//send child sequence via start()
		cseq.start(m_sequencer, this);
		//send sequence item
		void'($cast(req, tmp));
		start_item(req);
		req.randomize with {data == 20;};
		finish_item(req);
	endtask
endclass


class sequencer extends uvm_sequencer;
	`uvm_component_utils(sequencer)
	...
endclass

class driver extends uvm_driver;
	`uvm_component_utils(driver)
	...
	
	
	task run_phase(uvm_phase phase);
		REQ tmp;
		bus_trans req;
		forever begin
			seq_item_port.get_next_item(tmp);
			void'($cast(req, tmp));
			`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)
			seq_item_port.item_done();
		end
	endtask
endclass

class env extends uvm_env;
	sequencer sqr;
	driver drv;
	`uvm_component_utils(env)
	...
	
	function void build_phase(uvm_phase phase);
		sqr = sequencer::type_id::create("sqr", this);
		drv = driver::type_id::create("drv", this);
	endfunction
	
	function void connect_phase(uvm_phase phase);
		drv.seq_item_port.connect(sqr.seq_item_export);
	endfunction
endclass

class test1 extends uvm_test;
	env e;
	`uvm_component_utils(test1)
	...
	function void build_phase(uvm_phase phase);
		e = env::type_id::create("e", this);
	endfunction
	task run_phase(uvm_phase phase);
		top_seq seq;
		phase.raise_objection(phase);
		seq = new();
		seq.start(e.sqr);
		phase.drop_objection(phase);
	endtask
endclass

二、发送sequence/item方法解析

在这段代码中,主要使用了两种方法,第一个方法是针对将sequence挂载到sequencer上的应用。

uvm_sequence::start(uvm_sequencer_base_sequence, uvm_sequence_base_parent_sequence=null, int this_priority=-1, bit call_pre_post=1)

//在使用该方法的过程中,首先应该指明sequencer的句柄,如果该sequence是顶部的sequence,即没有更上层的sequence嵌套它,则它可以省略对第二个参数parent_sequence的指定

//第三个参数的默认值是-1,会使得该sequence如果有parent_sequence会继承其优先级值,如果它是顶部(root)sequence,则其优先级会被自动设定为100。

//第四个参数默认值为1,默认uvm_sequence::pre_body()和uvm_sequence::post_body()两个方法会在uvm_sequence::body()的前后执行

示例中child_seq被嵌套到top_seq中,继而在挂载时需要指定parent_sequence,而在test一层调用top_seq时,由于它是root sequence,则不需要再指定parent sequence

第二种发送方法是针对item挂载到sequencer上的应用。

uvm_sequence::start_item(uvm_sequence_item item, int set_priority=-1, uvm_sequencer_base_sequence=null);
uvm_sequence::finish_item(uvm_sequence_item, int set_priority=-1);

//对于start_item(),第三个参数需要注意是否将item挂载到“非当前parent sequence挂载的sequencer”上面,即如果将item和其parent sequence挂载到不同的sequencer上面,就需要指定这个参数。

对于一个item的完整传送,sequence要在sequencer一侧获得通过权限,才可以顺利将item发送至driver。拆解这些步骤如下:

  • 创建item
  • 通过start_item()方法等待获得sequencer的授权许可,其后执行parent sequence的方法pre_do()
  • item进行随机化处理。
  • 通过finish_item()方法在对item进行了随机化处理之后,执行parent sequencemid_do(),以及调用uvm_sequencer::send_request()uvm_sequencer::wait_for_item_done()来将item发送至sequencer再完成与driver之间的握手。最后执行了parent_sequence的post_do()

这些完整的细节有两个部分需要注意:

  • 第一,sequenceitem自身的优先级,可以决定什么时刻可以获取sequencer的授权。
  • 第二,parent sequence的虚方法pre_do()mid_do()post_do()会发生在发送item的过程中间。

对比start()方法和start_item()/finish_item(),首先要分清它们面向的挂载对象是不同的。在执行start()过程中,默认情况下会执行sequencepre_body()post_body(),但是如果start()的参数call_pre_post=0,那么就不会这样执行。

start()方法的源代码如下:

sub_seq.pre_start()				(task)
sub_seq.pre_body()				(task)		if call_pre_post=1
	parent_seq.pre_do(0)		(task)		if parent_sequence!=null
	parent_seq.mid_do(this)		(func)		if parent_sequence!=null
sub_seq.body()					(task)		//your stimulus code
	parent_seq.post_do(this)()	(func)		if parent_sequence!=null
sub_seq.post_body()				(task)		if call_pre_post=1
sub_seq.post_start()			(task)		

start_item()/finish_item()源代码如下:
在这里插入图片描述

三、发送序列的相关宏

在这里插入图片描述
通过这些sequence/item宏,可以使用'uvm_do'uvm_do_with来发送无论是sequence还是item。这种不区分对象是sequence还是item方式带来了不少便捷。不同的宏,可能会包含创建对象的过程,也可能不会创建对象。例如'uvm_do'uvm_do_with会创建对象,而'uvm_send则不会创建对象,也不会将对象做随机处理,因此要了解它们各自包含的执行内容和顺序。

四、序列宏的示例

class child_seq extends uvm_sequence;
	...
	task body();
		bus_trans req;
		`uvm_create(req)
		`uvm_rand_send_with(req, {data == 10;})
	endtask
endclass

class top_seq extends uvm_sequence;
	...
	task body();
		child_seq cseq;
		bus_trans req;
		//send child sequence via start()
		`uvm_do(cseq)
		//send sequence item
		`uvm_do_with(req, {data == 20;})
	endtask
endclass
  • 无论sequence处于什么层次,都应该让sequencetest结束前执行完毕,还应该保留出一部分时间供DUT将所有发送的激励处理完毕,进入空闲状态才可以结束测试。
  • 尽量避免使用fork_join_any或者fork_join_none来控制sequence的发送顺序。因此如果想终止在后台运行的sequence线程而简单使用disable方式,就可能在不恰当的时间点上锁住sequencer。一旦sequencer被锁住而又无法释放,接下来也就无法发送其它sequence,尽量在发送完item完成握手之后再终止sequence。如果要使用fork_join方式,应该确保有方法可以让sequence线程在满足一些条件后停止发送item,否则只要有一个sequence线程无法停止,则整个fork_join无法退出。

五、Sequencer的仲裁

在这里插入图片描述
uvm_sequencer类自建了仲裁机制用来保证多个sequence在同时挂载到sequencer时,可以按照仲裁规则允许特定sequence中的item优先通过。在实际使用中,可以通过uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)函数来设置仲裁模式,这里的仲裁模式UVM_SEQ_ARB_TYPE 有下面几种值可以选择:

  • UVM_SEQ_ARB_FIFO:默认模式。来自于sequences的发送请求,按照FIFO先进先出的方式被依次授权,和优先级没有关系。
  • UVM_SEQ_ARB_WEIGHTED:不同sequence的发送请求,将按照它们的优先级权重随机授权。
  • UVM_SEQ_ARB_RANDOM:不同的请求会被随机授权,而无视它们抵达顺序和优先级。
  • UVM_SEQ_ARB_STRICT_FIFO:不同的请求,会按照它们的优先级以及抵达顺序来依次授权,与优先级和抵达时间都有关系。
  • UVM_SEQ_ARB_STRICT_RANDOM:不同的请求,会按照它们的最高优先级随机授权,与抵达时间无关。
  • UVM_SEQ_ARB_USER:可以自定义仲裁方法user_priority_arbitration()来裁定哪个sequence的请求被优先授权。

六、Sequencer的仲裁示例

class top_seq extends uvm_sequence;
	...
	task body();
		child_seq seq1, seq2, seq3;
		m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
		fork
			`uvm_do_pri_with(seq1, 500, {base == 10;})
			`uvm_do_pri_with(seq2, 500, {base == 20;})
			`uvm_do_pri_with(seq3, 300, {base == 30;})
		join
	endtask
endclass

class sequencer extends uvm_sequencer;
	...
endclass

class bus_trans extends uvm_sequence_item;
	rand int data;
	...
endclass

class child_seq extends uvm_sequence;
	rand int base;
	task body();
		bus_trans req;
		repeat(2) `uvm_do_with(req, {data inside {[base:base+9]};})
	endtask
endclass

class driver extends uvm_driver;
	...
	
	task run_phase(uvm_phase phase);
		REQ tmp;
		bus_trans req;
		forever begin
			seq_item_port.get_next_item(tmp);
			void'($cast(req, tmp));
			`uvm_info("DRV", $sformatf("got a item %0d from parent sequence %s", req.data, req.get_parent_sequence().get_name()), UVM_LOW)
			seq_item_port.item_done();
		end
	endtask
endclass

class env extends uvm_env;
	sequencer sqr;
	driver drv;
	...
	function void build_phase(uvm_phase phase);
		sqr = sequencer::type_id::create("sqr", this);
		drv = driver::type_id::create("drv", this);
	endfunction
	
	function void connect_phase(uvm_phase phase);
		drv.seq_item_port.connect(sqr.seq_item_export);
	endfunction
endclass

class test1 extends uvm_test;
	env e;
	`uvm_component_utils(test1)
	...
	function void build_phase(uvm_phase phase);
		e = env::type_id::create("e", this);
	endfunction
	task run_phase(uvm_phase phase);
		top_seq seq;
		phase.raise_objection(phase);
		seq = new();
		seq.start(e.sqr);
		phase.drop_objection(phase);
	endtask
endclass

输出结果:
在这里插入图片描述
seq1seq2seq3在同一时刻发起传送请求,通过'uvm_do_prio_with的宏,在发送sequence时可以传递优先级参数。由于将seq1seq2设置为同样的高优先级,而seq3设置为较低的优先级,这样在随后的UVM_SEQ_ARB_STRICT_FIFO仲裁模式下,可以从输出结果看到,按照优先级高低和传送请求时间顺序,先将seq1seq2中的item发送完毕,随后将seq3发送完。除了sequence遵循仲裁机制,在一些特殊情况下,有一些sequence需要有更高权限取得sequencer的授权来访问driver。例如在需要响应中断的情况下,用于处理中断的sequence应该有更高的权限来获得sequencer的授权。

七、Sequencer的锁定机制

uvm_sequencer提供了两种锁定机制,分别通过lock()grab()方法实现,这两种的方法区别在于:

  • lock()unlock()这一对方法可以为sequence提供排外的访问权限,但前提条件是,该sequence首先需要按照sequencer的仲裁机制获得授权。而一旦sequence获得授权,则无需担心权限被收回,只有该sequence主动解锁它的sequencer,才可以释放这一锁定的权限,lock()是一种阻塞任务,只有获得了权限才会返回。
  • grab()ungrab()也可以为sequence提供排外的访问权限,而且它只需要在sequencer下一次授权周期时就可以无条件地获得权限。与lock方法相比,grab方法无视同一时刻内发起传送请求的其它sequence,而唯一可以阻止它的只有已经预先获得授权的其它lock或者grabsequence
  • 如果sequence使用了lock()或者grab()方法,必须在sequence结束前调用unlock()或者ungrab()方法来释放权限,否则sequencer会进入死锁状态而无法继续为其余sequence授权。

示例

class bus_trans extends uvm_sequence_item;
	...
endclass

class child_seq extends uvm_sequence;
	...
endclass

class lock_seq extends uvm_sequence;
	...
	task body();
		bus_trans req;
		#10ns;
		m_sequencer.lock(this);
		`uvm_info("LOCK", "get exclusive access by lock()", UVM_LOW)
		repeat(3) #10ns `uvm_do_with(req, {data inside {[100:110]};})
		m_sequencer.unlock(this);
	endtask
endclass

class grab_seq extends uvm_sequence;
	...
	task body();
		bus_trans req;
		#20ns;
		m_sequencer.grab(this);
		`uvm_info("LOCK", "get exclusive access by grab()", UVM_LOW)
		repeat(3) #10ns `uvm_do_with(req, {data inside {[200:210]};})
		m_sequencer.ungrab(this);
	endtask
endclass

class top_seq extends uvm_sequence;
	...
	task body();
		child_seq seq1, seq2, seq3;
		lock_seq locks;
		grab_seq grabs;
		m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
		fork
			`uvm_do_pri_with(seq1, 500, {base == 10;})
			`uvm_do_pri_with(seq2, 500, {base == 20;})
			`uvm_do_pri_with(seq3, 300, {base == 30;})
			`uvm_do_pri(locks, 300)
			`uvm_do(grabs)
		join
	endtask
endclass

输出结果:
在这里插入图片描述
对于sequence locks,在10ns时它跟其它几个sequence一同向sequencer发起请求,按照仲裁模式,sequencer先后授权给seq1seq2seq3,最后才授权给locks。而locks在获得授权之后,就可以一直享有权限而无需担心权限被sequencer收回,locks结束前,需要通过unlock()方法返还权限。
对于sequence grabs,尽管在20ns时就发起了请求权限(实际上seq1seq2seq3也在同一时刻发起了权限请求),而由于权限已经被locks占用,所以它也无权收回权限。因此只有当locks40ns结束时,grabs才可以在sequencer没有被锁定的状态下获得权限,而grabs在此条件下获取权限是无视同一时刻发起请求的其它sequence的。同样的在grabs结束前,也应当通过ungrab()方法释放权限,防止sequencer的死锁行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值