TLM事务级建模
概述
TLM:Transaction Level Modeling(事务级建模),它是一个独立于语言的一个标准,常用于系统建模,加速软硬件协同开发。在芯片开发中,常配合system C使用来进行系统设计。最新的标准是OCSI TLM 2.0。
TLM的端口整理
端口的按照类型可以划分为三种:
port:经常作为initiator的发起端,也凭借port,initiator才可以访问target中实现的TLM通信方法;
export:作为initiator和target中间层次的端口;
imp:只能是作为target接收request的末端,它无法作为中间层次的端口,imp无法向其他端口发送conne连接;
port和export体现的是一种控制流,port是控制的发起者,export是被动接收者。
PORT、 EXPORT、 IMP三者的优先级顺序为PORT > EXPORT > IMP, PORT的优先级最高,IMP的优先级最低。TLM通信连接只能从高优先级向低优先级发起,也就意味着port可以连接port、export或者imp;export可以连接export或者imp;imp只能作为数据传送的终点,无法扩展连接;
PORT、 EXPORT、 IMP每一种下面都包含15种,主要区分为:
- 阻塞和非阻塞之分
- 哪种通信数据流传输方式,put、get、peek、transport( request-response操作)等。
put:从initiator向target发送数据
get:从initiator向target获取数据
peek:peek和get在数据流和控制流上相似,区别在于get任务被调用时, FIFO内部缓存中会少一个transaction, 而peek被调用时, FIFO会把transaction复制一份发送出去
PORT、 EXPORT、 IMP在使用上是一一对应的,如果使用了uvm_blocking_put_port#(T)作为initiator则port一定要使用uvm_blocking_put_export#(T);imp一定要使用uvm_blocking_put_imp#(T);
port | export | imp |
uvm_blocking_put_port#(T); | uvm_blocking_put_export#(T); | uvm_blocking_put_imp#(T); |
uvm_nonblocking_put_port#(T); | uvm_nonblocking_put_export#(T); | uvm_nonblocking_put_imp#(T); |
uvm_put_port#(T); | uvm_put_export#(T); | uvm_put_imp#(T); |
uvm_blocking_get_port#(T); | uvm_blocking_get_export#(T); | uvm_blocking_get_imp#(T); |
uvm_nonblocking_get_port#(T); | uvm_nonblocking_get_export#(T); | uvm_nonblocking_get_imp#(T); |
uvm_get_port#(T); | uvm_get_export#(T); | uvm_get_export#(T); |
uvm_blocking_peek_port#(T); | uvm_blocking_peek_export#(T); | uvm_blocking_peek_imp#(T); |
uvm_nonblocking_peek_port#(T); | uvm_nonblocking_peek_export#(T); | uvm_nonblocking_peek_imp#(T); |
uvm_peek_port#(T); | uvm_peek_export#(T); | uvm_peek_imp#(T); |
uvm_blocking_get_peek_port#(T); | uvm_blocking_get_peek_export#(T); | uvm_blocking_get_peek_imp#(T); |
uvm_nonblocking_get_peek_port#(T); | uvm_nonblocking_get_peek_export#(T); | uvm_nonblocking_get_peek_imp#(T); |
uvm_get_peek_port#(T); | uvm_get_peek_export#(T); | uvm_get_peek_imp#(T); |
uvm_blocking_transport_port#(REQ, RSP); | uvm_blocking_transport_export#(REQ, RSP); | uvm_blocking_transport_imp#(REQ, RSP); |
uvm_nonblocking_transport_port#(REQ, RSP); | uvm_nonblocking_transport_export#(REQ, RSP); | uvm_nonblocking_transport_imp#(REQ, RSP); |
uvm_transport_port#(REQ, RSP); | uvm_transport_export#(REQ, RSP); | uvm_transport_imp#(REQ, RSP); |
与上述三种task对应的nonblocking非阻塞的方法分别是:
try_put//返回值默认为1或0
can_put//回调方法,在调用try_xxx()方法时自动调用
try_get
can_get
try_peek
can_peek
这六个函数与其对应的task的区别在于,它们必须立即返回,如果try_xxx函数可以发送或者获取数据,那么函数还应该返回1,如果执行失败则应该返回0。或者通过can_xxx函数先试探target是否可以接收数据,通过返回值,再通过try_xxx函数发送,提高数据发送的成功率 ;
put_export:用户可以通过该端口调用put()、try_put()、can_put()。
put_ap:调用了put方法写入的数据同时也会通过该端口的write()函数送出。
get_peek_export:用户可以通过该端口调用get()、try_get()、can_get()、peek()、try_peek()、can_peek()。
get_ap:调用了get和peek方法读出的数据也会通过该端口的write()函数送出。
analysis_port和analysis_export
除了port、export和imp三种端口,还有两种特殊端口analysis_port和analysis_export。analysis_port和analysis_export没有阻塞和非阻塞之分,在通信中他们是一对多的,相当于广播的方式,一个analysis_port或者analysis_export可以连接多个imp,imp的类型必须是uvm_analysis_imp。analysis_port和analysis_export来说, 只有一种操作: write。 在analysis_imp所在的component, 必须定义一个名字为write的函数。
如果在一个component中存在多个imp,使用时每个imp都需要有一个write函数,这种可以通过uvm_analysis_imp_decl来解决,指定write函数的后缀,这样在调用write函数时会自动调用write_后缀的函数。
`ifndef MY_SCOREBOARD__SV
`define MY_SCOREBOARD__SV
`uvm_analysis_imp_decl(_model0)
`uvm_analysis_imp_decl(_model1)
`uvm_analysis_imp_decl(_model2)
class my_scoreboard extends uvm_scoreboard;
my_transaction expect_queue[$];
uvm_analysis_imp_monitor#(my_transaction, my_scoreboard) monitor_imp;
uvm_analysis_imp_model0#(my_transaction, my_scoreboard) model0_imp;
uvm_analysis_imp_model1#(my_transaction, my_scoreboard) model1_imp;
uvm_analysis_imp_model2#(my_transaction, my_scoreboard) model2_imp;
`uvm_component_utils(my_scoreboard)
extern function new(string name, uvm_component parent = null);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern function void write_monitor(my_transaction tr);
extern function void write_model0(my_transaction tr);
extern function void write_model1(my_transaction tr);
extern function void write_model2(my_transaction tr);
endclass
function my_scoreboard::new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
function void my_scoreboard::build_phase(uvm_phase phase);
super.build_phase(phase);
monitor_imp = new("monitor_imp", this);
model0_imp = new("model0_imp", this);
model1_imp = new("model1_imp", this);
model2_imp = new("model2_imp", this);
endfunction
function void my_scoreboard::write_model0(my_transaction tr);
expect_queue.push_back(tr);
endfunction
function void my_scoreboard::write_model1(my_transaction tr);
expect_queue.push_back(tr);
endfunction
function void my_scoreboard::write_model2(my_transaction tr);
expect_queue.push_back(tr);
endfunction
function void my_scoreboard::write_monitor(my_transaction tr);
my_transaction tmp_tran;
bit result;
if(expect_queue.size() > 0) begin
tmp_tran = expect_queue.pop_front();
result = tr.compare(tmp_tran);
if(result) begin
`uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);
end
else begin
`uvm_error("my_scoreboard", "Compare FAILED");
$display("the expect pkt is");
tmp_tran.print();
$display("the actual pkt is");
tr.print();
end
end
else begin
`uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty");
$display("the unexpected pkt is");
tr.print();
end
endfunction
task my_scoreboard::main_phase(uvm_phase phase);
endtask
`endif
fifo
uvm_tlm_fifo和uvm_tlm_analysis_fifo两种,区别在于uvm_tlm_analysis_fifo存在一个analysis_export端口, 并且有一个write函数, 而uvm_tlm_fifo没有。
fifo的本质实际上是集成了两个imp完成的通信。
fifo上存在相当多的端口供给我们连接,function new(string name, uvm_component parent = null, int size = 1);可通过指定size的大小指定fifo的缓存深度,如果size=0表示无限大小。