devicetree sourceのincludeを展開して読みやすくする

2020年あけましておめでとうございます。

三ヶ日はRaspberry Pi 4のdevicetree source(dts)を読んでました。
まだ読んでる途中なので得られた情報に関してはまた今度どこかで出しますが、今回はdtsを読む際に使った小技をご紹介します。

# TL;DR

- `#include`のあるdtsはcppとdtcを通すと読みやすくなります

# devicetreeとは

devicetree(以降、dt)は、特定ハードウェアの どこ に どのような デバイスが接続されているかを説明するものです。

Linuxでは主にARMやPowerPCなどを使った組み込みマシンの構成を知るためにdtを利用しており、Raspberry Piもこのdtを用いてマシンの構成をLinux Kernelに教えています。

dtはコンパイル型言語のように、人間の読める形式で書かれたdevicetree source(以降、dts)を、devicetree compiler(以降、dtc)でコンパイルして、devicetree blob(以降、dtb)を生成して利用します。

例えばLinux kernel(ARM)では、ブートローダーなどによって予めメモリ上にロードされたdtbのアドレスをレジスタ経由で受け取り、メモリ上のdtbを参照してマシンの構成を知るようになっています。


その他dtに関する詳細は以下を参照してください。

- [Device tree - Wikipedia](https://en.wikipedia.org/wiki/Device_tree)
- [Device Tree Reference - eLinux](https://elinux.org/Device_Tree_Reference)
- [The Devicetree Specification - devicetree.org](https://www.devicetree.org/specifications/)

# なぜdtsを読むのか

Raspberry Pi 4に搭載されているBCM2711というSoCは、割り込みコントローラーにGIC-400を搭載するなど、Raspberry Pi 3までのSoCと異なる部分が複数見受けられます。

しかし、SoCに搭載されたペリフェラルやその扱い方などを説明したPeripheral Manualはまだ出ていません(2019年7月時点で「ほぼ完成してるけど、もう数週間かかりそうです」といったことを[中の人が書いています](https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=244479&sid=3752ded6249ea465c13b398289b2464b#p1491246)が、2020年1月6日時点ではまだ出ていません)。

そのため、現状Raspberry Pi 4上でペリフェラルを直接制御するベアメタル開発のようなことをするためには、[Raspberry Pi向けのLinux Kernelのソース](https://github.com/raspberrypi/linux)にあるdtsを参照して、ペリフェラルの構成やMMIOのマップされたアドレスを調べる必要があります。


# includeを展開したよみやすいdtsを作る

Raspberry Pi 4向けのdtsは以下で公開されています。

- https://github.com/raspberrypi/linux/blob/rpi-4.19.y/arch/arm/boot/dts/bcm2711-rpi-4-b.dts


このdtsファイルの先頭数行を一部引用して以下に示します。

```
#include "bcm2711.dtsi"
#include "bcm2711-rpi.dtsi"
#include "bcm283x-rpi-csi1-2lane.dtsi"
```

これを見ていただければわかるように、bcm2711-rpi-4-b.dtsではinclude文を利用して複数のdtsiファイルを読み込んでいます。

そして、ここで読み込んだbcm2711.dtsiは更にbcm2838.dtsiを読み込む、つまりincludeが入れ子状態になっているため、このまま読みすすめるのは少々面倒です。

よって、includeを予め展開した、読むためのdtsファイルを作りたいと思います。


## 準備

Raspberry Pi用のLinux kernelをgitから持ってきて、そのディレクトリ内に入ってください。

```bash
$ git clone --depth=1 https://github.com/raspberrypi/linux raspberrypi_linux
$ cd raspberrypi_linux
```

## includeの処理方法を調べる

dtsをdtbにする際に利用するdtcでは、dtsを入力にdtsを出力することが可能です。
そのため「dtcを通せばincludeが適切に処理されて読みやすいdtsが得られるはず...」と考えたのですが、実行してみると以下のようなエラーが出てうまくいきません。

```bash
$ dtc -I dts -O dts -o readable.dts arch/arm/boot/dts/bcm2711-rpi-4-b.dts 
Error: arch/arm/boot/dts/bcm2711-rpi-4-b.dts:3.1-9 syntax error
FATAL ERROR: Unable to parse input tree
```

原因を探るべく[Devicetree Specification Release v0.2](https://github.com/devicetree-org/devicetree-specification/releases/download/v0.2/devicetree-specification-v0.2.pdf)を参照すると、「6.1 Compiler directives」にdtsでのincludeは以下の書式で行うように書かれています。

```
/include/ "FILE"
```

一方、bcm2711-rpi-4-b.dtsではdevicetree specificationに書かれた書式でincludeが行われていないため、dtcがエラーを出していたようです。


ここまで調べた結果より、bcm2711-rpi-4-b.dtsのinclude文ではdtsをコンパイルできないほうが正しいはずです。
しかしなぜLinuxではdtsのコンパイルが行えているのでしょうか。

その理由を求めてLinuxのソースコードよりdtbのビルドルールを調べてみると、[scripts/Makefile.lib](https://github.com/raspberrypi/linux/blob/rpi-4.19.y/scripts/Makefile.lib#L287)よりLinuxではdtsをdtcでコンパイルする前にCプリプロセッサを通している事がわかりました。
以下、scripts/Makefile.libの当該部分を引用します。

```Makefile
cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
	$(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
	$(DTC) -O dtb -o $@ -b 0 \
		$(addprefix -i,$(dir $<) $(DTC_INCLUDE)) $(DTC_FLAGS) \
		-d $(depfile).dtc.tmp $(dtc-tmp) ; \
	cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
```

よって、このルールを参考にして、dtsを適切なオプションを用いてプリプロセッサに通した後、dtcでコンパイルすることで読みやすいdtsが得られそうです。

## 読みやすいdtsを作る

ここからはLinuxの行っている手順を参考に、読みやすいdtsを作りましょう。

### プリプロセッサを通す

includeを処理するために以下のようにしてプリプロセッサを通します。

```bash
cpp -nostdinc -undef -D__DTS__ -x assembler-with-cpp \
  -I./scripts/dtc/include-prefixes \
  ./arch/arm/boot/dts/bcm2711-rpi-4-b.dts \
  -o ~/bcm2711-rpi-4-b_readable.dts
```

### dtc

プリプロセッサを通しただけではプリプロセッサのつけたコメント等がたくさんあって読みづらいので、更にdtcでコンパイルして読みやすくします。

```bash
dtc -I dts -O dts -o ~/bcm2711-rpi-4-b_readable.dts ~/bcm2711-rpi-4-b_readable.dts
```

Warningはたくさん出ますが、エラーにならなければ`~/bcm2711-rpi-4-b_readable.dts`に読みやすくなったdtsができているはずです。


# おしまい

以上でincludeを展開した読みやすいdtsの作り方紹介を終わります。

Raspberry Pi 4のdtsは読んでいると、

「gpioのcompatibleの先頭がbcm2711-gpioになってるけど、bcm2835-gpioも入ってるから互換のおかげでRasPi3向けのコードも動きそうだな〜。でも新機能何が入ってるんだろう。。。ドライバ読まないと。。。」

など、色々発見や考えることがあって楽しいです。

本記事を読んでいただいた方々とこの楽しみを共有できると嬉しいなと思います。