[转]Sequencer和Sequence

  • Post author:
  • Post category:其他




一、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 sequence



    mid_do()

    ,以及调用

    uvm_sequencer::send_request()



    uvm_sequencer::wait_for_item_done()

    来将

    item

    发送至

    sequencer

    再完成与

    driver

    之间的握手。最后执行了

    parent_sequence的post_do()

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

  • 第一,

    sequence



    item

    自身的优先级,可以决定什么时刻可以获取

    sequencer

    的授权。
  • 第二,

    parent sequence

    的虚方法

    pre_do()



    mid_do()



    post_do()

    会发生在发送

    item

    的过程中间。

对比

start()

方法和

start_item()/finish_item()

,首先要分清它们面向的挂载对象是不同的。在执行

start()

过程中,默认情况下会执行

sequence



pre_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

    处于什么层次,都应该让

    sequence



    test

    结束前执行完毕,还应该保留出一部分时间供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

输出结果:

在这里插入图片描述


seq1



seq2



seq3

在同一时刻发起传送请求,通过

'uvm_do_prio_with

的宏,在发送

sequence

时可以传递优先级参数。由于将

seq1



seq2

设置为同样的高优先级,而

seq3

设置为较低的优先级,这样在随后的

UVM_SEQ_ARB_STRICT_FIFO

仲裁模式下,可以从输出结果看到,按照优先级高低和传送请求时间顺序,先将

seq1



seq2

中的

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

    或者

    grab



    sequence

  • 如果

    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

先后授权给

seq1



seq2



seq3

,最后才授权给

locks

。而

locks

在获得授权之后,就可以一直享有权限而无需担心权限被

sequencer

收回,

locks

结束前,需要通过

unlock()

方法返还权限。

对于

sequence grabs

,尽管在

20ns

时就发起了请求权限(实际上

seq1



seq2



seq3

也在同一时刻发起了权限请求),而由于权限已经被

locks

占用,所以它也无权收回权限。因此只有当

locks



40ns

结束时,

grabs

才可以在

sequencer

没有被锁定的状态下获得权限,而

grabs

在此条件下获取权限是无视同一时刻发起请求的其它

sequence

的。同样的在

grabs

结束前,也应当通过

ungrab()

方法释放权限,防止

sequencer

的死锁行为。

———————

作者:煎丶包

来源:CSDN

原文:https://blog.csdn.net/qq_39794062/article/details/114272496

版权声明:本文为作者原创文章,转载请附上博文链接!

内容解析By:

CSDN,CNBLOG博客文章一键转载插件