• *2_3_5_加入reference model


    摘自:http://book.2cto.com/201408/46009.html


    在2.1节中讲述验证平台的框图时曾经说过,reference model用于完成和DUT相同的功能。

    reference model的输出被scoreboard接收,用于和DUT的输出相比较。DUT如果很复杂,那么reference model也会相当复杂。

    本章的DUT很简单,所以reference model也相当简单:


    文件:src/ch2/section2.3/2.3.5/my_model.sv
      4 class my_model extends uvm_component;
      5
      6    uvm_blocking_get_port #(my_transaction)  port;
      7    uvm_analysis_port #(my_transaction)  ap;
      8
      9    extern function new(string name, uvm_component parent);
     10    extern function void build_phase(uvm_phase phase);
     11    extern virtual  task main_phase(uvm_phase phase);
     12
     13    `uvm_component_utils(my_model)
     14 endclass
     15
     16 function my_model::new(string name, uvm_component parent);
     17    super.new(name, parent);
     18 endfunction
     19
     20 function void my_model::build_phase(uvm_phase phase);
     21    super.build_phase(phase);
     22    port = new("port", this);
     23    ap = new("ap", this);
     24 endfunction
     25
     26 task my_model::main_phase(uvm_phase phase);
     27    my_transaction tr;
     28    my_transaction new_tr;
     29    super.main_phase(phase);
     30    while(1) begin
     31       port.get(tr);
     32       new_tr = new("new_tr");
     33       new_tr.my_copy(tr);
     34       `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
     35       new_tr.my_print();
     36       ap.write(new_tr);
     37    end
     38 endtask


    在my_model的main_phase中,只是单纯地复制一份从i_agt得到的tr,并传递给后级的scoreboard中。my_copy是一个在my_transaction中定义的函数,其代码为:


    代码清单 2-42
    文件:src/ch2/section2.3/2.3.5/my_transaction.sv
     41   function void my_copy(my_transaction tr);
     42      if(tr == null)
     43        `uvm_fatal("my_transaction", "tr is null!!!!")
     44      dmac = tr.dmac;
     45      smac = tr.smac;
     46      ether_type = tr.ether_type;
     47      pload = new[tr.pload.size()];
     48      for(int i = 0; i < pload.size(); i++) begin
     49        pload[i] = tr.pload[i];
     50      end
     51      crc = tr.crc;
     52   endfunction


    这里实现了两个my_transaction的复制。

    完成my_model的定义后,需要将其在my_env中实例化。其实例化方式与agent、driver相似,这里不具体列出代码。在加入my_model后,整棵UVM树变成了如图2-7所示的形式。

     

    my_model并不复杂,这其中令人感兴趣的是my_transaction的传递方式。my_model是从i_agt中得到my_transaction,并把my_transaction传递给my_scoreboard。在UVM中,通常使用TLM(Transaction Level Modeling)实现component之间transaction级别的通信。

    要实现通信,有两点是值得考虑的:

    第一,数据是如何发送的?

    第二,数据是如何接收的?

    在UVM的transaction级别的通信中,数据的发送有多种方式,其中一种是使用uvm_analysis_port。在my_monitor中定义如下变量:


    代码清单 2-43
    文件:src/ch2/section2.3/2.3.5/my_monitor.sv
      7    uvm_analysis_port #(my_transaction)  ap;


    uvm_analysis_port是一个参数化的类,其参数就是这个analysis_port需要传递的数据的类型,在本节中是my_transaction。

    声明了ap后,需要在monitor的build_phase中将其实例化:


    代码清单 2-44
    文件:src/ch2/section2.3/2.3.5/my_monitor.sv
     14    virtual function void build_phase(uvm_phase phase);
     …
     18       ap = new("ap", this);
     19    endfunction


    在main_phase中,当收集完一个transaction后,需要将其写入ap中:


    代码清单 2-45
    task my_monitor::main_phase(uvm_phase phase);
      my_transaction tr;
      while(1) begin
        tr = new("tr");
        collect_one_pkt(tr);
        ap.write(tr);
      end
    endtask


    write是uvm_analysis_port的一个内建函数。

    到此,在my_monitor中需要为transaction通信准备的工作已经全部完成。

    UVM的transaction级别通信的数据接收方式也有多种,其中一种就是使用uvm_blocking_get_port。这也是一个参数化的类,其参数是要在其中传递的transaction的类型。在my_model的第6行中,定义了一个端口,并在build_phase中对其进行实例化。

    在main_phase中,通过port.get任务来得到从i_agt的monitor中发出的transaction。

    在my_monitor和my_model中定义并实现了各自的端口之后,通信的功能并没有实现,还需要在my_env中使用fifo将两个端口联系在一起。在my_env中定义一个fifo,并在build_phase中将其实例化:


    代码清单 2-46
    文件:src/ch2/section2.3/2.3.5/my_env.sv
     10    uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
     …
     23      agt_mdl_fifo = new("agt_mdl_fifo", this);


    fifo的类型是uvm_tlm_analysis_fifo,它本身也是一个参数化的类,其参数是存储在其中的transaction的类型,这里是my_transaction。

    之后,在connect_phase中将fifo分别与my_monitor中的analysis_port和my_model中的blocking_get_port相连:


    代码清单 2-47
    文件:src/ch2/section2.3/2.3.5/my_env.sv
     31 function void my_env::connect_phase(uvm_phase phase);
     32   super.connect_phase(phase);
     33   i_agt.ap.connect(agt_mdl_fifo.analysis_export);
     34   mdl.port.connect(agt_mdl_fifo.blocking_get_export);
     35 endfunction


    这里引入了connect_phase。与build_phase及main_phase类似,connect_phase也是UVM内建的一个phase,它在build_phase执行完成之后马上执行。

    与build_phase不同的是,connect_phase的执行顺序并不是从树根到树叶,而是从树叶到树根——先执行driver和monitor的connect_phase,再执行agent的connect_phase,最后执行env的connect_phase。

    为什么这里需要一个fifo呢?不能直接把my_monitor中的analysis_port和my_model中的blocking_get_port相连吗?

    由于analysis_port是非阻塞性质的,ap.write函数调用完成后马上返回,不会等待数据被接收。假如当write函数调用时,blocking_get_port正在忙于其他事情,而没有准备好接收新的数据时,此时被write函数写入的my_transaction就需要一个暂存的位置,这就是fifo。

    在如上的连接中,用到了i_agt的一个成员变量ap,它的定义与my_monitor中ap的定义完全一样:


    代码清单 2-48
    文件:src/ch2/section2.3/2.3.5/my_agent.sv
      8   uvm_analysis_port #(my_transaction)  ap;


    与my_monitor中的ap不同的是,不需要对my_agent中的ap进行实例化,而只需要在my_agent的connect_phase中将monitor的值赋给它,换句话说,这相当于是一个指向my_monitor的ap的指针:


    代码清单 2-49
    文件:src/ch2/section2.3/2.3.5/my_agent.sv
     29 function void my_agent::connect_phase(uvm_phase phase);
     30    super.connect_phase(phase);
     31    ap = mon.ap;
     32 endfunction


    根据前面介绍的connect_phase的执行顺序,my_agent的connect_phase的执行顺序早于my_env的connect_phase的执行顺序,从而可以保证执行到i_agt.ap.connect语句时,i_agt.ap不是一个空指针。

  • 相关阅读:
    微信小程序页面标签中无法使用的js语法
    React-Native真机调试
    微信小程序button设置宽度无效
    CSS禁止选中文本
    vue之 ref 和$refs的使用
    scrapy之 Spider Middleware(爬虫中间件)
    kafka
    Linux select、poll和epoll
    C/C++ 在一个一维数组中查找两个数,使得它们之和等于给定的某个值
    C/C++ 求浮点数平方根
  • 原文地址:https://www.cnblogs.com/YINBin/p/6843637.html
Copyright © 2020-2023  润新知