目录
1.TLM通信的意义
在systemverilog中,组件之间的通信需要通过mailbox、event或者semaphore进行。在driver、monitor、scoreboard、reference_model 之间通信,往往需要编写大量的通信相关的代码,同时通信又存在阻塞和非阻塞的概念,这些复杂的通信加上一些全局变量的存在往往会造成逻辑和通信混乱。针对这些情况,UVM增加TLM(transaction level modeling 事务级建模),专门提供端口用于通信,用于数据的传输。
2. TLM通信的构成
TLM由对应的端口和端口方法构成。一般在不同组件中声明端口,然后在更高层将端口相连,并在 imp中调用端口方法实现事务级通信传输。
2.1 端口分类和端口方法
端口分类:
port:作为initiator的发起端,initiator凭借port才可以访问target的TLM通信方法。
export:作为initiator和target中间层次的端口。
imp:只能作为target接受request的末端,它无法作为中间层次的端口,所以imp的连接无法再延伸。
在介绍端口方法前,先介绍通信的一些基本概念。在TLM中,通信对象可以分为initiator和target。initiator是通信的发起方,而target端是通信的响应端。按照数据流来分又可以分为producer和consumer,即数据从哪里产生,又流向了哪里。
端口方法:
put:A(initiator)把transaction发送给B(target)。数据流向为A=>B,A为producer而B为consumer。
get:A(initiator)向给B(targt)索要transaction。数据流向为A <=B,B为producer而A为consumer。
transport: transport = A + B;即A先put给B,A再从B那边get。initiator都是A。
同时端口又分为阻塞和非阻塞,按照端口分类和端口类型,可以将port分为:
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);
export与port类似,可以看出每个port可以分为阻塞与非阻塞,并都有其对应实现的功能。
2.2 端口的连接
在声明好端口后,要将端口连接才能使端口之间产生联系。在UVM中,用connect函数进行端口连接。如:A.port.connect(B.export)(A是initiator,B是target,只有initiator才能调用connect函数而target作为被连接的参数)。
在端口连接时,port可以连接port、export、imp。 export可以连接export、imp。而imp则作为连接的终点。即port优先级最高,imp优先级最低,优先级高的能连接优先级低的(export不能连接port)。
同时对于TLM通信,连接的终点必须是imp。
在定义imp时(如`uvm_blocking_put_imp#(T,IMP)),会比port和export多一个参数,T是传输的transaction类型,而IMP是UVM实现这个接口的component。实际上,在调用put、get等函数/任务时,TLM只是起一个通道的作用,最终的方法实现还是要由imp所在的component实现。(如同流水线生产中的履带,只是起运输作用,最终产品的加工还是由机器来完成)。
示例:
class A extends uvm_component;
……
`uvm_blocking_put_port#(transaction) A_port; //port 声明
function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_port = ("A_port",this); ///port 创建
endfunction
task run_phase(uvm_phase phase);
transaction tr;
super.run_phase(phase);
phase.raise_objection(this);
repeat(10) begin
tr = new("tr");
assert(tr.randomize());
A.port.put(tr); //调用put函数
#10;
end
phase.drop_objection(this);
endtask
endclass
class B extends uvm_component;
……
`uvm_blocking_put_imp#(transaction,B) B_imp;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
B_imp = ("B_imp",this);
endfunction
function void put(transaction tr);
`uvm_info("B","Get a transaciton",UVM_LOW);
tr.print();
endtask
endclass
class y_env extends uvm_env;
……
A A_inst;
B B_inst;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_inst = A::type_id::create("A_inst",this); //port的例化
B_inst = B::type_id::create("B_inst",this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
A_inst.A_port.connect(B_inst.B_imp); //port的连接
endfunction
……
endclass
3. 双向通信
双向通信也分为initiator和target,但数据的流向是双向的,双向通信的端口如下:
`uvm_blocking_transport_port;`uvm_nonblocking_transport_port;`uvm_transport_port
示例:
class A extends uvm_component;
……
`uvm_blocking_transport_port#(transaction,transaction) A_transport; //req,rsp
function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_port = ("A_port",this); ///port 创建
endfunction
task run_phase(uvm_phase phase);
transaction tr;
super.run_phase(phase);
phase.raise_objection(this);
repeat(10) begin
tr = new("tr");
assert(tr.randomize());
A.port.transport(tr,rsp); //调用transport
#10;
end
phase.drop_objection(this);
endtask
endclass
class B extends uvm_component;
……
`uvm_blocking_transport_imp#(transaction,transaction,B) B_imp; //req,rsp, component
function void build_phase(uvm_phase phase);
super.build_phase(phase);
B_imp = ("B_imp",this);
endfunction
task transport(transaction req,ouput transaction rsp); //定义transport任务
`uvm_info("B","receive a transaction",UVM_LOW)
req.print();
#5;
rsp = new ("rsp");
endtask
endclass
class y_env extends uvm_env;
……
A A_inst;
B B_inst;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_inst = A::type_id::create("A_inst",this);
B_inst = B::type_id::create("B_inst",this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
A_inst.A_transport.connect(B_inst.B_imp); //port的连接
endfunction
……
endclass
4. analysis_port
之前介绍的port、export、imp在默认情况下都是一个port(export)对一个imp的 ,在UVM中可以通过analysis_port和analysis_export 来实现一对多的通信(一个port对多个imp)。
analysis_port没有阻塞和非阻塞的概念,它本身是广播,不需要与其相连端口的响应,analysis_port可以与多个imp相连,前提是imp类型必须是uvm_analysis_imp。
对于analysis_port,需要在imp所在的component函数中定义一个write函数,当analysis_port广播到imp时,会自动执行imp端口的write函数。
示例:
class A extends uvm_component;
……
`uvm_analysis_port#(transaction) A_ap; //声明ap端
function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_ap = ("A_ap",this); ///port 创建
endfunction
task run_phase(uvm_phase phase);
transaction tr;
super.run_phase(phase);
repeat(10) begin
tr = new("tr");
assert(tr.randomize());
A_ap.write(tr); //调用write 函数
#10;
end
endtask
endclass
class C extends uvm_component;
……
`uvm_analysis_imp#(transaction) B_imp; //声明ap_imp
function void build_phase(uvm_phase phase);
super.build_phase(phase);
B_imp = ("B_imp",this);
endfunction
function void write (transaction tr); //定义write函数
`uvm_info("B","receive a transaction",UMV_LOW)
tr.print();
endfunction
endtask
endclass
class B extends uvm_component;
……
`uvm_analysis_imp#(transaction) C_imp; //声明ap_imp
function void build_phase(uvm_phase phase);
super.build_phase(phase);
C_imp = ("C_imp",this);
endfunction
function void write (transaction tr); //定义write函数
`uvm_info("C","receive a transaction",UMV_LOW)
tr.print();
endfunction
endtask
endclass
class y_env extends uvm_env;
……
A A_inst;
B B_inst;
C C_inst;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_inst = A::type_id::create("A_inst",this);
B_inst = B::type_id::create("B_inst",this);
C_inst = C::type_id::create("C_inst",this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
A_inst.A_ap.connect(B_inst.B_imp); //一对多连接
A_inst.A_ap.connect(C_inst.C_imp);
endfunction
……
endclass
5. imp_decl宏
对于一个component中需要多个imp时,会出现重名的imp以及重名的方法名,为此可以通过`uvm_analysis(可替代)_imp_decl宏来声明,并定义对应的方法名,来避免重名的现象出现。
`uvm_analysis_imp_decl(_mon) //宏的声明
`uvm_analysis_imp_decl(_mdl)
class scb extends uvm_component;
……
uvm_analysis_imp_mon#(transaction,scb) mon_imp; //端口声明
uvm_analysis_imp_mdl#(transaction,mdl) mdl_imp;
function write_mon(transaction tr); //方法声明
……
endfunction
function write_mdl(transaction);
……
endfuction
endclass