1.sequence挂载sequencer
item对象的生命周期:开始于sequence的body()方法,经历了随机化并穿越sequencer最终到达driver,直到被driver消化后,它的生命一般才结束
class flat_seq extends uvm_sequence;
...
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);
void'($cast(rsp, tmp));
endtask
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);
flat_seq seq;
phase.raise_objection(phase);
seq = new();
seq.start(e.sqr);
phase.drop_objection(phase);
endtask
endclass
在这段例码中,主要使用两种方法:
1.针对将item挂载到sequence上的应用:
uvm_sequence::start_item(uvm_sequence_item item, int set_priority = -1, uvm_sequencer_base aequencer = null)
//应用
start_item(req)
- 第一个参数:item对象
- 第二个参数:优先级
- 第三个参数:指定item和其parent sequence挂载到sequencer是否是一个,默认相同
uvm_sequence::finish_item(uvm_sequence_item, int set_priority = -1)
//应用
finish_item(req)
- 第一个参数:item对象
- 第二个参数:优先级
2.针对将sequence挂载到sequencer上的应用:
uvm_sequence::start(uvm_sequencer_base sequencer, uvm_sequencer_base parent_sequence = null, int this_priority = -1, bit call_pre_post = 1)
//应用
seq.start(e.sqr)
- 第一个参数:sequencer的句柄
- 第二个参数:上层sequence
- 第三个参数:优先级
- 第四个参数:建议使用默认值指定pre_body()和post_body()执行次序
通过uvm_sequence::start()
来挂载root sequence
,而在内部的child sequence
则可以通过宏``uvm_do`来实现
2. sequencer 仲裁特性
class lock_seq extends uvm_sequence;
...
task body();
bus_trans req;
#10ns;
m_sequence.lock(this);
repeat(3) #10ns `uvm_do_with(req, {data inside {[100:100]};})
m_sequence.unlock(this);
endtask
endclass
class grap_seq extends uvm_sequence;
...
task body();
bus_trans req;
#10ns;
m_sequence.grap(this);
repeat(3) #10ns `uvm_do_with(req, {data inside {[200:200]};})
m_sequence.ungrap(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
uvm_sequence 自定义变量m_sequence
设置仲裁模式:
uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)
- 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的请求被优先授权
锁定机制:lock()和grab()区别
- lock():
当sequencer按照仲裁机制授权给该sequence,一旦该sequence拿到授权,就不会将授权返回。只有当sequence执行 **unlock()** 时,才会释放这一锁定的权限
- grab():
grab()操作比lock()操作优先级要高,grab请求被放入sequencer仲裁队列的最前面,它几乎是一发出就拥有了sequencer的所有权,当已经有其它sequence获得授权时,grab()就无法获得授权
注意:
如果sequence使用了lock()
或者grab()
方法,必须在sequence结束前调用unlock()
或者ungrab()
方法来释放权限,否则sequencer会进入死锁状态而无法继续为其余sequence授权
。