日常记录(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!

浙公网安备 33010602011771号