Analysis Components & Techniques(从Scoreboards开始)

上一篇 / 下一篇  2018-12-27 10:39:12

   Scoreboards

  Overview  

      记分板的工作是确定DUT是否正常运行。 记分板通常是要编写的测试平台中最困难的部分,但它可以概括为两部分:第一步是确定正确的功能究竟是什么。 一旦预测到正确的功能,记分板就可以评估在DUT上观察到的实际结果是否与预测结果相匹配。 最佳记分板架构是将预测task与评估task分开。 通过允许替换预测器和评估模型,并遵循关注点分离的最佳实践,这为您提供了最大的重用灵活性。

      【此处需插一幅图】
 
       在存在单个预测事务流和单个实际事务流的情况下,记分板可以使用简单的比较器执行评估。 最常见的比较器是有序和无序比较器。

  Comparing Transactions Assuming In-Order Arrival

      【此处需插一幅图】

       有序比较器假定匹配的事务将以预期和实际流的相同顺序出现。 它从预期和实际方面获取事务并对其进行评估。 事务将独立到达,因此评估必须是阻塞的,直到两个事务都到达存在。 在这种情况下,一个简单的实现方法是在比较器中嵌入两个analysis FIFO,并在run()task中执行同步和评估。 评估可以像调用事务的compare()方法一样简单,也可以更复杂,因为为了评估正确的行为,比较并不一定意味着相等。

        class comparator_inorder extends uvm_component;  
             `uvm_component_utils(comparator_inorder)

             uvm_analysis_export #(alu_txn) before_export;    
             uvm_analysis_export #(alu_txn) after_export;  

             uvm_tlm_analysis_fifo #(alu_txn) before_fifo, after_fifo;

             int m_matches, m_mismatches;

              function new( string name , uvm_component parent) ;
                   super.new( name , parent );
                   m_matches = 0;
                   m_mismatches = 0;
              endfunction

              function void build_phase( uvm_phase phase );
                   before_fifo = new("before_fifo", this);
                   after_fifo = new("after_fifo", this);
                   before_export = new("before_export", this);
                   after_export = new("after_export", this);
              endfunction

              function void connect_phase( uvm_phase phase );
                   before_export.connect(before_fifo.analysis_export);   
                   after_export.connect(after_fifo.analysis_export);
              endfunction

               task run_phase( uvm_phase phase );
                    string s;
                    alu_txn before_txn, after_txn;
                    forever begin
                        before_fifo.get(before_txn);
                        after_fifo.get(after_txn);
                        if (!before_txn.compare(after_txn)) begin $sformat(s, "%s does not match %s", before_txn.convert2string(), after_txn.convert2string());
                             uvm_report_error("Comparator Mismatch",s);  
                             m_mismatches++;
                        end else begin
                             m_matches++;
                        end
                    end
               endtask

               function void report_phase( uvm_phase phase );  
                    uvm_report_info("Inorder Comparator", $sformatf("Matches: %0d", m_matches));
                    uvm_report_info("Inorder Comparator", $sformatf("Mismatches: %0d", m_mismatches));
               endfunction

         endclass

  Comparing transactions out-of-order

       【此处需插一幅图】

        无序比较器不假设匹配事务将以预期和实际方面的相同顺序出现。 因此,需要存储不匹配的事务,直到匹配的事务出现在另一个的事务流上。对于大多数无序比较器,使用关联数组进行存储。该示例比较器具有通过analysis exports到达的两个输入流。比较器的实现是对称的,因此export名称没有任何实际重要性。此示例使用嵌入式fifos来实现analysis write()函数,但由于事务要么存储到关联数组中,要么在到达时进行评估,这个示例可以使用analysis imps和writ()函数轻松编写。
       由于需要确定两个事务是否匹配并且是否应该进行比较,因此该示例要求事务实现index_id()函数,该函数返回用作关联数组的键的值。如果关联数组中已存在具有此键的条目,则表示事务先前从另一个流到达,并且事务将进行比较。如果不存在key,则将此事务添加到关联数组。
       此示例还有一个附加功能,即它不会假定index_id()值在给定流上始终是唯一的。在这种情况下,属于同一事务流的多个未完成事务具有相同索引值,它们存储在队列中,并且队列是关联数组的值部分。当来自其他事务流的匹配到达时,将按FIFO顺序进行比较。

       class ooo_comparator
            #(type T = int,
               type IDX = int)
            extends uvm_component;

            typedef ooo_comparator #(T, IDX) this_type;
            `uvm_component_param_utils(this_type)

            typedef T q_of_T[$];
            typedef IDX q_of_IDX[$];

            uvm_analysis_export #(T) before_axp, after_axp;

            protected uvm_tlm_analysis_fifo #(T) before_fifo, after_fifo;
            bit before_queued = 0;
            bit after_queued = 0;

            protected int m_matches, m_mismatches;

            protected q_of_T received_data[IDX];
            protected int rcv_count[IDX];

            protected process before_proc = null;
            protected process after_proc = null;

            function new(string name, uvm_component parent);
                 super.new(name, parent);
            endfunction

            function void build_phase( uvm_phase phase );
                 before_axp = new("before_axp", this);
                 after_axp = new("after_axp", this);
                 before_fifo = new("before", this);
                 after_fifo = new("after", this);
            endfunction

            function void connect_phase( uvm_phase phase );
                 before_axp.connect(before_fifo.analysis_export);   
                 after_axp.connect(after_fifo.analysis_export);
            endfunction : connect

            // The component forks two concurrent instantiations of this task
            // Each instantiation monitors an input analysis fifo

            protected task get_data(ref uvm_tlm_analysis_fifo #(T) txn_fifo, ref process proc, input bit is_before); T txn_data, txn_existing;
                 IDX idx;
                 string rs;
                 q_of_T tmpq;
                 bit need_to_compare;
                 forever begin
                     proc = process::self();
                     // Get the transaction object, block if no transaction available
                     txn_fifo.get(txn_data);
                     idx = txn_data.index_id();
                     // Check to see if there is an existing object to compare
                     need_to_compare = (rcv_count.exists(idx) &&
                                                       ((is_before && rcv_count[idx] > 0) ||
                                                        (!is_before && rcv_count[idx] < 0)));
                     if (need_to_compare) begin
                          // Compare objects using compare() method of transaction
                          tmpq = received_data[idx];
                          txn_existing = tmpq.pop_front();
                          received_data[idx] = tmpq;
                          if (txn_data.compare(txn_existing))
                               m_matches++;
                          else
                               m_mismatches++;
                     end
                     else begin
                     // If no compare happened, add the new entry
                          if (received_data.exists(idx))
                               tmpq = received_data[idx];
                          else
                               tmpq = {};
                          tmpq.push_back(txn_data);
                          received_data[idx] = tmpq;
                      end

                      // Update the index count
                      if (is_before)
                          if (rcv_count.exists(idx)) begin
                              rcv_count[idx]--;
                          end
                          else
                               rcv_count[idx] = -1;
                      else
                           if (rcv_count.exists(idx)) begin
                               rcv_count[idx]++;
                           end
                           else
                                rcv_count[idx] = 1;

                       // If index count is balanced, remove entry from the arrays

                       if (rcv_count[idx] == 0) begin
                           received_data.delete(idx);
                           rcv_count.delete(idx);
                       end
                  end // forever
             endtask

             virtual function int get_matches();
                 return m_matches;
             endfunction : get_matches

             virtual function int get_mismatches();
                 return m_mismatches;
             endfunction : get_mismatches

             virtual function int get_total_missing();
                 int num_missing;
                 foreach (rcv_count[i]) begin
                     num_missing += (rcv_count[i] < 0 ? -rcv_count[i] : rcv_count[i]);
                 end
                 return num_missing;
             endfunction : get_total_missing

             virtual function q_of_IDX get_missing_indexes();
                 q_of_IDX rv = rcv_count.find_index() with (item != 0);
                 return rv;
             endfunction : get_missing_indexes;

             virtual function int get_missing_index_count(IDX i);
             // If count is < 0, more "before" txns were received
             // If count is > 0, more "after" txns were received
                 if (rcv_count.exists(i))
                     return rcv_count[i];
                 else return 0;
             endfunction : get_missing_index_count;

             task run_phase( uvm_phase phase );
                  fork
                      get_data(before_fifo, before_proc, 1);
                      get_data(after_fifo, after_proc, 0);
                  join
             endtask : run_phase

              virtual function void kill();
                  before_proc.kill();
                  after_proc.kill();
              endfunction
 
         endclass : ooo_comparator

  Advanced Scenarios

       在更高级的场景中,可以存在来自多个DUT接口的多个预期事务流和实际事务流。在这种情况下,简单的比较器是不够的,并且记分板的评估部分的实现更复杂并且特定于DUT。

  Reporting and Recording

       评估结果是布尔值,记分板应使用该值来报告和记录故障。通常,成功的评估不会单独报告,但可以记录以供后边的总结报告使用。

      (在#verificationacademy.com/uvm-ovm上在线下载源代码示例)。


TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

日历

« 2019-01-14  
  12345
6789101112
13141516171819
20212223242526
2728293031  

数据统计

  • 访问量: 19398
  • 日志数: 75
  • 建立时间: 2018-09-03
  • 更新时间: 2018-12-29

RSS订阅

Open Toolbar