Arduino UNO R4 WiFi のソースコードデバッグ

この度、次の2つの記事で紹介してきた R7FA4M1AB.svd の修正版 が、Arduino 公式 GitHub取り込まれることになりました

おそらく UNO R4 用ボードマネージャーの更新(本記事執筆時点のバージョンは 1.1.0)という形で配信されると思います。

2024年6月3日にバージョン 1.2.0 がリリースされ、貢献者にハンドル名が載りました。嬉しい限りです!

New Contributers

Port mn Pin Function Select Register in SVD file. #305

この修正により、UNO R4 Minima は CMSIS-DAP をサポートした JTAG/SWD デバッグプローブ)で、UNO R4 WiFi は ESP32 を介したオンボードデバッガで、それぞれソースコードデバッグが可能になります。

SVDファイルとは何者?」から、CMSIS-DAP や CMSIS-SVD、JTAG/SWD デバッグプローブについての図を再掲します。

CMSISの全体像とデバッガの位置付け ARM MCUからArduino IDEまでのブロック図

本来は Renesas RA4M1グループ ユーザーズマニュアル ハードウェア編 の仕様と対比して修正内容を提示したいところですが、Renesas の文書による許諾が必要なため、CC BY-SA の元に公開されている Arduino UNO R4 Minima のピン配図 と MIT ライセンスの元に公開されている R7FA4M1AB.svd を用いて説明を試みます。

とても退屈な話になりますが…、苦労話を書きたくなる気持ちを察してやって下さい :pensive:

R7FA4M1AB.svd とは

SVD は System View Description の略で、Arm Cortex-M の持つ機能を XML で記述したものになります。また R7FA4M1AB は UNO R4 に搭載された Renesas MCU の型番の一部です。

例えば、僕らがプロセッサやその周辺回路の使い方をマニュアルで調べるのと同様、Arduino IDE のデバッガは、どのレジスタが何処のアドレスにマッピングされているかを知るために SVD ファイルを参照します。

つまり Arduino IDE にとって R7FA4M1AB.svdは、人間にとっての ユーザーズマニュアル に相当し、そのフォーマットは CMSIS-SVD スキーマ として定められています。

人用のマニュアルが 1700 ページほどあるのに対し、R7FA4M1AB.svd は6万数千行もあります。その中身は共通部分も多く、親とその派生で定義が可能な感じですが、仮に手作業でこれを作成するとしたら、間違えも起きるよネというのが正直な感想です。

ちなみに R7FA4M1AB.svd の原版は、Arm のサイトに公開されています。

問題点の分析と修正方針

UNO R4 用オンボードマネージャのバージョン 1.1.0 では、デバッグボタン でデバッガを起動すると、以下のエラーが起きます。

Unable to parse SVD file /Users/xxxx/Library/Arduino15/packages/arduino/hardware/renesas_uno/1.1.0/svd/R7FA4M1AB.svd: Error: SVD error: Invalid 'derivedFrom' "P100PFS" for register "P30%sPFS"
Unable to parse SVD file

この問題は、Arduino 公式 GitHub にも「“Unable to parse SVD file” error when starting debugger #276」として挙がっていますが、「無効な P100PFS から P30xPFS を派生させている」ことを訴えています。

P100PFSPFS は Port Function Select の略で、汎用 I/O 端子 P000P915 の入出力方向の切り替え、プルアップ抵抗や割り込み有無の設定など、汎用 I/O 端子の機能を選択するレジスタ群になります。

ちなみにこのエラーによりプログラムが暴走したりすることはなく、単に IDE の UI を通してレジスタの内容を表示したり書き換えたりする機能に影響があるだけです。


さて、エラーを手掛かりに R7FA4M1AB.svd を精査すると、次の2つの問題があることが判明しました。

従って修正方針はこれらの裏返しです。

  • 派生元の親となり得るよう P100PFS を再定義する
  • 派生元の親を間違えている I/O 端子を抽出し、正しい親を定義し直す

何せ行数が多いため、汎用 I/O 端子ごとにチェックリストを作成しながら進めました。

SVD ファイルの修正内容

ここからは以下の章立てにより、修正内容の説明を試みます。

PFS レジスタの概要

CORTEX PERIPHERALS

Arduino IDE でデバッガを起動すると、MCU に内蔵された周辺装置のレジスタを「CORTEX PERIPHERALS」から、参照または編集ができるようになります。

詳細は ユーザーズマニュアル を参照してもらうとして、IDE で表示された P100PFS の内容と SVD ファイルの定義を一覧にすると、次のようになります。

ビットフィールド ビットレンジ 名称
PODR [0:0] Port Output Data
PIDR [1:1] Port Input Data
PDR [2:2] Port Direction
Reserved [3:3] 予約ビット
PCR [4:4] Pull-up Control
Reserved [5:5] 予約ビット
NCODR [6:6] N-Channel Open Drain Control
Reserved [9:7] 予約ビット
DSCR [10:10] Port Drive Capability
Reserved [11:11] 予約ビット
EOR [12:12] Event on Rising
EOF [13:13] Event on Falling
ISEL [14:14] IRQ input enable
ASEL [15:15] Analog Input enable
PMR [16:16] Port Mode Control
Reserved [23:17] 予約ビット
PSEL [28:24] Port Function Select
Reserved [31:29] 予約ビット

ここでのポイントは、汎用 I/O 端子は P000 から P915 まで多数あり、USB や シリアル通信など、特定用途で使われる端子の仕様が、他の I/O 端子と微妙に異なるという点です。

言い換えれば、これらの仕様違いを正しく XML で記述する必要があるという事です。

ユーザーズマニュアル の P.430 には、但し書きで仕様違いをを規定していますが、この規定と SVD ファイルとの相違が Arduino IDE でのエラー につながっていました。

中でも「P408 は DSCR1 ビットだけを持ちます」という記述は誤訳なので要注意です。原文は「P408 only has DSCR1 bit」であり、「P408 だけが DSCR1 ビットを持ちます」と訳すのが正解です(Renesas に確認済み)。

P100PFS の再定義

修正前は、P000PFS を派生の親とする子の位置付けで定義されていましたが、他の親となり得るよう、次のように再定義しました(→ 差分を確認

<register>
  <name>P100PFS</name>
  <description>P100 Pin Function Control Register</description>
  <addressOffset>0x040</addressOffset>
  <size>32</size>
  <access>read-write</access>
  <resetValue>0x00000000</resetValue>
  <resetMask>0xFFFFFFFD</resetMask>

ユーザーズマニュアル のメモリマップによると、汎用 I/O 関連のレジスタは 0x400000000x40100000 番地にマッピングされています。

このうち PFS (Pin Function Select) は、下記 XML の通り 0x40040800 から始まると定義されていて、P100PFS はこのセクションに属しています。

<peripheral>
  <name>PFS</name>
  <description>Pmn Pin Function Control Register</description>
  <baseAddress>0x40040800</baseAddress>
  ...
</peripheral>

即ち P100PFS は、baseAddress0x4004 0800addressOffset0x040 を加えた 0x4004 0840 番地にマッピングされています。

また resetValue は各レジスタで異なる値を取り、特にリセット後の値が不定なビットがあるため resetMask でマスクする定義となっています。

以下、予約ビットを除き、P100PFS の修正済み定義を示します。

PODR

ビットフィールド ビットレンジ 名称 機能 R/W
PODR [0:0] Port Output Data 0: Low output
1: High output
R/W
    <field>
      <name>PODR</name>
      <description>Port Output Data</description>
      <lsb>0</lsb>
      <msb>0</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Low output</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>High output</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>
  </fields>
</register>

PIDR

ビットフィールド ビットレンジ 名称 機能 R/W
PIDR [1:1] Port Input Data 0: Low input
1: High input
R
    <field>
      <name>PIDR</name>
      <description>Port Input Data</description>
      <lsb>1</lsb>
      <msb>1</msb>
      <access>read-only</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Low input</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>High input</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

PDR

ビットフィールド ビットレンジ 名称 機能 R/W
PDR [2:2] Port Direction 0: Input (Functions as an input pin)
1: Output (Functions as an output pin)
R/W
    <field>
      <name>PDR</name>
      <description>Port Direction</description>
      <lsb>2</lsb>
      <msb>2</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Input (Functions as an input pin.)</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Output (Functions as an output pin.)</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

PCR

ビットフィールド ビットレンジ 名称 機能 R/W
PCR [4:4] Pull-up Control 0: Disables an input pull-up
1: Enables an input pull-up
R/W
    <field>
      <name>PCR</name>
      <description>Pull-up Control</description>
      <lsb>4</lsb>
      <msb>4</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Disables an input pull-up.</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Enables an input pull-up.</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

NCODR

ビットフィールド ビットレンジ 名称 機能 R/W
NCODR [6:6] N-Channel Open Drain Control 0: CMOS output
1: NMOS open-drain output
R/W
    <field>
      <name>NCODR</name>
      <description>N-Channel Open Drain Control</description>
      <lsb>6</lsb>
      <msb>6</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>CMOS output</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>NMOS open-drain output</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

DSCR

ビットフィールド ビットレンジ 名称 機能 R/W
DSCR [10:10] Port Drive Capability 0: Low drive
1: Middle drive
R/W

先に示した通り、「P408 だけが DSCR1 ビットを持ちます」。それ以外の端子で DSCR1(ビットレンジ [11:11])は、予約ビットとなります。

  <field>
    <name>DSCR</name>
    <description>Port Drive Capability</description>
    <lsb>10</lsb>
    <msb>10</msb>
    <access>read-write</access>
    <enumeratedValues>
      <enumeratedValue>
        <name>0</name>
        <description>Low drive</description>
        <value>#0</value>
      </enumeratedValue>
      <enumeratedValue>
        <name>1</name>
        <description>Middle drive.</description>
        <value>#1</value>
      </enumeratedValue>
    </enumeratedValues>
  </field>

EOR

ビットフィールド ビットレンジ 名称 機能 R/W
EOR [12:12] Event on Rising 0: Do not care
1: Detect rising edge
R/W
    <field>
      <name>EOR</name>
      <description>Event on Rising</description>
      <lsb>12</lsb>
      <msb>12</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Do not care</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Detect rising edge</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

EOF

ビットフィールド ビットレンジ 名称 機能 R/W
EOF [13:13] Event on Falling 0: Do not care
1: Detect falling edge
R/W

EOREOF は、P100PFSP415PFS にだけ定義されています。修正前の P100PFS は、P000PFS を継承し予約ビットとなっていたため、あらためて P100PFS を派生元の親として再定義し、P200PFSP415PFS がこれを継承するように修正しました。

    <field>
      <name>EOF</name>
      <description>Event on Falling</description>
      <lsb>13</lsb>
      <msb>13</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Do not care</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Detect falling edge</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

ISEL

ビットフィールド ビットレンジ 名称 機能 R/W
ISEL [14:14] IRQ input enable 0: Not used as IRQn input pin
1: Used as IRQn input pin
R/W
    <field>
      <name>ISEL</name>
      <description>IRQ input enable</description>
      <lsb>14</lsb>
      <msb>14</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Not used as IRQn input pin</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Used as IRQn input pin</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

ASEL

ビットフィールド ビットレンジ 名称 機能 R/W
ASEL [15:15] Analog Input enable 0: Used other than as analog pin
1: Used as analog pin
R/W
    <field>
      <name>ASEL</name>
      <description>Analog Input enable</description>
      <lsb>15</lsb>
      <msb>15</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Used other than as analog pin</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Used as analog pin</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

PMR

ビットフィールド ビットレンジ 名称 機能 R/W
PMR [16:16] Port Mode Control 0: Uses the pin as a general I/O pin
1: Uses the pin as an I/O port
for peripheral functions
R/W
    <field>
      <name>PMR</name>
      <description>Port Mode Control</description>
      <lsb>16</lsb>
      <msb>16</msb>
      <access>read-write</access>
      <enumeratedValues>
        <enumeratedValue>
          <name>0</name>
          <description>Uses the pin as a general I/O pin.</description>
          <value>#0</value>
        </enumeratedValue>
        <enumeratedValue>
          <name>1</name>
          <description>Uses the pin as an I/O port for peripheral functions.</description>
          <value>#1</value>
        </enumeratedValue>
      </enumeratedValues>
    </field>

PSEL

ビットフィールド ビットレンジ 名称 機能 R/W
PSEL [28:24] Port Function Select These bits select the peripheral function R/W
  <fields>
    <field>
      <name>PSEL</name>
      <description>Port Function Select
      These bits select the peripheral function. For individual pin functions, see the MPC table</description>
      <lsb>24</lsb>
      <msb>28</msb>
      <access>read-write</access>
    </field>

以上が修正した P100PFS の全てです。

派生に関する修正

例えば修正前の P101PFSP107PFS は、次のように P100PFS もろとも P000PFS の派生として定義されていました。

<register derivedFrom="P000PFS">
  <dim>8</dim>
  <dimIncrement>0x4</dimIncrement>
  <dimIndex>0-7</dimIndex>
  <name>P10%sPFS</name>
  <description>P10%s Pin Function Control Register</description>
  <addressOffset>0x040</addressOffset>
  <size>32</size>
  <access>read-write</access>
  <resetValue>0x00000000</resetValue>
  <resetMask>0xFFFFFFFD</resetMask>
</register>

P101PFSP107PFSP100PFS の派生とするため、次の様に修正しました。(→ 差分

- <register derivedFrom="P000PFS">
+ <register derivedFrom="P100PFS">
-   <dim>8</dim>
+   <dim>7</dim>
    <dimIncrement>0x4</dimIncrement>
-   <dimIndex>0-7</dimIndex>
+   <dimIndex>1-7</dimIndex>
    <name>P10%sPFS</name>
    <description>P10%s Pin Function Control Register</description>
-   <addressOffset>0x040</addressOffset>
+   <addressOffset>0x044</addressOffset>
    <size>32</size>
    <access>read-write</access>
    <resetValue>0x00000000</resetValue>
    <resetMask>0xFFFFFFFD</resetMask>
  </register>

また P200PFSP400PFS も同様に修正しています。以下は P200PFS の例です。(→ 差分

- <register derivedFrom="P000PFS">
+ <register derivedFrom="P100PFS">
    <name>P200PFS</name>
    <description>P200 Pin Function Control Register</description>
    <addressOffset>0x080</addressOffset>
    <size>32</size>
    <access>read-write</access>
    <resetValue>0x00000000</resetValue>
    <resetMask>0xFFFFFFFD</resetMask>
  </register>

ここまで P100PFS を例に 32ビット幅のフィールド定義だけを示してきましたが、全ての汎用 I/O 端子に PxxxPFS_HA(16ビット)、PxxxPFS_BY(8ビット)が割り当てられており、これら全てについても仕様に基づく検査と修正を行っています。

以上が修正の全容です。

いつの日か、こういう地味な作業を AI に任せられる日が来ると良いですネ。

SVDConv utility による検査

CMSIS-Toolbox には、CMSIS-SVD スキーマ に従い SVD ファイルを検査するコマンド svdconv が含まれています。そこで修正前の R7FA4M1AB.svd をこのコマンドにかけてみましたが、Arduino IDE で見られたようなエラーは検出できませんでした。つまり、文法上は合っていても MCU の機能が正しく定義されているとは限らないと言うことです。

代わりに次のようなエラーが3つ検出されました。

Expression marker found but no <dim> specified: 'P00 Pin Function Control Register'

ここで言う <dim> とは配列などの要素数を示すと思われますが、何をどう修正すれば良いのか分かりません。まぁ、Arduino IDE には影響が無さそうなので今のところは放置です :relieved:

あとがき

最初は UNO R4 と同じ Cortex-M4 アーキテクチャの R7FA6M1AD.svd から P100PSF の定義をまるっとコピーし、「とりあえずエラーが出なくなったので OK!」とはしゃぎ、あとは「エキスパートによる修正を待つ」気でいました。

ところがどっこい、GitHub に上げてしまったところからプルリクのハナシが飛び出し、急遽再チェックしたところ初版の間違いに気づき、また日本語 ユーザーズマニュアル の誤訳に惑わされ、Renesas に真偽を問い合わせたり…と。

結局最後は自分で自分のケツを拭かざるを得なくなり、5000 行弱の該当部分をマニュアル片手に 1 行ずつチェックすると言う、実に地味な作業を強いられる羽目となりました。

でもまぁ、それもこれも UNO R4 ユーザーの皆さんの役に立てると思えばハッピーです! :beer: