15人の検証チームって、
どんななんでしょうね。
最近のSOC開発なら普通の人員と思います。ですが検証規模は人員の能力によりますよね。
検証エキスパートの集まりばかりなら、新規開発チップとしても結構な規模ですよね。
工数的には期間にもよりますね。決して20万人月ではないと思いますが。
一方こちらは川の向こうで一人開発...こういうのも楽しいですよね、きっと。
■
はじめに
諸事情によりUVMのコードを載せてみます。今回は最小限のHelloWorldテストなので、何の約にもたたないと思います。
なお、ソースコードはGitHubにあります。
このブログで扱うソースコードはできるだけGitHubにあげていきます。ライセンスなどご確認頂いた上でお好きにお使い下さい。 今回のコードはこちらになります。
GitHub - systemverilog-uvm - uvm_hello_world_test
中身の説明は後ほど...更新しました(2015/11/12)
ファイル構成
- uvm_hello_world_pkg.sv ... テストをインクルードするパッケージです。
- uvm_hello_world_test.sv ... テストです。
- uvm_hello_world_tb.sv ... テストベンチです。
それぞれの説明
uvm_hello_world_pkg.sv
パッケージでテストをインクルードしています。理由は...特に...ありません...osz ですが、テスト構成物が増えてくると、テストベンチが使うものをパッケージ化してインクルードする構成も悪くないと思います。 ⇒ 今回はテストしかインクルードしていないのでもったいない印象ですが...
package uvm_hello_world_pkg; import uvm_pkg::*; `include "uvm_hello_world_test.sv" endpackage : uvm_hello_world_pkg
uvm_hello_world_test.sv
テストファイルです。 uvm_testクラスを継承して、run_phase()でuvm_infoを使って文字列表示しています。 特に何もしていません...すみません...
`include "uvm_macros.svh" import uvm_pkg::*; class uvm_hello_world_test extends uvm_test; `uvm_component_utils(uvm_hello_world_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); phase.raise_objection(this); `uvm_info("TEST", "UVM_HELLO_WORLD !!!", UVM_MEDIUM); phase.drop_objection(this); endtask endclass
uvm_hello_world_tb.sv
テストベンチファイルです。 テストをインクルードするパッケージをインクルードしてrun_test()を呼んでいます。 はいそれだけです。
`include "uvm_macros.svh" `include "uvm_hello_world_pkg.sv" // `include "uvm_hello_world_test.sv" import uvm_pkg::*; import uvm_hello_world_pkg::*; module uvm_hello_world_tb(); initial run_test(); endmodule
run.do
ModelSim用の実行スクリプトです。 シミュレーション実行時にUVM_TESTNAMEでテストファイルのクラス名を与えています。 なお、シミュレーション実行前には各ファイルをコンパイルしておいて下さい(vlib、vlog)
vsim -c uvm_hello_world_tb +UVM_TESTNAME=uvm_hello_world_test
続きはまた後ほど...更新しました(2015/11/13)
実行結果
ModelSim実行時のログを示します。 uvm_infoで表示した [TEST] UVM_HELLO_WORLD !!! が確認できます。
# UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(215) @ 0: reporter [Questa UVM] QUESTA_UVM-1.2.2 # UVM_INFO verilog_src/questa_uvm_pkg-1.2/src/questa_uvm_pkg.sv(217) @ 0: reporter [Questa UVM] questa_uvm::init(+struct) # UVM_INFO @ 0: reporter [RNTST] Running test uvm_hello_world_test... # UVM_INFO uvm_hello_world_test.sv(13) @ 0: uvm_test_top [TEST] UVM_HELLO_WORLD !!! # UVM_INFO verilog_src/uvm-1.1d/src/base/uvm_objection.svh(1268) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract ' phase
今日はここまで
以上全く役に立たないUVMテストでした。ですが今回用意したパッケージにenvやagentやモデル、モニタ、スコアボードなどを追加していくとボトムアップ的にUVM環境構築できる気もします。 ということで、次回は、ここからもう少しボトムアップしたベンチを示したいと思っています。
CodingBat 振ってみた...
はじめに
Pythonの基礎文法を使いたくてCodingBatを振ってみた。Pythonをやる理由は...なんとなく。
サイトはこちら
CodingBat Python ... Python版のトップです。他にJava版がある模様。
お役にたてば
- 概要 ** 設問にの答えを関数(def:)として記述すると、コンパイルと答え合わせができます。
- コースは8種類
⇒ ウォームアップ、文字列、リスト、論理パズル、が各2個。 - 設問は6個とか9個
⇒ コースの中に問題が何個かあります。 - 所要時間
⇒ 文法が分かる(≒コンパイルエラーがなくなる)までは結構時間を要するかと思います。
⇒ 文法が分かれば、設問あたり1分から10分くらいで解けるものばかりです(電車の中でもやりやすい)
⇒ 但し、英語が少し難しい印象。たまに問題文が理解できずハマることがありますた。
⇒ 自分は全部やるのにトータル3時間くらいだったと思います.. - その他
⇒ 回答には、論理的に正しい、に加えて、実行時間的に長すぎないとこ、も求められます。
⇒ 2重のforループを使うと不意にタイムアウトになって回答できない場合あり、少しイミフではまりました。感想
リスト操作、文字列操作、に慣れるには良いと思いました。
テスト形式なので写経とは違う、頑張ってみようかな的な楽しみがありました。
Pythonやってみたい人にお勧めできると思いました。今日はここまで
さて次は何しましょう。やりたことに対して年齢がついていきません...
SVA - アサーションFireさせてみた。
はじめに
今回はsimple-req-ackのアサーションに対して、これまでと異なるテストベクタをくべてその振る舞いの違いを見てみます。
なお、ソースコードはGitHubにあります。
このブログで扱うソースコードはできるだけGitHubにあげていきます。ライセンスなどご確認頂いた上でお好きにお使い下さい。 今回のコードはこちらになります。
GitHub - systemverilog-assertion - simple-req-ack2
DUT仕様おさらい(dut.sv)
DUT仕様は、reqが1パルス入力されると2サイクル後ackを1パルス返すものでした。
プロパティおさらい(sva_req_ack.sv)
DUT仕様を確認するために似たような4つのプロパティを用意していました。
- sva_req_ack1 ・・・ Highレベルを確認
- sva_req_ack2 ・・・ Highへの立ち上がりを確認
- sva_req_ack3 ・・・ 立ち上がりと立ち下がりを確認
- sva_req_ack4 ・・・ 立ち上がりとレベルと立ち下がりを互いの信号関係を踏まえ確認
プロパティだけ抜粋しておきます。
// PROPERTY property sva_req_ack1 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) req |-> ##2 ack; endproperty property sva_req_ack2 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> ##2 $rose(ack); endproperty property sva_req_ack3 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> ##1 $fell(req) ##1 $rose(ack) ##1 $fell(ack); endproperty property sva_req_ack4 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> !ack ##1 ($fell(req) & !ack) ##1 ($rose(ack) & !req) ##1 ($fell(ack) & !req); endproperty
今回のテスト(bench.sv)
やっと本題です。今回は1パルスのreqに加えて、2サイクルのreqパルスと5サイクルのreqパルスをくべてみます。 シナリオ箇所だけ抜粋します。
task scenario(); req = 0; @(posedge rst_n) // 1st. req as 1 cycle pulse. repeat(5) @(posedge clk); req = 1; repeat(1) @(posedge clk); req = 0; repeat(3) @(posedge clk); // 2nd. req as 2 cycle pulse. req = 1; repeat(2) @(posedge clk); // 2 cycles req = 0; repeat(3) @(posedge clk); // 3rd. req as 5 cycle pulse. req = 1; repeat(5) @(posedge clk); // 5 cycles req = 0; repeat(5) @(posedge clk); endtask
実行結果
波形はこのようになります。
今回は2サイクルのreqパルスと5サイクルのreqパルスは期待しない動作なのでアサーションがFail(ファイア)することを期待します。おおかたよさそうですが、残念ながら2番目のプロパティ(sva_req_ack2:立ち上がりだけ)はしれっとPassしてしまいました。その他のプロパティについては、1番目(sva_req_ack1:Highレベル)はNGを検出していますが、複数回ファイアしています。また、3番目(sva_req_ack3:立ち上がりと立ち下がり)と4番目(sva_req_ack4:全部)は、最初のNGでのみファイアしています。
NGを検出した上で、どのプロパティがよいかは...自分は最小限のファイアが好みです。余計なファイアがないほうがデバッグしやすいので。ですがどれが良いかは一概には言えないと思います。丁寧なプロパティは場合によってはCPUリソースやメモリ食いかもしれませんし...何を検出するためプロパティを書くか、に依存すると思っています。
今日はここまで
ここ数回SVAについてどうでもいい内容を書いてみました。次は、UVM1.2も動き出すようですしまたUVM Cookbookに戻ろうかなと思っています。ですが座学だけだとつまらないのでUVMのサンプルでも作ってみようかなと思っています。
SVA - アサーション実行してみる?
はじめに
前回のアサーション記事はプロパティの中身の話でした。今日はアサーションの実行まわりの話を書きます。
なお、ソースコードはGitHubにあります。
このブログで扱うソースコードはできるだけGitHubにあげていきます。ライセンスなどご確認頂いた上でお好きにお使い下さい。
GitHub - systemverilog-assertion - simple-req-ack
登場人物
前回のアサーションでは以下の4人が登場します。
- DUT(dut.sv) ・・・ 検証対象のデザインです。
- アサーション(sva_req_ack.sv) ・・・ プロパティを書いてアサートします。
- バインダー(binder.sv) ・・・ 別ファイルに書いたアサーションを検証対象の信号にバインドします。
- テストベンチ(bench.sv) ・・・ DUTに刺激を与えます。
以下、簡単に紹介します。
DUT(dut.sv)
仕様は、reqが1パルス入力されると2サイクル後ackを1パルス返します。 記述がヘボいのは気にしないことにします。
module dut ( input clk, input rst_n, input req, output ack ); logic req_ff, ack_ff; always_ff @(negedge rst_n, posedge clk) if (!rst_n) req_ff <= '0; else if (req_ff) req_ff <= '0; else req_ff <= req; always_ff @(negedge rst_n, posedge clk) if (!rst_n) ack_ff <= '0; else if (ack_ff) ack_ff <= '0; else if (req_ff) ack_ff <= '1; assign ack = ack_ff; endmodule
アサーション(sva_req_ack.sv)
今回のチェッカーです。4つに似たようなプロパティを用意してアサートしています。
module sva_req_ack(input clk, rst_n, req, ack); // PROPERTY property sva_req_ack1 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) req |-> ##2 ack; endproperty property sva_req_ack2 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> ##2 $rose(ack); endproperty property sva_req_ack3 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> ##1 $fell(req) ##1 $rose(ack) ##1 $fell(ack); endproperty property sva_req_ack4 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> !ack ##1 ($fell(req) & !ack) ##1 ($rose(ack) & !req) ##1 ($fell(ack) & !req); endproperty // ASSERT PROPERTY assert_sva_req_ack1 : assert property( sva_req_ack1(clk, rst_n, req, ack) ); assert_sva_req_ack2 : assert property( sva_req_ack2(clk, rst_n, req, ack) ); assert_sva_req_ack3 : assert property( sva_req_ack3(clk, rst_n, req, ack) ); assert_sva_req_ack4 : assert property( sva_req_ack4(clk, rst_n, req, ack) ); endmodule
バインダー(binder.sv)
アサーションを接続(bind)するための記述です。sva_req_ack.svを使うのでincludeしています。
bindの文法はこんな感じのようです。
bind [アサーションを接続信号を含むmodule名] [アサーションのmodule名] [アサーションのインスタンス名] (ポート接続)
バインダーでポート宣言しない方法もありと思います。今回はポート未接続なWarningがいやだったのでポート宣言しています。
`include "sva_req_ack.sv" module binder(input clk, rst_n, req, ack); // bind [BIND_TARGET] [ASSERTION_NAME] [INSTANCE_NAME_OF_ASSERTION] bind bench sva_req_ack i_sva_req_ack( // .PORT_OF_ASSERTION(PORT_OF_TARGET) .clk(clk), .rst_n(rst_n), .req(req), .ack(ack) ); endmodule
テストベンチ(bench.sv)
テストベンチです。DUTとバインダーをインスタンスしています。その他クロック、リセット、シナリオのtaskでできています。
`include "dut.sv" `include "binder.sv" module bench #( parameter CLK_PERIOD=10, parameter RESET_CYCLE=5, parameter RESET_ACTIVE=0 ); logic clk, rst_n; logic req, ack; // DUT dut i_dut( .clk(clk), .rst_n(rst_n), .req(req), .ack(ack) ); // Instantiate Binder binder i_binder( .clk(clk), .rst_n(rst_n), .req(req), .ack(ack) ); // TASKs task clk_gen(); clk = 0; forever #(CLK_PERIOD/2) clk=!clk; endtask task rst_gen(); rst_n = RESET_ACTIVE; repeat(RESET_CYCLE) @(posedge clk); rst_n <= !RESET_ACTIVE; forever @(posedge clk); endtask task scenario(); req = 0; @(posedge rst_n) repeat(5) @(posedge clk); req = 1; repeat(1) @(posedge clk); req = 0; repeat(5) @(posedge clk); endtask initial begin fork rst_gen(); // forever clk_gen(); // forever scenario(); join_any $finish; end endmodule
実行してみる。
みんな大好きMentorのシミュレータで実行するためのコマンドはこのようになります。
vlog *.sv # Add -assertdebug to use assertion vsim bench -voptargs=+acc -assertdebug # Enable Assertion Thread View (ATV) to specify assertion # -enable ... Turn on assertion thread viewing # -asserts ... Turn on assertion thread viewing for only assertion # (not coverage) # -recursive ... Assertion is turned on recursivly from PATH. # PATH ... "/*", Assertion is in effect on this layer specifed by PATH. atv log -enable -asserts -recursive /* ## To open the Assertion Window: ## View -> Coverage -> Assertions
ポイントは、
vsim
コマンドに-assertdebug
を追加してアサーションを有効にします。atv
(Assertion Thread View)コマンドを有効にします。- GUI上でアサーション結果を見るためにAssertion Windowを開きます。
- メニューの View - Coverage - Assertions から開けます。
なお、vlib と run -all はお好みでお願いします。
今日はここまで
正直、自分はアサーション全然やったことがないのでして、SVAを超絶実践されている方面の方々から多大なご批判を受けそうな内容でありますが、そこは寛容にお願いしたいところであります。
次回は、今回書いている4つの似たようなプロパティについて考えられれば、と思っています。
SVA - アサーション、なにそれおいしいの?
はじめに
アクセス解析によると本ブログで1つしかないSVAの記事を引っ掛けてくれる方が多いようです。というわけで今日はアサーションネタいってみます。
こんにちはアサーション
アサーションって導入できたチームとできないチームが極端に分かれそうな検証手法のような気がします。食わず嫌いなチームの言い分はこんなではないでしょうか?
- ウチはホワイトボックス検証なんかしないよ?
- アサーション書けるほど実装仕様書ないよ?
- アサーション誰書くの?
- 書いたプロパティが正しいかレビューするの大変だよ?
- そもそSVA書ける人、読める人がいないよ?
- 今までの検証だけでも大変なのにに更に追加でアサーションなんて工数ないよ?
- 等々
なーんてそんなこと言っていても始まらないので、まずは適当なアサーションを書いてみたいと思います。
DUT仕様
適当なハンドシェイクするDUTを考えます。
仕様は、reqが1パルス入力されると2サイクル後ackを1パルス返します。
テストベンチ側から見た波形はこんな感じです。
これを確認するプロパティを考えてみます。
プロパティその1・・・レベルで確認
property sva_req_ack_pass1 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) req |-> ##2 ack; endproperty
プロパティに書かれた内容はこんな感じです:
- リセット=0でないときに
disable iff(!rst_n)
、req=1req
ならプロパティを発動して|->
、2サイクル後##2
に、ack=1ack
かを確認する。
実行すると、無事アサートされてパスします。
プロパティその2・・・片エッジで確認
プロパティその1はreqとackがレベルとして1であるのことだけを考えたプロパティでした。プロパティその2では立ち上がりを意識したプロパティを書いてみます。信号の立ち上がり検出には$rose()
を使います。
property sva_req_ack_pass2 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> ##2 ack; endproperty
プロパティに書かれた内容はこんな感じです:
- リセット=0でないときに
disable iff(!rst_n)
、reqが立ち上がり$rose(req)
ならプロパティを発動して|->
、2サイクル後##2
に、ackの立ち上がり$rose(ack)
を確認する。
実行すると、無事アサートされてパスします。
プロパティその3・・・両エッジで確認
次のプロパティでは、プロパティその2に更に信号立ち下がりを意識したプロパティを書いてみます。信号の立ち下がり検出には$fell
を使います。
property sva_req_ack_pass3 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> ##1 $fell(req) ##1 $rose(ack) ##1 $fell(ack); endproperty
プロパティに書かれた内容はこんな感じです:
- リセット=0でないときに
disable iff(!rst_n)
、reqが立ち上がり$rose(req)
ならプロパティを発動して|->
、1サイクル後##1
に、reqの立ち下がり$fell(req)
を確認し、1サイクル後にackの立ち上がり$rose(ack)
を確認し、1サイクル後にackの立ち下がり$fell(ack)
を確認する。
実行すると、無事アサートされてパスします。
プロパティその4・・・レベルとエッジでもっと確認
今回のDUT仕様ではreqとackが排他にアサートされることが伺えます。最後に2つの信号の関係を意識したプロパティを書いてみます。
property sva_req_ack_pass4 (clk, rst_n, req, ack); @(posedge clk) disable iff(!rst_n) $rose(req) |-> !ack ##1 ($fell(req) & !ack) ##1 ($rose(ack) & !req) ##1 ($fell(ack) & !req); endproperty
プロパティに書かれた内容はこんな感じです:
- リセット=0でないときに
disable iff(!rst_n)
、reqが立ち上がり$rose(req)
ならプロパティを発動して|->
、このサイクルはack=0!ack
を確認し、1サイクル後##1
に、reqの立ち下がりとack=0$fell(req) & !ack
を確認し、1サイクル後にackの立ち上がりとreq=0$rose(ack) & !req
を確認し、1サイクル後にackの立ち下がりとreq=0$fell(ack) & !req
を確認する。
実行すると、無事アサートされてパスします。
で、全部パスしたジャマイカ...osz
はいそうです...osz。今回用意したプロパティは全て正常動作をカバーしています。テストベクタでは典型的な正常動作だけだったので全てパスします。ですが、NG動作なテストベクタを入れた場合にどうなるか...NG時に発火するものとしないものとがありそうなのがなんとなく想像できますよね。
結局のところ何をチェックするか、に応じてプロパティを正しく書く必要があるわけです...そんなの知ってるyo!
今日はここまで
アサーションのプロパティって何を確認するか、にあわせてプロパティを書きたいですよね...自分は無理ですが! なお、今回使った全ソースは次の機会に準備しようと思います...たぶん。
UVM Cookbook 眺めてみた - Assigning Virtual Interfaces From The Configuration Space
はじめに
UVM Cookbookを眺めてみた、の続きです。今日はTestbenchのAssigning Virtual Interfaces From The Configuration Spaceです。
Assigning Virtual Interfaces From The Configuration Space
- UVMのrun_test()が呼ばれる前にトップ階層の信号とDUTの入出力はSystemVerilogのinterfaceで接続されているべきだよ。
- トップ階層とDUTのinterfaceはvirtual interfaceのハンドルにアサインされてuvm_config_db::setを使ってテストに渡されるよ。
- テストのbuildメソッドではvirtual interfaceは関連するコンポーネントのコンフィギュレーションオブジェクトにあるvirtual interfaceのハンドルにアサインされる必要があるよ。
- それから、個々のコンポーネントは自身のコンフィギュレーションオブジェクトにあるvirtual interfaceのハンドルにアクセスしてDUTの信号をドライブしたりモニタしたりするよ。
- コンポーネントをモジュール化したり再利用するために、driversとmonitorsはvirtual interfaceのポインタをコンフィギュレーション空間から直接取得するべきでは無いよ。
テストクラスでfirtual interfaceがコンフィギュレーションオブジェクトを通じて正しい検証コンポーネントにアサインされるか保障するよ。
次のコードは、SPIテストベンチでuvm_config_db::getメソッドでvirtual interfaceをapb_agentsのコンフィギュレーションオブジェクトのvirtual interfaceのハンドルにアサインする例だよ。
// これより前にbuildメソッドでapb agentのvirtual interfaceのアサインを追加しています。 // envをビルドしてenvのコンフィギュレーションを作りサブコンフィギュレーションとvirtual interfaceのアサインをインクルードします。 function void spi_test_base::build_phase( uvm_phase phase ); // envのコンフィギュレーションオブジェクトを作ります m_env_cfg = spi_env_config::type_id::create("m_env_cfg"); // envをコンフィギュレーションする関数をコールします configure_env(m_env_cfg); // apb agentのコンフィギュレーションオブジェクトを作ります m_apb_cfg = apb_agent_config::type_id::create("m_apb_cfg"); // apb agentをコンフィギュレーションする関数を呼びます configure_apb_agent(m_apb_cfg); // apbのvirtual interfaceを追加します if( !uvm_config_db #( virtual apb3_if )::get(this, "" , "APB_vif",m_apb_cfg.APB) ) `uvm_error(...) // 以降処理が続きます endfunction: build_phase