CMOS NANDゲートの出力特性のシミュレーションと3D可視化：ロジスティック関数による近似

### 冒頭
CMOS NANDゲートはデジタル回路における基本的な論理ゲートで、入力に対して論理的な出力を生成します。通常、NANDゲートは0と1のデジタル信号を用いて動作しますが、実際の回路ではこの動作が完全にデジタル的ではなく、アナログ的な挙動を示すことがあります。そこで、本記事では、CMOS NANDゲートの出力特性をロジスティック関数を用いて近似し、その動作をシミュレーションします。具体的には、入力電圧の組み合わせに対して出力電圧がどのように変化するかを3Dグラフを通じて視覚的に確認する方法について解説します。これにより、実際の回路での挙動に近い形で、NANDゲートの特性を理解することができます。

---



### コードを公開

```python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# NAND回路のロジスティック近似関数
def nand_logic(Vin1, Vin2, Vdd=5, Vth=2.5, gain=10):
    term1 = 1 / (1 + np.exp(-gain * (Vth - Vin1)))  # Vin1の影響
    term2 = 1 / (1 + np.exp(-gain * (Vth - Vin2)))  # Vin2の影響
    return Vdd * (1 - term1 * term2)  # NANDの特性を正しく反映

# パラメータ設定
Vdd = 5         # 電源電圧
Vth = 2.5       # しきい値電圧
gain = 10       # ゲイン

# 入力電圧範囲を作成
Vin = np.linspace(0, 5, 100)  # 0V から 5V までの範囲

# 3Dプロット用のグリッド作成
Vin1, Vin2 = np.meshgrid(Vin, Vin)

# (Vth, Vth) を中心に 180度回転
Vin1_rot = 2 * Vth - Vin1
Vin2_rot = 2 * Vth - Vin2

# 各出力の計算（回転後の入力を使用）
Vout = nand_logic(Vin1_rot, Vin2_rot, Vdd, Vth, gain)

# 3Dプロット
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(Vin1, Vin2, Vout, cmap="viridis", edgecolor='k', alpha=0.8)

# 軸ラベルとタイトル
ax.set_xlabel("Vin1 (V)")
ax.set_ylabel("Vin2 (V)")
ax.set_zlabel("Vout (V)")
ax.set_title("CMOS NAND Gate Output (180° Rotated at (Vth, Vth))")

plt.show()

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3759109/b92380a9-18c5-652f-fd80-e2291eaf576a.png)




# 解説


#### ロジスティック関数の説明

ロジスティック関数は、S字型の曲線を描く関数で、以下の式で表されます：

\[
\text{sigmoid}(x) = \frac{1}{1 + \exp(-x)}
\]

この関数は、入力がしきい値を越えると急激に変化する特性を持ち、デジタル回路の動作を模倣するのに適しています。`nand_logic`関数内で、この関数を利用して入力電圧Vin1およびVin2がしきい値電圧Vthを越えたときに出力が急激に変化するようにしています。

具体的には、`nand_logic`関数は次のように動作します：

- `Vin1`と`Vin2`の両方がしきい値Vthより高ければ、出力Voutは低く（0に近い値）なり、逆に両方がしきい値以下であれば、出力は高く（Vddに近い値）なります。
- `gain`は、ロジスティック関数の急激さを制御するパラメータで、これを調整することで出力の変化の鋭さを調整できます。

#### 関数の内部処理

```python
term1 = 1 / (1 + np.exp(-gain * (Vth - Vin1)))  # Vin1の影響
term2 = 1 / (1 + np.exp(-gain * (Vth - Vin2)))  # Vin2の影響
return Vdd * (1 - term1 * term2)  # NANDの特性を正しく反映
```

この部分では、入力電圧Vin1とVin2がどれだけしきい値Vthを越えているかをロジスティック関数を使って計算し、それらを掛け合わせて最終的な出力Voutを決定します。`term1`および`term2`は、各入力の影響を示しており、これらが掛け合わされることでNANDゲートの論理動作を模倣します。

---

### 3Dプロットの可視化

次に、得られた出力Voutを3Dプロットとして可視化します。このグラフでは、入力電圧Vin1とVin2の2つの変数に対して出力Voutを描画しています。

#### グリッドと入力範囲

```python
Vin = np.linspace(0, 5, 100)  # 0V から 5V までの範囲
Vin1, Vin2 = np.meshgrid(Vin, Vin)
```

`Vin`は0Vから5Vの範囲で100分割した配列を作成し、`meshgrid`を使ってVin1とVin2という2次元のグリッドを生成します。これによって、2つの入力電圧Vin1とVin2の組み合わせに対する出力を計算できます。

#### 180度回転

```python
Vin1_rot = 2 * Vth - Vin1
Vin2_rot = 2 * Vth - Vin2
```

ここでは、Vin1およびVin2の値をしきい値Vthを中心に180度回転させています。

#### 3Dプロット

```python
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(Vin1, Vin2, Vout, cmap="viridis", edgecolor='k', alpha=0.8)
```

`plot_surface`を使用して、入力電圧Vin1とVin2に対する出力Voutを3Dプロットとして描画します。色の変化は出力の変化を示しており、`viridis`というカラーマップが使用されています。`alpha=0.8`は透明度を設定して、プロットを少し透過させています。

#### 軸ラベルとタイトル

```python
ax.set_xlabel("Vin1 (V)")
ax.set_ylabel("Vin2 (V)")
ax.set_zlabel("Vout (V)")
ax.set_title("CMOS NAND Gate Output (180° Rotated at (Vth, Vth))")
```

軸ラベルとタイトルは、プロットの意味を明確にするために追加されています。X軸は入力電圧Vin1、Y軸は入力電圧Vin2、Z軸は出力電圧Voutを表しています。

---

### 結果

このコードを実行すると、CMOS NANDゲートの動作を示す3Dプロットが表示されます。プロットは、入力電圧Vin1およびVin2の組み合わせに対して、出力電圧Voutがどのように変化するかを視覚的に示します。特に、しきい値Vthを越えると急激に変化する出力がよく見て取れるため、NANDゲートの特性を深く理解することができます。

---




### おまけにand回路コードも公開

```python

import numpy as np
import matplotlib.pyplot as plt
from sympy import symbols, And, Or, Not, simplify, Equivalent

# 定数設定
Vdd = 1   # 電源電圧
Vth = 0.5 # しきい値電圧
gain = 500 # ゲインの設定

# 入力電圧範囲
Vin1 = np.linspace(0, 1, 50)
Vin2 = np.linspace(0, 1, 50)
Vin1_mesh, Vin2_mesh = np.meshgrid(Vin1, Vin2)

# ロジスティック関数の計算
def logistic(Vin1, Vin2):
    term1 = 1 / (1 + np.exp(-gain * (Vth - Vin1)))  # Vin1の影響
    term2 = 1 / (1 + np.exp(-gain * (Vth - Vin2)))  # Vin2の影響
    return term1 * term2 * Vdd

Vout = logistic(Vin1_mesh, Vin2_mesh)

# (Vth, Vth) を中心に 180度回転
Vin1_rot = 2 * Vth - Vin1_mesh
Vin2_rot = 2 * Vth - Vin2_mesh

# 三次元プロット
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(Vin1_rot, Vin2_rot, Vout, cmap='viridis')

# 軸ラベル設定
ax.set_xlabel("Vin1 (V)")
ax.set_ylabel("Vin2 (V)")
ax.set_zlabel("Vout (V)")
ax.set_title("3D Plot of Logistic Function Influence (Rotated)")

plt.show()

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3759109/0f1929d0-c1d1-4142-4c60-d061d3be5dba.png)