UVM TLM的基本概念介绍

描述

Transactions

在UVM中,transaction 是一个类对象,它包含了建模两个验证组件之间的通信所需的任何信息。一个用来传输信息的简单总线协议transaction 建模示例如下:

 

class simple_trans extends uvm_sequence_item;
rand data_t data;
rand addr_t addr;
rand enum {WRITE,READ} kind;
constraint c1 { addr < 16’h2000; }
...
endclass

 

transaction 对象包含变量、约束以及生成和操作事务所需的其他字段和方法。封装在transaction 中的信息表明了建模的抽象级别。上面的simple_trans可以被扩展为包含更多的信息(例如要注入的等待状态的数量、传输的大小或其他属性),还可以对此transaction 扩展以增加更多定向约束。

Transaction-Level 通信

Transaction-level接口定义了一组使用transaction 作为参数的方法。TLM port定义了一组方法,与之相连的TLM export提供了这些方法的实现(implementation )。

基本TLM通信

最基本的transaction-level操作就是一个组件将一个transaction put给另一个组件上。

FIFO存储

producer 上的方框表示一个port ,consumer 上的圆圈表示export。

producer 生成transactions 并将其发送出去:

 

class producer extends uvm_component;
uvm_blocking_put_port #(simple_trans) put_port; // 1 parameter
function new( string name, uvm_component parent);
  put_port = new(“put_port”, this);
  ...
endfunction
virtual task run();
  simple_trans t;
for(int i = 0; i < N; i++) begin
// Generate t.
  put_port.put(t);
  end
endtask

 

uvm_*_port参数化为将要通信的transaction 类型。put()调用的实际实现由consumer提供。

 

class consumer extends uvm_component;
uvm_blocking_put_imp #(simple_trans, consumer) put_export; // 2 parameters
...
task put(simple_trans t);
case(t.kind)
    READ: // Do read.
    WRITE: // Do write.
  endcase
endtask
endclass

 

uvm_*_imp接受两个参数:transaction 的类型和声明方法实现的对象类型。producer 中的put()调用将阻塞,直到consumer的put实现完成。除此之外,producer的操作完全独立于put 的实现(uvm_put_imp)。事实上,consumer 可以被另一个也实现了put方法的组件所取代,producer 可以继续以完全相同的方式工作。

所以说,TLM促进了验证环境开发的模块化,因为一个清晰的接口定义,所以组件很容易复用。

相反的操作是get

FIFO存储

在这种情况下,consumer 通过其get端口向producer 请求transactions :

 

class get_consumer extends uvm_component;
uvm_blocking_get_port #(simple_trans) get_port;
function new( string name, uvm_component parent);
  get_port = new(“get_port”, this);
  ...
endfunction
virtual task run();
  simple_trans t;
for(int i = 0; i < N; i++) begin
// Generate t.
    get_port.get(t);
  end
endtask

 

get()实现由producer提供。

 

class get_producer extends uvm_component;
  uvm_blocking_get_imp #(simple_trans, get_producer) get_export;
  ...
task get(output simple_trans t);
    simple_trans tmp = new();
// Assign values to tmp.
    t = tmp;
  endtask
endclass

 

与上面put()一样,get_consumer的get()将阻塞,直到get_producer的方法完成。在TLM术语中,put()和get()是阻塞方法(blocking )。

在这两个示例中,都有一个进程在运行,控制流都是从port 传递到export ,而数据流都是从producer 到consumer。

进程间通信

在上⾯的put ⽰例中,consumer只有在其 put() ⽅法被调⽤时才会处于活动状态。在许多情况下,组件可能需要独⽴运⾏,其中producer在⼀个进程中创建transactions,⽽consumer需要在另⼀个进程中对这些transactions进⾏操作。 

UVM 提供了 uvm_tlm_fifo来促进这种进程间通信。uvm_tlm_fifo实现了所有的TLM接⼝⽅法,因此producer将transaction放⼊uvm_tlm_fifo,⽽consumer则独⽴从fifo中获取transaction,如下图所⽰。 

FIFO存储

当producer向fifo中put一个transaction时,如果fifo已满则阻塞,否则成功将transaction放入fifo并立即返回。如果fifo中存在transaction,get操作将立即返回这个transaction,并且从fifo中删除这个transaction,否则它将阻塞直到能够获取一个transaction。

因此,两次连续的get() 将向consumer产生不同的transaction。而 peek()方法返回transaction的副本而不删除它,所以两次连续的peek()将返回同一transaction的副本。

阻塞(Blocking)和非阻塞(Nonblocking)

到目前为止我们看到的接口都是阻塞的(blocking),阻塞任务不允许失败,阻塞执行直到它们完成。阻塞期间,仿真时间会继续向后走。相反,非阻塞任务调用会立即(同一个delta cycle,不消耗仿真时间)返回。所以,在 UVM 中,非阻塞任务调用就是一个functions。

 

class consumer extends uvm_component;
uvm_get_port #(simple_trans) get_port;
task run;
  ...
for(int i=0; i<10; i++)
if(get_port.try_get(t))
//Do something with t.
      ...
endtask
endclass

 

如果存在transaction,它将在参数中返回,函数调⽤本⾝将返回TRUE。如果不存在transaction,函数将返回FALSE。类似地,还有try_peek()和try_put()⽅法 。

连接Transaction-Level组件

在为transaction-level组件定义了ports和exports之后,它们之间的连接是通过上一级验证组件中的connect()方法完成的。在验证环境中,ports和exports之间的 connect() 调用建立peer-to-peer连接或者层次化的连接,最终连接都终止于put()或者get()方法的实现。当组件调用

 

put_port.put(t);
意味着它实际上调用
target.put_export.put(t);
其中target是port连接到的组件。

 

Port/Export 兼容性

UVM中TLM 通信的另一个优点是在运行用例前检查所有TLM 连接的兼容性。为了使连接有效,export必须提供对应的方法并且数据参数类型相同。

blocking_put_port可以连接到 blocking_put_export 或 put_export,blocking_put_export需要实现put()方法的实现,put_export 还提供 try_put() 和 can_put()的实现。

​​​




审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分