DE10nano 向けLinux Kernel Build 

#はじめに
ARM CPU一体型FPGAの載った低価格ボード[Terasic DE10nano](https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=205&No=1046&PartNo=1)用のLinux KernelをbuildしてUpdateしてみる方法について書かせていただきます。


このボード用のLinux SD card ImageはTerasicのサイトに複数アップロードされており、ユーザー登録をすればダウンロードしてすぐLinuxを起動できるようになっています。このボードで遊んでいるうちに、Kernel Driverを追加したり、デバイツリーオバーレイでFPGAのコンフィグレーションを行ってみようとすると、Kernelを自分でビルドしたくなったりするので。

# 要約
組み込みLinuxのビルドに慣れている方は、この情報だけで十分かと思います。

- Kernel souceは
    - https://github.com/altera-opensource/linux-socfpga
- 必要なビルド工程は
    - `make socfpga_defconfig; make zImage; make dtbs`
    - `make modules; make modules_install ... `
- 作ったバイナリたちは、SD cardのどこに書くか
    - zImage, socfpga_cyclone5_de0_sockit.dtb ->  Fat Partitionの該当ファイルと置き換え
    - lib/modules/* ディレクトリ -> Linux Partitionの "/"下 にコピー 
- 参考：Intel SoC FPGA 用Linuxのポータルサイト [RocketBoards.org](https://rocketboards.org/)

#必要なもの
- 開発用のPC。OS は Ubuntu Linux。以下の環境下で動作しているものでもOKでしょう。
    - Virtual Machine
    - Windows SubSystem for Linux(WSL)
    - Docker container
- Internetアクセスが開通していること。Kernel Source等をダウンロードするため。

#作業の内容
Linuxを動作させるために必要なSD Cardの内容は、Kernel関連とRoot File Systemの2種類に分けることができます。ここでbuildするのはKernel関連のみで、Root File Systemのファイル群はそのまま使います。


## 手順　
Intel SoC FPGA 用Linuxのポータルサイト [RocketBoards.org](https://rocketboards.org/) のドキュメントを参考に作業を行います。
https://rocketboards.org/foswiki/Documentation/GSRDCompilingLinux
の中の"5. Building Kernel and U-Boot Separately From Git Trees" のBuilding Linux Kernelの部分を実行することになります。

### 環境準備
まずは、クロスコンパイラをダウンロードしたり、Kernel buildに必要なコマンド類を準備します。例えば開発用PCが素の状態の Ubuntu 18.04(64bit)だと最低限以下のパッケージが必要と思います。

```
$ sudo apt update
$ sudo apt install -y wget git build-essential bc kmod libncursesw5-dev
$ sudo apt install -y lib32stdc++6 lib32ncurses5 lib32z1 
```
### クロスコンパイラのダウンロード
クロスコンパイラをダウンロードし、CROSS_COMPILE環境変数を設定します。ここではホームディレクトリ下で作業してますが、お好きなディレクトリで行ってOK (その際は、CROSS_COMPILEに設定するPATHもそれなりに変更を)。

```
$ cd ~
$ wget https://releases.linaro.org/archive/14.04/components/toolchain/binaries/gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux.tar.bz2 --no-check-certificate
$ tar xjf gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux.tar.bz2
$ export CROSS_COMPILE=~/gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux/bin/arm-linux-gnueabihf-
```
(2020/12/09 追記)
コンパイルする Kernel Version が 5.x の場合、クロスコンパイラのバージョンも変える必要があります。（参照 https://rocketboards.org/foswiki/Documentation/CycloneVSoCGSRD)

```
cd ~
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-eabi/gcc-linaro-7.5.0-2019.12-x86_64_arm-eabi.tar.xz
tar xf gcc-linaro-7.5.0-2019.12-x86_64_arm-eabi.tar.xz
export PATH=$(pwd)/gcc-linaro-7.5.0-2019.12-x86_64_arm-eabi/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-eabi- 
```

 
### Kernel Sourceのダウンロード
Git リポジトリからKernel Source をダウンロード(clone)します。そして、好きな branch or tag をチェックアウトします。例ではsocfpga-4.14.130-ltsi ブランチをチェックアウトしています。ここでもホームディレクトリ下で作業開始してますが好きな場所でやってください。-b の後の "test_branch"も好きな名前に変えて可。

```
$ cd ~
$ git clone https://github.com/altera-opensource/linux-socfpga
$ cd linux-socfpga
$ git checkout -b test_branch origin/socfpga-4.14.130-ltsi 
```
(2020/12/09 追記)
Kerenel v5.4.44-lts の場合は、上記の git checkout... コマンドを下記に変更。

```
$ git checkout -b test_branch origin/socfpga-5.4.44-lts 
```
### zImage, .dtb のビルド
いよいよ最終目的のKernel本体であるzImageとデバイスツリーをビルドします。(export CROSS_COMPILE=... は既に設定されているという前提です）

```
$ export ARCH=arm 
$ make socfpga_defconfig
$ make zImage
$ make socfpga_cyclone5_de0_sockit.dtb
```
上記により、arch/arm/boot/の下にzImage, arch/arm/boot/dts/の下にsocfpga_cyclone5_de0_sockit.dtb が生成されます。ちなみに、socfpga_cyclone5_de0_sockit.dtb は、DE10nano用のデバイスツリーファイルです。
これらをSD cardのFAT partitionにあるzImage, .dtbファイルと置き換えます。

### SD card への書き込み例
まずは、書き換え対象のSD card(DE10nano を Linux boot できるcard)のパーティション内容を調べる必要があります。開発用PCに、SD cardを挿してpartition情報を表示してみます。PCでのSD card device名が /dev/mmcblk0 の場合 コマンドは、`sudo fdisk -l /dev/mmcblk0` です。

```
$ sudo fdisk -l /dev/mmcblk0
...略...
Device         Boot  Start     End Sectors  Size Id Type
/dev/mmcblk0p1        2048  206848  204801  100M  b W95 FAT32
/dev/mmcblk0p2      227331 7395332 7168002  3.4G 83 Linux
/dev/mmcblk0p3      206849  227330   20482   10M a2 unknown
```
この情報から、FAT partition は Partition1(/dev/mmcblk0p1) で、Linux partition はPartition2(/dev/mmcblk0p2) だと分かります。

FAT partition のPartition1(/dev/mmcblk0p1)に入っている zImage と .dtb を自分で作ったもので書き換えます。例えば、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)" のMicroSD Card Imageを使っていたとすると、FAT parttitionにある該当ファイルは zImage, soc_system.dtb という名前なので、これらを置き換えます。

```
$ sudo mount /dev/mmcblk0p1 /mnt
$ cp /mnt/zImage /mnt/zImage.org
$ cp /mnt/soc_system.dtb /mnt/soc_system.dtb.org
$ cp arch/arm/boot/zImage /mnt 
$ cp arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dtb /mnt/soc_system.dtb
$ sudo umount /mnt
```
（SD cardのdevice名が /dev/mmcblk0 、空ディレクトリ /mnt が既にあったとしています）

### Kernel modules も
前述の `make socfpga_defconfig` で一気にKernelのパラメータを設定してますが、ここで設定されたものには、zImage に含めず、moduleとして別ファイル(.ko)にするという設定[M]のモジュールもいくつかあります。これらもKernel関連ファイルなので、SD cardの所定の場所に書き込みます。Kernel Parameterで[M]として選択されているモジュールを使いたい場合はこの作業も必要です（通常DE10nanoを使っている範囲では必要ない気がしますが）。

```
$ make modules
$ mkdir /tmp/kmod
$ make modules_install INSTALL_MOD_PATH=/tmp/kmod
$ cd /tmp/kmod
$ tar cvzf /tmp/mod.tgz *
$ sudo mount /dev/mmcblk0p2 /mnt
$ sudo tar xf /tmp/mod.tgz -C /mnt
$ sudo umount /mnt
$ rm -rf /tmp/mod.tgz /tmp/kmod
```
（SD cardのdevice名が /dev/mmcblk0 、SD cardのLinux partitionが/dev/mmcblk0p2だった場合の例です）
(この例の場合 /tmp/kmod/lib/modules/\*/build, /tmp/kmod/lib/modules/\*/source は、開発用PCのKernel SourceディレクトリへのSymbolic Linkとなります。 クロスコンパイルで別環境にコピーする今回の場合は、これらは削除してから tar cvzf .. して構わないと思います. ARM側の環境でこの先 moduleのbuild作業をする際に必要とされるSymbolic Linkなので）

# おまけ FrameBuffer driver 追加
Terasicから提供されているSD card imageには、HDMIモニタからのGUIが使えるようになっているimageがあります(上述の"Linux LXDE Desktop" imageなど）が、上記の手順で作成したKernelに書き換えるとモニタ出力が出なくなります。モニタ出力のためのドライバ(frame buffer driver)がデフォルトの socfpga_defconfig には入っていないため。モニタ出力を可能とするためにはframe buffer driverが入ったKernelにする必要があります。前述の `make socfpga_defconfig` と `make zImage` の間に

```
$ make menuconfig 
```

 を実行してください。これにより、Kernelパラメータの設定用のGUI?が起動してくるので、カーソルキーとEnterキーを駆使して、
` Device Drivers-> Graphics support -> Frame buffer Devices -> Altera VIP Frame Reader framebuffer support ` の行に行き、そこのマークを(Spaceキーで）<*> にします。下部のメニューで save を選び .config に save して Exit します。
それから、今有効にしたframe bufferを起動するために、デバイツリーファイルの追記も必要です。socfpga_cyclone5_de0_sockit.dtsをsocfpga_cyclone5_de10nano_FB.dtsとしてコピーし、好きなエディタを使って、socfpga_cyclone5_de10nano_FB.dts を修正します。

```
$ cd arch/arm/boot/dts
$ cp socfpga_cyclone5_de0_sockit.dts socfpga_cyclone5_de10nano_FB.dts
$ vi socfpga_cyclone5_de10nano_FB.dts # エディットします
$ cd ../../../..
```
修正内容は、ファイルの最後に以下を追記する、です。

```
&base_fpga_region {
        ranges =  <0x00000000 0xff200000 0x00200000>;

        alt_vip_vfr_hdmi: vip@0x100031000 {
		compatible = "ALTR,vip-frame-reader-14.0", "ALTR,vip-frame-reader-9.1";
		reg = <0x00031000 0x00000080>;
		max-width = <1024>;
		max-height = <768>;
		bits-per-color = <8>;
		colors-per-beat = <4>;
		beats-per-pixel = <1>;
		mem-word-width = <128>;
	};
};
```

その後、zImageと変更した名前のデバイスツリー.dtbを作り、これらを SD cardのファイルと入れ替えます。

```
$ make zImage
$ make socfpga_cyclone5_de10nano_FB.dtb
```

*なお、2020/5月の段階では、この Frame Buffer driver は、Kernel sourceのブランチがsocfpga-5.\* だと入っていません。5.\* バージョンのカーネルを使って、frame buffer を使いたい場合は、socfpga-4.\* のコードからframe bufferを持って来る必要があります...*

# まとめ
DE10nanoのLinux SD cardのLinux kernelのみをbuild&updateする方法を書いてみました。実は、この内容は、別記事で書いている「デバイスツリーオーバーレイでFPGAコンフィグ」の中で必要となったので書いてみた、という側面があります。
なお、上記の作業コマンド例では、DE10nanoのSD cardを開発用PCに挿してmountしその内容を書き換える、としてますが、私はDE10nanoと開発用PCをEthernetで接続し、開発用PCで生成したバイナリをscpでDE10nanoに送り、DE10nanoでファイルを書き換えるという流れで作業してます（SD cardの抜き差しが面倒なので）。

# 参考資料
参考：Intel SoC FPGA 用Linuxのポータルサイトの [Compiling Linux](https://rocketboards.org/foswiki/Documentation/GSRDCompilingLinux)