FPGA+SoC+Linux実践勉強会に参加してArty-Z7でLチカをしたお話 

先日[こちら](https://fpgax.connpass.com/event/71364/)のイベントに参加して、Lチカをしたのでそちらの作業記録です。
本当は割り込み＋DMAまでやりたかったのですが、時間が足りなかったのでそちらは冬休みの宿題にしたいと思います。
FPGAボードはArty-Z7で行いましたが、他のでも大丈夫と思います。
環境は、Vivado 2017.3(Windows10)です。

#FPGAボード上で動くLinux環境が入ったmicroSDを作成
@ikwzm さんの [FPGA+SoC+Linux+Device Tree Overlay+FPGA Region(ブートイメージの提供)](https://qiita.com/ikwzm/items/7e90f0ca2165dbb9a577)を実施して、FPGAボードで動くLinuxを用意します。
Arty-Z7はリストにないですが、ZYBOのものを使用してみたところ問題なく(?)立ち上がりましたので、とりあえずこのまま使います。

#bitストリーム(FPGAのコンフィグデータ)生成
[Digilentさんのチュートリアル記事](https://reference.digilentinc.com/learn/programmable-logic/tutorials/zybo-getting-started-with-zynq/start)に沿って、Zynq+GPIO（汎用入出力）のデザインを作成します。
あらかじめ自分が持っているFPGAボードのボードファイルを[vivadoのフォルダに入れておく](https://reference.digilentinc.com/reference/software/vivado/board-files)必要があります。
今回はSDKは使わないので、6.Generate the Bitstream まで行ったらOKです。
プロジェクトフォルダの<プロジェクト名>.runs\impl_1\<topモジュール名>.bitというフォルダにビットストリームが生成されています。
ここで、生成したデザインのBlock DesignのAddress Editor見て、GPIOモジュールのベースアドレスを確認しておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/106476/21874fab-c9e0-cda5-2e3e-a2d9e69eaeb1.png)

この例では、GPIO_0が0x4120_0000でGPIO_1が0x4121_0000です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/106476/5746fe6d-d4f5-73b6-963a-892896ec9818.png)

#生成したFPGAバイナリをデバイスツリー経由で読み込ませる
ZynqボードをTeratermなどでつないで、fpgaユーザーでログイン。パスワードはfpga。

``` console
debian-fpga login: fpga
Password:
Last login: Fri Nov  4 02:17:08 JST 2016 on ttyPS0
Linux debian-fpga 4.12.14-armv7-fpga #3 SMP PREEMPT Mon Nov 13 15:14:45 JST 2017 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
fpga@debian-fpga:~$
```

サンプルを持ってくる。持ってくるのはexamples/uio_irq_sample。

```
fpga@debian-fpga:~$ cp examples/uio_irq_sample/ ~/my_uio_sample -r
fpga@debian-fpga:~$ cd my_uio_sample
fpga@debian-fpga:~/my_uio_sample$ ls -l
total 1648
-rw-r--r-- 1 fpga fpga    2958 Dec  3 09:31 Rakefile
-rw-r--r-- 1 fpga fpga     647 Dec  3 09:31 devicetree.dts
-rw-r--r-- 1 fpga fpga 4045564 Dec  3 09:31 pump_axi4.bin
-rw-r--r-- 1 fpga fpga    2541 Dec  3 09:31 sample1.c
-rw-r--r-- 1 fpga fpga    2746 Dec  3 09:31 sample2.c
-rw-r--r-- 1 fpga fpga    5844 Dec  3 09:31 sample_common.h
drwxr-xr-x 2 fpga fpga    4096 Dec  3 09:31 uio_irq_sample
```

ここに先ほど生成したbitストリームをコピーします。
自分は、ZynqボードにEthernetでつなげてSCP経由でファイルを転送しました。
しかし、Xilinxで生成されたbitストリームはそのまま使えないので、変換する必要があります。
参考：[Linux の FPGA Manager で Xilinx のビットストリームファイルを扱う方法](https://qiita.com/ikwzm/items/1bb63be0b86a1e0e56fa)

wgetでpythonスクリプトを取ってきて、変換します。
以下の例では、system_wrapper.bitというファイルからarty_gpio.binに変換しています。
**--flip**というオプションがキモです。

```
fpga@debian-fpga:~/my_uio_sample$ wget https://raw.githubusercontent.com/topic-embedded-products/meta-topic/master/recipes-bsp/fpga/fpga-bit-to-bin/fpga-bit-to-bin.py
--2017-12-03 09:41:38--  https://raw.githubusercontent.com/topic-embedded-products/meta-topic/master/recipes-bsp/fpga/fpga-bit-to-bin/fpga-bit-to-bin.py
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.72.133
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2964 (2.9K) [text/plain]
Saving to: 'fpga-bit-to-bin.py'

fpga-bit-to-bin.py  100%[===================>]   2.89K  --.-KB/s    in 0.001s  

2017-12-03 09:41:38 (4.42 MB/s) - 'fpga-bit-to-bin.py' saved [2964/2964]

fpga@debian-fpga:~/my_uio_sample$ python fpga-bit-to-bin.py --flip system_wrapper.bit arty_gpio.bin
Design name: system_wrapper;UserID=0XFFFFFFFF;Version=2017.3
Full bitstream
('Partname', '7z020clg400\x00')
('Date', '2017/11/11\x00')
('Time', '17:46:53\x00')
Found binary data: 4045564
Flipping data...
Writing data...
fpga@debian-fpga:~/my_uio_sample$ 
```

devicetree.dtsを修正します。

``` dts:devicetree.dts
/dts-v1/;
/ {
        fragment@0 {
                target-path = "/amba/fpga-region0";
                #address-cells = <0x1>;
                #size-cells = <0x1>;

                __overlay__ {
                        #address-cells = <0x1>;
                        #size-cells = <0x1>;

                        firmware-name = "arty_gpio.bin";      #Vivadoで生成して変換したbinファイルにする

                        pump-uio@41210000 {                   #VivadoのOffsetにする
                                compatible = "generic-uio";
                                reg = <0x41210000 0x10000>;   #VivadoのOffsetとRangeにする。LEDはGPIO1に接続されている。
                                #interrupts = <0x0 0x1d 0x4>; #割り込みは使用しない
                        };

                        pump-udmabuf4 {
                                compatible  = "ikwzm,udmabuf-0.10.a";
                                device-name = "udmabuf4";
                                size = <0x00400000>;
                        };

                        pump-udmabuf5 {
                                compatible = "ikwzm,udmabuf-0.10.a";
                                device-name = "udmabuf5";
                                size = <0x00400000>;
                        };
                };
        } ;
} ;
```

Rakefileを修正します。
FPGA_BITSTREAM_FILEだけ変えておけばとりあえず後は大丈夫です。

```ruby:Rakefile
CC                     = "gcc"
CFLAGS                 = ""
FPGA_BITSTREAM_FILE    = "arty_gpio.bin" #ここを先ほど生成したbinにする
DEVICE_TREE_DIRECTORY  = "uio_irq_sample"
DEVICE_TREE_FILE       = "devicetree.dts"
UIO_DEVICE_NAME        = "uio0"
UDMABUF4_DEVICE_NAME   = "udmabuf4"
UDMABUF5_DEVICE_NAME   = "udmabuf5"
```

そして、sudo rake installをすれば、生成したFPGAコンフィグが読み込まれるようです。

```
fpga@debian-fpga:~/my_uio_sample$ sudo rake install
sudo: unable to resolve host debian-fpga
[sudo] password for fpga:
cp arty_gpio.bin /lib/firmware/arty_gpio.bin
dtbocfg.rb --install uio_irq_sample --dts devicetree.dts
/config/device-tree/overlays/uio_irq_sample/dtbo: Warning (unit_a[   29.758004] fpga_manager fpga0: writing arty_gpio.bin to Xilinx Zynq FPGA Manager
ddress_vs_reg): Node /fragment@0 has a unit name, but no reg property
[   29.837519] udmabuf amba:fpga-region0:pump-udmabuf4: driver probe start.
[   29.861480] udmabuf udmabuf4: driver installed
[   29.865845] udmabuf udmabuf4: major number   = 245
[   29.872518] udmabuf udmabuf4: minor number   = 0
[   29.877058] udmabuf udmabuf4: phys address   = 0x1f100000
[   29.884086] udmabuf udmabuf4: buffer size    = 4194304
[   29.890086] udmabuf amba:fpga-region0:pump-udmabuf4: driver installed.
[   29.897096] udmabuf amba:fpga-region0:pump-udmabuf5: driver probe start.
[   29.920640] udmabuf udmabuf5: driver installed
[   29.925008] udmabuf udmabuf5: major number   = 245
[   29.931676] udmabuf udmabuf5: minor number   = 1
[   29.936213] udmabuf udmabuf5: phys address   = 0x1f500000
[   29.943232] udmabuf udmabuf5: buffer size    = 4194304
[   29.949204] udmabuf amba:fpga-region0:pump-udmabuf5: driver installed.
fpga@debian-fpga:~/my_uio_sample$

```

#サンプルプログラムを書く
[XilinxのAXI_GPIOの仕様書](https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf)を見ながらプログラムを書きます。
と、いいつつ実際は同じチームの方からソースをもらいました（笑）
勉強会ページの資料に添付されているpdfの6p参照
参考：http://www.motohashi.org/omi/tmp/Zynq-L-chika.pdf


``` C:led.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <assert.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int  fd;
    char *p;
    fd = open("/dev/uio0", O_RDWR);
    p = mmap(NULL, 4096, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, 0);
    *(p + 4) = 0; // Set tristate register to output mode.
    while (1) {
        *p = 0; // LED OFF
        sleep(1);
        *p = 0xff; // LED ON
        sleep(1);
    }
}
```

#コンパイルして実行

Zynqボード上でコンパイルして実行します。
無事Lチカしました。

``` console
fpga@debian-fpga:~/my_uio_sample$ gcc -o led led.c
fpga@debian-fpga:~/my_uio_sample$ ./led
```