UVM入门与进阶7

flat sequence

body自动调用,之所以没有run,是因为它不是component

 

hierarchical sequence

hierarchical sequence区别于flat_sequence的地方在于,它可以使用其他sequence,当然还有item,这么做是为了创建更丰富的激励场景

`uvm_do宏做的三件事:

创建seq/item

完成随机化

传送到seqr

class hier_seq extends uvm_sequence;
  `uvm_object_utils(hier_seq)
  function new(string name = "hier_seq");
    super.new(name);
  endfunction
  task body();
    bus_trans t1,t2;
    flat_seq s1,s2;
    `uvm_do_with(t1, {length == 2;})
    fork
       `uvm_do_with(s1, {length == 5;})     
       `uvm_do_with(s2, {length == 8;})     
    join
    `uvm_do_with(t2, {length == 3;})     
  endtask
endclass

 

sequencer与driver

 

sequencer没有req_fifo,但是有rsp_fifo,所以端口声明的时候,analysis_port为export

 

seq_item_pull_port方法:

task get_next_item()

function void item_done(input RSP rsp_arg=null)

task wait_for_sequences(): 往往与try_get_item联合使用

function bit has_do_available():

function void put_response():

 

由于继承,原先的这三个方法也都有

 

 

 

 1 class bus_trans extends uvm_sequence_item;
 2   rand int data;
 3   `uvm_object_utils_begin
 4      `uvm_field_int(data, UVM_ALL_ON)
 5   `uvm_object_utils_end
 6  7 endclass 
 8 class flat_seq extneds uvm_sequence;
 9   `uvm_object_utils(flat_seq)
10 11   task body();
12     uvm_sequence_item tmp;
13     bus_trans req, rsp;
14     tmp = create_item(bus_trans::get_type(), m_sequencer, "req");//这里不用create_item也可以,使用T::type_id::create()或new()也行
15     void'($cast(req, tmp));
16     strat_item(req);
17     req.randomize with {data == 10;};
18     `uvm_info("SEQ", $sformatf("sent a item \n %s",req.sprint()), UVM_LOW)
19     finish_item(req);
20     get_response(tmp);
21     void'($cast(rsp, tmp));
22     `uvm_info("SEQ", $sformatf("got  a item \n %s",rsp.sprint()), UVM_LOW)
23   endtask
24 endclass
25 
26 class sequencer extends uvm_sequencer;
27   `uvm_component_utils(sequencer)
28 29 enclass
30 class driver extends uvm_driver;
31   `uvm_component_utils(driver)
32 33   task run_phase(uvm_phase phase);
34     REQ tmp;
35     bus_trans req, rsp;
36     seq_item_port.get_next_item(tmp);
37     void'($cast(req, tmp));
38     `uvm_info("DRV", $sformatf("got  a item \n %s",rsp.sprint()), UVM_LOW)  
39     void'($cast(rsp, req.clone()));
40     rsp.set_sequence_id(req.get_sequence_id());
41     rsp.data +=10;
42     seq_item_port.item_done(rsp);
43     `uvm_info("DRV", $sformatf("sent  a item \n %s",rsp.sprint()), UVM_LOW)   
44   endtask
45 endclass
46 
47 class env extends uvm_env;
48   sequencer sqr;
49   driver drv;
50   `uvm_component_utils(env)
51 52   function void build_phase(uvm_phase phase);
53     sqr = sequencer::type_id::create("sqr", this);
54     drv = driver::type_id::create("drv",this);
55   endfunction 
56   function void connect_phase(uvm_phase phase);
57     drv.seq_item_port.connect(sqr.seq_item_export);
58   endfucntion
59 endclass
60 class test1 extends uvm_test;
61   env e;
62   `uvm_component_utils(test1)
63 64   function void build_phase(uvm_phase phase)
65     e = env::type_id::create("e", this);
66   endfunction 
67   task run_phase(uvm_phase phase);
68     flat_seq seq;
69     phase.raise_objection(phase);
70       seq = new();
71       seq.start(e.sqr); //将seq挂载到sqr上
72     phase.drop_objection(phase);
73   endtask
74 endclass

create_item: 创建了一个item,完成了两件事:

1.利用factory机制创建item

2.告知这个item挂载到指定的sequencer上,本例即m_sequencer

m_sequencer是uvm_sequence的成员变量

note:如果item没有挂载到sequencer上,使用start_item和finish_item的时候默认会将这个item挂载到这个sequence所挂载的sequencer上

15行的动态转换是为了访问子类中的成员变量进行constraint,但是如果一开始使用tmp.randomize(),使用父类句柄也可以随机化子类变量,因为randomize是虚方法

sqr仲裁的条件:

1.driver做get_next_item    2.要做start_item

然后选择对应的item,选择前做randomize

39行req.clone()返回的是句柄是uvm_object,为此需要转换

finish_item等到item_done的时候再release

当seq挂载到sqr上是,自身的body()将自动执行,挂载并不意味这随机化seq

上例没有指明driver拿到的类型,REQ的类型为默认的uvm_sequence_item

输出结果:

UVM_INFO @ 0: uvm_test_top.e.sqr@@flat_seq [SEQ] sent a item
…
UVM_INFO @ 0: uvm_test_top.e.drv [DRV] got a item
…
UVM_INFO @ 0: uvm_test_top.e.drv [DRV] sent a item
…
UVM_INFO @ 0: uvm_test_top.e.sqr@@flat_seq [SEQ] got a item
…

展示了从item定义,到sequence定义,最后到sequencer与driver的连接,即sequencer和driver之间的item传输过程,帮助理解传输过程的起点、各个节点以及终点

一旦理解了这其中的朴素原理,那么这两个组件之间的握手也就不再那么神秘了

对于理解driver从sequencer获取item,经过时序处理再返回给sequence的握手过程很有帮助

事务传输过程分析

在定义sequencer时,默认了REQ类型为uvm_sequence_item类型,这与稍后定义的driver时采取默认REQ类型保持一致

flat_seq作为动态创建的数据载体,它的主任务flat_seq::body()做了如下的几件事情:

  通过方法create_item()创建request item对象

  调用start_item()准备发送item

  在完成发送item之前对item进行随机处理

  调用finish_item()完成item发送  //start_item是立即返回的,finish_item才是阻塞的行为

  有必要的情况下可以从driver那里获取response item

在定义driver时,它的主任务driver::run_phase()也应通常做出如下处理:

  通过seq_item_port.get_next_item(REQ)从sequencer获取有效的request item

  从request item中获取数据,进而产生数据激励

  对request item进行克隆生成新的对象response item

  修改response item中的数据成员,最终通过seq_item_port.item_done(RSP)将response item 对象返回给sequence

对于uvm_sequence::get_response(RSP)和uvm_driver::item_done(RSP)这种成对的操作,是可选的而不是必须的,即用户可以选择uvm_driver不返回response item,同时sequence也无需获取response item

在高层环境中,应该在connect phase中完成driver到sequencer的TLM端口连接,比如例码在env::connect_phase()中通过drv.seq_item_port.connect(sqr.seq_item_export)完成了driver与sequencer之间的连接

在完成了flat_seq、sequencer、driver和env的定义之后,到了test1层,除了需要考虑挂起objection防止提前退出,便可以利用uvm_sequence类的方法uvm_sequence::start(SEQUENCER)来实现sequence到sequencer的挂载

通信时序

无论是sequence还是driver,他们通话的对象都是sequencer。当多个sequence试图挂载到同一个sequencer上时,涉及sequencer的仲裁功能

重点分析sequencer作为sequence和driver之间握手的桥梁,是如何扮演好这一角色的

我们将抽取去这三个类的主要方法,利用时间箭头演示出完整的TLM通信过程

 

posted on 2021-08-11 13:52  薛定谔's猫  阅读(660)  评论(0)    收藏  举报

导航