RXマイコン用 Renesas GNU-RX gcc-8.3.0 の利用

## GNU Tools と言う WEB ページ

お恥ずかしながら、最近見つけたのですが、[Open Source Tools for Renesas](https://llvm-gcc-renesas.com/ja/) と言う WEB ページがある事に気がつきました。

以前、ルネサスは、KPIT と言う企業と提携しており、そこが GNU 関係の開発環境をサポートしていました。
それが、無くなり、しばらくは、gcc-4.8.3 ベースのコンパイラを保守していたのは知っていましたが、今更、そんな古い gcc を使う気にならず、スルーしていました。

正規の gcc を取得して、それを自分でビルドして、アプリケーションをコンパイルして、十分な性能が出ていたので、あまり追及していませんでした。

ところが、RX72N の登場で、状況が一変します。

- RXv3 コアの採用
- 倍精度浮動小数点演算命令
- TFU（三角関数演算器）の搭載

これらの機能は、正規の gcc ツリーにある、RX マイコン用コードだけではサポートされていません。
※これは、ルネサス社が、gcc のソースツリーに機能改善したコードをマージしない事が原因なのですが、その真意は判りません。

そんな時、上記 WEB ページで「登録」すれば、gcc-8.3.0 ベースのツールチェインがダウンロード出来る事が判り、試してみました。
※gcc-8.3.0 は C++17 に対応しており、自分のフレームワークをコンパイル可能です。

このページは、「CyberTHOR Studios Limited」が保守、管理していて、ルネサス製マイコン用 GNU ツールチェインをサポートするのが目的のようです。

## ツールのダウンロードとインストール

「登録」が面倒なものの、ツールをダウンロードしてインストール出来ました。

ツールチェインは、Windows（多分 MinGW 環境による）版と Linux 版があり、ソースコードもダウンロードできます。
OS-X 環境の場合、Linux 版がある事から、ビルド出来るものと思います。

インストールには、登録メールアドレスに起因した「アクティベーションコード」が必要で、登録の際、メールで送られます。
※「登録」にはある程度の個人情報の提出が必要で（これは KPIT の場合もそうだった）、それが何故必要なのか判りません。

自分の場合、MSYS2 環境の CUI 環境で動かす事が「マスト」なので、以下のように、「.bash_profile」にパスを追加しました。

```
# rx-elf path
# PATH=$PATH:/usr/local/rx-elf/bin
PATH=$PATH:/C/'Program Files (x86)'/'GCC for Renesas RX 8.3.0.202002-GNURX-ELF'/rx-elf/rx-elf/bin
```

これで、MSYS2 のコンソールからでも、このコンパイラを利用可能です。

このツールチェインは以下の GNU ツールをアーカイブした物です。

- binutils_rx_2.24_2020q2
- gcc_rx_8.3.0_2020q2
- newlib_rx_3.1.0_2020q2
- gdb_rx_7.8.2_2020q2

このツール群は、ルネサスの IDE 環境（e2studio）などで、エミュレータによりデバッグ環境が充実するものと思います。

rx-elf-gcc の --target-help を実行すると、以下の表示が出ます。

```
% rx-elf-gcc --target-help
The following options are target specific:
  -fpu                        Enable the use of RX FPU instructions.  This is
                              the default.
  -m32bit-doubles             Stores doubles in 32 bits.  This is the default.
  -m64bit-doubles             Store doubles in 64 bits.
  -mallow-string-insns        Enables or disables the use of the SMOVF, SMOVB,
                              SMOVU, SUNTIL, SWHILE and RMPA instructions.
                              Enabled by default.
  -mas100-syntax              Generate assembler output that is compatible with
                              the Renesas AS100 assembler.  This may restrict
                              some of the compiler's capabilities.  The default
                              is to generate GAS compatible syntax.
  -mbig-endian-data           Data is stored in big-endian format.
  -mcpu=                      Specify the target RX cpu type.
  -mdfpu                      Enable the use of RX DFPU instructions.
  -mgcc-abi                   Enable the use of the old, broken, ABI where all
                              stacked function arguments are aligned to 32-bits.
  -mint-register=             Specifies the number of registers to reserve for
                              interrupt handlers.
  -misa=                      Specify RX ISA version.
  -mjsr                       Always use JSR, never BSR, for calls.
  -mlarge-function-growth=    Permited value of the limit growth of large
                              function (in percent).
  -mlittle-endian-data        Data is stored in little-endian format.
                              (Default).
  -mlra                       Enable the use of the LRA register allocator.
  -mmax-constant-size=        Maximum size in bytes of constant values allowed
                              as operands.
  -mno-balign                 Do not use .balign
  -mpid                       Enables Position-Independent-Data (PID) mode.
  -mrelax                     Enable linker relaxation.
  -mrx-abi                    Enable the use the standard RX ABI where all
                              stacked function arguments are naturally aligned.
                              This is the default.
  -mrxpeephole                Coremark improvement.
  -mrxv2-fsqrt                Enable to use of FSQRT hardware instruction for
                              RXv2 instruction set
  -msave-acc-in-interrupts    Specifies whether interrupt functions should save
                              and restore the accumulator register.
  -msim                       Use the simulator runtime.
  -msmall-data-limit=         Maximum size of global and static variables which
                              can be placed into the small data area.
  -mtfu=                      Enable the use of RX TFU instructions.
  -mwarn-multiple-fast-interrupts Warn when multiple, different, fast interrupt
                              handlers are in the compilation unit.
  -nofpu                      Disable the use of RX FPU instructions.

Assembler options
=================

Use "-Wa,OPTION" to pass "OPTION" to the assembler.

 RX specific command line options:
  --mbig-endian-data
  --mlittle-endian-data [default]
  --m32bit-doubles [default]
  --m64bit-doubles
  --muse-conventional-section-names
  --muse-renesas-section-names [default]
  --msmall-data-limit
  --mrelax
  --mpid
  --mint-register=<value>
  --mcpu=<rx100|rx200|rx230|rx600|rx610|rx64m|rx66T|rx71m|rx72T>
  --misa=<v1|v2|v3>
  --mno-allow-string-insns  --mgcc-abi
  --mrx-abi [default]

Linker options
==============

Use "-Wl,OPTION" to pass "OPTION" to the linker.

elf32rx:
  --build-id[=STYLE]          Generate build ID note
  -z common-page-size=SIZE    Set common page size to SIZE
  -z defs                     Report unresolved symbols in object files.
  -z execstack                Mark executable as requiring executable stack
  -z max-page-size=SIZE       Set maximum page size to SIZE
  -z muldefs                  Allow multiple definitions
  -z noexecstack              Mark executable as not requiring executable stack
  --no-flag-mismatch-warnings Don't warn about objects with incompatible
                                endian or dsp settings
  --flag-mismatch-warnings    Warn about objects with incompatible
                                endian, dsp or ABI settings
  --ignore-lma                Ignore segment LMAs [default]
                                (for Renesas Tools compatibility)
  --no-ignore-lma             Don't ignore segment LMAs
```

ここで、「-mdfpu」、「-mtfu=」の RX72N にとって重要な二つのオプションがあります。

## Renesas GNU-RX 8.3.0 のオプションの違い

Renesas 版を使う場合に、厄介なのは、正規ツリーの gcc とオプションが異なる点です。
※正規の gcc では、大雑把に言うと、RXv1 コアしかサポートされていないと言えますが、それでも、通常のアプリケーションでも、そんなに性能低下する事なく、アプリケーションを動かせます。
また、binutils は、最新版を使う事で、RXv3 の命令をアセンブル可能なので、C++/C ソースコードにアセンブラ命令を直で書く事が出来るので、FreeRTOS などでも何とかなっていました。

とりあえず、Makefile 中の、RX マイコンのオプションを修正して、 自分のプロジェクト全体を、リリースビルド、デバッグビルドでコンパイルしてみました。

-mcpu= オプションには、RX72N は無いのですが、以下のオプションで、実質、RX72N 用となります。

「-mcpu=\<rx100|rx200|rx230|rx600|rx610|rx64m|rx66T|rx71m|rx72T\>」

```
    -misa=v3
```

コンパイルは問題無く、全て通ったので、幾つかのアプリケーションを動かしてみました。

これも当然ながら、問題無く動作しました。

ここまで来ると、Renesas GNU-RX を使う事に何らハードルは無いものと思えます。

Makefile のオプションを GNU-RX 用に修正すると、正規の gcc の場合にコンパイルが出来ないので、Makefile の制御文を考えてみました。

```
# rx-elf-gcc compiler version check
TARGET_ISA_TEXT := $(shell rx-elf-gcc --target-help | grep ISA)

# AS_DEFS       =   --defsym NOT_USER=1
ifeq ($(TARGET_ISA_TEXT), )
# gcc-7.5.0 current gcc source build
CC_DEFS     =   -mcpu=rx600 -Wa,-mcpu=rxv2
CP_DEFS     =   -mcpu=rx600
else # Renesas GNU-RX gcc
CC_DEFS     =   -misa=v3
# CP_DEFS       =   -misa=v3 -mdfpu -mtfu=intrinsic,mathlib
CP_DEFS     =   -misa=v3 -mtfu=intrinsic,mathlib
# CP_DEFS       =   -misa=v3 -mdfpu
endif
```

これは、「--target-help」を実行して、その中に「ISA」の文字列があれば、Renesas 版と判断するものです。

## Renesas GNU-RX 8.3.0 の最適化性能

いつもの RAYTRACE_sample を動かすと、少しだけ「速い」事に気がつきました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/e4d6caff-0df8-d1bb-6a09-8cc306676e0e.jpeg" width="50%">

※正規ツリーの gcc-7.5.0 では、「361ms」

そこで、簡単なプログラムをコンパイルして、アセンブルリストを見てみました。

以下は、LED 点滅のメインループです。

```
    typedef device::PORT<device::PORT0, device::bitpos::B1> LED;

    while(1) {
        utils::delay::milli_second(250);
        LED::P = 0;
        utils::delay::milli_second(250);
        LED::P = 1;
    }
```

ここで、LED のポートに、「１」、「０」を書く部分に注目しました。

gcc-7.5.0 の場合、以下のようなコードになります。

```
ffc0029c:       cc 3e                           mov.b   [r3], r14
ffc0029e:       75 42 fa                        mov.l   #250, r2
ffc002a1:       75 2e fe                        and     #-2, r14
ffc002a4:       c3 3e                           mov.b   r14, [r3]

ffc002bc:       cc 3e                           mov.b   [r3], r14
ffc002be:       65 1e                           or      #1, r14
ffc002c0:       c3 3e                           mov.b   r14, [r3]
```

論理演算が使用されていますが、割と普通です。

GNU-RX 8.3.0 では

```
ffc00278:       f0 38                           bclr    #0, [r3].b

ffc00294:       f0 30                           bset    #0, [r3].b
```

ビット操作命令になっています。
※これは、gcc-4.8.x のツールチェインで、既に実装済みでした。

多分、上記以外にも、コアの違いによる専用命令など、割と細かい部分で、最適化が行われて、「高速化」したものと思います。

## 倍精度浮動小数点命令

次に、「-mdfpu」を調べてみました。

```
    -misa=v3 -mdfpu
```

「-mdfpu」のオプションは、コアが RXv3 である必要があり、「-misa=v3」も必要です。

以下の簡単なプログラム

```
    double a = 0.0;
    while(1) {
        utils::delay::milli_second(250);
        LED::P = 0;
        utils::delay::milli_second(250);
        LED::P = 1;
        a += 0.1;
        utils::format("%5.4f\n") % static_cast<float>(a);
    }
```

で、アセンブラ命令を見てみると・・・

```
ffc003d2:       fc c9 08 12 10                  dmov.D 72[r0], dr1
ffc003d7:       f9 03 00 9a 99 99 99            dmov.L #0x9999999a, drl0
ffc003de:       f9 03 02 99 99 b9 3f            dmov.L #0x3fb99999, drh0
ffc003e5:       76 90 00 11                     dadd  dr1, dr0, dr1
ffc003e9:       fc 79 08 12 10                  dmov.D dr1, 72[r0]
```

上記のように、「倍精度浮動小数点命令」が使われています。
※ 64 ビットの IEEE-754 で定数 0.1 の表現は、0x3fb9'9999'9999'999a となります。
※ 64 ビットの定数をレジスターにロードするコストは大きいようです・・・

## TFU（三角関数演算器）の利用

RX72N には、TFU（三角関数演算器）が内蔵されています。
ですが、仕様は公開されていません、コンパイラのサポートが必要です。

また、GNU-RX で TFU を有効にする場合の解説を見つけられなかったので、GNU-RX のソースコードを取得して、その部分を少し読んでみました。

gcc のソースコード「gcc/config/rx/rx.opt」を見ると・・・

```
EnumValue
Enum(rx_tfu_types) String(intrinsic) Value(RX_INTRINSIC)

EnumValue
Enum(rx_tfu_types) String(intrinsic,mathlib) Value(RX_MATHLIB)
```

とあり、「-mtfu=」の定数として、

```
    -mtfu=intrinsic

    -mtfu=intrinsic,mathlib
```
のどちらかを設定すれば良さそうだと判ります。

「gcc/config/rx/rx.c」で、

```
    if (TARGET_TFU)
    {
      ADD_RX_TFU_BUILTIN (INIT, "__init_tfu", void);
      ADD_RX_BUILTIN3 (SINCOSF, "sincosf", void, float, float_ptr, float_ptr);
      ADD_RX_BUILTIN4 (ATAN2HYPOTF, "atan2hypotf", void, float, float, float_ptr, float_ptr);
      if (rx_tfu_type == RX_MATHLIB)
      {
        ADD_RX_BUILTIN1 (SINF, "sinf", float, float);
        ADD_RX_BUILTIN1 (COSF, "cosf", float, float);
        ADD_RX_BUILTIN2 (ATAN2F, "atan2f", float, float, float);
        ADD_RX_BUILTIN2 (HYPOTF, "hypotf", float, float, float);
      }
    }
```

となっており、上記 API が使える事が判ります。

色々調べると、この API はビルトイン関数として登録されるようです。
関数のプロトタイプは、以下のようになっています。

```
    // -mtfu=intrinsic
    void __init_tfu(void);
    void __builtin_rx_sincosf(float, float*, float*);
    void __builtin_rx_atan2hypotf(float, float, float*, float*);
    // -mtfu=intrinsic,mathlib
    float __builtin_rx_sinf(float);
    float __builtin_rx_cosf(float);
    float __builtin_rx_atan2f(float, float);
    float __builtin_rx_hypotf(float, float);
```

これら API の仕様は、C の数学ライブラリに準拠するようです。
このプロトタイプ宣言は、「-mtfu=」を設定する事で内部的に「有効」になります。

これら、ビルトイン関数を利用する場合、多分、事前に初期化関数を呼んでおく必要があるようです。
※ビルトイン関数が有効になると、内蔵変数「__TFU」も有効になります。

```
#if definrd(__TFU)
    __init_tfu();
#endif
```

後は、普通に関数を呼べば良いものと思います。

```
    float a = vtx::get_pi<float>() * 0.25f;
    float si, co;
    __builtin_rx_sincosf(a, &si, &co);
    utils::format("%8.7f: %8.7f, %8.7f\n") % a % si % co;
    a = vtx::get_pi<float>() * 1.75f;
    si = __builtin_rx_sinf(a);
    co = __builtin_rx_cosf(a);
    utils::format("%8.7f: %8.7f, %8.7f\n") % a % si % co;
```

```
0.7853982: 0.7071068, 0.7071067
5.4977875: -0.7071068, 0.7071067
```

## 倍精度浮動小数点演算、TFU のパフォーマンス

ルネサスの HP に以下の資料を見つけました。

[RXv3 CPU搭載RXファミリ 数値計算用ライブラリ関数ベンチマーク](https://www.renesas.com/jp/ja/document/apn/rx-family-rxv3-cpu-mathematics-library-function-benchmark-data)

## むすび

これで、最新の RX72N を使い、GNU-RX gcc-8.3.0 で、十分な性能を出す土台が揃ったと言えます。

今後、他のオプションについても、色々研究していこうと思います。

それにしても、これら gcc への追加機能を、正規の gcc ソースツリーにマージしないのは何故なのか疑問は残ります・・