The UVM Primer -- chapter 23 UVM Sequence

chapter 23 UVM Sequence

此前,我们通过tester/put&get port/driver将激励和测试平台分割开来,但是我们并没有将data stimulus从structure中分离出来。tester需要创建新的transaction,并将其送至测试平台,这意味着tester需要选择transaction的顺序,并经其送至测试平台。我们可以通过override transaction type控制data的随机化,但是我们必须重写整个tester来改变transaction的发送数量,顺序等。一个好的测试平台需要将order of transaction(the stimulus)和测试平台组件testbench structure分离开来。

  • 我们通过创建三个类来实现此功能:
    • Fibonacci test:使用TinyALU来计算Fibonacci sequence;
    • Full Random test:使用约束的随机数据实现功能覆盖目标
    • Intleave test:Fibonacci & random test的随机执行
      此前,为了实现以上三个test我们需要完全重构tester,这样违背了代码重用性和易维护性,UVM sequence将stimulus和structure分割开来,这允许我们创建测试平台并运行不同的激励。我们接下来将使用UVM sequence将transaction-level的测试平台转成使用sequence的测试平台。

23.1 Transaction level -> sequence testbench

23.1.1 Create TinyALU Sequence Item

  • sequence_item和command_transaction的区别:
    • 扩展自uvm_seuqnce_item而不是uvm_transaction
    • 增加result变量,记录DUT计算的结果
    • 其余部分完全一致。
class sequence_item extends uvm_sequence_item;  //uvm_sequence_item扩展自uvm_transaction
   `uvm_object_utils(sequence_item);

   function new(string name = "");
      super.new(name);
   endfunction : new
   
   rand byte unsigned        A;
   rand byte unsigned        B;
   rand operation_t         op;
   shortint  unsigned       result;             //新增result 变量
   ......
endclass : sequence_item

23.1.2 Replace tester with uvm_sequencer

  • 在不适用uvm_agent的前提下,我们仍需要在env中例化各个组件。此时其中的tester需要负责两项功能,导致难以维护和重用。我们希望tester只是实现传递sequence items至driver,至于sequence items的顺序我们希望有另一个模块来负责,同时tester也将改名为sequencer,仅仅实现sequence items的传递功能。
  • sequencer从sequence中取出一系列的sequence items,并发送至driver,uvm提供了一个uvm_sequencer基类,我们直接使用其定义即可,如下图。
    The UVM Primer -- chapter 23 UVM Sequence_第1张图片
  • 使用typedef关键字定义sequencer为参数化的uvm_sequencer类。

23.1.3 Update driver to support sequence

  • UVM定义了基类uvm_driver(component)并且将其与uvm_sequencer关联起来,我们只需要扩展uvm_driver,并且重构run_phase即可。
  • 继承uvm_driver的时候,我们便继承了sequence_item_port,我们可以使用sequence_item_port从sequencer中获取sequence item。
class driver extends uvm_driver #(sequence_item);
   `uvm_component_utils(driver)

   virtual tinyalu_bfm bfm;
 
   function void build_phase(uvm_phase phase);
      if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
        `uvm_fatal("DRIVER", "Failed to get BFM")
   endfunction : build_phase

   task run_phase(uvm_phase phase);
      sequence_item cmd;

      forever begin : cmd_loop
         shortint unsigned result;            //get_next_item一直阻塞至sequencer将sequence item送入端口
         seq_item_port.get_next_item(cmd);    //和put/get_port、analysis port类似方式获取数据
         bfm.send_op(cmd.A, cmd.B, cmd.op, result);   
         cmd.result = result;
         seq_item_port.item_done();           //将数据通过send_op送到BFM,并将result数据传回
      end : cmd_loop                          //通过调用item_done函数告诉sequencer,可以开始发送下一个sequence item
   endtask : run_phase
   
   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction : new
   
endclass : driver

23.1.4 Instance driver & sequencer in ENV and connect them

  • 在测试平台中,使用sequencer替换tester,并将其和driver连接起来,如下图:
  • The UVM Primer -- chapter 23 UVM Sequence_第2张图片
  • sequencer和driver之间的链接不需要fifo。
class env extends uvm_env;
   `uvm_component_utils(env);

   sequencer       sequencer_h;            //在环境中声明sequencer
   coverage        coverage_h;
   scoreboard      scoreboard_h;
   driver          driver_h;
   command_monitor command_monitor_h;
   result_monitor  result_monitor_h;
   
   function new (string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new

   function void build_phase(uvm_phase phase);
      // stimulus
      sequencer_h  = new("sequencer_h",this);
      driver_h     = driver::type_id::create("driver_h",this);
      // monitors
      command_monitor_h    = command_monitor::type_id::create("command_monitor_h",this);
      result_monitor_h = result_monitor::type_id::create("result_monitor",this);
      // analysis
      coverage_h    = coverage::type_id::create ("coverage_h",this);
      scoreboard_h  = scoreboard::type_id::create("scoreboard",this);
   endfunction : build_phase

   function void connect_phase(uvm_phase phase);

      driver_h.seq_item_port.connect(sequencer_h.seq_item_export);
      //调用driver中的sequence_item_port的connect函数,并将sequencer中的seq_item_export作为参数传入
      //不需要使用TLM FIFO链接sequencer和dirver
      command_monitor_h.ap.connect(coverage_h.analysis_export);
      command_monitor_h.ap.connect(scoreboard_h.cmd_f.analysis_export);
      result_monitor_h.ap.connect(scoreboard_h.analysis_export);
   endfunction : connect_phase

endclass : env

23.1.5 Write UVM sequences

  • UVM sequenc处于UVM structure层次之外,没有parent argument,但是可以向UVM ENV输送激励。扩展自uvm sequence的类继承了三个功能,以完成向sequencer输送数据的功能。
    • m_sequencer:指向一个sequencer传递sequence items.
    • task body:uvm开始sequence的时候,调用此task
    • start_item/finish_item: 控制sequencer何时发送sequence item。
  • 我们扩展uvm_sequence来得到需要的sequences,通过参数化传入我们需要的sequence item类型,构造task函数创建sequence_items并将其送至sequencer。
  • fabinacci sequence:传建一些数字组合,新的值等于前两次值之和,如0 1 1 2 3 4 5 13 …
class fibonacci_sequence extends uvm_sequence #(sequence_item);  //扩展自uvm_sequencem,并传入sequence item类型的变量
   `uvm_object_utils(fibonacci_sequence);

   function new(string name = "fibonacci");
      super.new(name);
   endfunction : new
   
   task body();                              //在task函数中实现sequence的产生
      byte unsigned n_minus_2=0;
      byte unsigned n_minus_1=1;
      sequence_item command;
      
      command = sequence_item::type_id::create("command");   //创建sequence_item用于存储数据
         
      start_item(command);                   //阻塞,直至uvm_sequencer准备好接受sequence item
      command.op = rst_op;                   //解除阻塞之后,先进行DUT的Reset
      finish_item(command);                  //阻塞,直到driver完成当前命令

      `uvm_info("FIBONACCI", " Fib(01) = 00", UVM_MEDIUM);
      `uvm_info("FIBONACCI", " Fib(02) = 01", UVM_MEDIUM);
      for(int ff = 3; ff<=14; ff++) begin    //循环多次,产生固定序列的uvm sequence item
       start_item(command);                  // wait sequencer
       command.A = n_minus_2;                // update data & opcode
       command.B = n_minus_1;
       command.op = add_op;               
       finish_item(command);                 // wait driver's item_done
       n_minus_2 = n_minus_1;                // loop after driver has send last sequence item
       n_minus_1 = command.result;           // driver 将计算结果写回了result变量,取出来累加
       `uvm_info("FIBONACCI", $sformatf("Fib(%02d) = %02d", ff, n_minus_1),
                 UVM_MEDIUM);
      end 
   endtask : body
endclass : fibonacci_sequence

23.1.6 write base test

  • 在base test中定义sequencer并指向env中的sequencer
virtual class tinyalu_base_test extends uvm_test;

   env       env_h;                   //例化env,其中包括sequencer
   sequencer sequencer_h;             //test中同样声明sequencer
   
   function void build_phase(uvm_phase phase);
      env_h = env::type_id::create("env_h",this);
   endfunction : build_phase

   function void end_of_elaboration_phase(uvm_phase phase);
      sequencer_h = env_h.sequencer_h;          //将test中的sequencer指向env中的sequencer
   endfunction : end_of_elaboration_phase

   function new (string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new

endclas

23.1.6 Extends base test

  • 在fabonacci中声明并例化fabonacci sequence,调用fabonacci sequence的start函数,即调用sequence的body函数,并将sequencer_h传入,即将当前sequence传递至env中的sequencer。
class fibonacci_test extends tinyalu_base_test;
   `uvm_component_utils(fibonacci_test);

   task run_phase(uvm_phase phase);
      fibonacci_sequence fibonacci;       //
      fibonacci = new("fibonacci");

      phase.raise_objection(this);
      fibonacci.start(sequencer_h);
      phase.drop_objection(this);
   endtask : run_phase
      
   function new(string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new
endclass

得到的打印结果如下:
The UVM Primer -- chapter 23 UVM Sequence_第3张图片

  • 现在我们将stimulus和测试平台stucture分离开来,在不改变structure的前提下,我们可以运行不同sequence的组合。通过sequencer的句柄,我们可以调用不同的sequence(并行/串行)的start函数,并且把sequencer句柄传给sequence的start函数。
  • 此外,UVM提供了其他方式,使用"virtual sequence"以省略传递sequencer参数句柄。

23.2 Virtual Sequences

23.2.1 create virtual sequence

  • fabonacci是一个很简单地序列,然而我们需要混合各种不同的sequence并行/串行仿真。首先让我们先认识下virtual sequence,我们定义了runall_sequence (virtual sequence),不需要sequencer就可以传递sequence item。相反,通过sequencer的句柄,可以控制其他sequences的运行。
  • uvm_top提供了一些方法来实现一个耍杂技的功能,find()就是其中一种,我们可以通过给find()函数传递string类型的component,就可以在env中找到component类型的句柄,在virtaul sequence中,我们就通过find()函数找到测试环境中的sequencer。
class runall_sequence extends uvm_sequence #(uvm_sequence_item);
   `uvm_object_utils(runall_sequence);

   protected reset_sequence reset;
   protected maxmult_sequence maxmult;
   protected random_sequence random;
   protected sequencer sequencer_h;
   protected uvm_component uvm_component_h;
   
 function new(string name = "runall_sequence");
   super.new(name);
    uvm_component_h =  uvm_top.find("*.env_h.sequencer_h");  //uvm_top.find返回环境中的sequencer componentk类型
    if (uvm_component_h == null)
     `uvm_fatal("RUNALL SEQUENCE", "Failed to get the sequencer")

    if (!$cast(sequencer_h, uvm_component_h))                //将找到的component转换为sequecer,以备后续使用
      `uvm_fatal("RUNALL SEQUENCE", "Failed to cast from uvm_component_h.")
      
    reset = reset_sequence::type_id::create("reset");
    maxmult = maxmult_sequence::type_id::create("maxmult");
    random = random_sequence::type_id::create("random");
 endfunction : new

 task body();                     //顺序执行三个sequence
    reset.start(sequencer_h);     //将find返回的sequencer传递给各个sequence的start函数
    maxmult.start(sequencer_h);
    random.start(sequencer_h);
 endtask : body
   
endclass : runall_sequence

23.2.2 Lauching virtual sequences without sequencer

  • virtual sequencer之所以被称作virtual,就是因为调用的时候不需要使用sequencer;
class full_test extends tinyalu_base_test;
   `uvm_component_utils(full_test);
   
   runall_sequence runall_seq;

   task run_phase(uvm_phase phase);
      runall_seq = new("runall_seq");
      phase.raise_objection(this);
      runall_seq.start(null);              //调用virtual时,不需要传入sequencer
      phase.drop_objection(this);          //virtual通过uvm_top.find函数,自己寻找sequencer
   endtask : run_phase

   function new (string name, uvm_component parent);
      super.new(name,parent);
   endfunction : new

endclass

23.2.3 create parallel thread

  • 使用父类定义的m_sequencer传递sequence item;然后调用body函数;
  • 使用start函数调用其他sequences的body函数;
  • 先调用reset sequence,然后并行调用fibonacci和short random sequence。
class parallel_sequence extends uvm_sequence #(uvm_sequence_item);
   `uvm_object_utils(parallel_sequence);

   protected reset_sequence reset;
   protected short_random_sequence short_random;
   protected fibonacci_sequence fibonacci;

   function new(string name = "parallel_sequence");
      super.new(name);
      reset = reset_sequence::type_id::create("reset");
      fibonacci = fibonacci_sequence::type_id::create("fibonacci");
      short_random = short_random_sequence::type_id::create("short_random");
   endfunction : new

   task body();
      reset.start(m_sequencer);
      fork
	    fibonacci.start(m_sequencer);
   	    short_random.start(m_sequencer);
      join
   endtask : body
endclass : parallel_sequence

23.2.4 Lauching sequences with parallel thread

  • 使用system verilog的fork/join机制,可以实现多线程并行工作;
    • fork:并行负责启动不同的线程;
    • join:阻塞,直至fork启动的所有线程全部结束;
class parallel_test extends tinyalu_base_test;
   `uvm_component_utils(parallel_test);

   parallel_sequence parallel_h;
      
   function new(string name, uvm_component parent);
      super.new(name,parent);
      parallel_h = new("parallel_h");
   endfunction : new

   task run_phase(uvm_phase phase);
      phase.raise_objection(this);
      parallel_h.start(sequencer_h);
      phase.drop_objection(this);
   endtask : run_phase

endclass

得到的结果如下:
The UVM Primer -- chapter 23 UVM Sequence_第4张图片
其中fibonacci和random sequence交替运行,并且两个进程之间没有相互干扰,fabinacci依然按照此前的值进行累加。

23.3 Summary

  • 本章中我们学习了UVM技术的最后一个知识点:uvm sequence,seuqnce使我们将stimulus和structure分离开,混合多种sequence进行复杂场景的测试;
  • 我们学习了transaction level -> sequence level的转变,并且将DUT计算的值返回给测试平台。当然sequence之间可以相互调用,已形成更复杂的sequence;因此,我们可以并行/串行的运行多种sequence。

the uvm primer的学习暂时告一段段落,后面按照<>的顺序,进行uvm-1.1d源码学习。

你可能感兴趣的:(UVM)