Flip-Flopでお手軽UVM（Virtual Interface）

## はじめに

この記事は、下記の２つの続編です。

- [Flip-Flopでお手軽UVM（できなかった）](https://qiita.com/ds54e/items/64ee47b8fc1cb5ae3cc1)
- [Flip-Flopでお手軽UVM（uvm_test）](https://qiita.com/ds54e/items/82ed3068505a17064f05)

上の２つをまだ読んでない人は今すぐよんでｋ……あ、いや、やっぱ大丈夫っす。いきなりこの記事から読み始めてしまってもたぶん問題ないと思います[^pre]。それでは、今日も張り切って……！UVM、UVM！[^ag]

![Screenshot 2025-01-01 085406.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/f1130819-0907-6e83-1d2e-9060f621e7a9.png)
[第3次スーパーロボット大戦 AG](https://youtu.be/g6tVDR1wAYA?si=W9vcqHSaeJG10Kk9&t=2309)

[^pre]: でもでもやっぱり、 [前回](https://qiita.com/ds54e/items/64ee47b8fc1cb5ae3cc1)/[前々回](https://qiita.com/ds54e/items/82ed3068505a17064f05)の記事も読んでくれたら、[それはとっても嬉しいなって](https://www.madoka-magica.com/tv/story/02.html)。

[^ag]: [ニコニコ大百科 - ＡＧ（スパロボ）](https://dic.nicovideo.jp/a/ag%28%E3%82%B9%E3%83%91%E3%83%AD%E3%83%9C%29)

## ねえ見て！ぼくの初めてのUVMテストベンチ！ 

[前回](https://qiita.com/ds54e/items/64ee47b8fc1cb5ae3cc1)、なるべく最短でUVMに触れるために、私たちはやや強引に`uvm_test`を使い始めました。そのため、あれを満面の笑みで「みてみて！私が昨日書いたUVMテストベンチ！」と屈強なエンジニアのところに見せに行くと、もしかするともしかしなくても、微妙な表情をされるかもしれません[^expr]。

![Screenshot 2024-12-31 180122.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/a60530a3-f245-31e2-e157-d3ee288e7249.png)


実際、[あのテストベンチ](https://qiita.com/ds54e/items/82ed3068505a17064f05#%E3%83%86%E3%82%B9%E3%83%88%E3%83%99%E3%83%B3%E3%83%81)はかなり特殊な部類で、いわゆる普通のUVMテストベンチ[^com]では必ず使われているようなものが使われていません。なかでも特に重要なものが、Virtual Interfaceです[^abs]。

![download (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/566cbb89-52cd-d527-60c9-4310d056339e.png)
[パチンコフォントメーカー](https://codepen.io/jagarikin/full/PGNwrv)

[^expr]: あるいは、ものすごい勢いで「ホンモノのUVMテストベンチ」というものの何たるかを教えてもらえるかもしれません。知らんけど。

[^com]: [ふつう](http://gitanez.seesaa.net/article/63342880.html)って……なんですかね（哲学）

[^abs]: Virtual Interface以外にも不足しているものがたくさんあります……が、こまけぇこたぁいいんだよ！うるせぇエビフライぶつけんぞ（AA略

## あなたはVirtual Interfaceが使いたくなーる

とはいえ、いきなり「Virtual Interfaceを使え」なんて言われても困りますよね。なんでそんなん使わなあかんの？[^ese]……ってなるじゃないですか。なので[^nanode]、我、ここに簡単な導入をばせしめんとす[^oba][^seshimen]。こころして聞くそす[^sosu]。ゾス！！[^zosu]

[^ese]: [えせ関西弁](https://www.nicovideo.jp/watch/sm35617748)です。この記事の著者は生まれも育ちも関東です。あ、え？きみ大阪からきたの？すごーい！ねえねえ、関西の人ってみんなお笑いみたいなのうまいんでしょ～？何でもいいからなんか面白いこと言ってよ～（笑）

[^nanode]: [NHK 放送文化研究所 -「なので」ということばの使い方がおかしいと言われました](https://www.nhk.or.jp/bunken/summary/kotoba/term/117.html)

[^oba]: [学研全訳古語辞典 - ～をば](https://kobun.weblio.jp/content/%E3%82%92%E3%81%B0)

[^seshimen]: [日常生活の知恵袋 - ～せしめんとする](https://useful-everyday-life.com/%EF%BD%9E%E3%81%9B%E3%81%97%E3%82%81%E3%82%93%E3%81%A8%E3%81%99%E3%82%8B%EF%BD%9C%E6%84%8F%E5%91%B3%E3%82%84%E4%BD%BF%E3%81%84%E6%96%B9%E3%80%81%E4%BE%8B%E6%96%87%E3%82%92%E5%BE%B9%E5%BA%95%E8%A7%A3/)

[^sosu]: [ピクシブ百科事典 - ファプタが「そす」と言うのはなぜ？](https://dic.pixiv.net/a/%E3%83%95%E3%82%A1%E3%83%97%E3%82%BF#:~:text=%E6%9C%80%E5%88%9D%E3%81%AF%E8%AD%A6%E6%88%92%E3%81%97%E3%81%A6%E3%81%84,%E3%80%8C%E3%81%A7%E3%81%99%E3%81%BE%E3%81%99%E8%AA%BF%E3%80%8D%EF%BC%89%E3%80%82)

[^zosu]: [鎌田和樹 -「オス！」と「ゾス！！」について](https://note.com/kamadaman/n/ncd7bb638b7cb)

### 想像してごらん[^imagine]

例えばここに、検証しなければならない２つのFlip-Flopがあったとします[^ff]。さらに、１つのFlip-Flopを検証するようなテストの開発がすでに完了していると仮定します。このとき、私たちはどのようにして、<font color="DarkGray">~~ラクして仕事をサボれるでしょうか~~</font>検証を効率的に行うことで短TAT[^tat]を実現し、会社の利益に貢献できるでしょうか。

言い換えると、具体的に、どのような工夫によって「１つのFlip-Flopを検証するために用意されたテスト」を、これら２つのFlip-Flopの検証に流用できるでしょうか？ ちょっと考えてみましょう。

[^imagine]: [John Lennon - Imagine](https://www.youtube.com/watch?v=YkgkThdzX-8)

[^ff]: 検証しなければならない２つのFlip-Flopがあります……って冷静に考えてどんな状況だよ

[^tat]: TAT = Turn Around Timeの略。ここでは、「開発を開始してから実際に製品をリリースするまでにかかる時間」という意味で使っていますが、文脈によってどこからどこまでの時間を指してTATと言っているのかコロコロ変わったりします。[たぶんね](https://zukan.pokemon.co.jp/detail/0531)。

### `ifdef DUT1

まずは一つ目。`` `ifdef - `endif ``で囲って、インスタンスの接続をコンパイルごとに切り替える案。`-define DUT1`というオプションをつけてコンパイルし、シミュレーションを実行した後、今度は`-define DUT2`をつけてもう一度コンパイルとシミュレーションを行います。

![Screenshot 2024-12-30 201121.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/58362f05-8c22-8371-01f9-5de5aa218485.png)

この方法の欠点は、DUTを切り替えるために再コンパイルが必要なことです。また、シミュレーション実行中にDUTを動的に切り替えることはできません。逆に利点はVerilog HDLやVeriog-AMSといった従来の言語[^verilog]でも使用可能なこと……とかでしょうか。

[^verilog]: 従来の言語……って言っちゃったけど、実際のところVerilog HDLとVerilog-AMSはどっちもまだまだ現役だよ！特にVerilog-AMSのほうは代わりが無いから、まだバリバリ使われてるよ！ [SystemVerilog AMS](https://www.accellera.org/activities/working-groups/systemverilog-ams)が来るまでの辛抱！

### MUX + dut_sel

続いて二つ目。`dut_sel`という信号と、MUXを用意して、信号の経路を切り替える案です。`dut_sel`によってDUTを動的に切り替えられるので、少なくとも先ほどの一つ目の案の欠点を克服しているはず。

![Screenshot 2024-12-30 201157.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/636f1e31-6c1e-3db7-e3b3-90923302c6f1.png)

しかし、それでもまだちょっと微妙[^bimyo]な感じです。`simple_test`の中をみてください。階層参照を用いて`top`の変数`d`にアクセスしています。まだ`simple_test`と`top`の結合がちょっと密すぎるような気がします。

もしある時、テストベンチのモジュール名が`top`→`tb_top`に変わったら？[^tb_top] DUTやテストベンチの規模が大きくなってきたときに`simple_test`を`my_pkg.sv`というPackageに移動して管理したくなったら？[^pkg]……ちょっと困りそうです。

[^bimyo]: [ことば研究館 - 「微妙」の用法](https://kotobaken.jp/qa/yokuaru/qa-125/)

[^tb_top]: 「テストベンチのモジュール名に依存しないようにしたければ、`` `define TOP_NAME tb_top ``、`` `TOP_NAME.d = 1 ``とかにすればいいんじゃね？」「……せやな！」

[^pkg]: Package内での階層参照はSystemVerilogの仕様で禁止されているため、コンパイル時にエラーが出ます。[なぜかDSimだとエラーになりませんが。](https://qiita.com/ds54e/items/82ed3068505a17064f05#20241230-%E8%BF%BD%E8%A8%98)

### Virtual Interface

最後に、Virtual Interfaceを使って、DUT1とDUT2のどちらのInterface（`intf1`、`intf2`）にアクセスするかを切り替える案。これが本命です。Virtual Intefaceは、Interfaceインスタンスへのポインタであり、動的に、自由に受け取ったり渡したりできます[^pointer]。

![Screenshot 2024-12-30 201301.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/c5580d97-9692-391f-6ef1-6e2822eed50a.png)

[^pointer]: Virtual Interfaceについての説明はこのあたり。
[菅原システムズ - 仮想インターフェース (Virtual interface)](http://japanese.sugawara-systems.com/systemverilog/virtual_interface.htm)
[Verification Academy Forum - Why do we need virtual interfaces in system verilog?](https://verificationacademy.com/forums/t/why-do-we-need-virtual-interfaces-in-system-verilog/28949)

この方法は、二つ目の案で問題だった「階層参照を使っている」という欠点を解決しています。一般的にUVMベースのテストベンチは、この「Virtual Interface」を介して、DUTの物理的なピンへの読み書きをおこないます。


## テストベンチ

さて、話が長くなりましたが、私たちも、InterfaceとVirtual Interfaceを使い、[前回](https://qiita.com/ds54e/items/82ed3068505a17064f05)のテストベンチを改良してみましょう。こちらが、新しいテストベンチの全体像です。`simple_test`は、Interface（へのポインタであるVirtual Interface）を介してFlip-Flopのピンにアクセスします。

![Screenshot 2024-12-30 203000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/c93cf1ca-3586-39dd-1861-e41b531b3bdc.png)

コードはこんな感じです。`simple_test`クラスを`top`モジュール→`my_pkg`パッケージへと移し、さらに、新たにFlip-Flop用のInterface `dff_if`を用意しました。ぬるぽ。

```verilog: top.sv
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(), "*", "vif", intf);
    run_test();
  end
  
endmodule
```

```verilog: dff_if.sv
interface dff_if (
  input logic clk
);
  timeunit 1ns;
  timeprecision 1ps;

  logic rstn;
  logic d;
  logic q;

  modport mp (
    output rstn,
    output d,
    input q,
    input clk
  );

endinterface
```

```verilog: my_pkg.sv
package my_pkg;
  timeunit 1ns;
  timeprecision 1ps;

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

  class simple_test extends uvm_test;
    `uvm_component_utils(simple_test)
    
    virtual dff_if vif;

    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);
      if(!uvm_config_db#(virtual dff_if)::get(this, "", "vif", vif)) begin
        `uvm_fatal(get_type_name(), "Failed to get vif")
      end
    endfunction

    parameter realtime output_skew = 0.2ns;

    virtual task run_phase (uvm_phase phase);
      phase.raise_objection(this);
      `uvm_info(get_type_name(), "Started", UVM_MEDIUM)

      @(posedge vif.mp.clk);
      #(output_skew);
      vif.mp.d <= 1'b0;
      vif.mp.rstn <= 1'b0;
      `uvm_info(get_type_name(), "RSTN = 0", UVM_MEDIUM)

      @(posedge vif.mp.clk);
      #(output_skew);
      vif.mp.rstn <= 1'b1;
      `uvm_info(get_type_name(), "RSTN = 1", UVM_MEDIUM)

      @(posedge vif.mp.clk);
      #(output_skew);
      vif.mp.d <= 1'b1;
      `uvm_info(get_type_name(), "D = 1", UVM_MEDIUM)

      @(posedge vif.mp.clk);
      #(output_skew);
      vif.mp.d <= 1'b0;
      `uvm_info(get_type_name(), "D = 0", UVM_MEDIUM)

      repeat (2) begin
        @(posedge vif.mp.clk);
      end
      `uvm_info(get_type_name(), "Finished", UVM_MEDIUM)
      phase.drop_objection(this);
    endtask
  endclass
  
endpackage
```

### なにやってるの？

いったいこのテストベンチの中で何がおこなわれているのか。ここではそれを簡単に順を追って説明しましょう。まず、`top`モジュールにて、Interfaceインスタンス`intf`をDUTの各ピンにつなぎます。

![Screenshot 2024-12-30 203057.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/c8657295-8233-1552-b72a-86716714dd97.png)

次に、Virtual Interface変数`vif`に、先ほどDUTにつないだInterfaceインスタンス`intf`を代入します。すると、Virtual Interface `vif`がDUTのInterface `intf`を指すようになります。これをConfig DBと呼ばれるデータベースに登録します[^db]。

[^db]: Config DBについての説明はこのあたり。
[yakiraicho - 【SystemVerilog】UVMのconfig_dbって何なの](https://qiita.com/yakiraicho/items/9073580e531919c0c356)
[Shivam katiyar - ConfigDB: The Heart of UVM](https://medium.com/@shivamkatiyar274/configdb-the-heart-of-uvm-976086ec765a)

![Screenshot 2024-12-30 203319.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/289779c8-96f2-8922-4e41-232cc2ec5bf1.png)

その後、`simple_test`内でConfig DBからVirtual Interface `vif`を取り出します。この取り出されたVirtual Interfaceは、もちろん、DUTのInterfaceを指しています。

![Screenshot 2024-12-30 203523.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/fee0a222-2ab7-6b19-7a91-f3f36f869edf.png)

つまり、例えば`simple_test`内で`vif.d`を読み書きすると、実際には`top`モジュールに置かれたDUTのInteface `intf.d`への読み書きが行われることになります。今回のテストベンチではこのように、階層参照ではなく、Virtual Interfaceを利用して`simple_test`内からDUTのピンへのアクセスを実現しています。

![Screenshot 2024-12-30 203540.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/93a6f17c-1610-2dbd-dcb5-e6e3356ea32d.png)


## DSimの実行スクリプト

`dff_if.sv`と`my_pkg.sv`が増えた以外は、[前回](https://qiita.com/ds54e/items/82ed3068505a17064f05)と同じです。

```options.txt
// 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
dff_if.sv
my_pkg.sv
top.sv
```

```winbatch: run.bat
@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
```

## シミュレーション結果

あまり代わり映えしませんが、一応、ログの一部のスクリーンショットを貼っておきます。とくに深い意味はないです。ただの行数かせぎ、にぎやかしだったりします[^chikuwa]。

![Screenshot 2024-12-30 214214.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/530501da-4a81-0fd5-e31b-93a0876b9120.png)

[^chikuwa]: ちくわ大明神

## おわりに

[次回](https://qiita.com/ds54e/items/e20b3974d00ae5b6a0dd)「Clocking blockとInterface内タスクでだだんだんだだん[^dadan]」
デュエル、スタンバイ！

![Screenshot 2025-01-01 201526.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/a9c4edf2-1e37-5afa-1ff4-dcec75a7adcb.png)

[^dadan]: ダンダダン……といえば何ですかね。[ターミネーター](https://www.youtube.com/watch?v=dGhQAJCh83Q)のテーマ曲、とみせかけて[最近人気のマンガ](https://www.youtube.com/watch?v=Nw_XzGZ35As)とか。そういえば全然関係ないけど、今日はまだ一月一日ですね。あけおめことよろ。