SoC FPGA: Linux で使うときの小ネタ 

# はじめに
ARM CPU と FPGA が１chip になっている FPGA を Linux で使うときの小ネタを書かせていただきます。ちょっと便利なツールや、役立つ Tips など、今後、徐々に増やせていければと思います。

主に Intel®のCyclone V SoC FPGAが載ったボードを使って、Hardware/Softwareを開発するときの話となります。[Terasic DE10-nano](https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&No=1046)というボードを使っています。

# memtool
物理アドレスを指定して、そのアドレスの Register/Memory を読み書きできるアプリ(ユーティリティ）です。使うには root 権限が必要ですが、どんな物理アドレスでもアクセス可能で、もちろん、FPGA 内に作ったRegisterやMemory の Read/Write が可能です。これさえあれば、もう、Baremetal 感覚（OS less 環境）で、FPGA 回路をアクセスできちゃいます。ちょっとしたFPGA回路用にわざわざDevice Driverを作る必要が無いです。

入手方法は、こちらの [memtoolのリンク](https://gist.github.com/mike0/2910170) から C ソースをダウンロードし、gcc コンパイラでコンパイルします。（なお、internet で "memtool" と検索すると上記リンク以外にも複数の種類の "memtool" がヒットします。若干使い方が異なるようですが、気に入ったものをお使いください。）
gcc の入っている ARM Linux であれば、

``` 
gcc -o memtool memtool.c
```
x86PC でのクロスコンパラであれば arm-linux-gnueabihf-gcc でコンパイルします。
生成された memtool ファイルを SD card の Linux filesystem の /usr/local/bin にコピーしちゃいましょう。これでいつでも memtool が使えるようになりました。
基本的な使い方は、

```
memtool <targetの物理アドレス(16進)> <ワード数> # READ アクセス
memtool <targetの物理アドレス(16進)>=<データ (16進)> # WRITE アクセス
```

です。アドレスの直後に `=` を付けると Write アクセス、`space` だと読み込みアクセスを実行します。アクセスのデータ幅は、デフォルトでは 32bit 単位ですが、-8, -16 オプションを付けることで 8bit, 16bit 単位でのデータ幅のアクセスが可能です。入力するアドレスおよびデータは、0x を付けても付けなくても 16進数として解釈されます。

## memtool 具体例
Terasic サイトの [DE10-Nano Kit Resourcesページ ](https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=167&No=1046&PartNo=4) 中の"Linux LXDE Desktop (kernel 4.5)" SD card image を使って起動した DE10-nano での実行例を書きます。 
この SD card image の FPGA デザイン (RBF ファイル) は、同じく Terasic の[ DE10-Nano Kit Resourcesページ ](https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=167&No=1046&PartNo=4) 中の アーカイブ "DE10-Nano CD-ROM (rev. C Hardware)" の Demonstrations/SoC_FPGA/ControlPanel/Quartus がプロジェクトファイルとなっています。



__物理アドレス 0xFF201000 から 2 word 読み込む：__ `memtool ff201000 2`
0xff201000 には、System ID IP があり、デザイン中設定された値 0xACD51302 が読みだされています。

```
root@DE10_NANO:~# memtool ff201000 2
Reading 0x2 count starting at address 0xFF201000

0xFF201000:  ACD51302 5FC6E9D0
```

ちなみに、2番目の値の 58D36B67 は、このデザインが generate されたときの Unix時刻を示します（System ID IP の仕様）。date コマンドで解読してみると、2017年の3月に generate されたデザインだと分かります。

```
root@DE10_NANO:~# date -d @$((0x58D36B67))
Thu Mar 23 06:29:59 UTC 2017
root@DE10_NANO:~#
```



続いて、
__物理アドレス 0xFF203000 に 0x00000000 / 0x000000FF を書く：__ `memtool ff203000=0`
0xff203000 には、LED に接続された PIO IP がアサインされていますので、LED の ON/OFF が実行できます。

```
root@DE10_NANO:~# memtool ff203000=0
Writing 32-bit value 0x0 to address 0xFF203000
root@DE10_NANO:~# memtool ff203000=ff
Writing 32-bit value 0xFF to address 0xFF203000
root@DE10_NANO:~#
```

## memtool 参照応用
また、このソースコードは FPGA レジスタをアクセスする自作のアプリを作るときにも非常に参考になるかと思います。ポイントは /dev/mem を open() して mmap() することです。気を付けるのは、mmap に渡す引数で、4Kbyte の整数倍の 物理アドレスと 4Kbyte の整数倍のサイズを指定する必要があるという点です。


## memtool を使うにあたっての注意点
HPS のレジスタ空間の中には、リードすることすら許されていないアドレスがあります(間違ってアクセスすると CPU 停止となるアドレスがあります）。Read や Write してよいアドレスなのか、レジスタマップを確認してからアクセスするようにしましょう。

# Ethernet MAC アドレスの固定

DE10-nano もそうですが、開発用のボードには固定の MAC アドレスが割り当てられていない場合がほとんどかと思います。そのボードを Linux で使う場合、MAC アドレスは起動時にランダムな local MAC address が生成され、これが使われます。これを DHCP の環境で使う場合、ボードを再起動するたびに MAC アドレスが異なってしまうため、 IP アドレスも異なるものが割り振られて不便な場合があります（私の場合、自宅の DHCPサーバ(router) の IP アドレスを使い切ってしまい、スマホが wifi で使えなくなったと、家人に痛く怒られた経験があります）。それ以来、 uboot で MAC アドレスを固定(or 指定）する方法を取るようにしてます。SoC FPGA で使っている uboot /Linux の組み合わせでは、uboot で ethaddr という環境変数に MAC address を割り当てておくと、これが Linux での MAC アドレスに設定されるようになっています。uboot のプロンプトで、

```
setenv ethaddr xy:xx:xx:xx:xx:xx 
saveenv
```

と設定してます。設定するアドレスは、最初に Linux が自動で生成した値をコピペして、2回目以降の起動からその MAC address を使うようにするのが無難だと思います。（unicast の ローカル MACアドレスとするため、6桁の 8bit のうちの最初の 8bit の bit-1 は "1" , bit-0 は "0"になってい必要があるかと思います。）

# おわりに
ひとまず、以上にて。