sequence的启动方式和sequence内部函数的调用

sequence的启动

1,default_sequence 的方式启动

使用uvm_config_db#(uvm_object_wrapper)配置default_sequence ,代码如下:


function void testcase0::build_phase(uvm_phase phase);
    super.build_phase(phase);
    uvm_config_db#(uvm_object_wrapper)::set(this,
                                            "env.agent.main_phase",
                                            "default_sequence",
                                            testcase0_sequence::type_id::get());
endfunction

// 或者如下的方式:

function void testcase0::build_phase(uvm_phase phase);
    super.build_phase(phsae);
    my_sequence test0_sequence;
    
    testcase0_sequence = my_sequence::type_id::create("testcase0_sequence");

    uvm_config_db#(uvm_object_wrapper)::set(this,
                                            "env.agent.main_phase",
                                            "default_sequence",
                                            testcase0_sequence);
endfunction

2,通过start函数直接启动

我们先看start函数的uvm源码:

  // Task: start
  //
  // Executes this sequence, returning when the sequence has completed.
  //
  // The ~sequencer~ argument specifies the sequencer on which to run this
  // sequence. The sequencer must be compatible with the sequence.
  //
  // If ~parent_sequence~ is null, then this sequence is a root parent,
  // otherwise it is a child of ~parent_sequence~. The ~parent_sequence~'s
  // pre_do, mid_do, and post_do methods will be called during the execution
  // of this sequence.
  //
  // By default, the ~priority~ of a sequence
  // is the priority of its parent sequence.
  // If it is a root sequence, its default priority is 100.
  // A different priority may be specified by ~this_priority~.
  // Higher numbers indicate higher priority.
  //
  // If ~call_pre_post~ is set to 1 (default), then the <pre_body> and
  // <post_body> tasks will be called before and after the sequence
  // <body> is called.

  virtual task start (uvm_sequencer_base sequencer,
                      uvm_sequence_base parent_sequence = null,
                      int this_priority = -1,
                      bit call_pre_post = 1);

可以看出该函数有4个参数:

1,sequencer,sequence需要在这个sequencer上启动。

2,parent_sequence,如果start函数这个参数为null或者不配置,说明sequence没有parent,那么就不会调用parent的pre_do,mid_do和post_do的方法(有parent,也可以设为null)。

3,priority,sequence的执行优先级,默认值为100。

4,call_pre_post 如果设置为1,那么pre_body 和 post_body 都会被执行

也就是说如果start函数形式为:

seq.start(inst.seqr,null,-1,1);

(至于如何在sequence上用start函数启动其他的sequence,还是要找到这个sequence的parent_sequencer即可,我会在后面的文档中说明这种用法,这种玩法,其实还是在利用uvm源码中的函数在搞事情,其实用封装好的task或者uvm宏是够用的,但是有些人就是要与众不同)

因此,对于用start启动的sequence,设置起来就相当的灵活,可以在“任何”地方(UVM树的结点上)启动这个start 函数,比如说在testcase的 main_phase:

task case0:main_phase(uvm_phase phase);

    my_sequence testcase0_sequence;
    
    testcase0_sequence = my_sequence:type_id::create(testcase0_sequence);

    testcase0_sequence.start(uvm_test_top.env.agent.seqr);

endtask

这种方式也可以在sequencer上启动,只是在启动时,需要重新设置sequencer的路径。

sequence内部函数的调用

1,do函数的使用

class sequence0 extends uvm_sequence #(my_transaction);
	virtual task body();
		repeat (seq_num) begin
			`uvm_do(m_trans)
			`uvm_info("sequence0", "send one transaction", UVM_MEDIUM)
		end
	endtask
	`uvm_object_utils(sequence0)
endclass

seq_num 可以通过config_db的方式,从testcase上set到 sequence0上,sequence0再通过get方法送给seq_num。如果有兴趣看了uvm_sequence_defines.svh这个文件,你将会看到所有的带uvm_do的宏,其实到最后都是调用了uvm_do_on_pri_with这个宏,是不是很神奇,其实这才是正确的做法,对于新需求只扩展自旧的东西,不修改旧的东西!

2,start_item,finish_item的使用

我们先进一段源码:

//
//|
//|   sequencer.wait_for_grant(prior) (task) \ start_item  \
//|   parent_seq.pre_do(1)            (task) /              \
//|                                                      `uvm_do* macros
//|   parent_seq.mid_do(item)         (func) \              /
//|   sequencer.send_request(item)    (func)  \finish_item /
//|   sequencer.wait_for_item_done()  (task)  /
//|   parent_seq.post_do(item)        (func) /

也就是说,start_item 和 finish_item完全可以代替uvm_do宏(这里不算完全替换,具体为什么请看下面的解释),但是这里忽略了一个问题,在使用这两个函数之前,必须要对seq_item进行实例化,所以我们能看到下面这段源码,代码如下:

// MACRO: `uvm_do
//
//| `uvm_do(SEQ_OR_ITEM)
//
// This macro takes as an argument a uvm_sequence_item variable or object.
// The argument is created using <`uvm_create> if necessary,
// then randomized.
// In the case of an item, it is randomized after the call to
// <uvm_sequence_base::start_item()> returns.
// This is called late-randomization. 
// In the case of a sequence, the sub-sequence is started using
// <uvm_sequence_base::start()> with ~call_pre_post~ set to 0.
// In the case of an item,
// the item is sent to the driver through the associated sequencer.
//
// For a sequence item, the following are called, in order
//
//|
//|   `uvm_create(item)
//|   sequencer.wait_for_grant(prior) (task)
//|   this.pre_do(1)                  (task)
//|   item.randomize()
//|   this.mid_do(item)               (func)
//|   sequencer.send_request(item)    (func)
//|   sequencer.wait_for_item_done()  (task)
//|   this.post_do(item)              (func)
//|

从这里我们能看到,uvm_do宏其实就是 uvm_create 宏 + start_itme +finish_item+随机化item的组合,或者说是由uvm_create宏 + 6个task和函数+随机化item组成的,在源码上看到是先调用了uvm_create 这个宏,然后这才是下面的这些方法和task,至于如何使用start_item和finish_item如下代码:

class case0_sequence extends uvm_sequence #(my_transaction);
	virtual task body();
		repeat (seq_num) begin
			my_transaction tr;
            tr = new("tr"); // 也可以采用 type_id::create的方式
			//assert(tr.randomize() with {tr.pload.size == 200;}); //指定随机方式
            assert(tr.randomize());
			start_item(tr);
			finish_item(tr);
			end
	endtask
endclass

3,uvm_create 和 uvm_send 宏的使用

同样我们先进一段源码:

// MACRO: `uvm_create
//
//| `uvm_create(SEQ_OR_ITEM)
//
// This action creates the item or sequence using the factory.  It intentionally
// does zero processing.  After this action completes, the user can manually set
// values, manipulate rand_mode and constraint_mode, etc.

`define uvm_create(SEQ_OR_ITEM) \
  `uvm_create_on(SEQ_OR_ITEM, m_sequencer)



// MACRO: `uvm_create_on
//
//| `uvm_create_on(SEQ_OR_ITEM, SEQR)
//
// This is the same as <`uvm_create> except that it also sets the parent sequence
// to the sequence in which the macro is invoked, and it sets the sequencer to
// the specified ~SEQR~ argument.

`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
  begin \
  uvm_object_wrapper w_; \
  w_ = SEQ_OR_ITEM.get_type(); \
  $cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
  end

一方面,uvm_create宏其实是套用了uvm_create_on宏的实现,uvm_create_on宏,内部来看其实就是创建了一个新的item。

// MACRO: `uvm_send
//
//| `uvm_send(SEQ_OR_ITEM)
//
// This macro processes the item or sequence that has been created using
// `uvm_create.  The processing is done without randomization.  Essentially, an
// `uvm_do without the create or randomization.

`define uvm_send(SEQ_OR_ITEM) \
  `uvm_send_pri(SEQ_OR_ITEM, -1)
  

// MACRO: `uvm_send_pri
//
//| `uvm_send_pri(SEQ_OR_ITEM, PRIORITY)
//
// This is the same as `uvm_send except that the sequene item or sequence is
// executed with the priority specified in the argument.

`define uvm_send_pri(SEQ_OR_ITEM, PRIORITY) \
  begin \
  uvm_sequence_base __seq; \
  if (!$cast(__seq,SEQ_OR_ITEM)) begin \
     start_item(SEQ_OR_ITEM, PRIORITY);\
     finish_item(SEQ_OR_ITEM, PRIORITY);\
  end \
  else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
  end

同样的uvm_send 宏套用了uvm_send_pri宏,而uvm_send_pri宏却到最后调用了start_item和finish_item这两个方法,

也就是说,跟start_item ,finish_itme相比,uvm_create ,uvm_send 多了构造 seq_item的过程,同样都缺少了随机化seq_item的过程,因此对于uvm_create宏的使用方式如下:

class case0_sequence extends uvm_sequence #(my_transaction);
	virtual task body();
    my_transaction tr;

		repeat (seq_item) begin
			`uvm_create(tr)
			assert(tr.randomize());
			`uvm_send(tr)
		end
	endtask
endclass

在这之后,还有uvm_rand_send宏,直接把随机化的过程也补上了,但是对于需要用randomize() with {constraints} 的方式还是需要用到uvm_create宏+randomize+uvm_send。

关于如何在sequence上启动其他sequence的问题,这与启动seq_item的方式基本类似,这里就不展开讲了,可以参考张强的《UVM实战》或者直接在源码里面翻,都是好办法。

最后关于uvm_sequence_defines.svh 这个文件的阅读,我们通过学习这个文件,能够看到很多宏的实现办法,虽然这些宏在形式上不同,但是最后调用的却是同一个东西,还是要认真阅读源码,这样不仅能够更清晰的认识UVM方法的实现而且能够巩固我们从书上或者项目中学习到的用法,了解背后的原理,函数原型,调用关系,知其然也知其所以然,从根本上掌握UVM。

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
uvm中,sequence启动有两种方式:default_sequence启动方式和非default_sequence启动方式。 1. default_sequence启动方式:这种方式是最常用的启动sequence的方法,它会隐式地调用start函数启动sequence。在sequencer的main_phase中,可以使用以下代码启动sequence: ```verilog task my_sequencer::main_phase(phase); seq.starting_phase = phase; seq.start(this); endtask ``` 在sequence的body函数中,可以使用以下代码控制仿真时间并启动sequence: ```verilog task my_sequence::body(); if(starting_phase != null) starting_phase.raise_objection(this); // 执行sequence的内容 if(starting_phase != null) starting_phase.drop_objection(this); endtask ``` 2. 非default_sequence启动方式:在不使用default_sequence启动方式时,可以通过uvm_config_db来配置并启动sequence。以下是一个示例代码: ```verilog uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get()); ``` 上述代码将my_sequence作为default_sequence配置到env.i_agt.sqr.main_phase中,并在main_phase中启动sequence。 总结起来,uvm sequence启动方式可以通过default_sequence启动方式或者非default_sequence启动方式来实现。在具体的代码中,可以根据需要调用start函数或者使用uvm_config_db来配置并启动sequence。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [UVM启动sequence方法总结](https://blog.csdn.net/weixin_44969124/article/details/108249095)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【UVMsequence启动方式](https://blog.csdn.net/Holden_Liu/article/details/102757625)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值