sequence的启动
1,default_sequence 的方式启动
使用uvm_config_db#(uvm_object_wrapper)配置default_sequence ,代码如下:
function void testcase0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.agent.main_phase",
"default_sequence",
testcase0_sequence::type_id::get());
endfunction
// 或者如下的方式:
function void testcase0::build_phase(uvm_phase phase);
super.build_phase(phsae);
my_sequence test0_sequence;
testcase0_sequence = my_sequence::type_id::create("testcase0_sequence");
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.agent.main_phase",
"default_sequence",
testcase0_sequence);
endfunction
2,通过start函数直接启动
我们先看start函数的uvm源码:
// Task: start
//
// Executes this sequence, returning when the sequence has completed.
//
// The ~sequencer~ argument specifies the sequencer on which to run this
// sequence. The sequencer must be compatible with the sequence.
//
// If ~parent_sequence~ is null, then this sequence is a root parent,
// otherwise it is a child of ~parent_sequence~. The ~parent_sequence~'s
// pre_do, mid_do, and post_do methods will be called during the execution
// of this sequence.
//
// By default, the ~priority~ of a sequence
// is the priority of its parent sequence.
// If it is a root sequence, its default priority is 100.
// A different priority may be specified by ~this_priority~.
// Higher numbers indicate higher priority.
//
// If ~call_pre_post~ is set to 1 (default), then the <pre_body> and
// <post_body> tasks will be called before and after the sequence
// <body> is called.
virtual task start (uvm_sequencer_base sequencer,
uvm_sequence_base parent_sequence = null,
int this_priority = -1,
bit call_pre_post = 1);
可以看出该函数有4个参数:
1,sequencer,sequence需要在这个sequencer上启动。
2,parent_sequence,如果start函数这个参数为null或者不配置,说明sequence没有parent,那么就不会调用parent的pre_do,mid_do和post_do的方法(有parent,也可以设为null)。
3,priority,sequence的执行优先级,默认值为100。
4,call_pre_post 如果设置为1,那么pre_body 和 post_body 都会被执行
也就是说如果start函数形式为:
seq.start(inst.seqr,null,-1,1);
(至于如何在sequence上用start函数启动其他的sequence,还是要找到这个sequence的parent_sequencer即可,我会在后面的文档中说明这种用法,这种玩法,其实还是在利用uvm源码中的函数在搞事情,其实用封装好的task或者uvm宏是够用的,但是有些人就是要与众不同)
因此,对于用start启动的sequence,设置起来就相当的灵活,可以在“任何”地方(UVM树的结点上)启动这个start 函数,比如说在testcase的 main_phase:
task case0:main_phase(uvm_phase phase);
my_sequence testcase0_sequence;
testcase0_sequence = my_sequence:type_id::create(testcase0_sequence);
testcase0_sequence.start(uvm_test_top.env.agent.seqr);
endtask
这种方式也可以在sequencer上启动,只是在启动时,需要重新设置sequencer的路径。
sequence内部函数的调用
1,do函数的使用
class sequence0 extends uvm_sequence #(my_transaction);
virtual task body();
repeat (seq_num) begin
`uvm_do(m_trans)
`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
end
endtask
`uvm_object_utils(sequence0)
endclass
seq_num 可以通过config_db的方式,从testcase上set到 sequence0上,sequence0再通过get方法送给seq_num。如果有兴趣看了uvm_sequence_defines.svh这个文件,你将会看到所有的带uvm_do的宏,其实到最后都是调用了uvm_do_on_pri_with这个宏,是不是很神奇,其实这才是正确的做法,对于新需求只扩展自旧的东西,不修改旧的东西!
2,start_item,finish_item的使用
我们先进一段源码:
//
//|
//| sequencer.wait_for_grant(prior) (task) \ start_item \
//| parent_seq.pre_do(1) (task) / \
//| `uvm_do* macros
//| parent_seq.mid_do(item) (func) \ /
//| sequencer.send_request(item) (func) \finish_item /
//| sequencer.wait_for_item_done() (task) /
//| parent_seq.post_do(item) (func) /
也就是说,start_item 和 finish_item完全可以代替uvm_do宏(这里不算完全替换,具体为什么请看下面的解释),但是这里忽略了一个问题,在使用这两个函数之前,必须要对seq_item进行实例化,所以我们能看到下面这段源码,代码如下:
// MACRO: `uvm_do
//
//| `uvm_do(SEQ_OR_ITEM)
//
// This macro takes as an argument a uvm_sequence_item variable or object.
// The argument is created using <`uvm_create> if necessary,
// then randomized.
// In the case of an item, it is randomized after the call to
// <uvm_sequence_base::start_item()> returns.
// This is called late-randomization.
// In the case of a sequence, the sub-sequence is started using
// <uvm_sequence_base::start()> with ~call_pre_post~ set to 0.
// In the case of an item,
// the item is sent to the driver through the associated sequencer.
//
// For a sequence item, the following are called, in order
//
//|
//| `uvm_create(item)
//| sequencer.wait_for_grant(prior) (task)
//| this.pre_do(1) (task)
//| item.randomize()
//| this.mid_do(item) (func)
//| sequencer.send_request(item) (func)
//| sequencer.wait_for_item_done() (task)
//| this.post_do(item) (func)
//|
从这里我们能看到,uvm_do宏其实就是 uvm_create 宏 + start_itme +finish_item+随机化item的组合,或者说是由uvm_create宏 + 6个task和函数+随机化item组成的,在源码上看到是先调用了uvm_create 这个宏,然后这才是下面的这些方法和task,至于如何使用start_item和finish_item如下代码:
class case0_sequence extends uvm_sequence #(my_transaction);
virtual task body();
repeat (seq_num) begin
my_transaction tr;
tr = new("tr"); // 也可以采用 type_id::create的方式
//assert(tr.randomize() with {tr.pload.size == 200;}); //指定随机方式
assert(tr.randomize());
start_item(tr);
finish_item(tr);
end
endtask
endclass
3,uvm_create 和 uvm_send 宏的使用
同样我们先进一段源码:
// MACRO: `uvm_create
//
//| `uvm_create(SEQ_OR_ITEM)
//
// This action creates the item or sequence using the factory. It intentionally
// does zero processing. After this action completes, the user can manually set
// values, manipulate rand_mode and constraint_mode, etc.
`define uvm_create(SEQ_OR_ITEM) \
`uvm_create_on(SEQ_OR_ITEM, m_sequencer)
// MACRO: `uvm_create_on
//
//| `uvm_create_on(SEQ_OR_ITEM, SEQR)
//
// This is the same as <`uvm_create> except that it also sets the parent sequence
// to the sequence in which the macro is invoked, and it sets the sequencer to
// the specified ~SEQR~ argument.
`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
begin \
uvm_object_wrapper w_; \
w_ = SEQ_OR_ITEM.get_type(); \
$cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
end
一方面,uvm_create宏其实是套用了uvm_create_on宏的实现,uvm_create_on宏,内部来看其实就是创建了一个新的item。
// MACRO: `uvm_send
//
//| `uvm_send(SEQ_OR_ITEM)
//
// This macro processes the item or sequence that has been created using
// `uvm_create. The processing is done without randomization. Essentially, an
// `uvm_do without the create or randomization.
`define uvm_send(SEQ_OR_ITEM) \
`uvm_send_pri(SEQ_OR_ITEM, -1)
// MACRO: `uvm_send_pri
//
//| `uvm_send_pri(SEQ_OR_ITEM, PRIORITY)
//
// This is the same as `uvm_send except that the sequene item or sequence is
// executed with the priority specified in the argument.
`define uvm_send_pri(SEQ_OR_ITEM, PRIORITY) \
begin \
uvm_sequence_base __seq; \
if (!$cast(__seq,SEQ_OR_ITEM)) begin \
start_item(SEQ_OR_ITEM, PRIORITY);\
finish_item(SEQ_OR_ITEM, PRIORITY);\
end \
else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
end
同样的uvm_send 宏套用了uvm_send_pri宏,而uvm_send_pri宏却到最后调用了start_item和finish_item这两个方法,
也就是说,跟start_item ,finish_itme相比,uvm_create ,uvm_send 多了构造 seq_item的过程,同样都缺少了随机化seq_item的过程,因此对于uvm_create宏的使用方式如下:
class case0_sequence extends uvm_sequence #(my_transaction);
virtual task body();
my_transaction tr;
repeat (seq_item) begin
`uvm_create(tr)
assert(tr.randomize());
`uvm_send(tr)
end
endtask
endclass
在这之后,还有uvm_rand_send宏,直接把随机化的过程也补上了,但是对于需要用randomize() with {constraints} 的方式还是需要用到uvm_create宏+randomize+uvm_send。
关于如何在sequence上启动其他sequence的问题,这与启动seq_item的方式基本类似,这里就不展开讲了,可以参考张强的《UVM实战》或者直接在源码里面翻,都是好办法。
最后关于uvm_sequence_defines.svh 这个文件的阅读,我们通过学习这个文件,能够看到很多宏的实现办法,虽然这些宏在形式上不同,但是最后调用的却是同一个东西,还是要认真阅读源码,这样不仅能够更清晰的认识UVM方法的实现而且能够巩固我们从书上或者项目中学习到的用法,了解背后的原理,函数原型,调用关系,知其然也知其所以然,从根本上掌握UVM。