UVM Driver Pipeline功能的实现

UVM Driver多Phase Pipeline功能的实现

Pipeline简介

在很多总线协议中是支持pipeline操作的,比如AMBA总线的AHB总线和AXI总线,在实现Data总线和Address总线以及Response总线的并行传输过程中,会遇到很多问题,比如对于AXI总线来说需要6个通道独立的去实现Write Data,Write Control,Write Response,Read Data,Read Control,Read Response,如何实现其中信息的关联?Driver如何实现六个通道的pipeline的驱动等问题,如何保证Control命令发出以后,Data和Response可以接受完整,仿真才结束。。。。。。等问题。这里不介绍这些内容,从一个简单的DUT的Driver Pipeline驱动的实现开始介绍,麻雀虽小五脏俱全,对实现复杂的pipeline功能,有很多的借鉴意义。

UVM的driver实现pipeline的功能,在Mentor的cook-book中有相关介绍,其中运用了比较复杂的代理机制,通过Sequence和interface的BFM代理,以及Driver的相互控制来实现pipeline的功能。控制起来比较复杂,不容易debug其中的问题。这里不做介绍。

介绍一种简单通用的pipeline的实现方法。

DUT简介

DUT是一个乘除运算单元可以pipeline的接受数据,做乘除运算,并可以pipeline的发出数据,下图是port介绍和Timing图:

PortlistTypeDescription
data_ainput [7:0]Operation data A
data_binput [7:0]Operation data B
valid_iinputOperation data valid,high valid
data_coutput [7:0]Result data C
valid_ooutputResult data valid,high valid
clk_iinputWork clock
rst_ninputReset signal, low valid

DUT Pipline Timing

Pipe line driver的实现

以下是driver的实现方式:

class op_driver extends uvm_driver #(gf_op_item);
  gf_op_item pipeline[$];
  virtual gf_op_interface vif;
  gf_op_config cfg;
  
  `uvm_component_utils_begin(gf_op_driver)
  `uvm_component_utils_end
   
   function new(string name,uvm_component parent);
      super.new(name,parent);
   endfunction
   
   function build_phase(uvm_phase phase);
      super.build_phase(phase);
   endfuntion
   
   function connect_phase(uvm_phase phase);
      this.vif = cfg.vif;
   endfunction
   
   task run_phase(uvm_phase phase)
      vif.data_a <= 0;
      vif.data_b <= 0;
      vif.valid_i <=0;
      @(posedge vif.clk_i iff vif.rst_n_i !==0);
      
      fork
          forever begin
              gf_op_item trans;
              `uvm_info(get_type_name(),"waiting for data from sequencer",UVM_LOW)
              seq_item_port.get_next_item(req);
              if($cast(trans,req))
                 `uvm_fatal("CASTFL","Failed to cast req t this trans in get_and drive")
              drive_item(trans);
              seq_item_port.item_done();
          end
          
          forever begin
               wait_for_result();
          end
      join
   endtask
   
   task gf_op_driver::drive_item(gf_op_item trans);
       repeat(trans.valid_delay) begin
           vif.data_a <= 0;
           vif.data_b <= 0;
           vif.valid_i <= 0;
           @(posedge vif.clk_i);
       end
       vif.data_a <= trans.data_a;
       vif.data_b <= trans.data_b;
       vif.valid_i <= 1'b1;
       pipeline.push_back(trans);
       @(posedge vif.clk_i);
       vif.data_a <= 0;
       vif.data_b <= 0;
       vif.valid_i <= 0;
   endtask
   
   task wait_for_result();
       gf_op_item trans_done;
       trans_done = new();
       wait(pipeline.size>0);
       trans_done = pipeline.pop_front();
       while(vif.valid_o !=1) begin
          @(posedge vif.clk_i);
       end
       trans_done.data_c = vif.data_c;
       seq_item_port.put_response(trans_done);
       //Very important for the pipeline 
   endtask
endclass

通过pipeline queue可以实现两个独立通道的信息互通,当然你也可以采用其他方式实现。

Pipeline Sequence的实现

Driver实现以后还是不够的,我们的sequence也要进行相应的改变。下面对两种不同的sequence的写法进行解释:

class gf_op_sequence extends uvm_sequence #(gf_op_item);
   virtual gf_op_interface vif;
   ivt_gf_op_config cfg;
   rand int num_data;
   `uvm_object_utils_begin(gf_op_sequence)
   `uvm_object_utils_end
   `uvm_declare_p_sequencer(gf_op_sequencer)
   
   function new(string name='gf_op_sequence');
      super.new();
   endfunction
   
   virtual task pre_start();
      cfg=p_sequencer.cfg;
      vif=cfg.vif;
   endtask
   
   virtual task body();
      gf_op_item req;
      if(!uvm_config_db #(gf_op_config)::get(null,get_full_name,"cfg",cfg)) begin
        `uvm_fatal("pre_randomize","Cannot get GF_OP configuration in transaction")
      end
         repeat(num_data) begin
             req=gf_op_item::type_id::create("req");
             req.cfg = cfg;
             assert(req.randomize());
             start_item(req);
             finish_item(req);
         end
   endtask 
endclass

通过运行simulator发现在valid data_a 和data_b可以pipeline的发出,但是最后一笔的result data_c get不到数据,在发完最后一笔的valid数据之后,仿真就结束了。究其原因,是sequence已经执行完成,虽然driver在等result。修改sequence如下:

class gf_op_sequence extends uvm_sequence #(gf_op_item);
   virtual gf_op_interface vif;
   ivt_gf_op_config cfg;
   rand int num_data;
   `uvm_object_utils_begin(gf_op_sequence)
   `uvm_object_utils_end
   `uvm_declare_p_sequencer(gf_op_sequencer)
   
   function new(string name='gf_op_sequence');
      super.new();
   endfunction
   
   virtual task pre_start();
      cfg=p_sequencer.cfg;
      vif=cfg.vif;
   endtask
   
   virtual task body();
      gf_op_item req;
      if(!uvm_config_db #(gf_op_config)::get(null,get_full_name,"cfg",cfg)) begin
        `uvm_fatal("pre_randomize","Cannot get GF_OP configuration in transaction")
      fork
         repeat(num_data) begin
             req=gf_op_item::type_id::create("req");
             req.cfg = cfg;
             assert(req.randomize());
             start_item(req);
             finish_item(req);
         end
      join_none
         repeat(num_data) begin
             get_response(rsp);
         end
   endtask 
endclass

在这里虽然sequence不用等到result的结果,在monitor中会monitor result的结果上报给referenc model和scoreboard,所以必须保证数据的完整性。这样可以在最后一笔的valid数据发给dut后,等待所有的结果出来之后仿真才会结束。

但是这样做还不够,如果pipeline的结果需要等待,排队的valid数据超过8个以后,sequence在仿真中会被挂死,仿真结束不了,这是什么原因呢?经过查找uvm_sequence的源码会发现 response的queue的深度默认是8,如果超过8个,就会丢失get_response(rsp)的次数,导致sequence不能正常结束。

set_response_queue_depth(8);

通过修改sequence,仿真正常结束。

class gf_op_sequence extends uvm_sequence #(gf_op_item);
   virtual gf_op_interface vif;
   ivt_gf_op_config cfg;
   rand int num_data;
   `uvm_object_utils_begin(gf_op_sequence)
   `uvm_object_utils_end
   `uvm_declare_p_sequencer(gf_op_sequencer)
   
   function new(string name='gf_op_sequence');
      super.new();
   endfunction
   
   virtual task pre_start();
      cfg=p_sequencer.cfg;
      vif=cfg.vif;
   endtask
   
   virtual task body();
      gf_op_item req;
      set_response_queue_depth(512);
      if(!uvm_config_db #(gf_op_config)::get(null,get_full_name,"cfg",cfg)) begin
        `uvm_fatal("pre_randomize","Cannot get GF_OP configuration in transaction")
      fork
         repeat(num_data) begin
             req=gf_op_item::type_id::create("req");
             req.cfg = cfg;
             assert(req.randomize());
             start_item(req);
             finish_item(req);
         end
      join_none
         repeat(num_data) begin
             get_response(rsp);
         end
   endtask 
endclass

至此UVM 简单DUT pipeline可以正常工作,如果需要改为多通道的pipeline,可以参考以上代码进行实现。

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值