1.验证平台内部的通信
我们希望在验证平台内部找到两个component之间适合通信的方法,在接触TLM之前,想到的方法无非有采用全局变量、通过config_db传输等等。然而全局变量因为安全性不高,是我们长期以来竭力避免使用的方法;config_db虽然相对安全,但需要拉入basetest的参与,也不能保证由basetest派生而来的类会不会改变config_db类中某些变量的值,仍然存在风险。
不仅如此,加入了非阻塞和阻塞的概念之后,上述方法都难以满足要求。如果scoreboard主动向monitor请求数据,该如何实现这样的功能呢?
答案是在monitor和scoreboard之间建立一个专门的通道,借由TLM我们可以实现这种功能。
2.TLM(transaction level modeling)
TLM起源于SystemC一种通信标准,所谓的transcation level是相对DUT中各个模块之间信号线级别的通信而言。transaction就是把具有某一特定功能的一组信息封装在一起成为的一个类。
注意:TLM仅仅适用于component之间的连接。
2.1 initiator与target
initiator与target的概念描述的是控制流的方向,其中initiator永远是数据的请求方而target是响应方,这与数据究竟从谁流向谁无关。也就是说initiator永远有主动权,不论请求数据还是主动发送数据,永远是发起的那一方。
常用的操作包含有put、get、transport等。
put描述的是数据从initiator流向target;
get描述的是数据从target流向initiator;
transport相当于一次put加上一次get。三种操作都有阻塞和非阻塞之分。
2.2 各种端口的互联
通信端口分为3类:port、export、imp,表示从initiator向target发起的request传输方向。
port:通信请求方initiator的发起端;
export:作为initiator和target中间层次的端口;
imp:只能作为target接收请求的响应端,它无法作为中间层次的端口,imp为通信终点。
每种端口的声明各有15种,对应是否阻塞以及具体使用的通信方法:
uvm_blocking_put_port#(T);
uvm_nonblocking_put_port#(T);
uvm_put_port#(T);
uvm_blocking_get_port#(T);
uvm_nonblocking_get_port#(T);
uvm_get_port#(T);
uvm_blocking_peek_port#(T);
uvm_nonblocking_peek_port#(T);
uvm_peek_port#(T);
uvm_blocking_get_peek_port#(T);
uvm_nonblocking_get_peek_port#(T);
uvm_get_peek_port#(T);
uvm_blocking_transport_port#(REQ,RSP);
uvm_nonblocking_transport_port#(REQ,RSP);
uvm_transport_port#(REQ,RSP);
以port为例,这里声明的是采用put方法的port端口,T标志的是transaction的类型。对export的声明,只需要把port改成export,其他均与port一致。
而imp的声明还需要加入一个参数:IMP指示当前imp对应的component
uvm_blocking_put_imp#(T,IMP);
uvm_nonblocking_put_imp#(T,IMP);
uvm_put_imp#(T,IMP);
uvm_blocking_get_imp#(T,IMP);
uvm_nonblocking_get_imp#(T,IMP);
uvm_get_imp#(T,IMP);
uvm_blocking_peek_imp#(T,IMP);
uvm_nonblocking_peek_imp#(T,IMP);
uvm_peek_imp#(T,IMP);
uvm_blocking_get_peek_imp#(T,IMP);
uvm_nonblocking_get_peek_imp#(T,IMP);
uvm_get_peek_imp#(T,IMP);
uvm_blocking_transport_imp#(REQ,RSP,IMP);
uvm_nonblocking_transport_imp#(REQ,RSP,IMP);
uvm_transport_imp#(REQ,RSP,IMP);
端口优先级:port > export > imp, 使用connect()建立连接关系时,只有优先级高的才能调用connect()做连接,即port可以连接port、export或者imp;export可以连接export或者imp;imp只能作为数据传送的终点,无法扩展连接,无法调用connect()。
下面让我们来连接一个简单的get型传输:
class A extends uvm_component; //A作为动作接收者
`uvm_component_utils(A)
uvm_blocking_get_export#(my_transaction) A_export;
uvm_blocking_get_imp#(my_transaction,A) A_imp;
my_transaction tr_q[$];
...... // extern A的一系列phase
endclass
function void A::build_phase(uvm_phase phase);
super.build_phase(phase);
A_export = new("A_export",this);
A_imp = new("A_imp",this);
endfunction
function void A::connect_phase(uvm_phase phase);
super.connect_phase(phase);
A_export.connect(A_imp);
endfunction
task A::get(output my_transaction tr); // A作为接收者需要定义get任务
while(tr_q.size()==0) #2;
tr = tr_q.pop_front();
endtask
task A::main_phase(uvm_phase phase);
my_transaction tr;
repeat(10) begin
#10;
tr = new("tr");
tr_q.push_back(tr);
end
endtask
A的get任务每隔两个时间单位检查队列中是否有数据,如果有则向外发出,当B在main_phase中调用get任务时,最终执行的是A的get任务。A的connect phase需要完成export与imp的连接。
class B extends uvm_component;
`uvm_component_utils(B);
`uvm_blocking_get_port#(my_transaction) B_port;
......
endclass
function void B::build_phase(uvm_phase phase);
super.build_phase(phase);
B_port = new("B_port",this);
endfunction
task B::main_phase(uvm_phase phase);
my_transaction tr;
while(1) begin
B_port.get(tr);
`uvm_info("B","get a transaction",UVM_LOW);
tr.print();
end
endtask
在env中对他们进行连接:
function void my_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
B_inst.B_port.connect(A_inst.A_export);
endfunction
以上介绍了blocking_get_port与blocking_get_export、blocking_get_imp的连接。需要谨记的是连接的终点一定是一个imp。若我们需要使用到nonblocking端口,则要将原先的task换用函数(不耗时)来实现。