日常记录(42)TLM事务级模型、
TLM的put
my_case的main_phase中设定了raise_objection,并延时1000仿真单位后,drop,后续代码中的main_phase不设定raise和drop。
TLM的demo,(port->export->imp)
在A和B之间通信,首先在env中构建对应的实例(在不同的phase中),
然后给连接,A的port给数据到B的export中。
A A_inst; B B_inst; A_inst = A::type_id::create("A_inst", this); B_inst = B::type_id::create("B_inst", this); A_inst.A_port.connect(B_inst.B_export);
A中,
定义port,使用put发送。
uvm_blocking_put_port#(my_transaction) A_port; A_port = new("A_port", this); tr = new("tr"); assert(tr.randomize()); A_port.put(tr);
B中,
定义export,imp,进行连接,使用imp接收,其中imp连接到其put函数,最终连接到B的put函数。
uvm_blocking_put_export#(my_transaction) B_export; uvm_blocking_put_imp#(my_transaction, B) B_imp; B_export = new("B_export", this); B_imp = new("B_imp", this); B_export.connect(B_imp); function void B::put(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) tr.print(); endfunction
port->imp
env中连接
A_inst.A_port.connect(B_inst.B_imp);
A不变
uvm_blocking_put_port#(my_transaction) A_port; A_port = new("A_port", this); tr = new("tr"); assert(tr.randomize()); A_port.put(tr);
B只有imp,然后是put函数
uvm_blocking_put_imp#(my_transaction, B) B_imp; B_imp = new("B_imp", this); function void B::put(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) tr.print(); endfunction
注意:blocking_put类型,使用了put函数。如果是nonblocking_put,使用需要try_put和can_put两个函数;如果只有put,则需要以上三个函数。
如果为get类型,则需要对应的get函数。peek同理。get和peek同时(如blocking_get_peek),则同样结合二者需要的函数定义。
如果为transport,对应transport函数,非阻塞使用nb_transport函数。
阻塞部分不仅可以定义成函数,还可以定义成任务。
export->imp
env中,
A A_inst; B B_inst; A_inst = A::type_id::create("A_inst", this); B_inst = B::type_id::create("B_inst", this); A_inst.A_export.connect(B_inst.B_imp);
A中,
main_phase中为什么不用raise_objection
uvm_blocking_put_export#(my_transaction) A_export; A_export = new("A_export", this); tr = new("tr"); assert(tr.randomize()); A_export.put(tr);
B不变
uvm_blocking_put_imp#(my_transaction, B) B_imp; B_imp = new("B_imp", this); function void B::put(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) tr.print(); endfunction
port->port->imp
C的port发送给A的port,然后A的port发送给B的imp
env中,
A A_inst; B B_inst; A_inst = A::type_id::create("A_inst", this); B_inst = B::type_id::create("B_inst", this); A_inst.A_port.connect(B_inst.B_imp);
C中,
uvm_blocking_put_port#(my_transaction) C_port; C_port = new("C_port", this); task C::main_phase(uvm_phase phase); my_transaction tr; repeat(10) begin #10; tr = new("tr"); assert(tr.randomize()); C_port.put(tr); end endtask
A中,
由谁发出数据,就由谁作为主动连接的connect调用。
C C_inst; uvm_blocking_put_port#(my_transaction) A_port;
A_port = new("A_port", this); C_inst = C::type_id::create("C_inst", this); C_inst.C_port.connect(this.A_port);
B中,不变
uvm_blocking_put_imp#(my_transaction, B) B_imp; B_imp = new("B_imp", this); function void B::put(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) tr.print(); endfunction
port->export->export->imp
env中,
A A_inst; C C_inst; A_inst = A::type_id::create("A_inst", this); C_inst = C::type_id::create("B_inst", this); A_inst.A_port.connect(C_inst.C_export);
A中,
uvm_blocking_put_port#(my_transaction) A_port; A_port = new("A_port", this); tr = new("tr"); assert(tr.randomize()); A_port.put(tr);
C中,
B B_inst; uvm_blocking_put_export#(my_transaction) C_export; C_export = new("C_export", this); B_inst = B::type_id::create("B_inst", this); this.C_export.connect(B_inst.B_export);
B中,
uvm_blocking_put_export#(my_transaction) B_export; uvm_blocking_put_imp#(my_transaction, B) B_imp; B_export = new("B_export", this); B_imp = new("B_imp", this); B_export.connect(B_imp); function void B::put(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) tr.print(); endfunction
TLM的get、transport、non_blocking
(get)A的imp->export->B的port(数据流向)
B为动作的发起者,A为被动从imp发送数据的动作接收者(B的port,A的export,A的imp控制流向)
imp作为动作的最终点。
my_env中,
A A_inst; B B_inst; A_inst = A::type_id::create("A_inst", this); B_inst = B::type_id::create("B_inst", this); B_inst.B_port.connect(A_inst.A_export);
B中,
uvm_blocking_get_port#(my_transaction) B_port; B_port = new("B_port", this); B_port.get(tr); `uvm_info("B", "get a transaction", UVM_LOW) tr.print();
A中,
uvm_blocking_get_export#(my_transaction) A_export; uvm_blocking_get_imp#(my_transaction, A) A_imp; my_transaction tr_q[$]; A_export = new("A_export", this); A_imp = new("A_imp", this); A_export.connect(A_imp); task A::get(output my_transaction tr); 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
(transport)A的transport->B的imp(数据双向)
动作从A->B
在env中,
A A_inst; B B_inst; A_inst = A::type_id::create("A_inst", this); B_inst = B::type_id::create("B_inst", this); A_inst.A_transport.connect(B_inst.B_imp);
在A中,
uvm_blocking_transport_port#(my_transaction, my_transaction) A_transport; A_transport = new("A_transport", this); task A::main_phase(uvm_phase phase); my_transaction tr; my_transaction rsp; repeat(10) begin #10; tr = new("tr"); assert(tr.randomize()); A_transport.transport(tr, rsp); `uvm_info("A", "received rsp", UVM_MEDIUM) rsp.print(); end endtask
在B中,
uvm_blocking_transport_imp#(my_transaction, my_transaction, B) B_imp; B_imp = new("B_imp", this); task B::transport(my_transaction req, output my_transaction rsp); `uvm_info("B", "receive a transaction", UVM_LOW) req.print(); //do something according to req #5; rsp = new("rsp"); endtask
non_blocking通信。A的port->B的imp
env中,常规
A A_inst; B B_inst; A_inst = A::type_id::create("A_inst", this); B_inst = B::type_id::create("B_inst", this); A_inst.A_port.connect(B_inst.B_imp);
A中,
可以直接使用try_put即可,附带了判断功能,通过则直接发送
uvm_nonblocking_put_port#(my_transaction) A_port; A_port = new("A_port", this); task A::main_phase(uvm_phase phase); my_transaction tr; repeat(10) begin tr = new("tr"); assert(tr.randomize()); while(!A_port.can_put()) #10; void'(A_port.try_put(tr)); end endtask
B中,
需要定义can_put和try_put函数。然后接收过程中,使用非阻塞(获取数据暂存队列),然后调用队列。
uvm_nonblocking_put_imp#(my_transaction, B) B_imp; my_transaction tr_q[$]; B_imp = new("B_imp", this); function bit B::can_put(); if(tr_q.size() > 0) return 0; else return 1; endfunction function bit B::try_put(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) if(tr_q.size() > 0) return 0; else begin tr_q.push_back(tr); return 1; end endfunction task B::main_phase(uvm_phase phase); my_transaction tr; while(1) begin if(tr_q.size() > 0) tr = tr_q.pop_front(); else #25; end endtask
analysis
和阻塞、非阻塞区别。用于广播、
A的port->B和C的imp
env中,
A A_inst; B B_inst; C C_inst; 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); A_inst.A_ap.connect(B_inst.B_imp); A_inst.A_ap.connect(C_inst.C_imp);
B中,
接收必须也是analysis的,且使用的write函数。
uvm_analysis_imp#(my_transaction, B) B_imp; B_imp = new("B_imp", this); function void B::write(my_transaction tr); `uvm_info("B", "receive a transaction", UVM_LOW) tr.print(); endfunction
C中,
与B相同
uvm_analysis_imp#(my_transaction, C) C_imp; C_imp = new("C_imp", this); function void C::write(my_transaction tr); `uvm_info("C", "receive a transaction", UVM_LOW) tr.print(); endfunction
A中,
uvm_analysis_port#(my_transaction) A_ap; A_ap = new("A_ap", this); task A::main_phase(uvm_phase phase); my_transaction tr; repeat(10) begin #10; tr = new("tr"); assert(tr.randomize()); A_ap.write(tr); end endtask
imp_decl的多路通信
为不同通路添加后缀区别。
env中,
my_agent o_agt; my_model mdl; my_scoreboard scb; o_agt = my_agent::type_id::create("o_agt", this); o_agt.is_active = UVM_PASSIVE; mdl = my_model::type_id::create("mdl", this); scb = my_scoreboard::type_id::create("scb", this); mdl.ap.connect(scb.model_imp); o_agt.ap.connect(scb.monitor_imp);
i_agt的ap连接到fifo的export,mdl的port连接到fifo的export。
mdl的ap连接到scb的imp[model 已经添加了后缀],
o_agt的ap连接到scb的imp[monitor 已经添加了后缀].
双路通信。mdl和o_agt发送数据到scb中。
mdl中,
uvm_blocking_get_port #(my_transaction) port; uvm_analysis_port #(my_transaction) ap; port = new("port", this); ap = new("ap", this); task my_model::main_phase(uvm_phase phase); my_transaction tr; my_transaction new_tr; super.main_phase(phase); while(1) begin port.get(tr); new_tr = new("new_tr"); new_tr.copy(tr); `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW) new_tr.print(); ap.write(new_tr); end endtask
o_agt中
if (is_active == UVM_ACTIVE) begin sqr = my_sequencer::type_id::create("sqr", this); drv = my_driver::type_id::create("drv", this); end mon = my_monitor::type_id::create("mon", this); uvm_analysis_port #(my_transaction) ap; if (is_active == UVM_ACTIVE) begin drv.seq_item_port.connect(sqr.seq_item_export); end ap = mon.ap;
由于最终的ap来自于mon的ap,mon用于发射数据,如下
uvm_analysis_port #(my_transaction) ap; ap = new("ap", this); task my_monitor::main_phase(uvm_phase phase); my_transaction tr; while(1) begin tr = new("tr"); collect_one_pkt(tr); ap.write(tr); end endtask
scb中接收数据,使用decl宏
两个wirte函数,并带有后缀用于识别。
`uvm_analysis_imp_decl(_monitor) `uvm_analysis_imp_decl(_model) uvm_analysis_imp_monitor#(my_transaction, my_scoreboard) monitor_imp; uvm_analysis_imp_model#(my_transaction, my_scoreboard) model_imp; monitor_imp = new("monitor_imp", this); model_imp = new("model_imp", this); function void my_scoreboard::write_model(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
使用fifo配合port通信
使用fifo可以省去write等的函数定义。fifo中的port和export本质上为imp。
env中,
需求是,o_agt->scb, i_agt->mdl->scb, 对应三个fifo
然后分别实例化。i_agt.ap发送数据,是连接到fifo的export,然后mdl的port接收数据,是连接到fifo的get_port。然后是,mdl->scb
另一方面,o_agt->scb的通路,使用fifo、
my_agent i_agt; my_agent o_agt; my_model mdl; my_scoreboard scb; uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo; uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo; uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo; i_agt = my_agent::type_id::create("i_agt", this); o_agt = my_agent::type_id::create("o_agt", this); i_agt.is_active = UVM_ACTIVE; o_agt.is_active = UVM_PASSIVE; mdl = my_model::type_id::create("mdl", this); scb = my_scoreboard::type_id::create("scb", this); agt_scb_fifo = new("agt_scb_fifo", this); agt_mdl_fifo = new("agt_mdl_fifo", this); mdl_scb_fifo = new("mdl_scb_fifo", this); i_agt.ap.connect(agt_mdl_fifo.analysis_export); mdl.port.connect(agt_mdl_fifo.blocking_get_export); mdl.ap.connect(mdl_scb_fifo.analysis_export); scb.exp_port.connect(mdl_scb_fifo.blocking_get_export); o_agt.ap.connect(agt_scb_fifo.analysis_export); scb.act_port.connect(agt_scb_fifo.blocking_get_export);
agt中,
my_monitor mon; uvm_analysis_port #(my_transaction) ap; mon = my_monitor::type_id::create("mon", this); ap = mon.ap;
mon中,
uvm_analysis_port #(my_transaction) ap; ap = new("ap", this); task my_monitor::main_phase(uvm_phase phase); my_transaction tr; while(1) begin tr = new("tr"); collect_one_pkt(tr); ap.write(tr); end endtask
在model中,
接收port数据,用ap发送。
uvm_blocking_get_port #(my_transaction) port; uvm_analysis_port #(my_transaction) ap; port = new("port", this); ap = new("ap", this); task my_model::main_phase(uvm_phase phase); my_transaction tr; my_transaction new_tr; super.main_phase(phase); while(1) begin port.get(tr); new_tr = new("new_tr"); new_tr.copy(tr); `uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW) new_tr.print(); ap.write(new_tr); end endtask
在scb中,
两个线程get数据,一个是从mdl传入期望数据,一个是从o_agt传入实际数据。
my_transaction expect_queue[$]; uvm_blocking_get_port #(my_transaction) exp_port; uvm_blocking_get_port #(my_transaction) act_port; exp_port = new("exp_port", this); act_port = new("act_port", this); task my_scoreboard::main_phase(uvm_phase phase); my_transaction get_expect, get_actual, tmp_tran; bit result; super.main_phase(phase); fork while (1) begin exp_port.get(get_expect); expect_queue.push_back(get_expect); end while (1) begin act_port.get(get_actual); if(expect_queue.size() > 0) begin tmp_tran = expect_queue.pop_front(); result = get_actual.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"); get_actual.print(); end end else begin `uvm_error("my_scoreboard", "Received from DUT, while Expect Queue is empty"); $display("the unexpected pkt is"); get_actual.print(); end end join endtask
fifo一共有好多个端口(大于12个),有analysis,get,put,peek等,还有阻塞非阻塞。
用fifo和用imp各有优劣,fifo更常用,因为省去了一些定义,还可使用for循环。
如果针对两个模块的多路通信,由于涉及到decl的后缀重命名,还有函数定义,并且不同的命名不能使用for循环进行代码简化连接。
is_empty, is_full, flush函数。
Le vent se lève! . . . il faut tenter de vivre!
Le vent se lève! . . . il faut tenter de vivre!