こんにちは。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%高速化します。」
こんにちは。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_accGNURX : -msave-acc-in-interrupts
以下、ドキュメントの画面コピーです。CC-RX途中省略ICCRXGNURXgcc.gnu.org/onlinedocs/gcc/RX-Options.html(参) gcc.gnu.org/onlinedocs/gcc/RX-Function-Attributes.html(参2) コマンドラインヘルプによるとオンラインドキュメントには未反映のものも残っているようです
NoMay さんこんにちは。
色々調査してくれて助かります。
なるほど、最適化した場合も、危険なコードが出る場合があるようですね。
※自分の環境で、ある程度複雑な割り込み関数で、実験してみましたが、それは大丈夫でした。
自分の最適化は「-O3」です。
※デバッグビルド(-O0)は撃沈です・・・
呼び出し先コード(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 #0ffc080f4: fb 5e 2e 90 08 mov.l #0x8902e, r5
...
ffc0814b: fd 76 f0 00 rstr #0ffc0814f: 7f 95 rteffc08151: 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-r15ffc080f2: 6e 15 pushm r1-r5ffc080f4: fb 5e 2e 90 08 mov.l #0x8902e, r5
ffc0814b: 6f 15 popm r1-r5ffc0814d: 6f ef popm r14-r15ffc0814f: 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ほど酷くは無いけれども、やっぱり、ちょっと、それはもったいないかも、とも思わなくも無いような気もしてきた、かな、、、