SystemVerilogで電気回路を解こう

某流行病のせいで、天気のいい土日なのに、部屋にこもっていないといけないので、久しぶりにAdvent Calendarを見てみたら、HDLのページができてるじゃないですか:heart_eyes: しかも、結構盛況:sunglasses: こりゃ、オレも数年ぶりに記事書くしかないぜ!! 
でも、ネタがね〜:sob::sob::sob:

# さて、以下の回路を解いてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33580/527f1188-3438-8194-7672-5f064435d29b.png)

3つのドライバが接続されたノードの電圧はいくあら? ということを言われたら、どうするでしょうか? おそらく高校、大学生の皆様は、紙と鉛筆で(いや、今どきの大学生は鉛筆とかつかわんのかな?)。社会人の方は、SPICEで。そして、このコミュニティーの皆様は、SystemVerilogを使うだと思います。

# SystemVerilog 1800-2012 LRM6.6.7
SystemVerilog教の経典であるLRM6.6.7説には、以下の文言が書かれています。
> A user-defined nettype allows users to describe more general abstract values for a wire, including its resolution function. This nettype is similar to a typedef in some ways, but shall only be used in declaring a net. It provides a name for a particular data type and optionally an associated resolution function.

よくわからないですが、どうやら、自分でnetのdatatypeを定義して、更に、信号衝突の処理をresolution functionというfunctionで記述できるらしいです。

ということで、各ベンダーさんが出してくれているExampleを元に、簡単な電気回路を解く、nettypeを作ってみましょう。

# User Defined Type
通称UDTです。まずは、データタイプの定義です。電気回路においては、何を代表的なデータとして考えるか。昔、習った(ような気がする)テブナンの定理とかノートンの定理によると、複数の電源や抵抗からなる電気回路網は、電圧源とインピーダンス、そして電流源の等価回路に帰着できるようです。

ということは、そのまま、V, I, Rをデータ・タイプに選択するのがいいのかな、という気がします。
```verilog
typedef struct {
    real V;
    real I;
    real R;
} myUDT;
```
ここでは、myUDTというstruct名を付けておくことにします。

# User Defined Resolution Function
通称UDRです。Resolution Functionというのは、日本語でいうと、解決関数とか調停関数とか、そんな名前になるんでしょうか。

これは、一つのネットに複数のドライバが存在した場合、どのように解決するかを定義する関数になります。通常の4stateのデータタイプであれば、下記のような関数がビルトインで定義されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33580/e0d0b47c-9ce9-b8fe-2ac0-47cb4b99bb3c.png)

User Designed Nettypeの場合は、この解決関数を自分で作成する必要があります。
電気回路の場合、キルヒホッフの電流則(KCL)にしたがった関数を書くことになります。
いわゆる、「回路中の任意の点に流れ込む電流と出ていく電流の総和はゼロになる」というものです。また、任意のノードのインピーダンスは、各ドライバのインピーダンスの総和になります。

ということで、このようなfunctionの定義が出来上がります。

```verilog
  function automatic myUDT myUDR(input myUDT drivers[]);
    real IT;  // summed current
    real GT;  // summed conductance
    myUDT result;
    for (int i = 0; i < drivers.size(); i++) begin
      IT += drivers[i].V/drivers[i].R + drivers[i].I;
      GT += 1.0/drivers[i].R;
    end
    result = '{IT/GT, 0, 1/GT};
    return result;
  endfunction
```
driversには、そのノードに接続されているドライバがdyanamic arrayになっているので、それを順番に取り出してあげて、ノード電流とインピーダンスを累積加算してます。

そして、最後に結果という形で、
```verilog
  result.V = IT/GT;
  result.I = 0;
  result.R = 1/GT;
```
と、そのノードの電圧、電流、インピーダンスを計算しています。

ただ、このresoluton functionは、一例であって、他にも色んな考えに基づいてUDT, UDRを定義することができます。別に、「ノード電流の総和はゼロ」という考えに基づく必要もないのす。

ここは、作り手の思想に基づくところが多い気がします。

# User Defined Nettype
通称UDNと呼んでます。ここまで定義したUDTとUDRを使って、自分でnetのdatatypeを設定します。

```verilog
nettype myUDT myUDN with myUDR;
```
実は、Reslution Functionを定義しなくても、UDNを定義することはできます。ただ、その場合、複数のドライバが衝突しても、何も起きないので、最終的な結果は、ゼロ(実際はstructの初期値)になります。

# テストベンチ
最後にテストベンチです。冒頭の絵を解くテストベンチは、

```verilog
module top import myPkg::*; ();
  myUDN a;
  assign a = '{2.0, 0, 1e3};
  assign a = '{0.0, 1e-3, 4e3};

  initial $monitor("a.V=%g", a.V);
endmodule
```
こんな感じになります。これをシミュレータで実行すると、

> a.V=2.4

# おわりに
今回は、ノードが一つの場合の電気回路でしたが、ノードが複数ある場合や、ノードの間をスイッチが有ったり、抵抗があったりする場合にはどうなるんだ、という疑問が色々湧いてくると思います。

これを実現しようとすると、某流行病よりも頭が痛くなる無限ループ問題に陥ることになります。これを防ぐために、各EDAベンダさんは特別なシステムタスクを提供してくれていたりしますが、なるべくならそれも使いたくないとか思いますよね。これに関しては、来年、某流行病に感染して、また暇になったときに書ければ、と思います。もう、さすがに罹りたくないなぁ。。。:mask: