目录
1.概述
为什么要有sequence机制,要将数据输入到平台中怎么输入sequence,sequence,sequencer,driver之间的通信过程又是如何?
首先,无论是sequence还是driver,它们的通话对象都是sequencer。当多个sequence试图挂载到一个sequencer上时,涉及sequencer的仲裁功能,之后会详谈,先略过。重点分析sequencer作为sequence和driver之间的握手桥梁,是如何扮演这个角色的。如图所示
对于sequence而言,无论是flat sequence还是hierarchical sequence,进一步切分的话,流向sequencer的都是sequence item,所以就每个item的成长周期来看,它起始于creat_item(),继而通过start_item()尝试从sequencer获取可以通过的权限。
对于sequencer的仲裁机制和使用方法先略过,而driver一侧一直处于"吃不饱"状态,如果它没有item可以使用,则使用get_next_item()来尝试从sequencer一侧来获取item。
sequencer将通过权限交给某一个sequence前,目标sequence中的item应完成随机化,继而在获取sequencer通过权限之后执行finish_item()。
接下来sequence中的item将通过sequencer到达driver一侧
driver得到新的item后,会提取有效的数据信息,将其驱动到与DUT连接的接口上
完成驱动后,driver应该通过item_done()来告知sequence已完成了数据传送,sequence在获取消息之后,则表示driver与sequence双方完成了这一次的item握手传输。
2.sequence
2.1sequence的启动与执行
举例:一个常见的sequence应包含body()任务,当一个sequence启动后会自动调用body任务
class sequence1 extends uvm_sequence #(my_transaction);
...
virutal task pre_body();
...
endtask
virtual task body();
...
//发送数据
endtask
virtual task post_body();
...
endtask
`uvm_object_utils(sequence1)
endclass
有两种方式可以启动sequence,直接启动(利用start任务),和间接启动(利用default_sequence启动)
sequence1 my_seq;
my_seq=sequence1::type_id::creat("my_seq");
my_seq.start(sequencer);//直接启动
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase",/
"defult_sequence",sequence1::type_id::get());//间接启动
2.2sequence相关宏
1)uvm_do系列宏主要有八个,其他7个宏都是用uvm_do_on_pri_with宏来实现的,举个例子:
`uvm_do_on_pri_with(SEQ_OR_ITEM,m_sequencer,-1,{})第一个参数是transaction的指针,第二个参数是sequencer的指针,第三个参数是优先级,第四个是约束,其余uvm_do系列宏的参数即在这个范围之中。
2)除了用uvm_do宏产生transaction,还可以用uvm_create宏和uvm_send宏来产生,uvm_create宏的作用是实例化transaction,当一个transaction被实例化之后可以对其做更多的处理,处理完毕之后使用uvm_send宏发送出去。举个例子:
class sequence1 extends uvm_sequence #(my_transaction)
..
virtual task body();
repeat(10)
begin
`uvm_create(m_trans)
assert(m_trans.randomize());
`uvm_send(m_trans)
end
endtask
...
endclass
除此之外,还有uvm_send_pri宏,它的作用是将transaction交给sequencer时设定优先级,同理还有uvm_rand_send宏,它还有着对transaction随机化的功能,这里不一一赘述。
2.3vitual sequence
为解决sequence之间的同步问题,最好的方法就是采用virtual sequence,virtual sequence不发送任何transaction,只是控制其他的sequence,起统一调度的作用。而类似地,virtual sequencer桥接着所有底层sequencer的句柄,其本身也不需要传递item,不需要和driver连接。
举个例子:
先定义三个扁平型sequence
class drv0_seq extends uvm_sequence #(my_transaction);
...
virtual task body();
repeat(10) begin
`uvm_do(m_trans)
end
endtask
endclass
class drv1_seq extends uvm_sequence #(my_transaction);
...
virtual task body();
repeat(10) begin
`uvm_do(m_trans)
end
endtask
endclass
class read_file_seq extends uvm_sequence #(my_transaction);
my_transaction m_trans;
string file_name;
...
endclass
再定义virtual sequence对他们进行调度
class case0_vseq extends uvm_sequence;
...
virtual task body();
my_transaction tr;
read_file_seq seq0;
drv1_seq seq1;
`uvm_do_with(tr,p_sequencer.p_sqr0,{})
seq0=new("seq0");
seq0.file_name="data.text";
seq1=new("seq1");
fork
seq0.start(p_sequencer.p_sqr0);
seq1.start(p_sequencer.p_sqr1);
join
..
endtask
endclass
这就解决了sequence的同步问题
2.4sequence library
所谓sequence library就是一系列sequence集合,sequence library派生自uvm_sequence,从本质上说它还是一个sequence。它根据特定的算法随机选择注册在其中的一些sequence,并在body中执行这些sequence。
1)定义一个sequence library
class simple_seq_library extends uvm_sequence_library #(my_transaction);
function new(string name="simple_sequence_library");
super.new(name);
init_sequence_library();
endfunction
`uvm_object_utils(simple_sequene_library)
`uvm_sequence_library_utils(simple_seq_library);
endclass
值得注意的是,从uvm_sequence派生的时候要指明sequence library产生的transaction类型;其次在new()函数之中要调用init_sequence_library();最后要调用uvm_sequence_library_utils注册。
2)将sequence加入sequence library中
class seq0 extends uvm_sequence #(my_transaction);
...
`uvm_object_utils(seq0)
`uvm_add_to_seq_lib(seq0,simple_seq_library)
virtual task body();
...
endtask
endclass
uvm_add_to_seq_lib有两个参数,第一个是sequence名字,第二个是要加入的sequence library的名字。一个sequence可以加入多个不同的sequence library之中,同样地,多个sequence也可以加入同一个sequence library之中。
3)将sequence library作为sequencer的default sequence,这样UVM会随机从加入simple_seq_library的sequence中随机选择并启动。
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
simple_seq_library::type_id::get());
endfunction
当然我们也可以控制sequence library的选择算法,它由selection_mode决定:UVM_SEQ_LIB_RAND就是完全随机,UVM_SEQ_LIB_RANDC就是周期性随机;UVM_SEQ_LIB_ITEM是sequence library不执行其sequence队列中的sequence,而是自己产生transaction;UVM_SEQ_LIB_USER是用户自定义算法。举个例子:
function void my_case0::build_phase(uvm_phase phase);
...
uvm_config_db#(uvm_sequence_lib_mode)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence.selection_mode",
UVM_SEQ_LIB_RANDC);
endfunction
我们还可以控制sequence library的执行次数,sequence library会在min_random_count和max_random_count之间随意选择一个数来作为执行次数,举个例子:
function void my_case0::build_phase(uvm_phase phase);
...
uvm_config_db#(int unsigned)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence.min_random_count",
5);
uvm_config_db#(int unsigned)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence.max_random_count",
20);
endfunction
上述的设置最多产生20个,最少产生5个transaction。
3.sequencer
3.1sequencer的仲裁机制
此前在上文中介绍了sequence的优先级概念。可以通过uvm_do_pri以及uvm_do_pri_with改变transaction的优先级,优先级越高越先执行。sequencer的仲裁机制有以下几种,我们可以通过uvm_sequencer::set_arbitration()来设置仲裁模式:
1)UVM_SEQ_ARB_FIFO:默认模式。按照FIFO先入先出方式依次授权,和优先级没有关系。
2)UVM_SEQ_ARB_WEIGHTED:按照它们的优先级随机授权
3)UVM_SEQ_ARB_RANDOM:无视抵达顺序和优先级,随机授权
4)UVM_SEQ_ARB_STRICT_FIFO:按照它们优先级和抵达顺序依次授权
5)UVM_SEQ_ARB_STRICT_RANDOM:按照它们最高优先级随机授权
6)UVM_SEQ_ARB_USER:用户自定义
3.2锁定机制
uvm_sequencer提供了两种锁定机制,分别通过lock()和grab()方法实现:
1)lock()和unlock()这对方法可以为sequence提供排外的访问权限,但前提条件是,该sequence首先要按照sequencer的仲裁机制获得权限。而一旦sequence获得权限,则无需担心被收回,只有该sequence主动unlock他的sequencer才可以释放这一锁定权限。
2)grab()和ungrab()也可以为sequence提供排外的访问权限,而且它只需要在sequencer下一次授权周期就可以无条件获取授权。与lock方法相比,grab方法无视同一时刻发起的传送请求的其他sequence。
3)需要注意的是,"解铃还须系铃人",如果sequence使用了lock或者grab方法,必须在sequence结束前调用unlock或者ungrab方法来释放权限,否则sequencer会进入死锁状态。
4.driver
task my_driver::main_phase(uvm_phase phase);
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
rsp=new("rsp");
rsp.set_id_info(req);
seq_item_port.put_response(rsp);
seq_item_port.item_done();
end
endtask
sequence机制提供反馈的支持,允许driver将一个response返回给sequence。