UVM实战 卷I学习笔记9——UVM中的sequence(2)

目录

    • *sequencer的lock操作
    • *sequencer的grab操作
    • sequence的有效性


*sequencer的lock操作

当多个sequence在一个sequencer上同时启动时,每个sequence产生的transaction都需要参与sequencer的仲裁。考虑这样一种情况,某个sequence一旦要执行,那么它所有transaction必须连续地交给driver,如果中间夹杂着其他sequence的transaction就会发生错误。要解决这个问题,可以像上节一样对此sequence赋予较高的优先级。但假如有其他sequence有更高的优先级呢?所以这种解决方法并不科学。

在UVM中可使用lock操作来解决这个问题:lock就是sequence向sequencer发送一个请求,这个请求与其他sequence发送transaction的请求一同被放入sequencer的仲裁队列中。当前面所有的请求被处理完毕后,sequencer开始响应这个lock请求,此后sequencer会一直连续发送这个sequence的transaction直到unlock操作被调用。从效果上看,此sequencer的所有权并没有被所有的sequence共享,而是被申请lock操作的sequence独占。使用lock操作的sequence为:

class sequence1 extends uvm_sequence #(my_transaction);
	…
	virtual task body();repeat (3) begin
			`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
			`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
		end
		lock(); //notice
		`uvm_info("sequence1", "locked the sequencer ", UVM_MEDIUM)
		repeat (4) begin
			`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
			`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
		end
		`uvm_info("sequence1", "unlocked the sequencer ", UVM_MEDIUM)
		unlock(); //notice
		repeat (3) begin
			`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
			`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
		end
		…
	endtask
	…
endclass
class sequence0 extends uvm_sequence #(my_transaction);
	…
	virtual task body();repeat (5) begin
		`uvm_do(m_trans)
		`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
	end
	#100;
	…
	endtask
	`uvm_object_utils(sequence0)
endclass

在my_sequencer上同时启动了两个sequence: sequence1和sequence2:

task my_case0::main_phase(uvm_phase phase);
	sequence0 seq0;
	sequence1 seq1;
	seq0 = new("seq0");
	seq0.starting_phase = phase;
	seq1 = new("seq1");
	seq1.starting_phase = phase;
	fork
		seq0.start(env.i_agt.sqr);
		seq1.start(env.i_agt.sqr);
	join
endtask

将此sequence1与sequence0在env.i_agt.sqr上启动,会发现在lock语句前,sequence0和seuquence1会交替产生transaction;在lock语句后,一直发送sequence1的transaction直到unlock语句被调用,sequence0和seuquence1又开始交替产生transaction。

如果两个sequence都试图使用lock任务来获取sequencer的所有权会如何呢?答案是先获得所有权的sequence在执行完毕后才会将所有权交还给另外一个sequence

class sequence0 extends uvm_sequence #(my_transaction);
	…
	virtual task body();repeat (2) begin
			`uvm_do(m_trans)
			`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
		end
		lock();
		repeat (5) begin
			`uvm_do(m_trans)
			`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
		end
		unlock();
		repeat (2) begin
			`uvm_do(m_trans)
			`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
		end
		#100;
		…
	endtask
	`uvm_object_utils(sequence0)
endclass

将sequence0与sequence1同时在env.i_agt.sqr上启动,会发现sequence0先获得sequencer所有权,在unlock函数被调用前一直发送sequence0的transaction。在unlock被调用后,sequence1获得sequencer的所有权,之后一直发送sequence1的transaction,直到unlock函数被调用。

*sequencer的grab操作

与lock操作一样,grab操作也用于暂时拥有sequencer的所有权,只是grab操作比lock操作优先级更高lock请求是被插入sequencer仲裁队列的最后面,等到它时其前面的仲裁请求都已经结束了。grab请求则被放入sequencer仲裁队列的最前面,它几乎是一发出就拥有了sequencer的所有权

class sequence1 extends uvm_sequence #(my_transaction);
	…
	virtual task body();repeat (3) begin
		`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
		`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
	end
	grab(); //notice
	`uvm_info("sequence1", "grab the sequencer ", UVM_MEDIUM)
	repeat (4) begin
		`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
		`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
	end
	`uvm_info("sequence1", "ungrab the sequencer ", UVM_MEDIUM)
	ungrab(); //notice
	repeat (3) begin
		`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
		`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
	end
	…
	endtask
	`uvm_object_utils(sequence1)
endclass

如果两个sequence同时试图使用grab任务获取sequencer的所有权将会如何呢? 这种情况与两个sequence同时试图调用lock函数一样,先获得所有权的sequence执行完毕后才会将所有权交还给另外一个试图所有权的sequence

如果一个sequence在使用grab任务获取sequencer的所有权前,另一个sequence已经使用lock任务获得了sequencer的所有权会如何呢?答案是grab任务会一直等待lock的释放。grab任务还是比较讲文明的,虽然它会插队,但绝不会打断别人正在进行的事情。

sequence的有效性

当有多个sequence同时在一个sequencer上启动时,所有的sequence都参与仲裁,根据算法决定哪个sequence发送transaction。仲裁算法是由sequencer决定的,sequence除了可以在优先级上进行设置外,对仲裁的结果无能为力

通过lock任务和grab任务,sequence可以独占sequencer,强行使sequencer发送自己产生的transaction。UVM也提供措施使sequence可在一定时间内不参与仲裁,即令此sequence失效

sequencer在仲裁时,会查看sequence的is_relevant函数的返回结果如果为1,说明此sequence有效,否则无效。因此可通过重载is_relevant函数来使sequence失效:

class sequence0 extends uvm_sequence #(my_transaction);
	my_transaction m_trans;
	int num;
	bit has_delayed;
	…
	virtual function bit is_relevant(); //notice
		if((num >= 3)&&(!has_delayed)) return 0;
		else return 1;
	endfunction
	virtual task body();
		…
		fork
			repeat (10) begin
				num++;
				`uvm_do(m_trans)
				`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
			end
			while(1) begin
				if(!has_delayed) begin //发送3个transaction后变为无效
					if(num >= 3) begin
					`uvm_info("sequence0", "begin to delay", UVM_MEDIUM)
					#500000;
					has_delayed = 1'b1;
					`uvm_info("sequence0", "end delay", UVM_MEDIUM)
					break;
					end
				else
					#1000;
			end
		end
	join
	…
	endtask
	…
endclass

这个sequence在发送了3个transaction后开始变为无效,延时500000时间单位后又开始有效。 将此sequence与sequence1同时启动,会发现在失效前sequence0和sequence1交替发送transaction;而在失效的500000时间单位内,只有sequence1发送transaction;当sequence0重新变有效后,sequence0和sequence1又开始交替发送transaction。某种程度上, is_relevant与grab任务和lock任务是完全相反的。通过设置is_relevant,可使sequence主动放弃sequencer的使用权,而grab任务和lock任务则强占sequencer的所有权

除了is_relevant外,sequence中还有一个任务wait_for_relevant也与sequence的有效性相关:

class sequence0 extends uvm_sequence #(my_transaction);
	…
	virtual function bit is_relevant();
		if((num >= 3)&&(!has_delayed)) return 0;
		else return 1;
		endfunction
	virtual task wait_for_relevant();
		#10000;
		has_delayed = 1;
	endtask
	virtual task body();repeat (10) begin
			num++;
			`uvm_do(m_trans)
			`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
		end
	…
	endtask
	…
endclass

当sequencer发现在其上启动的所有sequence都无效时,此时会调用wait_for_relevant并等待sequence变有效。当此sequence与sequence1同时启动并发送3个transaction后,sequence0变为无效。此后sequencer一直发送sequence1的transaction直到全部transaction都发送完毕。此时sequencer发现sequence0无效,会调用其wait_for_relevant。换言之sequence0失效是自己控制,但重新变得有效却是受其他sequence控制。如果其他sequence永远不结束,sequence0将永远处于失效状态。这段sequence0代码与前面sequence0的区别是,前面代码中sequence0并不是等待sequence1的transaction全部发送完毕,而是主动控制着自己何时有效何时无效。

在wait_for_relevant中必须将使sequence无效的条件清除。上面代码中假如wait_for_relevant只是如下定义:

virtual task wait_for_relevant();
	#10000;
endtask

那么当wait_for_relevant返回后,sequencer会继续调用sequence0的is_relevant,发现依然是无效状态,则继续调用wait_for_relevant。系统会处于死循环的状态。

在前例的sequence0中,通过控制延时(500000)单位时间使sequence0重新变得有效。假如这段时间内sequence1的transaction发送完毕后,而sequence0中又没有重载wait_for_relevant任务,那么将会给出如下错误提示:

UVM_FATAL @ 1166700: uvm_test_top.env.i_agt.sqr@@seq0 [RELMSM] is_relevant()
was implemented without defining wait_for_relevant()

因此,is_relevant与wait_for_relevant一般应成对重载,不能只重载其中的一个。 前例中的sequence0没有重载wait_for_relevant是因为设置了延时,可保证不会调用到wait_for_relevant。 在使用时应该重载wait_for_relevant这个任务。

你可能感兴趣的:(UVM实战卷I,学习笔记,测试用例)