日常记录(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函数。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

posted @ 2022-01-18 08:42  大浪淘沙、  阅读(141)  评论(0)    收藏  举报