一、sequencer和sequence
sequencer是在sequence和driver之间传递数据的桥梁,这个数据信息就是sequence_item(可理解为my_transcation),sequence_item是一个子弹,sequence是弹夹,它的作用是装子弹,sequencer是一把枪,它的作用是把子弹射出去。sequencer是一个uvm_component,是验证平台运行过程中始终存在的,sequence是object组件,与my_transaction一样,sequence有其存在周期,比my_transaction要更长一些,当其中的transaction全部发送完毕后,它的存在周期也就结束了。
说明:
- export、port是TLM组件之间通信的通道
1、sequencer
sequencer就是充当激励环节的路由器的作用,即管理sequence,在某一个验证环境中 uvm_sequencer管理多个sequence,仲裁某一时刻传递哪个sequence的数据。
1、继承关系
说明:
- 所有的自定义sequencer都继承于uvm_sequencer#(REQ,RSP),(在源码中REQ=RSP,可以理解成AXI读操作,请求与响应在一个通道上面),uvm_push_sequence#(REQ,RSP)很少使用。
- 在其他父类中定义了库里面自带的一些变量、函数等操作。
- sequencer承载了sequence,sequence挂载在sequencer上才能发挥作用。
2、virtual sequencer
Virtual sequencer作用: 控制其他的sequencer,把多个sequener绑定起来,也是为了方便sequence的调度。Virtual sequencer并不和任何driver相连,Virtual sequencer本身并不处理item。
2、squence
1、transcation:就是要发送的数据和其他的一些信息,和UVM库里的uvm_sequence_item作用类似。
包含:域传输给DUT的数据和验证平台用到的一些控制信息等、约束、field_automation等其他的一些内容。
继承关系:
说明:
- 所有的自定义transaction(user_transaction)都继承于uvm_sequence_item。
2、sequence就是装transaction的容器,所有的自定义sequence都继承于uvm_sequence#(user_trans)。
说明:
- 由于sequence、transaction都是属于object类,存在周期较短,为了让他们能够在component组件之间传递,需要将sequence挂载在sequencer上才能发送给driver,再传给DUT。
3、virtual sequence
virtual sequence的作用就是负责对不同的sequence(可相同)去挂载到不同sequencer(可相同)上的顺序进行调度。
3、sequence的启动与挂载
启动:指就是通过哪种方式调用uvm_sequence中的body()函数,也可以理解为start函数调用。
sequence的挂载:它需要挂载在一个sequencer上,在这个sequencer的phase中被调用,同时uvm_sequence可以操作所挂载的sequencer的成员变量(比如:在virtual sequence中调用 virtual sequcner中其他 sequcner 句柄 )。
transaction的挂载:对于transaction,挂载的意思就是将transaction发送给所挂载的sequencer,sequencer再发送给连接的driver。
无论哪种启动方式最终目的是要把transaction传递给sequencer,主要是要传递信息。
一、sequence的启动有三种方式
1、default_sequence
`uvm_config_db#(uvm_object_wrapper)::set(this,"v_sqr.main_phase","default_sequence",virtual_sequence::type_id::get());
说明:
- 使用default_sequence启动sequence,会隐式得调用start函数,自动执行sequence的body、pre_body与post_body任务等,然后会把transcation自动传给sequencer。
- 验证平台的component组件运行,是按照phase机制运行的
- 通过default_sequence将virtual sequence指向那些实例化sequence挂载到了v_sqr并在其main_phase里面执行,v_sqr是virtual sequencer的实例化对象。
- default_sequence有两种定义方式,上述是常用的一种,另外一种方式是先实例化要启动的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
- 当通过default_sequence启动sequence时,uvm1.1中会自动给starting_phase赋值(uvm1.2使用了default_sequence,starting_phase也是空),starting_phase主要用于控制验证平台的运行,在sequence中控制仿真时间,因为sequence里面是发送的数据,当要发送的信息消耗完成后,就可以结束仿真了。
2、用start任务来启动sequence
class case_0 extends base_test;
`uvm_component_utils(case_0)
function new(string name = "case_0",uvm_component parent = null);
super.new ( name, parent);
endfunction
task main_phase( uvm_phase phase);
apb_sequence seq;
super.main_phase(phase);
seq =apb_sequence::type_id::create("seq");
seq.starting_phase = phase;
seq.start(apb_env.mst_agt.sqr);
`uvm_info(get_full_name(),$sformatf("end_case" ),UVM_INFO);
endtask
endclass
说明:
- 在sequence中显示调用strat()函数,第一个参数是需要挂载的sequencer;第二个是parent_sequence,一般传入this或者不传入;第三个是优先级;第四个call_pre_post默认为1,则自动执行pre_body/ post_body()函数。调用完这些函数用已经启动了sequence。
- 在start()函数中,首先调用函数set_item_context(),给成员变量m_sequencer, m_parent_sequence赋值。
- starting_phase需要手动赋值。
- 这里seq传入了apb_env.mst_agt.sqr,调用set_sequencer()函数完成挂载,并给m_sequencer赋值,m_sequencer就指向了此时传入的sqr。
宏 uvm_declare_p_sequencer
为什么要使用p_sequencer,为了在启动sequence、transaction时能够去访问sequencer里面的变量,因为sequence是object类,sequencer是component组件
举例:`uvm_declare_p_sequencer(apb_sequencer)
内部做了一个向下类型转换,m_sequencer,p_sequencer在库内部已经定义好了,用到了cast,m_sequencer是uvm_sequencer_base类型,它是uvm_sequencer的基类,但此时m_seuencer会指向类型为uvm_sequencer的实际sequencer(即apb_sequencer),此时p_sequencer就可以通过cast指向真正的squencer了,p_sequencer是m_sequencer的子类,p_sequencer的类型是uvm_sequencer;所以在使用start函数启动sequence时要保证start()函数传入的sequencer应该和宏 uvm_declare_p_sequencer声明的类型一致,在使用uvm_declare_p_sequencer后,就可以在sequence中调用挂载sequencer的成员函数和成员变量了。
3、用uvm_do宏来启动sequence
uvm_do系列所有宏都是由uvm_do_on_pri_with宏实现的
例如:
`define uvm_do(SEQ_OR_ITEM) `uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, {})
`uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)
说明:
- SEQ_OR_ITEM:第一个参数可以sequence也可以是item
- SEQR:指要挂载的sequencer
- PRIORITY:第三个是优先级
- CONSTRAINTS:第四个是约束
- 当在sequence中使用uvm_do等宏时,对于uvm_do宏而言,它默认的sequencer就是调用uvm_do宏的这个sequence在启动时指定的sequencer,当在sequence中使用uvm_do等宏时,他会去调用start函数,其默认的sequencer就是此sequence启动时为其指定的sequencer,最终会把ransaction传递给sequencer。
- 如果uvm_do宏传入的第一个参数不是sequence,就是transaction, 则调用start_item, finish_item函数,而不是调用start函数,start_item()三个参数,第一个是传入的transaction, 第二个是优先级,第三个是指定该transaction发挂载在哪一个sequencer上,如果之前没有给transaction的m_sequencer赋值,此处sequcner仍未null。
-
start_item用于获取sequencer的授权许可,finish_item将item发送至sequencer,进而完成与driver之间的交互。
-
class case0_sequence extends uvm_sequence #(my_transaction); my_transaction m_trans; function new(string name= "case0_sequence"); super.new(name); endfunction virtual task body(); if(starting_phase != null) starting_phase.raise_objection(this); repeat (10) begin `uvm_do(m_trans) `uvm_info("seq", $sformatf("get information from driver: %0s", m_trans.frm_drv), UVM_MEDIUM) end #100; if(starting_phase != null) starting_phase.drop_objection(this); endtask `uvm_object_utils(case0_sequence) endclass class my_case0 extends base_test; function new(string name = "my_case0", uvm_component parent = null); super.new(name,parent); endfunction extern virtual function void build_phase(uvm_phase phase); `uvm_component_utils(my_case0) endclass 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", case0_sequence::type_id::get()); endfunction
说明:
-
在case0_sequence类body函数中使用了`uvm_do(m_trans),这是一个item,此时并没有挂载,它的sequencer是null的,当后面default_sequence时,才去挂载到sqr上面的,并且在sqr的main_phase执行,然后把m_trans传递给sequencer。
- 下面是uvm_do宏参数是sequence或者sequence_item库内部所执行的操作
-
4、关系:
说明:
- 通过seq_item_port发送数据请求,seuquencer通过seq_item_export接收请求,再反馈给sequence,然后sequence收到请求后,创建req,req是源码默认创建例化好的类型是my_transcation类型或者uvm_sequence_iteme类型;此时sequence还要等待sequencer把其他发送数据都仲裁完了,到你了,sequencer再反馈给sequence,然后再进行req随机化,然后把数据依次传递给sequencer、driver,过程是通过TLM端口,然后driver收到数据再按协议等要求发送给DUT完成之后,就会发送item_done信号给sequence,sequence最后等到了这个已经完成的信号,就可以开始下一次的传输了