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给另一个组件上。
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。
在这种情况下,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,如下图所⽰。
当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()的实现。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !