1 TLM basic
Transaction-level interface有 两种,一种是port/export,另一种是analysis port/analysis export。
1.1 port/export
1.1.1 using put() and get() method实现blocking通信
先说port/export,又分为两种即blocking和non-blocking,以下图作为一个例子,写法如下:
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
注意这里调用的put_port.put()定义在后面consumer里的put_export里面。
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
这个例子讲了port和export的put method,下面的例子讲port和export的get method。
在这个例子中,get_consumer发起request(get_port.get(t)),但是transaction方向还是有producer向consumer。
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 method的implement都在对方component中实现,所以必须等对对方component中处理完才能进行下一个transaction的通信,故此体现出blocking的特点。
1.1.2 using try_get/put/peer method实现non-blocking通信
对于non_blocking的port/export,写法如下:
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
这里用if(get_port.try_get(t))来request。
1.1.3 using fifo
不同的verfication component之间也可以用uvm_tlm_fifo来实现通信。
值得注意的是,如果用两个连续的get,那么consumer将得到两个不同的transaction,如果用两个连续的peer,那个consumer将得到两个相同的transaction,这是英文peer method返回available transaction的copy。
1.1.4 层次化和封装带来的connection类型问题
通常一个port connect一个 export,但是由于testbench中component之间的层次化封装带来了一些问题,如下图所示:
A、B、D、F都是port和export的连接,这种类型一般称为peer-to-peer connection,C和E则不是,C是port-to-port,E是export-to-export,
port-to-port 的rule是:subcomponent.port.connect(port);
export-to-export 的rule是:export.connect(subcomponent.export);
总结如下:
1.2 analysis port/export(imp)
analysis port/export(imp)提供了另外一种interface,其与port/export不同之处在于:
port需要有一个对应的export来提供put函数的实现(implementation), 而analysis port则不一定需要。
uvm_analysis_port调用write函数,而write函数的实现则在target export/imp里。
在实际实践中,一个典型应用是在monitor里使用uvm_analysis_port,调用write函数来实现transaction发送,在uvm_scoreboard里使用uvm_analysis_export(imp),并实现write函数实现transaction接收。但是有时候scoreboard不单要用一个uvm_analysis_imp来接收来自monitor的transaction,还要有另外一个uvm_analysis_imp来接收reference model或者predictor输出的transaction,所以要有两个write函数实现,为了避免名字冲突,UVM采用的方法是首先用宏码声明两个后缀不一样的imp, UVM会根据两个后缀内建两个新的imp
`uvm_analysis_imp_decl(_ep1)
`uvm_analysis_imp_decl(_ep2)
uvm_analysis_imp_ep1 #(transaction, scoreboard) scb_imp1;
uvm_analysis_imp_ep2#(transaction, scoreboard) scb_imp2;
然后分别定义两个port的write函数实现
function void write_ep1(transaction txn);
function void write_ep2(transaction txn);
需要注意的一点是
analysis port广播出去的是handle,并没有为每一个export/imp的transaction分配alloction,如果两个imp都接收transaction, 那么其中一个imp修改transaction会导致另一个imp看到修改后的transaction. 所以好的习惯是在write function里面首先 new一个 transaction,并对传输过来的transaction进行copy后,再进行后面的操作。