LSI設計とMEMS半導体プロセスと計測と -Linuxによる計測器のGPIB制御を添えて

#はじめに
この記事は[eeic Advent Calendar 2016](http://qiita.com/advent-calendar/2016/eeic)の18日めの記事となっています。
どうも、EEIC2013の@Y_Okamo110です。投稿が遅くなり、大変申し訳ありませんでした。。。

本記事は、LSI設計と、MEMSプロセスにおいてどんなことをしているのかと、その試作課程で生じるもののうち、GPIBを利用した計測器の制御をお話したいと思います。
目次と致しましては

- MEMS・半導体プロセスとは

- そこで発生する計測作業

- LinuxでのGPIBの制御

をお話しようと思います。

#MEMS・半導体プロセスとは

さて、本題に入りたいと思います。まず、Qiitaでこういうことを書く人はなかなかいないと思いますが、MEMS・半導体プロセスについて簡単なお話をします。

## 半導体プロセス
さて、"半導体"といわれると何を指しているか非常にあいまいですが、ここでいう"半導体"は、いわゆるMOSトランジスタによるCMOS集積回路をいいます。
では、実際のPMOS・NMOS一つで形成されるインバータを見てみましょう。
<img src="https://qiita-image-store.s3.amazonaws.com/0/147087/38cbb1e1-678e-5b50-0f35-7a534bd3bd54.png" width=50%>
このようなCMOS回路は、基本的には同じような工程をひたすらやりつづけることで作製します。大学機関などで作ろうとすると下の図のような工程になります。
![mos_flow2.png](https://qiita-image-store.s3.amazonaws.com/0/147087/33f671b9-0325-7f6a-333e-d862b350874a.png)

さて、難しい言葉がいろいろとありますが、要は

- 成膜(積む)

- リソグラフィ(パターンを描く)

- エッチング(パターンに沿って削る)

をひたすらやり続けます。かなり雑な説明で、実際には研磨など平坦化作業や、積層基板作製のためのTSV(シリコン貫通電極)などいろいろとやることはありますが、基本はこんな感じです。

## MEMSプロセス　
MEMSとは？という話からになってしまいますが、wikipediaでは
「MEMS（メムス、Micro Electro Mechanical Systems）は、機械要素部品、センサ、アクチュエータ、電子回路を一つのシリコン基板、ガラス基板、有機材料などの上に微細加工技術によって集積化したデバイス」とあり、ここではシリコン基板ベースのものを指すことにします。
実際の写真を見てみますと、こんな感じのものです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/147087/cab881cf-3c58-4a25-cecd-b7920c41417a.jpeg" width=50%>
これだと、静電型アクチュエータ(センサ)といわれるもので、くしばとなっているところに電圧を印加して中央のメッシュ上の構造を動かしたり、慣性力で動いた部分をくしば部分の静電容量変化として検出したりすることができます。

では、MEMSプロセスはどんなもんでしょうか？
MEMSプロセスは、半導体プロセスを利用して作製しよう！としてできたもののため、工程としては半導体プロセスとほぼ同じです。
しかしながら、"薄く・小さく"を目指している集積回路とはことなり、機械構造ですから"分厚く・立体的に"作ることを目指したプロセスのため、いくつか特殊な工程があります。その最たる２つが、

- エッチング(削る)
 
- リリース

という工程です。
まず、エッチングについてです。
半導体プロセスではせいぜい数μmのエッチングですが、MEMSプロセスでは数100μmのエッチングも平気でします。
そこで、MEMSプロセスでは普通のプラズマエッチング(Reactive Ion Etching: RIE)だけでなく、深掘りエッチング(Deep RIE: DRIE)・別名ボッシュプロセスというものを用います。
これは、RIEではただプラズマによってひたすら削っていくのに対し、DRIEでは"積む・削る"を高速で繰り返し削りたくないところを保護しつつ削ります。
![rie.png](https://qiita-image-store.s3.amazonaws.com/0/147087/06523d1c-a061-1f54-d678-f5e0b2aa9381.png)
これによって、MEMSは写真のような立体的な構造を形成できます。


次に変わった動作としては、リリースがあります。
これは、MEMS独自のプロセスで、半導体とは違い、機械的に動作できる部分を作製するには基板と構造との切り離しを行う必要があります。それがこの動作です。これは、一番よくやられるやり方が、気層フッ酸(フッ酸の気体)環境におき、基板と可動構造の間のSiO2をエッチングするという方法です。
![MEMS_flow.png](https://qiita-image-store.s3.amazonaws.com/0/147087/2749989a-9537-5fea-3f44-33cbac1df881.png)

前述のMEMSの写真を見ると、真ん中の構造には、穴がたくさんあいており、メッシュになっているのがわかります。
これは、このようにすることで気層フッ酸が入りやすくしてリリースを行うためです。
つまり、リリースしたくない部分はメッシュ上に穴を配置せず、リリースしたい部分はメッシュ上に穴を配置しておきます。このようにすることで、基板上で選択的に可動構造を作製することができるのです。

# そこで発生する計測
非常に雑なものでしたが、プロセスというものがどんなことをやるかということを説明いたしました。
これらの工程の中で、重要なのが計測です。例えば、成膜工程やエッチング工程ではどれくらいの深さで行ったかはしっかりと計測する必要があります。
また、特に重要なのが配線層の作製時の電気的接触がしっかりととれているか、といった電気的特性を計測することです。これを行う際は、LCRパラメータアナライザーや半導体パラメータアナライザーといったI-V(電流-電圧)特性を測る器具を利用して計測を行います。
ときに面倒なのが、アレイ上に配置されたデバイスです。これらを計測るときは、大体のアナログスイッチなどが中に配置されており、X,Yをデジタル信号で指定してそのときに入力電圧を掃引して出力を計測する、ということを行います。
これをおこなうには、何かしら自動で0,1の電圧の信号を制御しながらIV特性をとっていく、という機器が必要です。
しかし、自分のデバイスに都合よくあった計測機器は大体の場合ありません。そこで、このようなことをする場合、I-V特性を測る機器をPCにつなぎ、さらに0,1のIO(GPIO)を制御する、ということをやろうとします。
では、I-V特性を測る機器とPCをどのようにつなぐのでしょう？
新しい機器にはUSBがついていますので、ケーブルを繋げばできます。しかしながら、古い機器だとUSBではなく、GPIBといわれる端子しか出ていない場合があります。
今回は、よくあるような下図のような接続をしたものの制御・計測を題材にしたいと思います。
<img src="https://qiita-image-store.s3.amazonaws.com/0/147087/d3b3b46f-9fd5-6060-1464-1a72f723e246.png" width=50%>

そこで、Linuxボード(ここではBeaglebone Black)を利用して、PythonにてGPIOを操作しつつ、GPIB経由でI-V特性を計測していく、ということを紹介します。

# GPIBの利用
## GPIBケーブル・Linuxボードの準備
LinuxボードについてはRaspberry Pi・Beaglebone blackなどを想定しています。OSはDebian/Ubuntuであれば動作します。Arch Linuxでも行えます。MicroSDへのOSインストールなど、OSの準備はご自分でお願いします。
GPIBケーブルは[これ](http://sine.ni.com/nips/cds/view/p/lang/ja/nid/201586)を利用します。

## 必要なパッケージのインストール
```
sudo apt-get install -y gcc-4.8 g++-4.8 build-essential texinfo texi2html libcwidget-dev libncurses5-dev libx11-dev binutils-dev bison flex libusb-1.0-0 libusb-dev libmpfr-dev libexpat1-dev tofrodos subversion autoconf automake libtool  tcl-dev tk-dev fxload
sudo apt-get install python2.7-dev python3-dev
```
大体これで足ります。GPIBを利用するためにはモジュールのコンパイルが必要なので、そのために必要なパッケージを多く含んでいます。
次に、gpibドライバのコンパイル・インストールを行います。

```
mkdir linux-gpib  && cd linux-gpib
svn checkout svn://svn.code.sf.net/p/linux-gpib/code/trunk linux-gpib-code
cd linux-gpib-code/linux-gpib/
./bootstrap
./configure
make
sudo make install
```

つぎに、/etc/gpib.confを書き換えます。

```
sudo nano /etc/gpib.conf
/* type of interface board being used */
    board_type = "ni_usb_b"
```
最後に、kernel moduleをロードします。

```
sudo modprobe gpib_common
sudo modprobe ni_usb_gpib
```

これで、準備完了です。

## GPIBの接続
では、いよいよGPIBを挿してみましょう。...何も反応しないと思います。
GPIBケーブルは数種類あるため、特定のfirmwareをロードするため、もう少し作業が入ります。

```
wget http://linux-gpib.sourceforge.net/firmware/gpib_firmware-2008-08-10.tar.gz
tar xvf gpib_firmware-2008-08-10.tar.gz
lsusb # gpibが接続されているusbポートが分かる
sudo fxload -D /dev/bus/usb/001/006 -I  gpib_firmware/ni_gpib_usb_b/niusbb_firmware.hex -s gpib_firmware/ni_gpib_usb_b/niusbb_loader.hex
sudo gpib_config
```
ここまですると、GPIBケーブルにあるLEDが緑に点灯すると思います。接続成功です。

## GPIBによる機器のPC制御
ここまでいけば、後はPythonで制御するだけです。(ライブラリが一番しっかしりてたのがPythonでした)
GPIBでIVをとる簡単プログラムです。GPIBのコマンドは、機器によって異なるので、そこは説明できません。ごめんなさい。今回は,
KeysightのB2902AというSMU(Source Measure Unit)を利用しました。

```python:gpib.py
# -*- coding: utf-8 -*-

import os
import gpib
import numpy as np

class B2902A:
    #StartVOl: Starting voltage
    #StopVol:  End voltage
    #Point:    The Number of plotting point
    #NPLC:     Power Line Cycle
    #CurProt:  Current protection value
    def __init__(self, StartVol, StopVol, Point, NPLC, CurProt):
        gpib.write(h,"*RST")
        gpib.write(h,":sour:func:mode volt")
        gpib.write(h,":sour:volt:mode swe")
        gpib.write(h,":sour:volt:star " + str(StartVOl))
        gpib.write(h,":sour:volt:stop " + str(StopVol))
        gpib.write(h,":sour:volt:poin "+ str(Point))
        
        gpib.write(h,":sens:func ""curr""")
        gpib.write(h,":sens:curr:nplc "+ str(NPLC))
        gpib.write(h,":sens:curr:prot "+ str(CurProt))
        gpib.write(h,":trig:sour aint")
        gpib.write(h,":trig:coun 1" + str(Point))

        self.X = np.linspace(StartVOl, StopVol, Point)
        
    def X(self):
        return self.X
    
    def measure(self):
        gpib.write(h,":outp on")
        gpib.write(h,":init (@1)")
        gpib.write(h,":fetc:arr:curr? (@1)")
        s=gpib.read(h,16500)
        gpib.write(h,":outp off")
        return s
        
if __name__ == "__main__":
    SMU=B2902A(0, 5, 100, 0.1, 0.1)
    Y = SMU.measure()

```

##GPIOの制御
さて、次はGPIOの制御です。これは、Beaglebone BlackやRaspberry Piだと専用のライブラリがあるためそちらを利用するのもいいのですが、非常に反応が悪く、私としては利用したくありません。そこで、今回は/sys/classにアクセスする、比較的原始的な方法をとります。
また、Linuxボードり利用するとき、よく困るのがピン番号が連続でないため、比較的単調な動作をloop構文などで書きづらいことです。
そこは、Pythonの便利さを用いたコードにしましょう。
GPIBの制御例です。

``` python:gpio.py
# -*- coding: utf-8 -*-

import os
import numpy as np

class GpioWriter:
    def __init__(self, pinNumber):
        self.pinNumber = pinNumber
        os.system("echo " + str(pinNumber) + " > /sys/class/gpio/export")
        os.system("echo out > /sys/class/gpio/gpio" + str(pinNumber) + "/direction")
        self.fd = os.open("/sys/class/gpio/gpio" + str(pinNumber) + "/value", os.O_WRONLY)
    
    def release(self):
        os.close(self.fd)
        os.system("echo " + str(self.pinNumber) + " > /sys/class/gpio/unexport")
    
    def setBit(self, bit):
        os.write(self.fd, str(bit))

    
if __name__ == "__main__":
    ROW  = map(lambda x: GpioWriter(x), [ 49, 30, 31]) 
    COL  = map(lambda x: GpioWriter(x), [ 60, 51,  2]) 

    SMU=B2902A(0, 5, 100, 0.1, 0.1)
    
    for i in xrange(8):
        for j in xrange(8):
            for k in xrange(3):
                ROW[k].setBit((i >> k) & 1)
                COL[k].setBit((j >> k) & 1)
                
    for R in ROW:
        R.release()
    for C in COL:
        C.release()
```
このようにすれば、一応ROWのIO列、COLのIO列を連続して制御できます。

##GPIOを掃引しながらI-V特性を取得し、最後にtar ballで固める
以下のプログラムをまとめ、最後に"日時.tar.gz"の名前でデータを圧縮するようにするとこうなります。

```python:B2902A_Measurement.py
# -*- coding: utf-8 -*-

import os
import time
import tarfile
import gpib
import pandas as pd
import numpy as np
from datetime import datetime

class GpioWriter:
    def __init__(self, pinNumber):
        self.pinNumber = pinNumber
        os.system("echo  " + str(pinNumber) + " > /sys/class/gpio/export")
        os.system("echo out > /sys/class/gpio/gpio" + str(pinNumber) + "/direction")
        self.fd = os.open("/sys/class/gpio/gpio" + str(pinNumber) + "/value", os.O_WRONLY)
    
    def release(self):
        os.close(self.fd)
        os.system("echo " + str(self.pinNumber) + " > /sys/class/gpio/unexport")
    
    def setBit(self, bit):
        os.write(self.fd, str(bit))

class B2902A:
    #StartVOl: Starting voltage
    #StopVol:  End voltage
    #Point:    Tself.he Number of plotting point
    #NPLC:     Power Line Cycle
    #CurProt:  Current protection value
    def __init__(self, StartVol, StopVol, Point, NPLC, CurProt):
	print StartVol
	self.h=gpib.dev(0,23)
        gpib.write(self.h,"*RST")
        gpib.write(self.h,":sour:func:mode volt")
        gpib.write(self.h,":sour:volt:mode swe")
        gpib.write(self.h,":sour:volt:star " + str(StartVol))
        gpib.write(self.h,":sour:volt:stop " + str(StopVol))
        gpib.write(self.h,":sour:volt:poin "+ str(Point))
        
        gpib.write(self.h,":sens:func ""curr""")
        gpib.write(self.h,":sens:curr:nplc "+ str(NPLC))
        gpib.write(self.h,":sens:curr:prot "+ str(CurProt))
        gpib.write(self.h,":trig:sour aint")
        gpib.write(self.h,":trig:coun " + str(Point))

        self.X = np.linspace(StartVol, StopVol, Point)
        
    def X(self):
        return self.X
    
    def measure(self):
        gpib.write(self.h,":outp on")
        gpib.write(self.h,":init (@1)")
        gpib.write(self.h,":fetc:arr:curr? (@1)")
        s=gpib.read(self.h,16500)
        gpib.write(self.h,":outp off")
        return s
    

def CSV_Output(X, XLabel, Y, YLabel, FileName):
    XLabel = "#"+ XLabel
    df = pd.DataFrame({XLabel:X, YLabel:Y})
    df.to_csv(FileName+".csv", index=False)
    
def tar_compress(dirname):
    _tar = tarfile.open(dirname+".tar.gz","w:gz")
    for _root, _dirs, _files in os.walk(dirname):
        for _file in _files:
            _tar.add(os.path.join(_root, _file))

    _tar.close()

if __name__ == "__main__":
    ROW  = map(lambda x: GpioWriter(x), [ 69, 68, 45]) 
    COL  = map(lambda x: GpioWriter(x), [ 44, 23, 26]) 

    SMU = B2902A(0, 5, 100, 0.1, 0.1)    

    dirname=datetime.now().strftime("%Y%m%d%H%M%S")
    os.mkdir(dirname)

    for i in xrange(8):
        for j in xrange(8):
            for k in xrange(3):
                ROW[k].setBit(~((i >> k) & 1))
                COL[k].setBit(~((j >> k) & 1))
            Y = SMU.measure().strip("\n").split(",")
	    print len(SMU.X)
	    print len(Y)
            CSV_Output(SMU.X, "Voltage", Y, "Crrent", dirname+"/""test_"+str(i)+"_"+str(j))
                
    for R in ROW:
        R.release()

    for C in COL:
        C.release()

    tar_compress(dirname)
```


#終わりに
半導体プロセスの中で使うプログラミングについてのお話をしました。
実際、こんな感じでプログラミングが必要なさそうなことをしていても結構必要になることはあります。
ちょっと亜種に触れる感じで面白半分に見てもらえたら、と思います。

##参考

- http://www.messtechniklabor.de/artikel-h0014-raspberry_pi_3_model_b_mit_linux_gpib_und_kernel_4_1.html

- http://yasuand.hatenablog.com/entry/2014/01/26/095014

- SEM写真は自分で作製・撮影したものなので問題ないです