sequence基础
1.
如果将激励放在driver的main_phase中,是可行的,但是如果要对激励作修改,那么扩展性较差,所以我们将激励改为放在sequence中去写。
driver就负责驱动激励就好了,而具体是什么样的激励,应该由sequence去完成。
2.
当定义完一个sequence后,可以使用start
任务将其启动。
my_sequence my_seq;
my_seq = my_sequence::type_id::create("my_seq");
my_seq.start(sequencer);
除了上述直接启动之外,还可以使用default_sequence
启动。
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
case0_sequence::type_id::get());
还可以先实例化要启动的sequence,之后再通过default_sequence
启动:
function void my_case0::build_phase(uvm_phase phase);
case0_sequence cseq;
super.build_phase(phase);
cseq = new("cseq");
uvm_config_db#(uvm_sequence_base)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
cseq);
endfunction
当一个sequence
启动后会自动执行sequence
的body
任务。其实,除了body
外,还会自动调用sequence
的pre_body
与post_body
:
class case0_sequence extends uvm_sequence #(my_transaction);
virtual task pre_body();
`uvm_info("sequence0", "pre_body is called!!!", UVM_LOW)
endtask
virtual task post_body();
`uvm_info("sequence0", "post_body is called!!!", UVM_LOW)
endtask
virtual task body();
#100;
`uvm_info("sequence0", "body is called!!!", UVM_LOW)
endtask
`uvm_object_utils(case0_sequence)
endclass
上述的sequence
在执行时,会打印出:
# UVM_INFO my_case0.sv(11) @ 0: uvm_test_top.env.i_agt.sqr@@cseq [sequence0] pre_body is called!!!
# UVM_INFO my_case0.sv(22) @ 100000: uvm_test_top.env.i_agt.sqr@@cseq [sequence0] body is called!!!
# UVM_INFO my_case0.sv(15) @ 100000: uvm_test_top.env.i_agt.sqr@@cseq [sequence0] post_body is called
sequence的仲裁机制
1.
UVM支持在同一个sequencer上可以启动多个sequence。
在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
其中sequence0的定义为:
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
sequence1的定义为:
class sequence1 extends uvm_sequence #(my_transaction);
virtual task body();
repeat (5) begin
`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
end
#100;
endtask
`uvm_object_utils(sequence1)
endclass
运行如上代码后,会显示两个sequence交替产生transaction:
# UVM_INFO my_case0.sv(15) @ 85900: uvm_test_top.env.i_agt.sqr@@seq0 [sequence0] send one transaction
# UVM_INFO my_case0.sv(37) @ 112500: uvm_test_top.env.i_agt.sqr@@seq1 [sequence1] send one transaction
# UVM_INFO my_case0.sv(15) @ 149300: uvm_test_top.env.i_agt.sqr@@seq0 [sequence0] send one transaction
# UVM_INFO my_case0.sv(37) @ 200500: uvm_test_top.env.i_agt.sqr@@seq1 [sequence1] send one transaction
# UVM_INFO my_case0.sv(15) @ 380700: uvm_test_top.env.i_agt.sqr@@seq0 [sequence0] send one transaction
# UVM_INFO my_case0.sv(37) @ 436500: uvm_test_top.env.i_agt.sqr@@seq1 [sequence1] send one transaction
但这里存在sequencer的仲裁算法:
SEQ_ARB_FIFO,
SEQ_ARB_WEIGHTED,
SEQ_ARB_RANDOM,
SEQ_ARB_STRICT_FIFO,
SEQ_ARB_STRICT_RANDOM,
SEQ_ARB_USER
- 在默认情况下sequencer的仲裁算法是
SEQ_ARB_FIFO
。它会严格遵循先入先出的顺序,而不会考虑优先级。 SEQ_ARB_WEIGHTED
是加权的仲裁;SEQ_ARB_RANDOM
是完全随机选择;SEQ_ARB_STRICT_FIFO
是严格按照优先级的,当有多个同一优先级的sequence时,按照先入先出的顺序选择;SEQ_ARB_STRICT_RANDOM
是严格按照优先级的,当有多个同一优先级的sequence时,随机从最高优先级中选择;SEQ_ARB_USER
则是用户可以自定义一种新的仲裁算法。
因此,若想使优先级起作用,应该设置仲裁算法为SEQ_ARB_STRICT_FIFO
或者SEQ_ARB_STRICT_RANDOM
:
因此,my_case0代码改为如下:
task my_case0::main_phase(uvm_phase phase);
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);
fork
seq0.start(env.i_agt.sqr);
seq1.start(env.i_agt.sqr);
join
endtask
经过如上的设置后,会发现直到sequence1发送完transaction后,sequence0才开始发送。
除transaction有优先级外,sequence也有优先级的概念。可以在sequence启动时指定其优先级:
task my_case0::main_phase(uvm_phase phase);
env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);
fork
seq0.start(env.i_agt.sqr, null, 100);
seq1.start(env.i_agt.sqr, null, 200);
join
endtask
start任务的第一个参数是sequencer,第二个参数是parent sequence,可以设置为null,第三个参数是优先级,如果不指定则此值为-1,它同样不能设置为一个小于-1的数字。
运行上述代码,会发现sequence1中的transaction完全发送完后才发送sequence0中的transaction。
2.
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();
`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();
repeat (3) begin
`uvm_do_with(m_trans, {m_trans.pload.size < 500;})
`uvm_info("sequence1", "send one transaction", UVM_MEDIUM)
end
endtask
endclass
将此sequence1与代码清单6-10中的sequence0使用代码清单6-9的方式在env.i_agt.sqr上启动,会发现在lock语句前,sequence0和seuquence1交替产生transaction;在lo