2

初识Layering Sequence - 程默白

 1 year ago
source link: https://www.cnblogs.com/ChengMobai/p/LayeringSequence.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、为什么需要Layering Sequence

随着集成电路技术的发展,芯片的复杂度日益提升。对验证而言,为了更好地应对这种复杂性,一方面是提高各个级别的可移植性和复用性,另一方面是提高抽象级别,减小复杂度。
Layering Sequence这正是从第二个方面出发衍生的,它能够将高抽象级的uvm_sequence_item和低抽象级的uvm_sequence_item相互转换。

二、Layering Sequence机制

下面以《芯片验证漫游指南》13.5.3节为例,简单分析Layering Sequence的工作机制

1、高抽象级的layer_trans和低抽象级的bus_trans

首先定义一个低抽象级的bus_trans,并且将其打包成为packet_seq

点击查看代码

//低抽象级的bus_trans
class bus_trans extends uvm_sequence_item;
    rand phy_cmd_t cmd;
    rand int addr;
    rand int data;
    constraint cstr{
      soft addr == 'h0;
      soft data == 'h0;
    }
    ...
endclass

//打包后的packet_seq
class packet_seq extends uvm_sequence;
    rand int len;
    rand int addr;
    rand int data[];
    rand phy_cmd_t cmd;
    constraint cstr{
      soft len inside {[30:50]};
      soft addr[31:16] == 'hFF00;
      data.size() == len;
    }
    ...
    task body();
      bus_trans req;
      foreach(data[i])
        `uvm_do_with(req, {cmd == local::cmd; 
                           addr == local::addr;
                           data == local::data[i];})
    endtask
  endclass

其对应的sequencer是:

点击查看代码

 class phy_master_sequencer extends uvm_sequencer;
    layering_sequencer up_sqr;
	...
endclass

接下来定义一个高抽象级layer_trasn

点击查看代码 ```verilog class layer_trans extends uvm_sequence_item; rand layer_cmd_t layer_cmd; rand int pkt_len; rand int pkt_idle; constraint cstr { soft pkt_len inside {[10: 20]}; layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]}; layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]}; layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]}; } ... endclass ```

其对应的sequencer是:

点击查看代码 ```verilog class layering_sequencer extends uvm_sequencer; ... endclass ```

可以看到高抽象级的layer_trans和低抽象级的bus_trans差别很大,也没有任何层次上的关系,因此需要有中间的转化层将两者进行转化后才能够正常发送给DUT。

2、转化层的sequece

用adapter_seq作为转化层

点击查看代码

class adapter_seq extends uvm_sequence;
    `uvm_object_utils(adapter_seq)
    `uvm_declare_p_sequencer(phy_master_sequencer)//句1
    function new(string name = "adapter_seq");
      super.new(name);
    endfunction
    task body();
      layer_trans trans;
      packet_seq pkt;
      forever begin
          p_sequencer.up_sqr.get_next_item(req);//句2-1
        void'($cast(trans, req));
        repeat(trans.pkt_len) begin
            `uvm_do(pkt)//句3
          delay(trans.pkt_idle);
        end
          p_sequencer.up_sqr.item_done();//句2-2
      end
    endtask
    virtual task delay(int delay);
    endtask
  endclass

3、layering sequence的工作机制

下面结合结构图分析一下layering sequence的工作机制

3114000-20230305133442746-1061611495.png
  • 句1:通过uvm_declare_p_sequencer宏指定adapter_seq的item由phy_master_sequencer来发送,也构建了adapter_seq访问phy_master_sequencer成员变量和成员方法的桥梁(详见初识m_sequencer、p_sequencer和uvm_declare_p_sequencer宏

  • 句2-1:如图绿色曲线所示,通过p_sequencer访问phy_master_sequencer中的up_sqr,而up_sqr在test中通过句4将up_sqr指向layer_sqr(test代码第13行)。此时adapter_seq能够调用layer_sqr的get_next_item任务取得一个高抽象级的layer_trans。

  • 句3:按照已取得的layer_trans中的属性(pkt_len和pkt_idle)约束低抽象级的packet_seq类型的pkt的属性,并交给phy_master_sequencer通过driver发送给DUT。

  • 句2-2:依旧通过绿色曲线所示通路,使用item_done通知layer_sqr。

4、在test中的连接关系

test代码:

点击查看代码

class test extends uvm_test;
    layering_sequencer layer_sqr;
    phy_master_agent phy_agt;
    `uvm_component_utils(test1)
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
      layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);
      phy_agt = phy_master_agent::type_id::create("phy_agt", this);
    endfunction
    function void connect_phase(uvm_phase phase);
      phy_agt.sqr.up_sqr = layer_sqr;//句4
    endfunction
    task run_phase(uvm_phase phase);
      top_seq seq;
      adapter_seq adapter;
      phase.raise_objection(phase);
      seq = new();
      adapter = new();
      fork
          adapter.start(phy_agt.sqr);//句5
      join_none
        seq.start(layer_sqr);//句6
      phase.drop_objection(phase);
    endtask
  endclass
  • 句4:将phy_master_sequencer中的up_sqr指向layer_sqr,构建与layer_sqr的通路,使得adapter能够取得layer_trans。
  • 句5:将adapter_seq挂载到phy_master_sequencer。
  • 句6:将top_seq挂载到layer_sqr。

一些其他问题:

  • 为什么使用fork-join_none:在adapter_seq的body()中使用的forever语句,使得adapter_seq永不停歇的取得高抽象级item,转化成低抽象级的item去执行。因此使用fork-join_none使adapter_seq不阻挡其他功能正常运行。
  • 为什么采用句4和get_next_item()而不是TLM的方式:这是为了最大程度的提高复用性和减小复杂度。
  1. 《芯片验证漫游指南——从系统理论到UVM的验证全视界》刘斌著
  2. Universal Verification Methodology (UVM) 1.2 User’s Guide

完整代码如下:

点击查看代码

module layer_seq;
  import uvm_pkg::*;
  `include "uvm_macros.svh"

  typedef class phy_master_sequencer;
  typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} phy_cmd_t;
  typedef enum {FREQ_LOW_TRANS, FREQ_MED_TRANS, FREQ_HIGH_TRANS} layer_cmd_t;

  class bus_trans extends uvm_sequence_item;
    rand phy_cmd_t cmd;
    rand int addr;
    rand int data;
    constraint cstr{
      soft addr == 'h0;
      soft data == 'h0;
    }
    `uvm_object_utils_begin(bus_trans)
      `uvm_field_enum(phy_cmd_t, cmd, UVM_ALL_ON)
      `uvm_field_int(addr, UVM_ALL_ON)
      `uvm_field_int(data, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name = "bus_trans");
      super.new(name);
    endfunction
  endclass

  class packet_seq extends uvm_sequence;
    rand int len;
    rand int addr;
    rand int data[];
    rand phy_cmd_t cmd;
    constraint cstr{
      soft len inside {[30:50]};
      soft addr[31:16] == 'hFF00;
      data.size() == len;
    }
    `uvm_object_utils(packet_seq)
    function new(string name = "reg_test_seq");
      super.new(name);
    endfunction
    task body();
      bus_trans req;
      foreach(data[i])
        `uvm_do_with(req, {cmd == local::cmd; 
                           addr == local::addr;
                           data == local::data[i];})
    endtask
  endclass

  class layer_trans extends uvm_sequence_item;
    rand layer_cmd_t layer_cmd;
    rand int pkt_len;
    rand int pkt_idle;
    constraint cstr {
      soft pkt_len inside {[10: 20]};
      layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};
      layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};
      layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
    }
    `uvm_object_utils(layer_trans)
    function new(string name = "layer_trans");
      super.new(name);
    endfunction
  endclass

  class adapter_seq extends uvm_sequence;
    `uvm_object_utils(adapter_seq)
    `uvm_declare_p_sequencer(phy_master_sequencer)
    function new(string name = "adapter_seq");
      super.new(name);
    endfunction
    task body();
      layer_trans trans;
      packet_seq pkt;
      forever begin
        p_sequencer.up_sqr.get_next_item(req);
        void'($cast(trans, req));
        repeat(trans.pkt_len) begin
          `uvm_do(pkt)
          delay(trans.pkt_idle);
        end
        p_sequencer.up_sqr.item_done();
      end
    endtask
    virtual task delay(int delay);
    endtask
  endclass

  class top_seq extends uvm_sequence;
    `uvm_object_utils(top_seq)
    function new(string name = "top_seq");
      super.new(name);
    endfunction
    task body();
      layer_trans trans;
      `uvm_do_with(trans, {layer_cmd == FREQ_LOW_TRANS;})
      `uvm_do_with(trans, {layer_cmd == FREQ_HIGH_TRANS;})
    endtask
  endclass

  class layering_sequencer extends uvm_sequencer;
    `uvm_component_utils(layering_sequencer)
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
  endclass

  class phy_master_sequencer extends uvm_sequencer;
    layering_sequencer up_sqr;
    `uvm_component_utils(phy_master_sequencer)
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
  endclass

  class phy_master_driver extends uvm_driver;
    `uvm_component_utils(phy_master_driver)
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    task run_phase(uvm_phase phase);
      REQ tmp;
      bus_trans req;
      forever begin
        seq_item_port.get_next_item(tmp);
        void'($cast(req, tmp));
        `uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)
        seq_item_port.item_done();
      end
    endtask
  endclass

  class phy_master_agent extends uvm_agent;
    phy_master_sequencer sqr;
    phy_master_driver drv;
    `uvm_component_utils(phy_master_agent)
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
      sqr = phy_master_sequencer::type_id::create("sqr", this);
      drv = phy_master_driver::type_id::create("drv", this);
    endfunction
    function void connect_phase(uvm_phase phase);
      drv.seq_item_port.connect(sqr.seq_item_export);
    endfunction
  endclass

  class test extends uvm_test;
    layering_sequencer layer_sqr;
    phy_master_agent phy_agt;
    `uvm_component_utils(test1)
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
      layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);
      phy_agt = phy_master_agent::type_id::create("phy_agt", this);
    endfunction
    function void connect_phase(uvm_phase phase);
      phy_agt.sqr.up_sqr = layer_sqr;
    endfunction
    task run_phase(uvm_phase phase);
      top_seq seq;
      adapter_seq adapter;
      phase.raise_objection(phase);
      seq = new();
      adapter = new();
      fork
        adapter.start(phy_agt.sqr);
      join_none
      seq.start(layer_sqr);
      phase.drop_objection(phase);
    endtask
  endclass

  initial begin
    run_test("test");
  end
endmodule


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK