RXv3コアのレジスタ一括退避機能の使い方(Register Bank Save Function Usage)を調べてみるスレッド

こんにちは。NoMaYです。

RXv3コア搭載の120MH動作のRXマイコンも、RX66T以降、RX671、RX66N、RX660と品種が増えてきましたが、RXv3コアのセールスポイントの1つであるレジスタ一括退避機能の使い方が今ひとつピンと来ません。そこで、いつものように、ちょっと好奇心からスレッドを立ててみました。(注: RX66Tは、160MHz動作、レジスタ一括退避機能未搭載、です。) いつものように、ぼちぼちと続きます。

ホワイトペーパー
卓越したMCU性能と電力効率を実現するRXv3コア
2019年10月
www.renesas.com/jp/ja/document/whp/introducing-rxv3-core-superior-performance-excellent-power-efficiency#page=6

割り込み応答時間の改善

モータ制御システムなどは、高速な割り込み処理によるリアルタイム性能が必要となってきます。

RXv3コアには、割り込み処理時にレジスタを高速退避/復帰するために、オプション機能として、レジスタ退避バンクと呼ばれる専用メモリを実装しています。図6に示すように、レジスタ退避バンクを使用することで割り込み応答時間を短縮でき、割り込み処理全体の時間を短縮することができます。 割り込み処理ルーチンの中で、SAVE命令を使用すると汎用レジスタとアキュムレータを1クロックで専用メモリに保存できます。RSTR命令は、保存されたレジスタを3~6cycleで復元します。レジスタ退避バンクは専用メモリを複数面持っており、多重割り込みにも対応することが可能です。

図6.割り込み応答時間の改善

レジスタ退避バンクは、割り込みハンドラだけでなく、RTOSコンテキスト切り替えにも使用できます。 RTOSコンテキスト切り替え時間は、レジスタバンク保存機能により最大20%高速化します。


Parents
  • こんにちは。NoMaYです。

    割り込み関数でレジスタ一括退避機能を使用する場合の各コンパイラの構文は以下の通りです。また、割り込み関数先頭で多重割り込みを許可する場合の構文と、割り込み関数の初めと終わりでアキュムレータの値を退避/復帰させるコードを生成させるコンパイラコマンドラインオプションも書いておきます。

    レジスタ一括退避機能を使用する場合:

    CC-RX : #pragma interrupt <関数名>(bank=バンク番号[,<他の割り込み仕様>])
    ICCRX : #pragma bank=バンク番号
    GNURX : 未サポート

     
    割り込み関数先頭で多重割り込みを許可する場合:

    CC-RX : #pragma interrupt <関数名>(enable[,<他の割り込み仕様>])
    ICCRX : __interrupt __nested void <関数名>(void);
    GNURX : 未サポート

     
    割り込み関数の初めと終わりでアキュムレータの値を退避/復帰させる場合:

    CC-RX : -save_acc (#pragma interruptでも指定可能)
    ICCRX : --save_acc
    GNURX : -msave-acc-in-interrupts

     
    以下、ドキュメントの画面コピーです。

    CC-RX

    途中省略



    ICCRX




    GNURX
    gcc.gnu.org/onlinedocs/gcc/RX-Options.html


    (参) gcc.gnu.org/onlinedocs/gcc/RX-Function-Attributes.html

    (参2) コマンドラインヘルプによるとオンラインドキュメントには未反映のものも残っているようです


     

  • NoMay さんこんにちは。

    色々調査してくれて助かります。

    なるほど、最適化した場合も、危険なコードが出る場合があるようですね。

    ※自分の環境で、ある程度複雑な割り込み関数で、実験してみましたが、それは大丈夫でした。

    自分の最適化は「-O3」です。

    ※デバッグビルド(-O0)は撃沈です・・・

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  TPU 制御クラス
            @param[in]  TPU     TPU チャネル・クラス
            @param[in]  TASK    タイマー動作ファンクタ・クラス
            @param[in]  ORDER   ポート・マップ候補型
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        template <class TPU, class TASK = utils::null_task, port_map_order::ORDER ORDER = port_map_order::ORDER::FIRST>
        class tpu_io : public tpu_io_base {

            TYPE        type_;
            uint8_t     level_;
            uint8_t     shift_;
            uint32_t    rate_;

            ICU::VECTOR intr_vec_;

            static TASK task_;

    //      static INTERRUPT_FUNC void tpu_task_() noexcept {
            static void tpu_task_() noexcept {
                asm("save #0");
                task_();
                asm("rstr #0");
                asm("rte");
            }

    呼び出し先コード(github にある):

    DSOS_sample/capture.hpp

    アセンブラコード:

    ffc080f0 <__ZN6device6tpu_ioINS_7tpu_x_tILm557328ELNS_10peripheralE27ELNS_5icu_tIvE11VECTOR_SELBE15ELS5_16ELS5_17ELS5_18ELS5_19ELS5_0EEEN4dsos7captureILm16384EE8cap_taskELNS_14port_map_order5ORDERE1EE9tpu_task_Ev>:
    ffc080f0: fd 76 e0 00 save #0
    ffc080f4: fb 5e 2e 90 08 mov.l #0x8902e, r5

    ...

    ffc0814b: fd 76 f0 00 rstr #0
    ffc0814f: 7f 95 rte
    ffc08151: 02 rts

    通常の割り込み関数の場合:

    ffc080f0 <__ZN6device6tpu_ioINS_7tpu_x_tILm557328ELNS_10peripheralE27ELNS_5icu_tIvE11VECTOR_SELBE15ELS5_16ELS5_17ELS5_18ELS5_19ELS5_0EEEN4dsos7captureILm16384EE8cap_taskELNS_14port_map_order5ORDERE1EE9tpu_task_Ev>:
    ffc080f0: 6e ef pushm r14-r15
    ffc080f2: 6e 15 pushm r1-r5
    ffc080f4: fb 5e 2e 90 08 mov.l #0x8902e, r5

    ffc0814b:    6f 15                             popm    r1-r5
    ffc0814d:    6f ef                             popm    r14-r15
    ffc0814f:    7f 95                             rte


    自分はC++17をサポートしていないコンパイラでは自分フレームワークをコンパイル出来ないので、戦力外です。

    C++17 をサポートしているICCRXは選択枠としてはアリですが、コストが見合わないので、結局、GNU-RXがサポートするまで待つしかありません。

    どうしても使いたい場合は、アセンブラソースを sed などで無理やり編集(又は、駄目なコードを吐いたらエラーを出出すなど)するなどの強引な方法も考えられますが・・


    また、GNU-RX(C++) を使う理由として:

    (1) boost が使える

    (2) 最適化が C より深くより高速に動く(経験的な感触)

    (3) 記憶割り当てを使っても、メモリーリークをまずしないように書ける

    (4) より構造的に書けて、些細なミスを防げる

    (5) コードの再利用性

    (6)ヘッダーのみで全て完結するので、ソースをリンクする必要性が無く、管理も簡単

    (7)昔のコードを保守する場合に「楽」だと感じる

    (8)GNU-RXの特性をある程度理解しているので、デバッグがしやすく、対応がしやすい

    利点を上げると沢山ありますが、今更Cでプログラム作る理由が無いので、当面は、GNU-RX一択になるのでしょうね・・・

  • hirakuni45さん、こんにちは。NoMaYです。

    情報どうもありがとうございます。それで、実は、こちら側では、RXスマートコンフィグレータ、というか、FIT/CGの事情になってしまうのですけれども、もろもろのモジュール/コンポーネントにおいて、関数先頭で多重割り込みが許可されるようになっていなくて、ユーザさんが自前で全てコーディングしているような高い割り込み優先度の割り込みの受け付けを、FIT/CGの低い優先度の割り込み処理が邪魔する/遅延させる、ような作りになっていて、ひょっとしたら、100クロックだの1000クロックだの割り込み要求の発生から受け付けまでが遅延するようなこともあるかも知れなくて、そんな構造で、高い割り込み優先度の割り込み関数の先頭処理が、5クロックとか、10クロックとか、20クロックとか、短くなっても価値のあることなのかなぁ、、、という気がしているのです。

    なので、このスレッドの前の方にも書いていますが、小細工を考えないと、、、とか考えていたりするのです。

    hirakuni45さんのように、独自フレームワークで、全ての割り込み処理において処理の早い段階で多重割り込みを許可するように出来る場合には、レジスタ一括退避機能で容易にメリットが出てくる、のですけれども。

    [追記]

    すみません、独り言ですが、ちょっと待ってくださいよ。>> 自分

    GNURXでは、関数先頭で多重割り込みを許可する構文が無いのだから、低い優先度の割り込み処理が、5クロックとか、10クロックとか、20クロックとか、高い優先度の割り込みの受け付けを遅延させることがある筈なのだから、FIT/CGほど酷くは無いけれども、やっぱり、ちょっと、それはもったいないかも、とも思わなくも無いような気もしてきた、かな、、、

Reply
  • hirakuni45さん、こんにちは。NoMaYです。

    情報どうもありがとうございます。それで、実は、こちら側では、RXスマートコンフィグレータ、というか、FIT/CGの事情になってしまうのですけれども、もろもろのモジュール/コンポーネントにおいて、関数先頭で多重割り込みが許可されるようになっていなくて、ユーザさんが自前で全てコーディングしているような高い割り込み優先度の割り込みの受け付けを、FIT/CGの低い優先度の割り込み処理が邪魔する/遅延させる、ような作りになっていて、ひょっとしたら、100クロックだの1000クロックだの割り込み要求の発生から受け付けまでが遅延するようなこともあるかも知れなくて、そんな構造で、高い割り込み優先度の割り込み関数の先頭処理が、5クロックとか、10クロックとか、20クロックとか、短くなっても価値のあることなのかなぁ、、、という気がしているのです。

    なので、このスレッドの前の方にも書いていますが、小細工を考えないと、、、とか考えていたりするのです。

    hirakuni45さんのように、独自フレームワークで、全ての割り込み処理において処理の早い段階で多重割り込みを許可するように出来る場合には、レジスタ一括退避機能で容易にメリットが出てくる、のですけれども。

    [追記]

    すみません、独り言ですが、ちょっと待ってくださいよ。>> 自分

    GNURXでは、関数先頭で多重割り込みを許可する構文が無いのだから、低い優先度の割り込み処理が、5クロックとか、10クロックとか、20クロックとか、高い優先度の割り込みの受け付けを遅延させることがある筈なのだから、FIT/CGほど酷くは無いけれども、やっぱり、ちょっと、それはもったいないかも、とも思わなくも無いような気もしてきた、かな、、、

Children
No Data