• 4.小白学uvm验证


      一个基本的 uvm 验证环境结构如下图所示,包含两个 agent,其中 in_agent 用于驱动 DUT ,同时将驱动数据同时传递给 reference model, out_agent 用于按照协议采集 DUT 的输出数据,并将数据传递给 scoreboard,在 scoreboard 收集到 reference model 的运算结果后,再进行比对验证。

      要想实现 monitor 和 reference model 进行通信,大致可以有以下几种方式,第一种,通过全局变量;第二种,通过 定义一个参数类来进行信息传递;第三种:通过前面刚谈到的 uvm_config 来进行传递信息。似乎这些东西都可以实现我们定义的任务,但是有个很重要的点还没有提到,如果我们需要一种 阻塞的场景,即有数据时就取数据,没有数据时就等待。这在验证中是一种比较常见的场景。为了解决这个问题 uvm 提炼出了一种具有阻塞和非阻塞功能的类型,取名为TML
      通俗意义上讲,TLM就是一些带阻塞和非阻塞 port 端口, 为了满足缓存需要,其中也有一些能缓存能力的 port。TLM 端口的使用有点像我们看电视时用插头插插座,插看好后电网的电量就源源不断的通过接口输入进电视机,结合我们的验证环境来说 driver 和 reference model 中包含一对匹配的结构,一个用于发送数据,一个用于接收数据。

    1.端口连接

      在 UVM 中 port 可以分为三种:

    a.port : 优先级最高,可以作为数据链路的端口的起点或者端口中间节点 ;
    b.export :优先级最中,可以作为数据链路的或者起点端口中间节点 ;
    c.import :优先级最低,只能作为数据链路的重点 。

    连接形式可以为

    a.port -> import; port -> export -> import; port -> port -> import; port -> export -> export -> import ;
    b.export -> import; export -> export -> import;
    注意 -> 表示连接关系,A port -> B import,表示为 A.connect(B) ,其中 A 的优先级需要比 B 的优先级高。

    2.端口操作

      如下图所示,在 UVM 中 port 常见的两种操作就是:

    a. put 操作 :通信的发起者 A 把一个 transaction 发送给接受者 B ;
    b. get 操作 :通信的发起者 A 向 接受者 B 请求一个 transaction ;
    c. transport操作,transport操作相当于一次 put 操作加一次 get 操作 。

      uvm 包含了很多类型的 port/export/import,如 uvm_blocking_put_port/export/import、uvm_blocking_peek_port/export/import 和 uvm_blocking_transport_port/export/import 等,但除了这些,uvm 还包含了两种特殊的端口 analysis_port 和 analysis_export, 这两种端口和 put/peek/get 等系列端口类似,但是包含一些重要区别:

    • 默认情况下,一个 analysis_port(analysis_export)可以连接多个 analysis_import,即 analysis_port(analysis_export)与 analysis_import 之间是一对多通信;而 put/peek/get 等系列端口只能进行一对一通信。analysis_port(analysis_export)的通信方式像一个广播。
    • put/peek/get 等系列端口都具有阻塞和非阻塞之分,但对于 analysis_port(analysis_export)来说,没有阻塞和非阻塞的概念。
    • 不同于put/peek/get 等系列端口,对于analysis_port(analysis_export)两种端口仅包含 write 操作与之相连的 analysis_imp 所在的 component 必须定义一个名为 write 的函数
    3.端口实例1 - uvm_analysis_port

      在 uvm 环境中,常用 TML 通信的组件有 monitor、scoreboard和reference model 三者之间的通信,下面以 monitor 和 scoreboard 通信为例,monitor端 uvm_analysis_port的代码为:

    my_monitor
    class my_monitor extends uvm_monitor;
        `uvm_component_utils(my_monitor)
        uvm_analysis_port(my_transaction) A_port;
        ...
    endclass
    ...
    virtual function void my_monitor::build_phase(uvm_phase phase);
        super.build_phase(phase);
        A_port=new("A_port",this);
        ...
    endfuction
    

    virtual function void my_monitor::main_phase(uvm_phase phase);
    my_transaction tr;
    forever begin
    assert(tr.randomize());
    A_port.write(tr);
    end
    endfuction

      scoreboard 端 uvm_analysis_port 的代码为:

    scoreboard
    class my_scoreboard extends uvm_scoreboard;
        `uvm_component_utils(my_scoreboard)
        uvm_analysis_import(my_transaction,my_scoreboard) A_import;
        ...
    endclass
    ...
    virtual function void my_scoreboard::build_phase(uvm_phase phase);
        super.build_phase(phase);
        A_import=new("A_import",this);
        ...
    endfuction
    

    virtual function void my_scoreboard::write(my_transaction tr);
    deal_with(tr);
    endfuction

      顶层 env 连接代码:

    my_env
    class my_env extends uvm_env;
        ...
    endclass
    ...
    virtual function void my_scoreboard::connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        agt.monitor.A_port.connect(scb.A_import)
        ...
    endfuction

      实例中,在 monitor 中定义了 analysis_port;socreboard 中定义了 analysis_import;在 env 的 connect_phase 中对 analysis_port 和 analysis_import 进行连接。数据在 monitor 产生后,数据的流通过程如下图所示, 数据首先通过 analysis_port 的系统函数 write 调用 analysis_import 的系统函数 write,再由 analysis_import 的系统函数 write 触发 my_scoreboard 中用户自定义的 write 函数,这也为什么在定义 scoreboard 的 impport时,需要通过 uvm_analysis_port(my_transaction,my_scoreboard) A_import; 将 my_scoreboard 和 A_import 绑定的原因,就是为了 能顺利触发 自定义的 write 函数。而且这种触发是没有延迟的触发机制,可以通过这种 write 方式来满足一些特定时序要求。

      在了解了上述内容后,在来看看 uvm_analysis_port 的最重要的特性,一对多通信是怎么实现的,现在考虑一种场景,scoreboard 需要收集 monitor 和 reference model 两个组件传输过来的数据,但是我们定义write 函数的时候只有一个名字,那么 monitor 和 reference model 中的 analysis_import 的系统函数 write 怎么触发 my_scoreboard 中用户自定义的 write 函数呢?
      为了解决上述问题 uvm 定了一宏 uvm_analysis_imp_decl 来解决上述问题,现在我们来看看这个宏怎么用,更新后的 my_scoreboard 代码如下:

    scoreboard
    `uvm_analysis_imp_decl(_monitor)
    `uvm_analysis_imp_decl(_model)
    class my_scoreboard extends uvm_scoreboard;
        my_transaction inpect_queue[$]
        my_transaction expect_queue[$] 
        uvm_analysis_imp_monitor(my_transaction,my_scoreboard) mon_import;
        uvm_analysis_imp_model(my_transaction,my_scoreboard) model_import;
        ...
        extern function void write_monitor(my_transaction tr);
        extern function void write_model(my_transaction tr);
        ...
    endclass
    ...
    

    virtual function void my_scoreboard::write_monitor(my_transaction tr);
    inpect_queue.push_back(tr);
    ...
    endfuction

    virtual function void my_scoreboard::write_model(my_transaction tr);
    expect_queue.push_back(tr);
    ...
    endfuction

      上述代码通过宏 uvm_analysis_imp_decl 声明了两个后缀 _monitor 和 _model 。UVM 会根据这两个后缀定义两个新的 import 类:uvm_analysis_imp_monitor 和 uvm_analysis_imp_model ,并在 my_scoreboard 中分别实例化这两个类:monitor_imp 和 model_imp。当与 monitor_imp 相连接的 analysis_port 执行 write 函数时,会自动调用 write_monitor 函数,而与 model_imp 相连接的 analysis_port 执行 write 函数时,会自动调用 write_model 函数。所以,只要完成后缀的声明,并在write后面添加上相应的后缀就可以正常工作了。

    3.端口实例2 - uvm_tlm_analysis_fifo

      另外一种带缓存能力的 analysis port ,叫做 uvm_tlm_analysis_fifo,其本质为 一块 FIFO 加一组端口,如下图所示,共有 3 类,12 import 为一类,包含 一族 *_put_export 、一族 *_get_export 和一族 *_peek_export ,似乎上述都是 export,其实这只是取名为 export 而已,它们本质上还是 import 。

    *_put_export 族:调用时,导致 uvm_tlm_analysis_fifo 中的缓存单元(**本质为 mailbox ),元素个数加 1;
    *_get_export 族:调用时,导致 uvm_tlm_analysis_fifo 中的缓存单元(
    本质为 mailbox ),元素个数减 1;
    *_peek_export 族:调用时,导致 uvm_tlm_analysis_fifo 中的缓存单元(
    本质为 mailbox **),元素个数不变,同时把 transaction 复制一份发送出去。

       现在来说说图中的另外两个端口 - put_ap 和 get_ap, 这两组的 端口的源码如下

    scoreboard
    virtual task void put(int T t);
        m.put(t);
        put_ap.write(t);
    endtask 
    virtual task void get(int T t);
        m_pending_blocked_gets++;
        m.get( t );
        m_pending_blocked_gets--;
        get_ap.write( t );
    endtask

       当 uvm_tlm_analysis_fifo 上 的 _put_export 族端口被连接到 一个 put 端口时,且调用 put 函数时,put 函数会把传递过来的 transactoin 放在 uvm_tlm_analysis_fifo 的缓存单元 m (mailbox)中,同时把这个 transaction 通过 put_ap 端口的 write 函数发送给与之相连的端口;当 uvm_tlm_analysis_fifo 上 的_get_export 族 被连接到一个 get 端口,且调用 get 函数时,过程类似。我们常用的是 analysis_export 端口。带 FIFO 缓存的端口类型除了 uvm_tlm_analysis_fifo,还有一种是 uvm_tlm_fifo, 区别在于前者有一个 analysis_export 端口和一个 write 函数。

       现在以 monitor 与 reference model 的通信为例,其 monitor 端代码如下

    monitor
    class my_monitor extends uvm_monitor;
        `uvm_component_utils(my_monitor)
        uvm_analysis_port(my_transaction) A_port;
        ...
    endclass
    ...
    virtual function void my_monitor::build_phase(uvm_phase phase);
        super.build_phase(phase);
        A_port=new("A_port",this);
        ...
    endfuction
    

    virtual function void my_monitor::main_phase(uvm_phase phase);
    my_transaction tr;
    forever begin
    assert(tr.randomize());
    A_port.write(tr);
    end
    endfuction

       其 reference model 端代码如下

    reference model
    class my_reference  extends uvm_component;
        `uvm_component_utils(my_reference  )
        uvm_tlm_analysis_fifo(my_transaction) A_fifo_port;
        ...
    endclass
    ...
    virtual function void my_reference  ::build_phase(uvm_phase phase);
        super.build_phase(phase);
        A_fifo_port=new("A_fifo_port",this);
        ...
    endfuction
    

    virtual function void my_reference ::main_phase(uvm_phase phase);
    my_transaction tr = null;
    forever begin
    A_fifo_port.get(tr);
    ...
    end
    endfuction

      顶层 env 连接代码:

    my_env
    class my_env extends uvm_env;
        ...
    endclass
    ...
    virtual function void my_scoreboard::connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        agt.monitor.A_port.connect(ref.A_fifo_port.analysis_export)
        ...
    endfuction

      其中 monitor 通过 uvm_analysis_port A_port 调用 write 函数将 my_transaction 存入 uvm_tlm_analysis_fifo 的缓存单元中,在 reference model 中通过 get 函数将 tranansction 取出进行后续处理。

    3.端口特例

      这组特例 port 就是 uvm_seq_item_pull_port 和 uvm_seq_item_pull_imp ,通过名字可以知道,这组端口通常是在 sequencer 和 driver 中使用的,用于它们之间的数据传递。uvm library 中 原型代码如下所示:

    uvm_seq_item_pull_port
    class uvm_driver #(type REQ=uvm_sequence_item,type RSP=REQ) extends uvm_component;
        uvm_seq_item_pull_port #(REQ,RSP) seq_item_port;
        ...
    endclass
    class uvm_sequencer #(type REQ=uvm_sequence_item,type RSP=REQ) extends uvm_sequencer_para_base #(REQ,RSP);
        uvm_seq_item_pull_imp #(REQ,RSP) seq_item_export;
        ...
    endclass

      在使用时,因为 sequencer 中的操作被隐藏,所以使用时很简单,实例如所示:

    uvm_seq_item_pull_port 实例
    class my_sequence extends uvm_sequence #(my_transaction);
        `uvm_object_utils(my_sequence);
        virtual task body(uvm_phase phase);
            `uvm_do(req);
            get_response(rsp);
        endtask
    endclass
    class my_sequencer extends uvm_sequencer #(my_transaction);
        `uvm_component_utils(my_sequencer);
        function new(string name, uvm_component phase);
            super.new(name,parent);
        endfunction
        function void build_phase(uvm_phase phase);
            super.build_phase(phase);
        endfunction  
    endclass
    class my_driver  extends uvm_driver #(my_transaction);
         `uvm_component_utils(my_driver  );
        ...
        virtual task run_phase(uvm_phase phase);
            my_transaction tr;
            while(1)begin
                seq_item_port.get_next_item(req);
                send(tr);
                se_item_port.item_done();
                seq_item_port.put_response(rsp);
            end
        endtask
    endclass
      sequence 发送数据,driver 接受数据,具体细节将在第 5 节中讲述。
  • 相关阅读:
    Max Sum Plus Plus_DP
    Prime Ring Problem_DFS
    Swaps in Permutation _并查集 + 优先队列
    Roadblocks_次短路
    Reward_toposort
    确定比赛名次_toposort
    Zipper_DFS
    Chopsticks_DP
    搬寝室_DP
    Passing the Message_单调栈
  • 原文地址:https://www.cnblogs.com/dreamCll/p/11756766.html
Copyright © 2020-2023  润新知