RXマイコン、ソフト遅延を改修

ソフト遅延ループを再度、再考

「attribute ((optimize(1)))」で、上手くいったと思ったら、ループの回数が8回以下だと、インライン展開されるようだ・・

色々、あーでもない、こーでもないと、色々やったが、どうも駄目で、最後はインラインアセンブラで書き直す事とした。
しかし、gcc におけるインラインアセンブラの書式がイマイチ判っていないので、google 先生に尋ねながら何とか、物になる実装を見つけた。
※以前、最適化でループが無くならないように「asm("nop");」を入れていたが、アセンブラならその必要は無いので1マシンサイクル短くなる。
※ラベルの付け方がよくわからない・・、なので、他と「かぶらない」ように冗長な名称にしてある。

        //-----------------------------------------------------------------//
        /*!
            @brief  マイクロ秒単位の待ち @n
                    あまり正確では無く、速度に関連する設定に依存します。 @n
                    ※キャッシュ有効、最高速度での物理計測によるパラメータ
            @param[in]  us  待ち時間(マイクロ秒)
        */
        //-----------------------------------------------------------------//
        static __attribute__((optimize(1))) void micro_second(uint32_t us) noexcept
        {
            asm("cmp #0, %[us]" : : [us] "r" (us) );
            asm("beq.b micro_second_loop0\n\t"
                "micro_second_loop1:");
                uint32_t lpn = device::clock_profile::DELAY_MS;
                asm("micro_second_loop2:\n\t"
                    "sub #1, %[lpn]" : : [lpn] "r" (lpn));
                asm("bne.b micro_second_loop2"); 
            asm("sub #1, %[us]" : : [us] "r" (us));
            asm("bne.b micro_second_loop1\n\t"
                "micro_second_loop0:");
//          while(us > 0) {
//              for(uint32_t n = 0; n < device::clock_profile::DELAY_MS; ++n) {
//                  asm("nop");
//              }
//              if(device::clock_profile::DELAY_T1) { asm("nop"); }
//              --us;
//          }
        }

アセンブラコード(RX24T 80MHz):

fffc01a0 <__ZN5utils5delay12micro_secondEm>:
fffc01a0:   61 01                           cmp #0, r1
fffc01a2:   20 0d                           beq.b   fffc01af <__romdatastart+0xfffffefb>

fffc01a4 <micro_second_loop1>:
fffc01a4:   75 45 14                        mov.l   #20, r5

fffc01a7 <micro_second_loop2>:
fffc01a7:   60 15                           sub #1, r5
fffc01a9:   21 fe                           bne.b   fffc01a7 <__romdatastart+0xfffffef3>
fffc01ab:   60 11                           sub #1, r1
fffc01ad:   21 f7                           bne.b   fffc01a4 <__romdatastart+0xfffffef0>

fffc01af <micro_second_loop0>:
fffc01af:   02                              rts

rxprog を更新

ついでに、rxprog を更新して、RX231 をサポートした。
このプロトコルは、RX24T とほぼ同じもので、プログラム/イレースへ推移する為のコマンドが異なるだけだった。

ほぼ、同じコードの一部を修正して、コードをコピペするのは、悪手でしかないが、見直すには時間的余裕が必要なので、後回しとする。

RX24T もそうなのだが、ID コードのプロテクトが無い場合、プログラム/イレースへ推移すると、自動で、フラッシュをイレースする仕組みがあるようだ・・
なので、コードフラッシュのプログラムでは、「イレース動作」をしないで、いきなり書きに行くようにしている。


まとめ

「時間待ち」は、ソフトで行わなくても、RXマイコンなら、必ずある「TMR タイマー」とかを使って、やれば、正確で、今回のような問題は起きないとも思のだが・・・

TMR を使った時間待ちは、今後の課題としたい・・

又、動作クロックの違いで、プログラムの動作条件(高速動作の場合、フラッシュメモリの速度が足枷となりオペコードの読出しに遅延を設けている)が変わり、クロック数ファクターが異なるので、それに対応したい・・
constexpr を使えば、コンパイル時に計算して、定数を計算可能と思う。

RX231 動作確認と部分的最適化の抑制


RX231 の動作確認

RX231 は、RX26T と同じ時期に購入してあったけど、中々作業が出来ないでいた。
RX26T も、落ち着き、余裕が出来たので実験基板を配線した。
今回、Flash:256K、RAM:32K のデバイスで、64 ピンパッケージを購入した、RX200 系は比較的安価な製品が多い。

いつものように、変換基板を直にユニバーサル基板へハンダ付けしている。
64 ピンだと、実験基板を作成するのは楽(電源ピンが少ない)なのだが、色々な機能をアサインする場合にピンが足りなくなる・・
RX231 は動作周波数が低く、それ程興味が無かったのもあって、なるべく安いデバイスを購入。
でも、100ピン版を購入しておくべきだったかもしれない・・・
Flash も 256K は少し物足りない・・・


RX231 の特徴

  • RXv2 コア(最大 54MHz 動作)
  • USB インターフェース
  • SDHI インターフェース(SD カードアクセス)
  • SSI インターフェース(シリアルサウンドインタフェース)
  • 静電容量式タッチセンサ
  • CAN インターフェース(RSCAN なので、専用ドライバーを実装する必要があり、現在開発中)

など、意外と機能が豊富で、応用範囲が広い。

※USB のクロック用に専用 PLL 回路があり、USB を使う場合でも、CPU のクロック周波数に影響しない。
※USB を使う場合、専用 PLL の制限により、外部発振周波数は、6MHz、12MHz のいずれか。(48MHz を作る必要がある為)


gcc 関数の最適化を制御

一般に、gcc の最適化 -O3 では、インライン展開や、直接埋め込みなど、色々な手法を駆使して、最大限高速に動作するようなバイナリーを生成する。
ただ、それだと、意図と異なった動作になる場合がある。

この C++ フレームワークでは、ソフトウェアーによる時間待ちが、その影響を受ける。

以下のコードは、ソフトウェアーによる時間待ち関数になる。

        //-----------------------------------------------------------------//
        /*!
            @brief  マイクロ秒単位の待ち @n
                    あまり正確では無く、速度に関連する設定に依存します。 @n
                    ※キャッシュ有効、最高速度での物理計測によるパラメータ
            @param[in]  us  待ち時間(マイクロ秒)
        */
        //-----------------------------------------------------------------//
        static __attribute__((optimize(1))) void micro_second(uint32_t us) noexcept
        {
            while(us > 0) {
                for(uint32_t n = 0; n < device::clock_profile::DELAY_MS; ++n) {
                    asm("nop");
                }
                --us;
            }
            if(device::clock_profile::DELAY_T1) { asm("nop"); }
        }

この時間待ちは、大体1マイクロ秒になるように、ループ数を調整している。
CPU の速度が 80MHz とかだと問題無いが、54MHz だと、ループの回数が少なく、for ループが展開されて、NOP 命令のみとなり高速に動作してしまう。
for ループを最適化で展開させないようにする為「attribute((optimize(1)))」を宣言してある。
※この方法を見つける為に、色々試行錯誤した。

「((optimize(1)))」が無い場合のアセンブルリスト(for ループが展開されている):

fffc0278:   fb 5a e8 03                     mov.l   #0x3e8, r5
fffc027c:   03                              nop
fffc027d:   03                              nop
fffc027e:   03                              nop
fffc027f:   03                              nop
fffc0280:   03                              nop
fffc0281:   03                              nop
fffc0282:   03                              nop
fffc0283:   03                              nop
fffc0284:   03                              nop
fffc0285:   03                              nop
fffc0286:   03                              nop
fffc0287:   03                              nop
fffc0288:   60 15                           sub #1, r5
fffc028a:   21 f2                           bne.b   fffc027c <__romdatastart+0xffffffc4>
fffc028c:   03                              nop
fffc028d:   60 14                           sub #1, r4
fffc028f:   21 e9                           bne.b   fffc0278 <__romdatastart+0xffffffc0>

「((optimize(1)))」がある場合:

fffc01a0:   61 01                           cmp #0, r1
fffc01a2:   20 0d                           beq.b   fffc01af <__romdatastart+0xffffff07>
fffc01a4:   66 c5                           mov.l   #12, r5
fffc01a6:   03                              nop
fffc01a7:   60 15                           sub #1, r5
fffc01a9:   21 fd                           bne.b   fffc01a6 <__romdatastart+0xfffffefe>
fffc01ab:   60 11                           sub #1, r1
fffc01ad:   21 f7                           bne.b   fffc01a4 <__romdatastart+0xfffffefc>
fffc01af:   03                              nop

※「noinline」の場合、関数呼び出しを消して、インライン展開するのを除外する、関数内のループには通常の最適化が行われるので、適当ではない。


RX231 のオーバークロック耐性

とりあえず、72MHz は動作するようだ・・(この位が安全圏か!?)

2024-01-28 01:18:18 Sunday
RX231 には、「動作電力コントロールレジスタ(OPCCR)」があって、初期状態では「中速モード」になっている。
これを「高速モード」に切り替える事で、高いクロックでも安定して動作するようだ。
90MHz で駆動したが、ちゃんと動作するようだ。

PLL USB専用PLL HOCO LOCO IWDTCLK メインクロック発振器 サブクロック発振器
高速動作モード ○(注 1) ○(注 1)
中速動作モード ○(注 1) ○(注 1)
低速動作モード × × × × ×

RX231/clock_profile.hpp の定義:

#if 0
        static constexpr uint32_t   PLL_BASE    = 54'000'000;     ///< PLL ベースクロック(最大 54MHz)

        static constexpr uint32_t   ICLK        = 54'000'000;     ///< ICLK 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKA       = 54'000'000;     ///< PCLKB 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKB       = 27'000'000;     ///< PCLKB 周波数(最大 32MHz)
        static constexpr uint32_t   PCLKD       = 54'000'000;     ///< PCLKD 周波数(最大 54MHz)
        static constexpr uint32_t   FCLK        = 27'000'000;     ///< FCLK 周波数(最大 1 ~ 32MHz)
        static constexpr uint32_t   BCLK        = 27'000'000;     ///< BCLK 周波数(最大 32MHz)
#else
//      static constexpr uint32_t   PLL_BASE    = 72'000'000;     ///< PLL ベースクロック(最大 54MHz)

//      static constexpr uint32_t   ICLK        = 72'000'000;     ///< ICLK 周波数(最大 54MHz)
//      static constexpr uint32_t   PCLKA       = 72'000'000;     ///< PCLKB 周波数(最大 54MHz)
//      static constexpr uint32_t   PCLKB       = 36'000'000;     ///< PCLKB 周波数(最大 32MHz)
//      static constexpr uint32_t   PCLKD       = 72'000'000;     ///< PCLKD 周波数(最大 54MHz)
//      static constexpr uint32_t   FCLK        = 36'000'000;     ///< FCLK 周波数(最大 1 ~ 32MHz)
//      static constexpr uint32_t   BCLK        = 36'000'000;     ///< BCLK 周波数(最大 32MHz)

        static constexpr uint32_t   PLL_BASE    = 90'000'000;     ///< PLL ベースクロック(最大 54MHz)

        static constexpr uint32_t   ICLK        = 90'000'000;     ///< ICLK 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKA       = 90'000'000;     ///< PCLKB 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKB       = 45'000'000;     ///< PCLKB 周波数(最大 32MHz)
        static constexpr uint32_t   PCLKD       = 90'000'000;     ///< PCLKD 周波数(最大 54MHz)
        static constexpr uint32_t   FCLK        = 45'000'000;     ///< FCLK 周波数(最大 1 ~ 32MHz)
        static constexpr uint32_t   BCLK        = 45'000'000;     ///< BCLK 周波数(最大 32MHz)
#endif

まとめ

現状、RX231 向けに、以下のプロジェクトを動作確認して、コミットしてある。

  • FIRST_sample
  • SCI_sample
  • FLASH_sample
  • FreeRTOS
  • RAYTRACER_sample
  • PSG_sample

※RAYTRACER_sample では、RXv2 コアの優位性で、54MHz 動作の RX231 の方が、96MHz 動作の RX62N より高速だった。

マイコン core FPU fsqrt 命令 周波数 [MHz] 描画方式 時間 [ms]
RX231 RXv2 O O 54 8 bits, port-bus 1736
RX62N RXv1 O X 96 8 bits, port-bus 1860