概述
UVM TLM2.0简介及应用实例分析
通过前面博文的介绍,我们知道了UVM中各个组件之间的通信是通过TLM1.0的方式实现的。而伴随着SystemC模型的广泛应用,SystemC的主要通信机制TLM2.0也引起了UVM标准委员会的广泛兴趣。我们之前介绍过TLM协议本身并不依赖于某一种语言,而可以跨语言地完成它的传输标准。TLM2.0是SystemC的模型之间的核心传输方式,它在2009年发布,并随后成为IEEE的标准IEEE-1666-2011。与TLM1.0相比,TLM2.0的提供了一些更丰富的特性,它们主要包括:
- 双向的阻塞或者非阻塞接口
- 时间标记
- 统一的数据包
通过这些更加一致和丰富的特性,使得接口之间的通信接口更趋于标准,并且更容易构建更抽象的模型。虽然TLM2.0一开始作为SystemC标准库的一部分(由C++实现),但是RTL与SystemC模型的混合仿真趋势,要求SystemVerilog也能够有与之匹配的接口便于日后的互相嵌套。所以我们本节将重点介绍TLM2.0的若干特性、它们在UVM中的实现方式以及通过一些例码为读者们介绍它们的使用方式。
一、接口实现
TLM2.0的传输是双向的,即意味着在一次完整的传输中有request和response类型。这么听起来,似乎和我们在TLM1.0中的transport接口时类似的。先别急着下结论,TLM2.0首先支持blocking和nonblocking两种transport方式。
- blocking的传输方式要求在一次传输过程中,完成request和response的传输。
- nonblocking的传输方式则将request和response的传输分为了两个独立的单向传输,而整体完成了一次完整传输。
1.1、上面的两种传输方式对应的SV的方法如下:
task b_transport(T t, uvm_tlm_time delay);
function uvm_tlm_sync_e nb_transport_fw(T t, ref P p, input uvm_tlm_time delay);
function uvm_tlm_sync_e nb_transport_bw(T t, ref P p, input uvm_tlm_time delay);
- 这里T代表着统一的传输数据类uvm_tlm_generic_payload,而P代表着在nonblocking传输方式中用来做状态同步的类型。
在定义TLM2.0的过程中,仍然有initiator和target的概念,也有port、export以及imp的端口类型。正如上一节课谈到的,对于port类型,它是用来发起请求用来调用target一端的接口方法,而export用来传导这一要求,最后由imp端口的例化组件来实现数据传输的方法。
为了区别于TLM1.0对于端口类型的称谓,在UVM中实现的TLM2.0端口类型称之为socket。它们是由port、export和imp组合而成的。一个socket首先是双向的传输的,这一点类似于上一节中谈到的双向传输。
- 例如在TLM1.0中的双向传输中transport可以用来做单次完成的双向传输,master和slave端口用来完成多次单向的传输。
- 而socket则按照blocking和nonblocking的传输方式,和initiator和target的发起区别一共可以分为下面的端口类型:
uvm_tlm_b_initiator_socket
uvm_tlm_b_target_socket
uvm_tlm_nb_initiator_socket
uvm_tlm_nb_target_socket
uvm_tlm_b_passthrough_initiator_socket
uvm_tlm_b_passthrough_target_socket
uvm_tlm_nb_passthrough_initiator_socket
uvm_tlm_nb_passthrough_target_socket
从下面的类的继承树来看,这些socket类型也都继承于uvm_port_base,具有同TLM1.0端口一样的基函数。而对于这些socket的内部,它们则是通过例化port、export以及imp最终来实现数据双向传输的。
二、TLM2.0的port、export和imp类型与TLM1.0的差异
TLM2.0的port、export和imp类型不同于TLM1.0的部分,首先这些相关的端口类型是新引入的类,例如uvm_tlm_b_transport_port、uvm_tlm_b_transport_export和uvm_tlm_b_transport_imp。这里没有改变的概念是不同端口类型之间的连接关系,改变的只是新的端口类型名所匹配的方法不再是put()、get()、peek(),而是变为了b_transport()、nb_transport_fw()和nb_transport_bw()。这些socket通过内置例化这些端口的例化,最终可以实现数据的双向传输。上面8种socket可以按照传输方式和发起方向来区分:
Blocking | Nonlocking | |
initiator | IS-A fw port | IS-A fw port HAS-A bw imp |
target | IS-A fw port | IS-A fw imp HAS-A bw port |
pass-through initiator | IS-A fw port | IS-A fw port HAS-A bw export |
pass-through target | IS-A fw export | IS-A fw export HAS-A bw port |
- IS-A代表这继承关系
- HAS-A表示对象之间的关联
- fw表示request的发送通道
- bw表示response的发送通道。
三、传送数据
在TLM1.0中传送的数据类型是由用户自己定义的,这就对组件之间的数据传输做出了更多的限制。例如,如果端口的传输数据类型不同,则端口无法连接,同时针对不同的传送数据类型,相应的传送方法也要做出调整,因此这种方式实际上不利于组件之间的快速连接和整个平台的搭建。
TLM2.0中对于传送的数据也提出了一致化的要求,这里统一的数据类由uvm_tlm_generic_payload表示,即上面的传输方法中使用的数据类都应该为uvm_tlm_generic_payload。
为了保持TLM2.0端口部分的良好连接性,我们并不建议再在uvm_tlm_generic_payload的基础上做出更多的扩展,实际上这个类本身就可以容纳更多的扩展数据域部分。接下来,我们逐个分析这个类种的各个域的功能,在分析前读者需要知道的是,TLM2.0一开始发布的背景就是为了解决总线级别的抽象问题,所以它的统一数据包格式也是按照总线数据的内容来定义的。
No. | 描述 | |
1 | bit [63:0] m_address | 数据读写的地址 |
2 | uvm_tlm_command_e m_command | 数据的读写命令 |
3 | byte unsigned data[] | 写入的数据或者读出的数据,由byte unsigned的类型构成动态数组,这是按照总线传输的最小粒度进行划分,便于target一侧进行数据的整合 |
4 | int unsigned length | data数组的长度,该数值应该与data数组的实际容量保持一致 |
5 | uvm_tlm_response_status_e m_response_status | 由target返回的状态值,表示数据传输是否完成和有效 |
6 | byte unsigned m_byte_enable[] | 用来标记写入数据的有效性,标记哪个byte应该写入 |
7 | int unsigned m_byte_enable_length | 该数值应该等于m_byte_enable数组的容量值 |
8 | m_stream_width | 用来表示连续传输时的数据传输长度 |
9 | uvm_tlm_extension_base m_extensions [uvm_tlm_extension_base] | 如果一些数据域不在上面的部分,那么则可以在这个数据延伸域中添加 |
从上面各个数据域的介绍来看,对于一般的总线传输而言,这里的包含的数据信息已经足够,那么如果该传输还包括其它的数据内容,该怎么办呢?一种办法是,将其压包作为数据成员data数组中的一部分,或者另外一种办法是创建新的uvm_tlm_extension类,将其它额外的数据成员装入到该数据延伸对象中,通过下面的方法:
uvm_tlm_generic_payload::set_extension(uvm_tlm_extension_base ext)
来添加这一部分的数据。对于一个数据类而言,拷贝、比较和打印功能是必不可少的,而该类已经提供了如下回调函数来满足这一要求:
- do_copy()
- do_compare()
- do_print()
四、时间标记
不同的时间标记间隔是SystemC可以构建不同时间精确度的重要手段。尽管SystemC原则上也可以通过自建时钟源通过时钟事件来驱动内部逻辑,但是为了提高模型的运行效率,还是将整个数据传输和处理的时间都通过标记的时间来反映出来,这就很大程度上避免了时钟依赖。在TLM2的传输中,由于可以标定延迟时间,这使得target端可以模拟延迟,并且在准确的延迟时刻来做出响应。
为了能够便于标记延迟时间,例如实数范围的延迟1.1ns(SystemVerilog继承与Verilog的时间精度方式,只能使用整数的延迟方式),UVM新建了一个时间类uvm_tlm_time,这个时间类的便捷之处在于用户可以随时设置它的时间单位(默认为1ps),并且进行时间的增减设置操作。同时,这个类的存在,也是为了解决在不同模块或包之间出现不同时间单位和精度单位的问题。可以有了这么灵活的时间类,对于target一侧要进行时间等待这些操作就容易得多了,也不会出现时间和精度单位错误的问题。
五、典型使用
接下来我们就利用TLM2的socket来建立一个轻量的例子,读者们可以从这个例子中体会一下与TLM1.0相比,TLM2.0传输的特点有哪些。
5.1、Component A中声明initiator_socket
class component_a extends uvm_component;
uvm_tlm_b_initiator_socket b_int_skt;
function new(string name, uvm_component parent);
super.new(name, parent);
b_int_skt = new("b_int_skt", this);
endfunction : new
`uvm_component_utils(component_a)
external virtual task run phase(uvm_phase phase);
endclass : component_a
task component_a::run_phase(uvm_phase phase);
byte unsigned data[] = {1, 2, 3, 4, 5, 6, 7, 8};
uvm_tlm_generic_payload pkt = new("pkt");
uvm_tlm_time delay = new("delay");
pkt.set_address('h0000_F000);
pkt.set_data_length(8);
pkt.set_data(data);
pkt.set_byte_enable_length(8);
pkt.set_write();
delay.incr(0.3ns, 1ps);
`uvm_info("INITRSP", $sformatf("initiated a trans @ %0d ps", $realtime()), UVM_LOW)
b_int_skt.b_transport(pkt, delay);
endtask : run_phase
5.2、Component B的实现
class component_b extends uvm_component;
uvm_tlm_btarget_socket#(component_b) b_tgt_skt;
function new(string name, uvm_component parent);
super.new(name, parent);
b_tgt_skt = new("b_tgt_skt", this);
endfunction : new
`uvm_component_utils(component_b)
external virtual task b_transport(uvm_tlm_generic_payload pkt, uvm_tlm_time delay);
endclass : component_b
task component_b::b_transport(uvm_tlm_generic_payload pkt, uvm_tlm_time delay);
`uvm_info("TGTTRSP", $sformatf("received a trans @ %0d ps", $realtime()), UVM_LOW)
pkt.print();
#(delay.get_realtime(1ps));
pkt.set_pesponse_status(UVM_TLM_OK_RESPONSE);
`uvm_info("TGTTRSP", $sformatf("completed a trans @ %0d ps", $realtime()), UVM_LOW)
pkt.print();
endtask : run_phase
5.3、在Environment中完成例化和链接
class test_env extends uvm_env;
component_a comp_a;
component_b comp_b;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
`uvm_component_utils(test_env)
external virtual function void build_phase(uvm_phase phase);
external virtual function void connect_phase(uvm_phase phase);
endclass : test_env
function test_env::build_phase(uvm_phase phase);
super.build_phase(phase);
comp_a = component_a::type_id::create("comp_a", this);
comp_b = component_b::type_id::create("comp_b", this);
endfunction : build_phase
function test_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
comp_a.b_int_skt.connect(comp_b.b_tgt_skt);
endfunction : connect_phase
5.4、Testcase实现
class my_test extends uvm_test;
test_env env;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
`uvm_component_utils(my_test)
external virtual function void build_phase(uvm_phase phase);
external virtual task run_phase(uvm_phase phase);
endclass : my_test
function my_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = test_env::type_id::create("env", this);
endfunction : build_phase
task test_env::run_phase(uvm_phase phase);
super.run_phase(phase);
phase.rase_objection();
#1us;
phase.drop_objection();
endtask : run_phase
5.5、 Top的实现
module tlm2_socket_test();
import uvm_pkg::*;
`include "uvm_macro.svh"
initial begin
run_test("my_test");
end
endmodule
从这个例子可以看出,有了标准的传输数据包和准确的延迟时间,方便了模块之间的复用和建立更高层级的模型。尽管目前UVM组件之间的传输仍建立在TLM1.0,也不排除日后要求支持TLM2.0的传输方式。至少目前,我们已经可以看到一些成熟的商业VIP的接口已经同时支持TLM1.0的sequence item传输方式和TLM2.0的socket接口方式。从实用角度来看,TLM2.0在UVM中的实现,是为了可以同SystemC的TLM2.0接口可以无缝衔接
六、致谢
6.1、本文转自:https://www.sohu.com/a/139682910_778637
6.2、特别鸣谢:路科验证
最后
以上就是可靠乌龟为你收集整理的[UVM]UVM TLM2.0简介及应用实例分析 UVM TLM2.0简介及应用实例分析的全部内容,希望文章能够帮你解决[UVM]UVM TLM2.0简介及应用实例分析 UVM TLM2.0简介及应用实例分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复