【学习记录丨UVM】1.10UVM的终极大作:sequence

在规范化的验证平台中,driver只负责驱动transaction,sequence机制用于产生激励。

sequence机制有两大组成部分,一是sequence,二是sequencer。

一、sequencer的定义

1. sequencer的介绍

class my_sequencer extends uvm_sequencer#(my_transaction);
  `uvm_component_utils(my_sequencer);//factory注册
  function new(string name,uvm_component parent)
    super.new(name,parent);
  endfunction
endclass

sequencer派生自uvm_sequencer。uvm_sequencer是一个参数化的类,其参数是my_transaction,即该sequencer产生的transaction类型。

sequencer负责产生transaction,driver负责接收transaction。

前边的例子中,my_driver直接派生自uvm_driver,【学习记录丨UVM】1.2驱动器driver(1)——最简单的driver及其实例化_uvm 一个driver驱动多个bus-优快云博客但是 正确格式应该如下,直接指明driver要驱动的transaction的类型。

不建议:class my_driver extends uvm_driver;
建议:  class my_driver extends uvm_driver #(my_transaction);

这样定义的好处是,可以直接使用uvm_driver中预先定义好的成员变量,比如说 #()传递给uvm_driver的参数类型:req,rsp. 可以直接用。req = new ("req") ;...

2. UVM树形结构

sequencer 和 driver 和 monitor 一起被封装在agent里。

...
my_sequencer sqr;
...
if(is_active == UVM_ACTIVE) begin
  sqr = my_sequencer::type_id::create("sqr",this);//在build_phase中实例化
  drv = ...
...

此时的UVM树形组织结构为:

二、sequence机制 

此时验证平台框图如下所示:

因为o_agt只是用来监测DUT的输出,不用提供激励,所以不必实例化driver和sequencer。如何控制是通过改变uvm_agent的成员变量is_active的值来实现的,在my_agent的build_phase中给is_active赋值。详见:【学习记录丨UVM】1.6代理人agent-优快云博客

sequence不属于验证平台的任何一部分。但是它和sequencer联系密切。sequence就像是一个弹夹,里面的子弹是transaction,而sequencer是一把枪。二者只有共同存在才有意义。 

1.一个sequence示例

class my_sequence extends uvm_sequence#(my_transaction);
  my_transaction m_trans;

  `uvm_object_utils(my_sequence)//factory注册
  function new(string name="my_sequence");
    super.new(name);
  endfunction

  virtual task body();//当一个sequence启动后会自动执行body()中的代码
    repeat(10)begin
      `uvm_do(m_trans)//调用宏产生事务对象,每调用一次产生一个
    end
    #1000;
  endtask
endclass
  1. sequence是一个参数化的类,派生自uvm_sequence,指定产生的transaction类型;
  2. 和transaction一样,sequence是一个uvm_object。所以用uvm_object_utils宏注册;
  3. 和transaction一样,sequence的new函数只有一个参数 name;
  4. sequence中最重要的是负责控制和产生transaction序列的body()任务;
  5. 一种sequence一般只产生一种类型的transaction。

2. 如何发送sequence中的transaction给验证平台

一个sequence在向sequencer发送transaction之前,要先给sequencer发出一个请求,sequencer把这个请求放在一个仲裁队列里。(这个以后再做介绍)

① sequencer的职能 

作为sequencer,它需要做两件事:第一,检查仲裁队列里是否有某个sequence发送transaction的请求;第二,检测driver是否申请transaction。

  1. 商品已准备好,等你来买;你来了,我就让供货商给我送来一个卖给你。——如果仲裁队列里有发送请求,但是driver没有申请transaction,那么sequencer将会处于等待driver的状态,直到driver申请新的transaction。——此时sequencer同意sequence的发送请求,sequence就产生一个transaction交给sequencer,再由sequencer交给driver。
  2. 顾客driver来买了,但是供货商sequence还没准备好,那我代销店sequencer就等下供货商,一旦ok,就让供货商给我送来一个卖给你。
  3. 顾客也来了(意思是driver在向sequencer请求给个transaction),供货商也有货(sequence请求发送transaction),那直接让(让:就是同意sequence的请求)送来卖给你。

 ② driver如何向sequencer请求transaction呢?

1. driver和sequencer之间要有通信通道

在uvm_driver中有成员变量seq_item_port,UVM源码如下所示:里边写到了seq_item_port就是用于向sequencer请求发送transaction。

在uvm_sequencer里有成员变量seq_item_export。这两者之间就可以建立一个通道,其中传递的transaction类型就是在定义my_sequencer和my_driver时指定的transaction类型。

那么这个通道如何连接呢?其实UVM在uvm_driver里边介绍了如何进行driver和sequencer之间接口连接:driver.seq_item_port.connect(sequencer.seq_item_export)

现在sequencer和driver,monitor都封装在agent里,我们在my_agent的connect_phase里进行通道连接即可:记住这个就好了√

function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  if(is_active=UVM_ACTIVE)begin
    drv.seq_item_port.connect(sqr.seq_item_export);
  end
endfunction
2. 通道已经建好了,怎么发送请求呢?

通道连接好之后,就可以在driver中通过get_next_item任务向sequencer申请新的transaction。

task my_driver::main_phase(uvm_phase phase);
  ...
  while(1)begin
    seq_item_port.get_next_item(req);
    drive_one_pkt(req);
    seq_item_port.item_done();
  end
endtask

如上,最显著的一个特征是使用了while(1)循环,因为driver只负责驱动transaction,而不负责产生,只要有transaction,driver就进行驱动。所以必须做成一个无限循环模式。

通过get_next_item任务来获得一个新的req(transaction),并且驱动它,驱动完成后调用item_done通知sequencer。这里为什么会有一个item_done呢?

答:当driver通过get_next_item获得一个transaction时,sequencer自己也保留一份刚刚发送出的tr..当出现sequencer发出了tr,但是driver并没有得到的情况时,sequencer会把保留的这份tr再发送出去。那么sequencer如何知道driver是否已经成功得到transaction呢?如果在下次调用get_next_item之前,item_done被调用,那么sequencer就认为driver已经得到了这个transaction,将会把自己保存的这份transaction删除。

这增加了可靠性。


ps: 其实除了get_next_item,也可以使用try_next_item。二者的区别在于:get_next_item是阻塞的,它会一直等到transaction才会返回;try_next_item则是非阻塞的,它尝试着问sequencer是否有新的transaction,如果有就拿走,否则就空手返回。


③sequence如何产生transaction,又怎么向sequencer发送?

1. 首先,sequence是通过`uvm_do()宏来产生transaction的。调用一次,产生一个。

uvm_do宏什么时候会返回呢?

答:当uvm_do宏产生一个transaction交给sequencer后被driver取走后,uvm_do()并不会立即返回,而是等待在那里,直到driver返回item_done信号。此时uvm_do宏才算是执行完毕,返回后开始执行下一个uvm_do.

2. 那么第二个问题,sequence要如何给sequencer送出transaction呢?

前边已经定义了sequence,我们只要在某个component的main_phase中启动这个sequence即可。以my_env为例:

task my_env::main_phase(uvm_phase phase);
  my_sequence seq;//声明sequence
  phase.raise_objection(this);//objection机制
  seq = my_sequence::type_id::create("seq");//注意这里没有参数this了
  seq.start(i_agt.sqr);
  phase.drop_objection(this);
endtask

首先创建一个my_sequence的实例seq,之后调用start任务。start任务的参数是一个sequencer指针,指明产生的transaction要交给哪个sequencer。

在UVM中,objection一般伴随着sequence,通常只有在sequence出现的地方才提起和撤销objection。

除了env,也可以在sequencer中启动sequence,唯一不同的就是start的指针变成this。

三、default_sequence的使用

 刚才我们是在my_env和my_sequencer的main_phase中通过seq.start(xx.sqr);方式手动启动sequence的。在实际应用中,有另外一种方式启动sequence:default_sequence。

1. 如何使用default_sequence?

使用default_sequence的方法:在某个component的build_phase中设置以下代码即可。示例:

1.在env中:

function void my_env::build_phase(uvm_phase phase);
  super.build_phase(phase);
  ...
  uvm_config_db#(uvm_object_wrapper)::set(this,
                                          "i_agt.sqr.main_phase",
                                          "default_sequence",
                                          my_sequence::type_id::get());
endfunction

2.在agent中: 

function void my_agent::build_phase(uvm_phase phase);
  super.build_phase(phase);
  ...
  uvm_config_db#(uvm_object_wrapper)::set(this,
                                          "sqr.main_phase",
                                          "default_sequence",
                                          my_sequence::type_id::get());
endfunction

3. 在top_tb中:

module top_tb;
  ...
  initial begin
     uvm_config_db#(uvm_object_wrapper)::set(null,
                                            "uvm_test_top.i_agt.sqr.main_phase",
                                            "default_sequence",
                                            my_sequence::type_id::get());
  end
endmodule

 看上边的3个示例,我们可以发现代码都是:uvm_config_db#(uvm_object_wrapper)::set(x,"xx","default_sequence",my_sequence::type_id::get());

  1. uvm_condig_db#()是一个参数化的类,其参数(括号里的)就是要寄信的类型。这里是UVM规定好的,我们只要记住这里的参数是uvm_object_wrapper即可;
  2. 以上三个示例,set函数的第一二个参数不一样:第二个参数是相对于第一个参数的相对路径。env和agent都是class,所以可以使用指针this,第二个参数设置成针对当前this指针的相对路径即可。但是top_tb并不是class,不能够使用this,所以第一个参数是null,第二个参数就是"uvm_test_top.i_agt.sqr.main_phase".
  3. config_db通常是成对出现,但这里我们不必考虑get。UVM自己规定好了。

 2. default_sequence和objection机制

前边提到,在sequence出现的地方会提起和撤销objection,以保证任务按照预期执行。在seq.start(xx.sqr)的手动启动sequence方式中,前后出现了phase.raise_objection(this)和phase.drop_objection(this). 

但是在现在使用default_sequence之后,要怎么控制验证平台的开启关闭呢?还是用objection机制吗?要怎么使用呢?

答:在uvm_sequence中,有uvm_phase starting_phase。在sequencer启动default_sequence时,会自动做出以下操作:

task my_sequencer::main_phase(uvm_phase phase);
  ...
  seq.starting_phase=phase;
  seq.start(this);
  ...
endtask

一旦启动default_sequence,my_sequence的starting_phase就不为空, 然后就seq.start()让sequence产生transaction。 所以starting_phase 的状态(是否为空)可以作为判断是否提objection的标志。

我们把objection从my_sequencer这样的component中转移到sequence里:starting_phase不为空->需要raise_objection -> 允许sequence产生transaction->结束drop_objection.

class my_sequence extends uvm_sequence#(my_transaction);
  my_transaction m_trans;
  `uvm_object_utils(my_sequence)
  ...
  virtual task body();
    if(starting_phase != null)
      starting_phase.raise_objection(this);
    repeat(10) begin
      `uvm_do(m_trans)
    end
    #1000;
    if(starting_phase != null)
      starting_phase.drop_objection(this);
  endtask
endclass

差别就在 原来sequence无条件产生transaction,只要objection后seq.start();但是现在“无条件start”(只要启动default_sequence),不过把“sequence产生transaction”这个动作放在objection条件后。

 现在,objection和sequence完全关联在了一起,在其他地方都不必再设置objection。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值