modernAVRファミリーの定義済マクロ判別

ひとつのC/C++ソースコードで複数種類のMCU別にバイナリを作り分けたい場合はよくある。そんな時は定義済マクロで場合分けするのだが、ではどんな定義済マクロでどんな分岐ができるのか？というのがこの記事の主題だ。

- この記事は 2023/10時点では tinyAVR-0系統 から AVR_EB系統までに対応する。
- AVR 8bit MCU系統の中でも`<avr/io.h>`の様式が変更され、UPDI対応となった系列をまとめて __modernAVR__ と仮称する。周辺機能の制御様式が大幅に変更されており、AVR命令セットを使っている事実以外の、旧来製品との共通性は薄い。

## まず確認

まずコンパイラがどんな定義済マクロを認識しているのかの調べ方を紹介しておこう。ソースコードなしの単純な基本形はこうだ。

```sh
# 素のgccの定義ずみマクロだけ
echo | gcc -dM -E -
```

これで`#define`行が全て抽出されるので、その標準出力結果を`sort`や`grep`に通すなりしてテキストファイルに保存し、エディタでじっくり眺めることになる。

Arduino IDEを使用している場合は、Windowsでも他のOSでも次のようにすると良い。

1. 設定で「より詳細な情報を表示する」の「コンパイル」にチェックをつけておく
1. 適当なスケッチを開いて「検証」する（Ctrl+R あるいは Command+R キー）
1. ログウィンドウの「スケッチをコンパイルしています...」の次行を選んでコピー
1. 「-c」オプションを探して「-dM -E -x c++」に書き換え
1. 最後の「-o」オプション以降を削る
1. 編集できたコマンド行を、ターミナルやコマンドプロンプトで実行

```sh
# 例えば
/path/to/avr-g++ -dM -E -x c++ -g -Os -std=gnu++17 \
  -fpermissive -fno-exceptions -fno-threadsafe-statics \
  -Wno-error=narrowing -ffunction-sections 
  -fdata-sections -MMD -flto -mrelax \
  -DF_CPU=20000000L -DARDUINO=10819 \
  -DARDUINO_AVR_AVR64EA32 -mmcu=avr64ea32 \
  -I/Users/askn/Library/Arduino15/packages/MultiX-Zinnia/hardware/modernAVR/0.2.7/cores/modernAVR \
  -I/Users/askn/Library/Arduino15/packages/MultiX-Zinnia/hardware/modernAVR/0.2.7/variants/avr_ea32 \
  /var/folders/mz/_z9vt_y13rdf9tt_4qkvv0k40000gn/T/arduino_build_426011/sketch/Blink.ino.cpp


```

## AVRであることの証明

avr-gcc でソースコードをビルドしたなら必ず現れるマクロ定義は以下のようなものだ。これらが存在しないならAVR-LIBC以外のビルド環境だとわかる。特に明記していなければ定義値は`1`だ。

|定義名|存在|
|-|-|
|\_\_AVR|AVR基盤であることの証明
|\_\_AVR_ARCH\_\_|AVRアーキテクチャ番号
|\_\_AVR_AVR64EA32\_\_ など|ビルド行の`-mmcu`に対応した名前
|\_\_AVR_DEVICE_NAME\_\_|`-mmcu=`の指定値（文字列）が入っている
|\_\_AVR_LIBC_VERSION\_\_|AVR-LIBCのバージョン番号。`20000UL`など
|\_\_BUILTIN_AVR_CLI など|使用可能なavr-gccのビルトイン拡張命令
|\_\_AVR_MEGA\_\_|AVR命令セットが MEGA系列互換（AVRe+）
|\_\_AVR_XMEGA\_\_|AVR命令セットが XMEGA系列互換（AVRxm、AVRxt）
|\_AVR_IOXXX_H_|`<avr/io.h>`で実際に選択されたヘッダファイル名文字列が入っている
|\_VECTOR_SIZE|割込テーブルの粒度。フラッシュ容量が 2=8KiB以下、4=16KiB以上
|SIGNATURE_1|後述

- \_\_AVR_DEVICE_NAME\_\_ の持つ文字列はダブルクォートされていないのでそのままでは使えない。プリプロセッサ条件式は文字列比較ができないので、条件判断にも使えない。
- \_AVR_IOXXX_H_ はダブルクォートされた文字列値を持っているので、print に直接書ける。逆アセンブル時の手掛かりとして埋め込んだりするのに使う。

個別のMCUは普通、\_\_AVR_AVR64EA32\_\_ や \_\_AVR_ATmega328P\_\_ といった製品名を含む定義名で振り分けるが「メモリ容量が違うだけの同族」などを纏めたい場合は多数の条件を連ねることになるので面倒だ。

__SIGNATURE_1__ は、modernAVRの場合 `0x92` から `0x98` が存在し、これはフラッシュ容量の 2KiB から 128KiB に対応する。例外的に megaAVR-0系統での 48KiB品種は、64KiB品種と同じ値を示す。

|0x92|0x93|0x94|0x95|0x96|0x97|0x98
|-|-|-|-|-|-|-|
|2KiB|4KiB|8KiB|16KiB|32KiB|64KiB or 48KiB|128KiB

## modernAVRの系統判定

ここからは __modernAVR__ つまりUPDI対応世代以後に特化した話になる。それらより旧世代の`<avr/io.h>`ヘッダファイルは記載情報量に乏しいので、以下で述べているほど簡単に品種を切り分けることは難しい。

まず \_\_AVR_ARCH\_\_ の定義値についてだが、これはフラッシュ容量で異なり、103=64KiB未満、102=64KiB、104=128KiB を示す。100未満なら旧世代AVR（非UPDI世代）と見做してほとんど構わない。

各品種の違いは周辺機能の有無に強く現れるので、それを判断基準にする。パッケージpin数の違いは、`USART[1-5]`の存在や特定の`PORT[x]`の有無から判断できる。

|定義名|存在|
|-|-|
|\_\_AVR_ARCH\_\_|AVRアーキテクチャ番号。102、103、104ならUPDI世代
|RSTCTRL_RSTFR|UPDI世代全品種（modernAVR）に固有。なければ旧世代
|MAPPED_PROGMEM_START|UPDI世代では、megaAVR-0系統だけが`0x4000U`、その他は`0x8000U`
|PROGMEM_SIZE|バイト単位でのフラッシュ容量。`2048U`から`131072U`まで
|NVMCTRL_ADDRH|tinyAVR-0/1/2系統とmegaAVR-0系統固有（NVMCTRLv0系列）
|NVMCTRL_ADDR2|AVR_DA/DB/DD/EA/EB系統固有（NVMCTRLv2/3系列）
|EVSYS_ASYNCCH0_gm|tinyAVR-0/1系統固有
|EVSYS_ASYNCCH2_gm|tinyAVR-1系統固有
|ADC_TIMEBASE_gm|tinyAVR-2系統固有
|EVSYS_GENERATOR_gm|megaAVR-0系統固有
|CLKCTRL_AUTOTUNE_bm|AVR_DA/DB/DD系統固有（RSTCTRL_SWRST_bmに同等）
|RSTCTRL_SWRST_bm|AVR_DA/DB/DD系統固有（RSTCTRL_SWRE_bmの逆）（F_CPU=24MHz系列）
|RSTCTRL_SWRE_bm|tinyAVR/megaAVR世代と、AVR_EA/EB系統固有（RSTCTRL_SWRST_bmの逆）（F_CPU=20MHz/16MHz系列）
|OPAMP_TIMEBASE_gm|AVR_DB系統固有
|ZCD3_ZCD_vect_num|AVR_DD系統固有
|MVIO_MVIO_vect_num|AVR_DB/DD系統固有
|FUSE_UPDIPINCFG_bm|AVR_DD/EA/EB系統固有（HV=8.2V制御系列）
|CLKCTRL_TIMEBASE_gm|AVR_EA/EB系統固有（NVMCTRLv3系列）
|BOOTROW_START|AVR_EB系統固有
|NVMCTRL_EE_vect_num|後述
|NVMCTRL_NVMREADY_vect_num|後述
|ZCD0_ZCD_vect_num|後述

- NVMCTRL_ADDRH は NVMCTRL version 0 採用品種に固有。
- NVMCTRL_ADDR2 は NVMCTRL version 2/3 採用品種に固有。
- NVMCTRL version 1 は欠番。
- NVMCTRL version 3 採用品種は現在 AVR_EA/EB のみなので CLKCTRL_TIMEBASE_gm で判別可能。
- tinyAVR-0/1/2 系統のみ HV=12V制御系列。つまり NVMCTRLv0 かつ megaAVR-0 以外。

## 特定品種のベクタ番号判定

`NVMCTRL_NVMREADY_vect_num`や`ZCD0_ZCD_vect_num`などの数少ない品種にしか定義がないものは、その値を調べれば容易に絞り込むことができる。

|Symbol|Num|Target|
|-|-|-|
|NVMCTRL_EE_vect_num|25|ATtiny202/402/404/406 ATtiny212/214/412/414/416/816/817
||29|tinyAVR-2
||30|megaAVR-0 ATtiny804/806/807/1604/1606/1607 ATtiny1614/1616/1617/3216/3217
||35|AVR_DA/DD
||36|AVR_DB
||-|other
|NVMCTRL_NVMREADY_vect_num|30|AVR_EB
||33|AVR_EA
||-|other
|ZCD0_ZCD_vect_num|26|AVR_DA
||28|AVR_DB
||-|other

## 系統別分岐例

```cpp
// 
// AVR 世代の大まかな分類
//

#if defined(RSTCTRL_RSTFR)
  /* found modernAVR */
#else
  #error This ARCHITECTURE not supported
#endif

//
// modernAVR 世代の大まかな分類
//

#if defined(NVMCTRL_ADDRL)
  /* tinyAVR-0/1/2 and megaAVR-0 */
  /* NVMCTRL version 0 */
#endif

#if defined(NVMCTRL_ADDR2)
  /* AVR_DA/DB/DD/EA/EB */
  /* NVMCTRL version 2/3 */
#endif

#if defined(RSTCTRL_SWRE_bm)
  /* tinyAVR-0/1/2 , megaAVR-0 and AVR_EA/EB */
  /* F_CPU = 20MHz/16MHz */
#endif

#if defined(RSTCTRL_SWRST_bm)
  /* AVR_DA/DB/DD */
  /* F_CPU = 24MHz */
#endif

//
// modernAVR ファミリー別
//

#if defined(EVSYS_ASYNCCH0_gm) && !defined(EVSYS_ASYNCCH2_gm)
  /* tinyAVR-0 only */
#endif

#if defined(EVSYS_ASYNCCH2_gm)
  /* tinyAVR-1 only */
#endif

#if defined(ADC_TIMEBASE_gm)
  /* tinyAVR-2 only */
#endif

#if defined(EVSYS_GENERATOR_gm)
  /* megaAVR-0 only */
#endif

#if (SPI1_INT_vect_num == 36)
  /* AVR_DA only */
#endif

#if defined(OPAMP_TIMEBASE_gm)
  /* AVR_DB only */
#endif

#if defined(ZCD3_ZCD_vect_num)
  /* AVR_DD only */
#endif

#if (NVMCTRL_NVMREADY_vect_num == 33)
  /* AVR_EA only */
#endif

#if defined(BOOTROW_START)
  /* AVR_EB only */
#endif
```

- EVSYS_STROBE を調べると、megaAVR-0系統だけに絞り込める。
- tinyAVR-0系統には独自の機能がないため、判別に複数条件が必要。
- GPIOの本数を直接示すマクロ定義はないので、パッケージpin数規模を見分けるには、USART割込の有無を調べると容易。

## 関連リンク

- [GCC Compilers for AVR® and Arm®-Based MCUs and MPUs](https://www.microchip.com/en-us/tools-resources/develop/microchip-studio/gcc-compilers/)
- [Microchip Packs Repository](https://packs.download.microchip.com)
- [AVR.JP(日本語訳)](https://avr.jp)
- [AVR-LIBC(日本語訳)](https://cega.jp/avr-libc-jp/)

## Copyright and Contact

Twitter(X): [@askn37](https://twitter.com/askn37) \
BlueSky Social: [@multix.jp](https://bsky.app/profile/multix.jp) \
GitHub: [https://github.com/askn37/](https://github.com/askn37/) \
Product: [https://askn37.github.io/](https://askn37.github.io/)

Copyright (c) askn (K.Sato) multix.jp \
Released under the MIT license \
[https://opensource.org/licenses/mit-license.php](https://opensource.org/licenses/mit-license.php) \
[https://www.oshwa.org/](https://www.oshwa.org/)