Flip-Flopでお手軽UVM（Driver）

## はじめに

やあ （´・ω・｀)　ようこそ、バーボンハウスへ。
このテキーラはサービスだから、まず飲んで落ち着いて欲しい[^bourbon]。

[^bourbon]: [ニコニコ大百科 - バーボンハウス](https://dic.nicovideo.jp/a/%E3%83%90%E3%83%BC%E3%83%9C%E3%83%B3%E3%83%8F%E3%82%A6%E3%82%B9)

１）[Flip-Flopでお手軽UVM（できなかった）](https://qiita.com/ds54e/items/64ee47b8fc1cb5ae3cc1)
２）[Flip-Flopでお手軽UVM（uvm_test）](https://qiita.com/ds54e/items/82ed3068505a17064f05)
３）[Flip-Flopでお手軽UVM（Virtual Interface）](https://qiita.com/ds54e/items/48412f9172af9a7c4823)
４）[Flip-Flopでお手軽UVM（Clocking block）](https://qiita.com/ds54e/items/e20b3974d00ae5b6a0dd)
５）Flip-Flopでお手軽UVM（Driver）←いまここだよ

……というわけで、このシリーズもついに第５話に突入しました。すごいですね。多くの投稿者が挫折し失踪すると呼ばれる第５話目、５話目ですってよ奥さん。これはお祝いしないといけないレベルでございますわ！[^page][^fifth]

[^page]: 実際のところ、通常なら１ページで終わるような短い内容を引き延ばして引き延ばして、それこそもう向こう側の景色が透けて見えてしまうくらいに、あるいは、なんだこれおまえの実家のカルピスかよってくらいに薄くして、やっとこさ五回分、って感じかもしれなくもない。

[^fifth]: <font color="DarkGray">~~まあまさに今日今回これを最後に失踪する予定なわけですが~~</font>

![Screenshot 2025-01-04 155117_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/e5be7c61-efd2-fed6-3e13-bd8e1712fa05.png)
[投資入門アニメ](https://www.youtube.com/watch?v=U8Pnp1AA8RQ)


## 想定読者

[ないんだな、それが](https://dic.pixiv.net/a/%E3%81%AA%E3%81%84%E3%82%93%E3%81%A0%E3%81%AA%E3%80%81%E3%81%9D%E3%82%8C%E3%81%8C)。しいて言えば、おれだ。俺自身が読者なのだ[^dokusha]。
それでは、今日も一日がんばるぞいっ[^zoi]。

[^dokusha]: このように言っておくと、自分の記事が誰にも読まれなかったときに「いや、でもこれ自分用に備忘録として書いたやつだしっ、ぜんぜん誰にも読まれなくてもかなしくねーしっ、全然ノーダメージだしっ、むしろそのほうがいい、っていうか！！？？？そんなかんじ？？？（泣」っていう逃げ道になるのでオススメだぞっ、この卑怯者っ！

[^zoi]: [NEW GAME！-「今日も一日がんばるぞい！」](https://alu.jp/series/NEW_GAME%EF%BC%81/article/vbW9tsjQLcqFv9mhuwsK)


## 今回の目標

[前回](https://qiita.com/ds54e/items/e20b3974d00ae5b6a0dd)までに、やりたいことはおおよそ完了しました[^todo]。あれでも一応動きはするので、まあとりあえず完成と言ってしまっても良い……のですが、今回はもうちょっと頑張って、[第二話](https://qiita.com/ds54e/items/82ed3068505a17064f05)で見たような「普通のUVMテストベンチ」の構成に少し近づける工夫をしてみましょう。

![UVM-test-bench-Architecture-All-complex-test-benches-may-be-architected-as-shown-in-the.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/fd478414-6208-ff0d-b875-bc84201aa280.png)
[Pankaj S. Vitankar - UVM ARCHITECTURE FOR VERIFICATION](https://www.researchgate.net/publication/303759959_UVM_ARCHITECTURE_FOR_VERIFICATIONJTypeIJECETVType7IType3)

[^todo]: ※「いまだに何のチェッカーも実装されておらず、毎回シミュレーション結果を目視確認している」という致命的な問題点を除く


## 今回組み上げる予定のテストベンチの構成

これが今回作る予定のテストベンチの全体像です。どうでしょう。だいぶサマになってきたんじゃないでしょうか。少なくとも、[第二話](https://qiita.com/ds54e/items/82ed3068505a17064f05)で作ったベンチと比べて、当社比1000倍くらいそれっぽい見た目になった気がします。

![Screenshot 2025-01-03 214521.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/a65e85b1-1612-f605-377a-56eb3270f140.png)


## テストベンチ

テストベンチトップのコードはこんなかんじ。`my_pkg.sv`を見ると……なんかやたらとファイルが増えてますね。トランザクションとか、シーケンスとか、ドライバーとか。横文字ばっかで意味わかんにゃい。一方、`top.sv`のほうは[前回](https://qiita.com/ds54e/items/e20b3974d00ae5b6a0dd)とほぼ同じです。

```verilog: my_pkg.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
```

```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(), "uvm_test_top.env.agt", "vif", intf);
    run_test();
  end
  
endmodule
```


## Sequence、Sequence item、Driver

さて、真面目な解説記事であれば、[トランザクションレベルモデリング](https://vlsiweb.com/uvm-tlm-transaction-level-modeling/)がどうとか、[こういう図](https://vlsiverify.com/uvm/sequence-driver-sequencer-communication-in-uvm/)を使った、なにやら難しそうな話がここで始まったりするわけですが……うーん、まあとりあえず動けばいいから手短に使い方だけ教えてよ、ってことでね、ここではやっていこうと思います。

お↓ら↑よ↓[^orayo]。[強者たちよ、出てこいやっ！](https://youtu.be/VhLvSxAcIfo?si=soEHf7uJQFpT8aO_&t=55)

[^orayo]: [ホロライブ非公式Wiki - 戌神ころね 語録集](https://seesaawiki.jp/hololivetv/d/%D8%FC%BF%C0%A4%B3%A4%ED%A4%CD%A1%DA%B8%EC%CF%BF%A1%DB)

### 気になるあの娘はシーケンス[^kininaru]

[^kininaru]: [気になるあの娘 - 相対性理論 (cover)｜somunia](https://www.youtube.com/watch?v=7UjuRvU07E8)

まずはこの子、「**シーケンス**」の紹介から始めましょう。

UVMでは「**トランザクション**」というカタマリを介して、コンポーネント[^comp]間で必要な情報のやり取りをします。そのトランザクションとやらを作ってるのが、この「**シーケンス**」です[^seq]。ほら、見てください、`` `uvm_info(get_type_name(), "Reset", UVM_MEDIUM) ``のすぐ下のあたり。「DUTをリセットしろー」的な情報というか指示みたいなものを詰め込んでいるような……[そんな波動を感じませんか](https://www.teslabiohealing.com/ja-jp/products/tesla-biohealer-biophotonizer-m)？

[^comp]: DriverとかMonitorとか。（正確にはちがうけど）静的なオブジェクト的なサムシングだと思っておいてください。[Difference Between UVM_OBJECT and UVM_COMPONENT](https://verificationacademy.com/forums/t/difference-between-uvm-object-and-uvm-component/29650)

[^seq]: 大雑把に、シーケンス＝「実際にDUTのピンを操作してくれるドライバー君への指示書をつくるための場所」みたいな理解でもいいと思います。たぶんね。


```verilog: my_sequence.sv
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) ``の下の部分は、「ランダムなデータを送ってねー、ただし、**リセット無し＆ディレイ無し**で。よろぴくー[^yoro]」というTransactionを50回送ってるかんじのアレです。

[^yoro]: [のりピー語辞典 - よろぴく](http://norip.55street.net/z.html)


### 君に胸キュン、トランザクション[^kimini]

[^kimini]: [君に、胸キュン。- YMO (cover)｜まりあ†ほりっく](https://www.youtube.com/watch?v=hRo1uK2NILo)

つづいて、これが今回の私たちの「**トランザクション**」です。

`reset`、`data`、`delay`というメンバー変数を持っています。`rand`キーワードが付いているので、`` `uvm_do(tr) ``や`` `uvm_do_with(tr, constraints) ``マクロを使った際、これらの変数には基本的にランダムな値がセットされます。それだけっちゃそれだけ。

```verilog: my_transaction.sv
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
```

### みむかｩわナイスドライバー[^mimukawa]

[^mimukawa]: [みむかｩわナイストライ - ぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬぬ (cover)｜ななひら](https://www.youtube.com/watch?v=5RJX7eCUOoI&list=LL)

ざぁーこざぁーこ♡、ざぁーこざぁーこ♡

……ごほん。えー、これが今回の主役、「**ドライバー**」です。先ほど定義したトランザクションを受け取って、実際に、DUTであるFlip-Flopを操作してくれるコンポーネントです。言い換えると、「トランザクション」という抽象的なサムシングを、`D <= 0;`とか`RSTN <= 0; @(cb); RSTN <= 1;`のような抽象度の低い「**DUTに対する具体的な操作**」に変換してくれるえらいやつです[^driver]。仲良くしてあげてね。

[^driver]: [前回の記事](https://qiita.com/ds54e/items/e20b3974d00ae5b6a0dd)で、Interfaceの中に`write`や`reset`といったタスクを入れておいたので、今回の私たちのテストベンチでのDriverくんのお仕事は、「受け取ったトランザクションに従って、適切にそれらのタスクを呼び出すこと」だけです。「簡単なお仕事でサクッと高収入☆彡」「未経験者歓迎☆彡」「アットホームな明るい職場☆彡」

```verilog: my_driver.sv
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は、シーケンサ[^seqr]とドライバを入れる箱です。以上！

[^seqr]: Environmentの中にいるのが「シーケンス(`Sequence`)」で、Agentの中にいるのが「シーケンサ(`Sequencer`)」です。文字にすると一文字違いでややこしいので、慣れるまではご注意くださいまし。シーケンスはオブジェクトで、シーケンサはコンポーネント。

```verilog: my_agent.sv
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（シーケンス[^hyoukiyure]）とEnvironmentを入れる箱です。

[^hyoukiyure]: アルファベットでSequenceって書いたり、カタカナでシーケンスって書いたり……表記揺れしまくっててごめんぬ！英語ばっかだとなんか文章がお堅く見えちゃう気がして、この記事では、なるべくカタカナで代用したいなぁ……という気持ちがあったりなかったりするよ！[外来語表記警察](https://www.bunka.go.jp/seisaku/bunkashingikai/kokugo/kokugo_kadai/iinkai_45/pdf/93390601_09.pdf)のひとも生暖かく見守ってあげてね。

```verilog: simple_test.sv
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
```


```verilog: my_environment.sv
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`が追加されたことにだけご注意くださいませ[^incdir]。

[^incdir]: 実は`options.txt`の`-incdir .`は無くてもよかったりします。DSimくんは、カレントディレクトリをデフォで走査してくれるっぽいので。あと、今回は`` `include ``するファイルをフォルダにまとめずに、`run.bat`と同じ場所に直にベタ置きしてます。雑ぅ!

```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
```

```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
-incdir .
dff_if.sv
my_pkg.sv
top.sv
```

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

[ｷｪｪｪｪｪｪｱｧｧｧｧｧｧｳｺﾞｲﾀｧｧｧｧｧｧｧ!!!!!！！！！！！！](https://www.nicovideo.jp/watch/sm13365336)

![Screenshot 2025-01-04 165154_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/238e06ed-7c1f-889b-ac9a-7bb45b596b9f.png)
![Screenshot 2025-01-04 165311_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/58b83157-2965-571c-6fd6-a90dd72d3d92.png)


## おわりに

[Flip-Flopでお手軽UVMシリーズ](https://qiita.com/ds54e/items/64ee47b8fc1cb5ae3cc1)は、これにておしまい！[^fin]
ここまで読んでくれてありがとう！

![Screenshot 2025-01-03 180028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/33d12936-7b3a-1b44-fc15-9817cc0bc074.png)
[びんちょうたん](https://www.youtube.com/watch?v=FG7UZGeMnbw)

[^fin]: ちょっとシリーズが長くなりすぎたのでここでいったん一区切り。MonitorとかScoreboardとか、RALとかその辺の話はまた今度別枠でやれたらやりたいなあと思ったーり、[ぽけったーり、もんすたーり、おどったり、まわったり](https://youtu.be/s-PIh2NwxSo?si=ZqIKszSIU6Ao7Hha&t=50)