はじめに
やあ (´・ω・`) ようこそ、バーボンハウスへ。
このテキーラはサービスだから、まず飲んで落ち着いて欲しい1。
1)Flip-Flopでお手軽UVM(できなかった)
2)Flip-Flopでお手軽UVM(uvm_test)
3)Flip-Flopでお手軽UVM(Virtual Interface)
4)Flip-Flopでお手軽UVM(Clocking block)
5)Flip-Flopでお手軽UVM(Driver)←いまここだよ
……というわけで、このシリーズもついに第5話に突入しました。すごいですね。多くの投稿者が挫折し失踪すると呼ばれる第5話目、5話目ですってよ奥さん。これはお祝いしないといけないレベルでございますわ!23
想定読者
ないんだな、それが。しいて言えば、おれだ。俺自身が読者なのだ4。
それでは、今日も一日がんばるぞいっ5。
今回の目標
前回までに、やりたいことはおおよそ完了しました6。あれでも一応動きはするので、まあとりあえず完成と言ってしまっても良い……のですが、今回はもうちょっと頑張って、第二話で見たような「普通のUVMテストベンチ」の構成に少し近づける工夫をしてみましょう。
Pankaj S. Vitankar - UVM ARCHITECTURE FOR VERIFICATION
今回組み上げる予定のテストベンチの構成
これが今回作る予定のテストベンチの全体像です。どうでしょう。だいぶサマになってきたんじゃないでしょうか。少なくとも、第二話で作ったベンチと比べて、当社比1000倍くらいそれっぽい見た目になった気がします。
テストベンチ
テストベンチトップのコードはこんなかんじ。my_pkg.sv
を見ると……なんかやたらとファイルが増えてますね。トランザクションとか、シーケンスとか、ドライバーとか。横文字ばっかで意味わかんにゃい。一方、top.sv
のほうは前回とほぼ同じです。
package my_pkg;
timeunit 1ns;
timeprecision 1ps;
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "my_transaction.sv"
`include "my_sequence.sv"
`include "my_driver.sv"
`include "my_agent.sv"
`include "my_environment.sv"
`include "simple_test.sv"
endpackage
module top;
timeunit 1ns;
timeprecision 1ps;
import uvm_pkg::*;
import my_pkg::*;
parameter realtime clk_period = 1ns;
bit clk = 1'b0;
initial begin
forever begin
#(0.5*clk_period);
clk = ~clk;
end
end
dff_if intf (
.clk
);
DFF dut (
.D(intf.d),
.CLK(intf.clk),
.RSTN(intf.rstn),
.Q(intf.q)
);
initial begin
uvm_config_db#(virtual dff_if)::set(uvm_root::get(), "uvm_test_top.env.agt", "vif", intf);
run_test();
end
endmodule
Sequence、Sequence item、Driver
さて、真面目な解説記事であれば、トランザクションレベルモデリングがどうとか、こういう図を使った、なにやら難しそうな話がここで始まったりするわけですが……うーん、まあとりあえず動けばいいから手短に使い方だけ教えてよ、ってことでね、ここではやっていこうと思います。
お↓ら↑よ↓7。強者たちよ、出てこいやっ!
気になるあの娘はシーケンス8
まずはこの子、「シーケンス」の紹介から始めましょう。
UVMでは「トランザクション」というカタマリを介して、コンポーネント9間で必要な情報のやり取りをします。そのトランザクションとやらを作ってるのが、この「シーケンス」です10。ほら、見てください、`uvm_info(get_type_name(), "Reset", UVM_MEDIUM)
のすぐ下のあたり。「DUTをリセットしろー」的な情報というか指示みたいなものを詰め込んでいるような……そんな波動を感じませんか?
class my_sequence extends uvm_sequence;
`uvm_object_utils(my_sequence)
function new (string name="my_sequence");
super.new(name);
endfunction
virtual task body ();
my_transaction tr;
`uvm_info(get_type_name(), "Reset", UVM_MEDIUM)
tr = my_transaction::type_id::create("tr");
tr.reset = 1'b1;
tr.data = 1'b0;
tr.delay = 'd0;
start_item(tr);
finish_item(tr);
`uvm_info(get_type_name(), "50 random data", UVM_MEDIUM)
for (int i=0; i<50; i++) begin
`uvm_do_with(tr, {tr.reset == 1'b0; tr.delay == 'd0;})
end
endtask
endclass
ちなみに、`uvm_info(get_type_name(), "50 random data", UVM_MEDIUM)
の下の部分は、「ランダムなデータを送ってねー、ただし、リセット無し&ディレイ無しで。よろぴくー11」というTransactionを50回送ってるかんじのアレです。
君に胸キュン、トランザクション12
つづいて、これが今回の私たちの「トランザクション」です。
reset
、data
、delay
というメンバー変数を持っています。rand
キーワードが付いているので、`uvm_do(tr)
や`uvm_do_with(tr, constraints)
マクロを使った際、これらの変数には基本的にランダムな値がセットされます。それだけっちゃそれだけ。
class my_transaction extends uvm_sequence_item;
rand bit reset;
rand bit data;
rand int unsigned delay;
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(reset, UVM_DEFAULT)
`uvm_field_int(data, UVM_DEFAULT)
`uvm_field_int(delay, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name="my_transaction");
super.new(name);
endfunction
endclass
みむかゥわナイスドライバー13
ざぁーこざぁーこ♡、ざぁーこざぁーこ♡
……ごほん。えー、これが今回の主役、「ドライバー」です。先ほど定義したトランザクションを受け取って、実際に、DUTであるFlip-Flopを操作してくれるコンポーネントです。言い換えると、「トランザクション」という抽象的なサムシングを、D <= 0;
とかRSTN <= 0; @(cb); RSTN <= 1;
のような抽象度の低い「DUTに対する具体的な操作」に変換してくれるえらいやつです14。仲良くしてあげてね。
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual dff_if vif;
function new (string name="my_driver", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual dff_if)::get(this, "", "vif", vif)) begin
`uvm_fatal(get_type_name(), "Failed to get vif")
end
endfunction
virtual task run_phase (uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
if (req.reset) begin
vif.mp.reset();
end else begin
vif.mp.write(req.data);
end
vif.mp.delay(req.delay);
seq_item_port.item_done();
end
endtask
endclass
Agent
Agentは、シーケンサ15とドライバを入れる箱です。以上!
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
uvm_sequencer #(my_transaction) sqr;
my_driver drv;
virtual dff_if vif;
function new (string name="my_agent", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = uvm_sequencer#(my_transaction)::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
if (!uvm_config_db#(virtual dff_if)::get(this, "", "vif", vif)) begin
`uvm_fatal(get_type_name(), "Failed to get vif")
end
uvm_config_db#(virtual dff_if)::set(this, "drv", "vif", vif);
endfunction
virtual function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
Test、Environment
Environmentは、Agentを入れる箱です。
Testは、Sequence(シーケンス16)とEnvironmentを入れる箱です。
class simple_test extends uvm_test;
`uvm_component_utils(simple_test)
my_environment env;
my_sequence seq;
function new (string name="simple_test", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
env = my_environment::type_id::create("env", this);
seq = my_sequence::type_id::create("seq", this);
endfunction
virtual task run_phase (uvm_phase phase);
phase.raise_objection(this);
`uvm_info(get_type_name(), "Started", UVM_MEDIUM)
seq.start(env.agt.sqr);
`uvm_info(get_type_name(), "Finished", UVM_MEDIUM)
phase.drop_objection(this);
endtask
endclass
class my_environment extends uvm_env;
`uvm_component_utils(my_environment)
my_agent agt;
function new (string name="my_environment", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
agt = my_agent::type_id::create("agt", this);
endfunction
endclass
DSimの実行スクリプト
もう五回目なので、見慣れた光景ですね。今回から`include
を使い始めたので、options.txt
に、新しく-incdir
が追加されたことにだけご注意くださいませ17。
@echo off
set "DSIM_LICENSE=%USERPROFILE%\AppData\Local\metrics-ca\dsim-license.json"
cd "%USERPROFILE%\AppData\Local\metrics-ca\dsim\20240923.7.0"
call shell_activate.bat
cd %~dp0
dsim -f options.txt
pause
exit
// DSim
-top top
+acc
-waves waves.vcd
// UVM
-suppress IneffectiveDynamicCast:MissingTimescale
-uvm 1.2
+UVM_NO_RELNOTES
+UVM_TESTNAME=simple_test
+UVM_VERBOSITY=UVM_MEDIUM
// DUT
DFF.v
// Test bench
-incdir .
dff_if.sv
my_pkg.sv
top.sv
シミュレーション結果
キェェェェェェアァァァァァァウゴイタァァァァァァァ!!!!!!!!!!!!
おわりに
Flip-Flopでお手軽UVMシリーズは、これにておしまい!18
ここまで読んでくれてありがとう!
-
実際のところ、通常なら1ページで終わるような短い内容を引き延ばして引き延ばして、それこそもう向こう側の景色が透けて見えてしまうくらいに、あるいは、なんだこれおまえの実家のカルピスかよってくらいに薄くして、やっとこさ五回分、って感じかもしれなくもない。 ↩
-
まあまさに今日今回これを最後に失踪する予定なわけですが↩ -
このように言っておくと、自分の記事が誰にも読まれなかったときに「いや、でもこれ自分用に備忘録として書いたやつだしっ、ぜんぜん誰にも読まれなくてもかなしくねーしっ、全然ノーダメージだしっ、むしろそのほうがいい、っていうか!!???そんなかんじ???(泣」っていう逃げ道になるのでオススメだぞっ、この卑怯者っ! ↩
-
※「いまだに何のチェッカーも実装されておらず、毎回シミュレーション結果を目視確認している」という致命的な問題点を除く ↩
-
DriverとかMonitorとか。(正確にはちがうけど)静的なオブジェクト的なサムシングだと思っておいてください。Difference Between UVM_OBJECT and UVM_COMPONENT ↩
-
大雑把に、シーケンス=「実際にDUTのピンを操作してくれるドライバー君への指示書をつくるための場所」みたいな理解でもいいと思います。たぶんね。 ↩
-
みむかゥわナイストライ - ぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬ (cover)|ななひら ↩
-
前回の記事で、Interfaceの中に
write
やreset
といったタスクを入れておいたので、今回の私たちのテストベンチでのDriverくんのお仕事は、「受け取ったトランザクションに従って、適切にそれらのタスクを呼び出すこと」だけです。「簡単なお仕事でサクッと高収入☆彡」「未経験者歓迎☆彡」「アットホームな明るい職場☆彡」 ↩ -
Environmentの中にいるのが「シーケンス(
Sequence
)」で、Agentの中にいるのが「シーケンサ(Sequencer
)」です。文字にすると一文字違いでややこしいので、慣れるまではご注意くださいまし。シーケンスはオブジェクトで、シーケンサはコンポーネント。 ↩ -
アルファベットでSequenceって書いたり、カタカナでシーケンスって書いたり……表記揺れしまくっててごめんぬ!英語ばっかだとなんか文章がお堅く見えちゃう気がして、この記事では、なるべくカタカナで代用したいなぁ……という気持ちがあったりなかったりするよ!外来語表記警察のひとも生暖かく見守ってあげてね。 ↩
-
実は
options.txt
の-incdir .
は無くてもよかったりします。DSimくんは、カレントディレクトリをデフォで走査してくれるっぽいので。あと、今回は`include
するファイルをフォルダにまとめずに、run.bat
と同じ場所に直にベタ置きしてます。雑ぅ! ↩ -
ちょっとシリーズが長くなりすぎたのでここでいったん一区切り。MonitorとかScoreboardとか、RALとかその辺の話はまた今度別枠でやれたらやりたいなあと思ったーり、ぽけったーり、もんすたーり、おどったり、まわったり ↩