10
7

More than 5 years have passed since last update.

[SystemVerilog]Jenkinsを利用したUVMテスト環境の構築

Last updated at Posted at 2014-07-21

はじめに、動機

SystemVerilogのUVMは、テストのためのフレームワークと呼べるわけで、継続的インテグレーション(CI)を使用したブロックテストができないかと思いました。
CIといえばJenkinsですね。HDLを使うとなると、こんなイメージでしょうか。

flow.png

ソースコード管理システムにHDLコードをCommitして、それをトリガとしてテスト実施し、結果を得る仕組みです。
JenkinsとUVMを使うと、Commit後フローは自動化され、かつたくさんのテストシナリオをスケーラブルに流し込めるなと思い、まずは環境を構築してみることにしました。

OSはCentOS 6.5です。

必要なツール

HDLシミュレータ

SystemVerilogに対応した適当なやつです。

ソースコード管理ツール

普通はGitだろ、って感じですが、ここではBazaarを使います。自分がBazaar好きなんですよね。死兆星が見えますが。

Webサーバ

Apache2です。

CIツール

Jenkinsです。

構築作業

Jenkinsのインストール

こちらを参考にしました。
/etc/sysconfig/jenkinsの中を見ると、$JENKINS_HOMEは、/var/lib/jenkinsに設定されているようです。

追加したプラグイン

インストールされたJenkinsに、下記のプラグインを追加しました。

  • Bazaar plugin
  • Python Plugin
  • SCM API Plugin
  • Simple Theme Plugin
  • xUnit Plugin

Bazzarのインストール

sudo yum install bzr

Apache+Bazaar環境構築

公式ドキュメントを参考にしました。
CentOSだとあまり情報がなかったので、ApacheとWSGIをインストールし、Bazaarのスマートサーバを立てるまでをメモしておきます。

Apache, WSGIインストール

sudo yum install httpd mod_wsgi

Apacheの設定ファイル

ここに、公式ドキュメントにあるWSGI(mod_wsgi)設定を追加します。
自分の場合、CentOSのApacheのhtmlディレクトリ直下に、リポジトリ保存場所として「bzr」ディレクトリを作成しました。

/etc/httpd/conf/httpd.conf
WSGIScriptAliasMatch ^/code/.*/\.bzr/smart$ /var/www/cgi-bin/bzr.wsgi

RewriteEngine On
RewriteCond %{REQUEST_URI} !^/code/.*/\.bzr/smart$
RewriteRule ^/code/(.*/\.bzr/.*)$ /var/www/html/bzr/$1 [L]

<Directory "/var/www/html/bzr">
    WSGIApplicationGroup %{GLOBAL}
</Directory>

WSGIの設定

これも公式通りです。

/var/www/cgi-bin/bzr.wsgi
from bzrlib.transport.http import wsgi

def application(environ, start_response):
    app = wsgi.make_app(
        root="/var/www/html/bzr",
        prefix="/bzr",
        readonly=False,
        load_plugins=True,
        enable_logging=False)
    return app(environ, start_response)

Apacheの有効化

sudo /sbin/chkconfig httpd on
sudo /etc/init.d/httpd start

リポジトリ作成・Jenkinsによるテスト実行

リポジトリの作成、add, commit

共用リポジトリを「uvm_example」、ブランチを「trunk」として作成します。

リポジトリ作成

cd /var/www/html/bzr
bzr init-repository uvm_example
cd uvm_example
bzr init trunk

add, commit

とりあえずUVM1.1dを入れてみます。

cp -pr $UVM_HOME/.. .
bzr add uvm-1.1d
bzr commit -m "uvm init" uvm-1.1d

Jenkinsのジョブ作成・設定

SystemVerilog関連のテンプレートなどありませんから、「フリースタイル・プロジェクトのビルド」を使います。
主に設定した箇所を取り上げます。Web画面は以下の感じです。

jnkns2.png

ソースコード管理

Bazaar pluginを入れたので、ソースコード管理にBazaarが出てきます。
ここにブランチ(共用リポジトリではない)URLを入れます。

SCMポーリング

Bazaarでソースコードやスクリプトをcommitすると、ブランチが更新されるので、Jenkinsがこれを検知して、自動的にビルドを開始させます。ここでは5分間隔でポーリングさせます。
初めは、commit直後にJenkinsにpushさせてビルドを走らせようと考えましたが、うまくいきませんでした。

ビルド

「シェルの実行」を使います。
どうもJenkinsは、ユーザーがJenkinsで実行するためか、ツールへのPATHとか再設定しなくてはならないため、シェルスクリプトにまとめました。
PATH設定が、「eda.bashrc」になります。

build.bash
source  env/eda.bashrc

cd sim

make -f Makefile.vcs

シェルスクリプトから呼び出すMakeファイルはこんな感じで。
ここではシミュレーターとしてVCSを想定していますが、他のシミュレーターを使う場合は$UVM_HOME/exampleにあるのMakefile.iusMakefile.questaを参考にして下さい。
ここでは、$UVM_HOME/examples/integrated/ubusに記載されているサンプルを使っています。XML関係は後述します。

Makefile.vcs
UVM_HOME=/usr/uvm/uvm-1.1d
DUT     =/var/www/html/bzr/uvm_sample/trunk/uvm-1.1d
XML     = /var/www/html/bzr/uvm_sample/trunk/xml

include $(UVM_HOME)/examples/Makefile.vcs

all: comp run

comp:
        $(VCS) \
        -full64 \
        +incdir+$(DUT)/examples/integrated/ubus/sv \
        +incdir+$(DUT)/examples/integrated/ubus/examples \
        +incdir+$(XML) \
        $(XML)/xml_pkg.sv \
        $(DUT)/examples/integrated/ubus/examples/ubus_tb_top.sv

run:
        $(SIMV) +UVM_TESTNAME=test_2m_4s
        $(CHECK)


clean:
        @rm -fr \
          csrc \
          simv* \
          *.log \
          *.key \
          vc_hdrs.h \
          log.xml

この辺、Sconsにしたい…。

ブランチへのCommit

話は前後しますが、上記のスクリプトの入ったsimやenvなどのディレクトリもあらかじめ作った後にCommitしておきました。

cd /var/www/html/bzr/trunk
bzr add sim env
bzr commit -m "test environment" sim env xml

Jenkins実行

左にある「ビルドの実行」をクリックすると、ビルド履歴が動き出し、最終的に青になればテストはパスします。

jnkns1.png

Console Outputをクリックすると、実行ログが出てきます。

SCMポーリングによるJenkins実行

SCMで5分間隔でブランチを監視しているので、Bazaarでブランチへcommitすると、Jenkinsがこれを見つけて自動的にビルドを実行させます。
以下の図は、Bazaarのブランチを5分ごとにチェックし、ブランチに変化がなかったので何もしなかった例です。

jnkns3.png

commitを実施すると、アップデートがあったとログに記されます。

jnkns4.png

アップデートで自動実行されたビルドは、こんな感じでSCMポーリングで実行したよと知らせてくれます。

jnkns5.png

XML変換・レポート取得

いろいろ調べていたら、Verilab社がJenkinsやXMLと、メソドロジを組み合わせてやっていることがわかりました。

Verilab社の取り組み

上記のうち、下の二つはBitbucketにてApache2.0形式で公開されています。
https://bitbucket.org/verilab/

XMLを出力させてみる。

2番目に紹介されているSystemVerilogからXMLを出力させるユーティリティを組み込んでみました。
元ソースはこれです。

xml_report_server.svh
//----------------------------------------------------------------------
//   Copyright 2012 Verilab Inc.
//   Gordon McGregor (gordon.mcgregor@verilab.com)
//
//   All Rights Reserved Worldwide
//
//   Licensed under the Apache License, Version 2.0 (the
//   "License"); you may not use this file except in
//   compliance with the License.  You may obtain a copy of
//   the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in
//   writing, software distributed under the License is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//   CONDITIONS OF ANY KIND, either express or implied.  See
//   the License for the specific language governing
//   permissions and limitations under the License.
//----------------------------------------------------------------------

import uvm_pkg::*;
`include "uvm_macros.svh"

// Class: xml_report_server
//
// Replacement for uvm_report_server to log messages to an XML format
// that can be more easily reused and manipulated by external tools/ viewers
//
// Basic XML schema
// <log>
//   <msg verbosity="val" severity="string" file="filename" line="val" id="string" time="val" context="string(optional)">text</msg>
//   ...
//   <msg verbosity="val" severity="string" file="filename" line="val" id="string" time="val" context="string(optional)">text</msg>
//   <msg verbosity="val" severity="string" file="filename" line="val" id="string" time="val" context="string(optional)">text</msg>
//   ...
// </log>
//
class xml_report_server extends uvm_report_server;

  uvm_report_server old_report_server;
  uvm_report_global_server global_server;

  // characters that are invalid XML that have to be encoded
  string replacements[string] = '{ "<" : "&lt;",
                                   "&" : "&amp;",
                                   ">" : "&gt;",
                                   "'" : "&apos;",
                                   "\"": "&quot;"
                                 };
  integer logfile_handle;

  //Function: new
  // constructor
  function new(string name = "xml_report_server", log_filename = "log.xml");
    super.new();
    set_name(name);

    global_server = new();
    install_server();
    logfile_handle = $fopen(log_filename, "w");
    report_header(logfile_handle);
  endfunction

  // Function: install_server
  // replace the global server with this server
  function void install_server;
    old_report_server = global_server.get_server();
    global_server.set_server(this);
  endfunction

  // Function: enable_xml_logging
  // Configure all components to use UVM_LOG actions to trigger XML capture
  // has to be called after components have been instantiated (end of elaboration, run etc)
  function void enable_xml_logging(uvm_component base=null);
    uvm_root top;

    if (base == null) begin
      top = uvm_root::get();
      base = top;
    end

    base.set_report_default_file_hier(logfile_handle);
    base.set_report_severity_action_hier(UVM_INFO, UVM_DISPLAY | UVM_LOG);
    base.set_report_severity_action_hier(UVM_WARNING, UVM_DISPLAY | UVM_LOG);
    base.set_report_severity_action_hier(UVM_ERROR,   UVM_DISPLAY | UVM_LOG | UVM_COUNT);
    base.set_report_severity_action_hier(UVM_FATAL,   UVM_DISPLAY | UVM_LOG | UVM_EXIT);
  //  base.get_report_handler().dump_state();
  endfunction

  // Function: convert_verbosity_to_string
  // Helper function to convert verbosity value to appropriate string, based on uvm_verbosity enum if an equivalent level
  function string convert_verbosity_to_string(int verbosity);
    uvm_verbosity l_verbosity;

    if ($cast(l_verbosity, verbosity)) begin
        convert_verbosity_to_string = l_verbosity.name();
    end else begin
        string l_str;
        l_str.itoa(verbosity);
        convert_verbosity_to_string = l_str;
    end
  endfunction

  // Function: report_header
  // Output standard XML header to log file
  function void report_header(UVM_FILE file = 0);
    f_display(file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?xml-stylesheet type=\"text/xsl\" href=\"uvm.xsl\"?><log>\n");
  endfunction

  // Function: report_footer
  // Output XML closing tags to log file
  function void report_footer(UVM_FILE file = 0);
    integer result;
    $fflush(file);
    result = $fseek(logfile_handle, 0, 2);
    f_display(logfile_handle, "</log>");
  endfunction

  // Function: summarize
  // tidy up logging and restore global report server
  function void summarize(UVM_FILE file = 0);
    report_footer();
    global_server.set_server(old_report_server);
    $fclose(logfile_handle);
    old_report_server.report_summarize(file);
    old_report_server.summarize(file);
  endfunction

  // Function: process_report
  //
  // Processes the message's actions.
  virtual function void process_report(
    uvm_severity severity,
    string name,
    string id,
    string message,
    uvm_action action,
    UVM_FILE file,
    string filename,
    int line,
    string composed_message,
    int verbosity_level,
    uvm_report_object client
    );
    // update counts
    incr_severity_count(severity);
    incr_id_count(id);

    if(action & UVM_DISPLAY)
      $display("%s",composed_message);

    // if log is set we need to send to the file but not resend to the
    // display. So, we need to mask off stdout for an mcd or we need
    // to ignore the stdout file handle for a file handle.
    if(action & UVM_LOG)
      if( (file == 0) || (file != 32'h8000_0001) ) //ignore stdout handle
      begin
        UVM_FILE tmp_file = file;
        if( (file&32'h8000_0000) == 0) //is an mcd so mask off stdout
        begin
          tmp_file = file & 32'hffff_fffe;
        end
        composed_message = compose_xml_message(severity, verbosity_level, name, id, message, filename, line);
        f_display(tmp_file, composed_message);
      end

    if(action & UVM_EXIT) client.die();

    if(action & UVM_COUNT) begin
      if(get_max_quit_count() != 0) begin
        incr_quit_count();
        if(is_quit_count_reached()) begin
          client.die();
        end
      end
    end

    if (action & UVM_STOP) $stop;

  endfunction

  // Function: sanitize
  //
  // Given an unencoded input string, replaces illegal characters for XML data format
  function string sanitize(string data);

    for(int i = data.len()-1; i >= 0; i--) begin
      if (replacements.exists(data[i])) begin
          data = {data.substr(0,i-1), replacements[data[i]], data.substr(i+1, data.len()-1)};
      end
    end
    return data;
  endfunction : sanitize


  // Function: xla
  // XML Attribute
  // Generate an XML attribute ( tag = "data" )
  function string xla(string tag, string data);
    xla="";
    if (data != "") begin
      xla = {" ", tag, "=\"", sanitize(data), "\" "};
    end
  endfunction

    // Function: xle
    // XML Element
    // Generate an XML element ( <tag attributes>data</tag> )
  function string xle(string tag, string data, string attributes="");
    xle = "";
    if (data != "") begin
      xle = {"<", tag, attributes, ">", sanitize(data), "</", tag, ">\n"};
    end
  endfunction

  // Function: compose_log_report_message
  // Generate the XML encapsulated report message, for logging
  virtual function string compose_xml_message(
    uvm_severity severity,
    int verbosity,
    string name,
    string id,
    string message,
    string filename,
    int    line
    );
    uvm_severity_type sv;
    string severity_string;
    string time_str;
    string line_str;
    string verbosity_str;
    integer result;

    sv = uvm_severity_type'(severity);
    severity_string = sv.name();
    $swrite(time_str, "%0t", $time);
    line_str.itoa(line);
    verbosity_str.itoa(verbosity);

    compose_xml_message = xle("msg", message,
                             {xla("verbosity", verbosity_str),
                              xla("severity", severity_string),
                              xla("file", filename),
                              xla("line", line_str),
                              xla("id", id),
                              xla("time", time_str),
                              xla("context", name) });
  endfunction


endclass

このxml_report_serverクラスを、uvm_envクラスを継承している$UVM_HOME/examples/integrated/ubus/examples/ubus_examples_tb.svにインスタンスさせ、end_of_elaboration_phaseに追記します。

ubus_expamples_tb.sv
//----------------------------------------------------------------------
//   Copyright 2007-2010 Mentor Graphics Corporation
//   Copyright 2007-2011 Cadence Design Systems, Inc.
//   Copyright 2010 Synopsys, Inc.
//   All Rights Reserved Worldwide
//
//   Licensed under the Apache License, Version 2.0 (the
//   "License"); you may not use this file except in
//   compliance with the License.  You may obtain a copy of
//   the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in
//   writing, software distributed under the License is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//   CONDITIONS OF ANY KIND, either express or implied.  See
//   the License for the specific language governing
//   permissions and limitations under the License.
//----------------------------------------------------------------------

`include "ubus_example_scoreboard.sv"
`include "ubus_master_seq_lib.sv"
`include "ubus_example_master_seq_lib.sv"
`include "ubus_slave_seq_lib.sv"

`include "xml_report_server.svh"

//------------------------------------------------------------------------------
//
// CLASS: ubus_example_tb
//
//------------------------------------------------------------------------------

class ubus_example_tb extends uvm_env;

  // Provide implementations of virtual methods such as get_type_name and create
  `uvm_component_utils(ubus_example_tb)

  // ubus environment
  ubus_env ubus0;

  // Scoreboard to check the memory operation of the slave.
  ubus_example_scoreboard scoreboard0;

  // XML logging server
  xml_report_server xml_reporter;


  // new
  function new (string name, uvm_component parent=null);
    super.new(name, parent);
    xml_reporter = new();
  endfunction : new

  // build_phase
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    uvm_config_db#(int)::set(this,"ubus0",
                   "num_masters", 1);
    uvm_config_db#(int)::set(this,"ubus0",
                   "num_slaves", 1);

    ubus0 = ubus_env::type_id::create("ubus0", this);
    scoreboard0 = ubus_example_scoreboard::type_id::create("scoreboard0", this);
  endfunction : build_phase

  function void connect_phase(uvm_phase phase);
    // Connect slave0 monitor to scoreboard
    ubus0.slaves[0].monitor.item_collected_port.connect(
      scoreboard0.item_collected_export);
  endfunction : connect_phase

  function void end_of_elaboration_phase(uvm_phase phase);
    // Set up slave address map for ubus0 (basic default)
    ubus0.set_slave_address_map("slaves[0]", 0, 16'hffff);

    // Enable XML logging 
    xml_reporter.enable_xml_logging();
  endfunction : end_of_elaboration_phase

endclass : ubus_example_tb

XML出力関係は、後々拡張のためにPackege化させます。

xml_pkg.sv
package xml_pkg;
  import uvm_pkg::*;

  `include "xml_report_server.svh"
endpackage

XML出力

ここまでのコードをCommitさせ、Jenkinsにて実行させると、下記のような感じでXMLが書き出されました。

log.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="uvm.xsl"?><log>

<msg verbosity="100"  severity="UVM_INFO"  file="/var/www/html/bzr/uvm_sample/trunk/uvm-1.1d/examples/integrated/ubus/ex
amples/test_lib.sv"  line="55"  id="test_2m_4s"  time="0"  context="uvm_test_top" >Printing the test topology :
--------------------------------------------------------------------
Name                         Type                     Size  Value   
--------------------------------------------------------------------
uvm_test_top                 test_2m_4s               -     @459    
  ubus_example_tb0           ubus_example_tb          -     @493    
    scoreboard0              ubus_example_scoreboard  -     @513    
      item_collected_export  uvm_analysis_imp         -     @521    
      disable_scoreboard     integral                 1     &apos;h0     
      num_writes             integral                 32    &apos;d0     
      num_init_reads         integral                 32    &apos;d0     
      num_uninit_reads       integral                 32    &apos;d0     
      recording_detail       uvm_verbosity            32    UVM_FULL
    ubus0                    ubus_env                 -     @505    
      bus_monitor            ubus_bus_monitor         -     @535    
      masters[0]             ubus_master_agent        -     @567    
      masters[1]             ubus_master_agent        -     @579    
      slaves[0]              ubus_slave_agent         -     @590    
      slaves[1]              ubus_slave_agent         -     @598    
      slaves[2]              ubus_slave_agent         -     @606    
      slaves[3]              ubus_slave_agent         -     @614    
      has_bus_monitor        integral                 1     &apos;h1     
      num_masters            integral                 32    &apos;h2     
      num_slaves             integral                 32    &apos;h4     
      intf_checks_enable     integral                 1     &apos;h1     
      intf_coverage_enable   integral                 1     &apos;h1     
      recording_detail       uvm_verbosity            32    UVM_FULL
    recording_detail         uvm_verbosity            32    UVM_FULL
--------------------------------------------------------------------
</msg>

<msg verbosity="0"  severity="UVM_INFO"  file="/var/www/html/bzr/uvm_sample/trunk/uvm-1.1d/examples/integrated/ubus/exam
ples/ubus_example_master_seq_lib.sv"  line="236"  id="loop_read_modify_write_seq"  time="0"  context="uvm_test_top.ubus_
example_tb0.ubus0.masters[0].sequencer@@loop_read_modify_write_seq" >loop_read_modify_write_seq starting...itr = 6</msg>

<msg verbosity="0"  severity="UVM_INFO"  file="/var/www/html/bzr/uvm_sample/trunk/uvm-1.1d/examples/integrated/ubus/exam
ples/ubus_example_master_seq_lib.sv"  line="236"  id="loop_read_modify_write_seq"  time="0"  context="uvm_test_top.ubus_
example_tb0.ubus0.masters[1].sequencer@@loop_read_modify_write_seq" >loop_read_modify_write_seq starting...itr = 8</msg>

(中略)

<msg verbosity="100"  severity="UVM_INFO"  file="/var/www/html/bzr/uvm_sample/trunk/uvm-1.1d/examples/integrated/ubus/sv/ubus_slave_monitor.sv"  line="243"  id="uvm_test_top.ubus_example_tb0.ubus0.slaves[3].monitor"  time="3030"  context="uvm_test_top.ubus_example_tb0.ubus0.slaves[3].monitor" >Covergroup &apos;cov_trans&apos; coverage: 30.000000</msg>

<msg verbosity="0"  severity="UVM_INFO"  file="/var/www/html/bzr/uvm_sample/trunk/uvm-1.1d/examples/integrated/ubus/examples/test_lib.sv"  line="70"  id="test_2m_4s"  time="3030"  context="uvm_test_top" >** UVM TEST PASSED **</msg>

</log>

おわりに

Jenkinsは分散ビルド・テスト等があるため、UVMのUVM_TESTやUVM_SEQUENCEと組み合わせると、シミュレーターのライセンス数に依りますが、多数のテストを同時実行しかつスケーラビリティに富む環境が構築可能と思っています。
もっとも、スケーラビリティがあるということは構築は大変なんですけどね。

後日、XMLによるレポート出力をJunitで読める形式に合わせ、Jenkinsで統計が取れるようにしていこうと思います。

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7