Claude Media
Claude CodeでZephyrのdevicetree overlayを書きビルドエラーを潰す

Claude CodeでZephyrのdevicetree overlayを書きビルドエラーを潰す

Zephyrのボード定義とオーバーレイの読み分け、zephyr.dtsを正としてノード名の誤りを直すループを、Claude Codeに渡す指示と権限設定つきでまとめます。

Zephyrのdevicetreeオーバーレイは、ボード定義の.dtsを書き換えずに、ペリフェラルの有効化やノードの追加を上書きする仕組みです。Claude Codeに任せるときの要点は一つで、「オーバーレイを推測で書かせず、ビルドが吐くzephyr.dtsを正解として読ませる」ことです。ノード名やラベルの誤りは、この方針だけでほぼ機械的に潰せます。

westビルド自体の回し方やKconfigとの役割分担はClaude CodeでZephyr RTOSをwestビルドで開発する手順にあります。ここではオーバーレイの記述と、エラーからの修正ループに絞ります。

ボード定義とオーバーレイは何が違うのか

devicetreeの入力ファイルは4種類です。ボードごとのBOARD.dts、SoCや共通部品を書く.dtsi、アプリ側の.overlay、そしてノードの意味を定義する.yamlのバインディングです。

BOARD.dtsは#includeで.dtsiを取り込み、そのボード固有のハードウェアを記述します。オーバーレイも同じDTS形式のファイルで、.overlayという拡張子は目的を分かりやすくするための慣習です。ビルドシステムはBOARD.dtsとオーバーレイを連結し、オーバーレイを後ろに置きます。後ろに置かれるので、同じノードへの記述はBOARD.dtsより優先されます。

くらべる

どのファイルを触るか

Zephyr本体側

ボード定義(BOARD.dts / .dtsi)

ボードやSoCの標準構成が書かれています。アプリのために編集しない場所です。

アプリ側

オーバーレイ(.overlay)

無効なペリフェラルを有効にする、センサーを選ぶ、サブノードを足すといった差分だけを書きます。

Claude Codeには、この線引きを先に伝えます。ボード定義を直接書き換えると、Zephyr本体の更新で差分が消え、他のアプリにも影響します。差分は常にアプリ側のオーバーレイに置きます。

どのオーバーレイがビルドに使われるか

DTC_OVERLAY_FILEを指定しなければ、ビルドシステムはアプリケーション設定ディレクトリを次の順で探します。

  1. socs/<SOC>_<BOARD_QUALIFIERS>.overlayがあれば使う
  2. boards/<BOARD>.overlayがあれば、1に加えて使う
  3. ボードにリビジョンがありboards/<BOARD>_<revision>.overlayがあれば、さらに加えて使う
  4. 1から3で見つかったら、そこで探索を止める
  5. 見つからなければ<BOARD>.overlayを探し、それも無ければapp.overlayを使う

app.overlayを作ったのに効かないときは、boards/<BOARD>.overlayが先に見つかって探索が止まっていないかを疑います。Claude Codeに「どのオーバーレイが使われたか」を聞くより、ビルドの出力を見せるほうが確実です。構成フェーズで次の形の行が出ます。

-- Found devicetree overlay: .../some/file.overlay

この行が期待したファイルを指していなければ、オーバーレイの中身を直す前に、ファイルの置き場所やDTC_OVERLAY_FILEの指定を見直します。複数を明示するなら、DTC_OVERLAY_FILEに空白かセミコロン区切りで並べます。自動探索を残したまま1枚足したいときはEXTRA_DTC_OVERLAY_FILEを使います。こちらはDTC_OVERLAY_FILEの後ろに追加されるため、優先度も高くなります。値はCMakeのキャッシュに保存され、次のビルドにも引き継がれます。古い指定が残っている疑いがあれば、ビルドディレクトリを作り直します。

Claude Codeに先に作らせる「正解」: zephyr.dts

オーバーレイを書く前に、最終的なdevicetreeを手元に出します。手順は、ボードをビルドしてbuild/zephyr/zephyr.dtsを開くことです。CMakeだけ走らせれば足ります。

west build -b qemu_cortex_m3 samples/hello_world --cmake-only

ボード名は手元のものに置き換えます。例ではCMakeがBOARD.dtsとzephyr.dts、devicetree_generated.hの場所を表示します。zephyr.dtsがBOARD.dtsとオーバーレイを合わせた最終形で、デバッグに使うファイルだと位置づけられています。

ここがClaude Codeの使いどころです。ノードのラベルや階層を記憶から書かせると外れます。zephyr.dtsを読ませ、そこにあるラベルとパスだけを使わせます。

build/zephyr/zephyr.dtsを読んで、i2cを含むノードのラベルとstatusを一覧にして。オーバーレイはまだ書かないで。

こう頼むと、無効のまま残っているノードや、ラベルの実在が先に分かります。

ノードを上書きするオーバーレイの書き方

書き方は、ラベルで参照する方法とパスで参照する方法の2つで、どちらも同じ結果になります。BOARD.dtsにserial0というラベルのノードがある場合の例です。

/* ラベルで参照 */
&serial0 {
    current-speed = <9600>;
};
 
/* パスで参照 */
&{/soc/serial@40002000} {
    current-speed = <9600>;
};

ほかによく使う形は次のとおりです。いずれもZephyrのHOWTOにある例に沿っています。

/* ペリフェラルを有効にする */
&i2c0 {
    status = "okay";
};
 
/* エイリアスとchosenを足す */
/ {
    aliases {
        my-serial = &serial0;
    };
    chosen {
        zephyr,console = &serial0;
    };
};
 
/* 不要なプロパティを消す(真のブール値を偽にする用途にも使う) */
&serial0 {
    /delete-property/ some-unwanted-property;
};

SPIやI2Cのデバイスは、既存のバスノードの子として足します。regはユニットアドレスと一致させる必要があります。SPIではregがチップセレクト番号、I2Cではデバイスのアドレスです。プロパティの中身は、そのデバイスのバインディングに合わせます。

&spi1 {
    my_spi_device: temp-sensor@0 {
        compatible = "...";
        reg = <0>;
        spi-max-frequency = <DT_FREQ_M(4)>;
    };
};

compatibleの値はここでは"..."のままにしています。実際の値はデバイスのバインディングで決まるため、Claude Codeに推測で埋めさせないことが大切です。HOWTOは、バインディングの探し方としてdevicetree_generated.h冒頭のノード一覧から対象のパスを見つけ、そのノードの記述に出るBindingの行を読む手順を挙げています。

ビルドエラーから直す修正ループ

devicetreeまわりのエラーは、出る場所で原因が分かれます。トラブルシューティングの出発点は、ビルドディレクトリの作り直しです。出力物は構成フェーズで作られ、入力が変わっても毎回は再生成されないため、先にこれを試すよう書かれています。

切り分け

エラーが出たときの切り分け

  1. Qビルドディレクトリを作り直しても同じエラーか?
    答えがいいえのとき

    先にディレクトリを作り直してから再ビルドします。

    答えがはい
  2. Qzephyr.dtsに対象ノードは存在するか?
    答えがいいえのとき

    ラベルやパスの綴りをzephyr.dtsと突き合わせて直します。

    答えがはい
  3. Qそのノードのstatusはokayか?
    答えがいいえのとき

    オーバーレイでstatusをokayにします。

    答えがはい
  4. Qcompatibleに対応するドライバのKconfigは有効か?
    答えがいいえのとき

    prj.confで該当のCONFIGを有効にします。

    答えがはい

それでも解けないとき

前処理後のソースを見て、devicetreeのマクロが何に展開されたかを確かめます。

それぞれの段階で、Claude Codeに見せる材料が違います。

ノード名・ラベルの誤り

オーバーレイ中の&i2c0のように、zephyr.dtsに存在しないラベルを参照している場合です。直し方は単純で、zephyr.dtsの実在するラベルかパスに合わせます。Claude Codeには「エラーの行番号が指すオーバーレイの行と、zephyr.dtsで該当しそうなノードを並べて」と頼みます。エラーメッセージの文面は版やツールで変わるため、文言ではなく行番号とノード名を手がかりにします。

名前の書き方の取り違え

トラブルシューティングが例示している典型的な誤りです。C/C++側とオーバーレイ側で、名前の規則が逆になります。

書く場所規則例
C/C++のマクロ規則小文字とアンダースコアに変換する例DT_PROP(DT_PATH(soc, i2c_1234000), clock_frequency)
オーバーレイ(DTS)規則DTSの名前をそのまま書く例clock-frequency、i2c@12340000

例では、オーバーレイにclock_frequencyやi2c_12340000と書くとdevicetreeのエラーになります。Cのマクロに書くべき形をオーバーレイに持ち込む、またはその逆が起きやすいので、CLAUDE.mdにこの表の趣旨を一行で書いておくと再発を防げます。

statusがdisabledのままリンクで落ちる

コンパイルは通るのに、リンクエラーundefined reference to __device_dts_ord_Nが出る場合です。原因は2つに分けられています。ノードのstatusが"okay"でないか、対応するドライバを有効にするKconfigがyになっていないかです。

手順は次のとおりです。

  1. エラーの番号Nを、build/zephyr/include/generated/zephyr/devicetree_generated.h冒頭のノード一覧で引き、対象のパスを特定する
  2. そのパスのノードをbuild/zephyr/zephyr.dtsで探し、statusを確認する
  3. disabledならオーバーレイでstatus = "okay";にして、ビルドディレクトリを作り直す
  4. それでも直らなければ、compatibleに対応するドライバのKconfigが.configでyか確認する

この4段は、Claude Codeに番号を渡して機械的にたどらせるのに向いています。ドライバの特定は、DT_DRV_COMPATをcompatibleの小文字・アンダースコア版と照合するのが目安ですが、すべてのドライバがこのマクロを使うわけではない点に注意します。見つからないときは、ドライバのファイル名を手がかりにします。

マクロ展開のエラーが長すぎて読めない

devicetreeのエラーは、マクロの展開段階ごとにメッセージが出て長くなることがあります。CONFIG_COMPILER_TRACK_MACRO_EXPANSIONを無効にすると、1エラーにつき1メッセージ程度に減ると説明しています。

west build -b <BOARD> <APP> -- -DCONFIG_COMPILER_TRACK_MACRO_EXPANSION=n

マクロが何に展開されたかを見たいときは逆に、-DCONFIG_COMPILER_SAVE_TEMPS=yで前処理後の.c.iを保存させます。Claude Codeに読ませる前にclang-formatで整形すると、読みやすくなります。

DT Doctorという診断ツールも用意されています。west buildに-DZEPHYR_SCA_VARIANT=dtdoctorを渡すと有効になります。出力の読み方は、Zephyr文書のdtdoctorの節にあります。

ループをClaude Codeに任せる設定

人が毎回エラーを貼らなくても回るよう、プロジェクトに2つ置きます。

CLAUDE.mdには、判断の拠り所を書きます。

## devicetree
- ボード定義(BOARD.dts / .dtsi)は編集しない。差分は app.overlay か boards/<BOARD>.overlay に書く
- オーバーレイを書く前に build/zephyr/zephyr.dts を読み、実在するラベル・パスだけを使う
- オーバーレイ内の名前はDTSの表記(clock-frequency, i2c@12340000)、Cのマクロでは小文字とアンダースコア
- devicetree関連のエラーは、まずビルドディレクトリを作り直してから原因を探る
- compatible は推測で書かない。バインディングのYAMLを探して値を確かめる

権限は、ビルドと検索を許可し、Zephyr本体の編集を拒否します。パスは例なので、自分の環境に合わせて置き換えます。allowとdenyのどちらにもパターンを書けます。

{
  "permissions": {
    "allow": [
      "Bash(west build *)",
      "Bash(grep *)"
    ],
    "deny": [
      "Edit(//opt/zephyrproject/zephyr/**)"
    ]
  }
}

Editの//始まりは絶対パスを意味します。deny側に書いておけば、ボード定義へ手を入れる近道を、指示だけに頼らず塞げます。

よくある取り違え

app.overlayを編集したのに反映されないとき、多くは次のどれかです。

  • boards/<BOARD>.overlayが先に見つかり、app.overlayは探索されていない
  • 以前のDTC_OVERLAY_FILEがCMakeキャッシュに残っている
  • 出力のFound devicetree overlayの行が、期待と違うファイルを指している

3つとも、ビルドディレクトリを作り直し、構成フェーズの出力を読み直せば切り分けられます。同じ「元のファイルは触らず差分だけ重ねる」考え方は、KubernetesのkustomizeのoverlayをClaude Codeで組む手順にも通じます。ただしZephyrでは重ねた後の最終形がzephyr.dtsとして出力されるので、確認の手がかりはdiffではなくこのファイルです。

まとめ

オーバーレイの誤りは、Claude Codeの記述力より、参照する名前の出どころで決まります。zephyr.dtsを先に読ませ、エラーはstatus、Kconfig、名前の表記の順で切り分けます。この順序をCLAUDE.mdに固定しておけば、ビルドエラーの修正は何度でも同じ手順で回せます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. docs.zephyrproject.org/latest/build/dts/index.html
  2. docs.zephyrproject.org/latest/build/dts/howtos.html
  3. docs.zephyrproject.org/latest/build/dts/troubleshooting.html
  4. docs.zephyrproject.org/latest/build/dts/intro-input-output.html
  5. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn