デジタル信号をなまらせよう

## はじめに

Verilogのシミュレータで表示されるデジタル信号って、立上りエッジや立下りエッジがシュッとしてて、なんだか見ていてつらいですよね。肩肘張ってちょっと無理して働いているかんじ。たまにはアナログ信号みたいに、波形をゆっくり変化させてあげましょう。

1. [デジタル信号をなまらせよう](https://qiita.com/ds54e/private/f821bb6010355d6cb020) ← 今ここ
2. [デジタル信号をもっとなまらせよう](https://qiita.com/ds54e/private/a56d97306a7a08cbec70)
3. [デジタル信号をもっともっとなまらせよう](https://qiita.com/ds54e/private/7a13da3d7586f1901a7c)
4. [デジタル信号にノイズをのせよう](https://qiita.com/ds54e/private/f2b61a7ce50397087137)

## 波形をなまらせる手順

デジタル信号の立上りや立下りが緩やかになることを、しばしば、「波形がなまる」と表現します。波形をなまらせる最も簡単な方法は、低域通過フィルタ (LPF: Low-pass filter) を掛けることです。この記事では、下図のように、デジタル値をアナログ値に変換 (D2A = Digital to Analog) したあと、LPFを掛けるという手順で、デジタル信号をなまらせます。
<img width="600" alt="d2a_lpf.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/f7729827-6a44-81d7-2de0-ed73622c800b.png">
最初のD2Aは、2値型や4値型の0/1を、real型の実数値に変換する処理です。例えば、`assign analog_value = digital_value ? 1.0 : 0.0;`のように記述できます。この記事ではD2A後のLPF部分に焦点を当てたいので、D2Aの説明はこの1行で終わりです。

### 微分方程式を解く
ここでは、波形をなまらせるために、下図のような1次LPFを使います。
<img width="400" alt="rc_lpf.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/db94d069-783a-112a-34f8-2924dcf3910b.png">
上図から、1次LPFの出力電圧が満たすべき式として、次の微分方程式が得られます。

```math
\begin{align}
&V_\mathrm{out}
= V_\mathrm{in} - R \times I_\mathrm{C}
= V_\mathrm{in} - R \times C\frac{dV_\mathrm{out}}{dt}\\
&\therefore
V_\mathrm{out} + RC\frac{dV_\mathrm{out}}{dt}
= V_\mathrm{in}
\end{align}
```

この微分方程式を解いて、$V_\mathrm{out}(t)$ を求めます。過渡現象論や微分方程式の教科書を引っぱり出して、学生時代を思い出しながら手計算で解く……のは面倒なので、今回は[WolframAlpha](https://www.wolframalpha.com/input/?i=solve+x+%2B+b*dx%2Fdt+%3D+a+for+x%2C+x%280%29%3Dc)を使います。

```
>>> solve x + b*dx/dt = a for x, x(0)=c
x(t) = e^(-t/b) (a (e^(t/b) - 1) + c)
```

したがって、1次LPFの出力電圧を次式で表せます。

```math
\begin{align}
&V_\mathrm{out}(t)
= V_\mathrm{in}(t)
- V_\mathrm{in}(t)\,e^{\displaystyle -\frac{1}{RC}t}
+ V_\mathrm{out}(0)\,e^{\displaystyle -\frac{1}{RC}t}
\end{align}
```

### SystemVerilogで記述する

先ほど得られた出力電圧の式をもとに、SystemVerilogで1次LPFの動作モデル[^1]を記述します。

1次LPFの出力電圧は、時定数 $\tau = RC$ の5倍の時間で最終値の99%に到達します[^2]。このことを考慮し、入力電圧が変化してから $5 \tau$ 経過した時点で、出力電圧に $t\rightarrow\infty$ の値を代入し、次に入力電圧が変化するまで、出力電圧の計算を行わないようにしています。

[^1]: 英語で言うところの Behavioral model、Functional model のこと。もしあなたがこの分野に興味があるなら、[International Behavioral Modeling and Simulation Conference](https://bmas.designers-guide.org/) のアーカイブにある多くの資料を目の前にして、「[ち…わき にく…おどるとは このことだ](https://ja.wikipedia.org/wiki/%E3%83%89%E3%83%A9%E3%82%B4%E3%83%B3%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%A2%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%BC%E3%82%BA_%E3%83%86%E3%83%AA%E3%83%BC%E3%81%AE%E3%83%AF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%89)」てきな気持ちの高ぶりを感じることでしょう。そして、トップページの「After several years of sagging attendance aggravated by the economic down turn, BMAS ceased operations after the 2010 workshop. (訳: BMASは数年に渡る景気悪化の影響で出席者数が減少した後、2010年のワークショップ終了後に運営を停止しました)」という一文に気づき、あ……(察し)

[^2]: [Basic Electronics Tutorials and Revision - RC Charging Circuit](https://www.electronics-tutorials.ws/rc/rc_1.html)

```verilog:LPF1.sv
module LPF1 #(
  parameter realtime timestep = 100ps,
  parameter real initial_vc = 0,
  parameter real R = 1e3,
  parameter real C = 500e-15
)(
  input real VIN,
  input real VSS,
  output real VOUT
);
  timeunit 1s;
  timeprecision 1ps;

  real _vin;
  assign _vin = VIN - VSS;

  event vin_changed;
  always @(_vin) begin
    -> vin_changed;
  end

  event start_timer;
  event stop_timer;
  event next_step;

  always @(start_timer, stop_timer) begin
    if (start_timer.triggered()) begin
      fork begin
        #(timestep);
        -> next_step;
      end join_none
    end else begin // stop_timer
      disable fork;
    end
  end

  localparam realtime tau = R*C;
  realtime t0;
  realtime t;
  real vc0 = initial_vc;
  real vc = initial_vc;

  always @(vin_changed, next_step) begin
    -> stop_timer;
    if (vin_changed.triggered()) begin
      t0 = $realtime();
      vc0 = vc;
      -> start_timer;
    end else begin // next_step
      t = $realtime() - t0;
      if (t > 5*tau) begin
        vc = _vin;
      end else begin
        vc = _vin - (_vin - vc0)*$exp(-t/tau);
        -> start_timer;
      end
    end
  end

  assign VOUT = vc + VSS;

endmodule
```

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

* Simulator: [ModelSim ALTERA STARTER EDITION vsim 10.5b Simulator 2016.10 Oct  5 2016](https://www.intel.com/content/www/jp/ja/programmable/downloads/download-center.html)
* Viewer: [GTKWave Analyzer v3.3.71](http://gtkwave.sourceforge.net/)

![lpf1_wave.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/863735/e9d02d6c-4bec-b811-0509-13e749736b01.png)
 
## おわりに

こんな記事を最後まで見てくれてありがとう[^nyaan]。

[^nyaan]: この分野、日本語で書かれた情報が少ないので、ちょっとさみしい。そんななか、率先してネット上にこの分野の情報を流してくれている、下記のような神や仏のような人もいて、(恥ずかしいので絶対言いませんが) めっちゃリスペクトしてます。はやく続きがアップロードされないかな、投稿されないかな、って毎日ワクワクしながら、何度も同じページを開いたり閉じたりしています。つづきまだかな……まだかな……
[wyamamo74212 - Real Number Modeling (RNM) 超・初級編](https://www.slideshare.net/wataruyamamoto2/real-number-modeling-rnm) 
[mizutomo - wrealを使ったアナログ風シミュレーション](https://qiita.com/mizutomo/items/598c3f0bb364050040c5)