2011-01-13 18:44:02| 分类: SystemVerilog | 标签: |举报 |字号大中小 订阅
UVM中TLM的使用非常方便,通过简单的connect()函数,就可以将两个不相关的对象紧紧联系在一起。为什么会如此简单,其背后的原理是什么呢?今天抽空分析了UVM的源代码,基本上了解了put_port/put_imp的实现机制。于是借用UVM的实现原理,我简单了编写了一个小程序,抛掉不必要的繁文缛节,基本上能够展示其背后的实现原理。
代码如下:
1 /** 2 * * tlm.sv 3 * * 说明: 通过简单的模型来阐述 UVM/OVM中TLM uvm_put_port 4 * * 和uvm_port_imp直接连接的基本原理。直接编译运行即可。 5 * * 作者:http://electron64.blog.163.com 6 */ 7 virtual class port_base#(type T=int) ; // 构造一个抽象类 8 typedef port_base#(T) this_type; 9 protected this_type port_handle; // 申明一个对象句柄(指针) 10 11 pure virtual task put_t(T trans); // 纯虚函数,put_port和put_imp都必须以它为基类 12 13 function void connect(this_type port); // connect仅仅是实现对象句柄的赋值 14 port_handle = port; 15 endfunction // connect 16 17 endclass // port_base 18 19 class put_port#(type T=int) extends port_base#(T); 20 virtual task put_t(T trans); 21 port_handle.put_t(trans); 22 endtask // put_t 23 endclass // put_port 24 25 class put_imp#(type T=int, type IMP=int) extends port_base#(T); 26 local IMP m_imp; // imp表示具体实现put_t()函数的对象句柄。 27 function new(IMP imp); 28 m_imp = imp; 29 endfunction // new 30 31 virtual task put_t(T trans); 32 m_imp.put_t(trans); // 调用实现对象对应的put_t()函数 33 endtask // put_t 34 35 endclass // put_imp 36 37 class producer; 38 put_port#(int) m_put_port; 39 function new(); 40 m_put_port = new(); 41 endfunction // new 42 43 task run(); 44 for(int i=0; i<10; i++) begin // 顺序产生10个int类型的transaction. 45 m_put_port.put_t(i); 46 end 47 endtask // run 48 endclass // producer 49 50 class consumer; 51 put_imp#(int, consumer) m_put_export; 52 function new(); 53 m_put_export = new(this); //将consumer句柄传递给put_imp中imp变量,从而实现接管put_t()最终实现 54 endfunction // new 55 56 task put_t(int trans); 57 $display("[INFO]: Got the transaction:%d", trans); 58 endtask // put_t 59 endclass // consumer 60 61 module top; 62 producer p; 63 consumer c; 64 initial begin 65 p = new(); 66 c = new(); 67 p.m_put_port.connect(c.m_put_export); //通过句柄赋值进行连接 68 p.run(); 69 #10 $finish; 70 end 71 endmodule
总共需要5个类,其中一个是抽象类(port_base),必须保证相互连接的两个对象port/export/imp都属于同一个基类,这样句柄赋值才能够类型一致。另外,要注意的是,为了调用consumer的真正put_t()的实现,需要借用put_imp这个桥梁,put_imp对象对put_t()的实现仅仅是调用其m_imp对象的put_t()(第31-33行),用这种方式实现了函数层次的传递。
完成整个TLM put_port/put_imp用到了面向对象设计中的继承、组合、多态的技术。其中抽象类 port_base提供了一个数据接口port_handle,和两个任务接口:put_t()和connect(). put_t()表示需要进行连接的任务名称,connect()的目的是进行对象句柄赋值。第13-15行可以看出,connect()的实现非常简单。其中用到了systemverilog中对象的特性:参考http://electron64.blog.163.com/blog/static/106033970201092785450187/。
类对象通过引用传递。其他Systemverilog类型通过值传递。
因此,port_handle看作一个指针,用它来指向最终想要的目标对象,通过调用目标对象的相应方法,就可以完成方法调用的层次化传递,完成TLM的功能。
UVM中,实现了port_base, put_port, put_imp三个类,分别对应于uvm_port_base, uvm_blocking_put_port, uvm_blocking_put_imp,他们之间的UML关系示意图如下:
运行结果如下:
# [INFO]: Got the transaction: 0
# [INFO]: Got the transaction: 1
# [INFO]: Got the transaction: 2
# [INFO]: Got the transaction: 3
# [INFO]: Got the transaction: 4
# [INFO]: Got the transaction: 5
# [INFO]: Got the transaction: 6
# [INFO]: Got the transaction: 7
# [INFO]: Got the transaction: 8
# [INFO]: Got the transaction: 9
# ** Note: $finish : tlm.sv(68)
# Time: 10 ns Iteration: 0 Instance: /top
评论