「e2studio + GCC for Renesas RL78」ROMサイズが少し大きくなるといろいろ出る不具合【 near領域のROMサイズに注意しましょう】

【解決後のコメント】この問題はRL78の nearアドレッシング可能領域外にコンパイラがnearアドレッシング定数を配置してしまうことで起きる暴走でした。
単純化したコードでは、定数のサイズを加減して原因調査し、Rsply3までに解決しました。
コンパイル後のMAPファイルで(.rodata)セクション(nearアドレッシング)のサイズがRL78のミラー領域を超えていないか、確認することで異常動作を防止できます。

しかし定数エリアのサイズがそれほど大きくないにもかかわらず、プログラムコードのサイズが大きくなると暴走します。
4番目のreply で説明していますが、プログラムコードのサイズが大きくなると nearアドレッシング可能なROM領域をプログラムコードがつぶしてしまうため、メモリーマッピング的に失敗していました。
結局のところリンカーファイルを手直しで編集して、 nearアドレッシング可能領域を確保する必要がありました。

-------------------------------

GCC for Renesas RL78の不具合と思われる現象で困っています。
e2studioで使うコンパイラのツールチェーンは「GCC for Renesas RL78」バージョン4.9.2.201902です。

単純化したので、プロジェクトを新規作成してすぐ再現できます。
事の発端はCS+とCCRLコンパイラの組み合わせででは問題なく動いていたプログラムを、GCC RL78の開発環境へ移したら暴走したことです。
GCC RL78の環境に変える理由は、CCRLコンパイラの無償評価期間が過ぎて64KB以上のプログラムを開発できなくなったためです。
CPUはRL78/G14 ROMサイズが256KBの104GJ で、エミュレータE1を接続してデバッグしています。

暴走するのはCのプログラムですが、調べると switch文 のところで暴走します。
動かない原因を探るためにコードをどんどん落としていくと、コードサイズが二十数KB程度のプログラムなら問題なく動くことが分かりました。
どうもコードサイズに関係する不具合のようなので、さらに単純化しました。
割り込み、周辺機能は一切使用せずハードウェアデバッグ機能のみ使用する。
調査に使用するソースは単純なCのソース一本だけ。
異常を確認するソース "r_main.c" は次のような簡単なものになっています。


const long Array1[2048] = {1, 1, 0, 0, 0, 0, 0 ... 略};    // 配列定数(1行でROM領域8Kバイト)
const long Array2[2048] = {1, 1, 2, 0, 0, 0, 0 ... 略};
const long Array3[2048] = {1, 1, 0, 2, 0, 0, 0 ... 略};
const long Array4[2048] = {1, 1, 0, 0, 5, 0, 0 ... 略};
const long Array5[2048] = {1, 1, 0, 0, 0, 10, 0 ... 略};

char c;
unsigned short i;
long lwk1, lwk2, lwk3, lwk4, lwk5;

char SwtchTest(char ac);
void R_MAIN_UserInit(void);

/***********************************************************************************************************************
* メイン関数
***********************************************************************************************************************/
void main(void)
{
    R_MAIN_UserInit();

    lwk1 = 0; lwk2 = 0; lwk3 = 0; lwk4 = 0; lwk5 = 0;

    for(i = 0; i<2; i++){
        lwk1 += Array1[i];
        lwk2 += Array2[i];
        lwk3 += Array3[i];
        lwk4 += Array4[i];    // 行をコメント化するとROM定数エリアが8Kバイト減る
        lwk5 += Array5[i];      <--------- ※1. Array5[0] にとんでもない大きな数値が代入される
    }

    lwk1 = lwk1 + lwk2 +lwk3 + lwk4 + lwk5;

    while (1U)
    {
        // test switch case
        c = 1;
        c = SwtchTest(c);     <--------- ※2. swich文に入ったとたんに暴走してプログラムカウンタが 0 になる(0番地へジャンプする)
    }
}

/***********************************************************************************************************************
* Function Name: R_MAIN_UserInit
***********************************************************************************************************************/
void R_MAIN_UserInit(void)
{
    EI();
}

char SwtchTest(char ac)     <--------- ※ swich文の関数
{
    char ret;

      switch(ac)
       {
          case 0:
              ret = ac +1;
             break;
          case 1:
              ret = ac +2;
             break;
          case 2:
              ret = ac +3;
             break;
          case 3:
              ret = ac +4;
             break;
          case 4:
              ret = ac +5;
             break;
          default:
             return -1;
       }
       return ret;
}
以上


説明

異常現象は上記ソース中にコメントしたように下記2点です
※1. Array5[0] にとんでもない大きな数値が代入される
※2. swich文に入ったとたんに暴走してプログラムカウンタが 0 になる(0番地へジャンプする)

使用する配列定数(const Array1[])が3つまでなら、ROMサイズは24KB程度でプログラムは正常に動きます。(ROMサイズが24KB 、プログラムサイズは828バイト)
使用する配列を4つにすると、swich文で暴走します。(ROMサイズが32KB 、プログラムサイズは896バイト)
使用する配列を5つにすると、swich文で暴走するほか、※1.の配列の参照値がとんでもない値になります。(ROMサイズが41KB 、プログラムサイズは978バイト)

ROMサイズの調整は、ソース中の  lwk4 += Array4[i]; といった1行をコメント化すると1行当たり8Kバイト分のROM定数エリアを使わなくなります。
(これはconst Array4[... で定義した定数配列が使われないのでコンパイラが定数として実体化させないため)

単純化したので、プロジェクトを新規作成してすぐ再現できます。
このように明らかにROMサイズと関係して不具合が発生しています。(今回はROM定数エリアでサイズを調整しましたが、プログラムコードサイズでも同じことが起こるようです)

対策としては、switch文を全て if文に変えても、定数の扱いでおかしな現象が残ってしまうため、GCCが怖くて使えない状態で困っております。

--2019年8月11日 追記--

ハードウェアがない環境でRL78 Simulatorを使用したデバッグでも同様の現象を確認出来ます。
またGCCのバージョン4.9.2.201801でも同じ、Cの言語仕様は、Defaultの"GNU ISO C90" に加えて"GNU ISO C99"、"GNU ISO C11" で試しても同様でした。
switch文のcase分岐数を減らすと暴走しなくなります。(逆アセンブラのコードもかなり変化します)

Parents
  • NoMayさん、じま さん、RePlyありがとうございます。
    結論から言うとアドバイスを参考に __far型修飾子を追加することにより暴走を停めることができました。

    自分もメモリマッピング上の問題がありそうと薄々感じていましたが、これ以上頑張って調べるよりアドバイスをもらおうと今回投稿した次第です。
    CPUの選択までさかのぼってお話しすると、コードサイズが大きくなりそうだったのでメモリの大きめのCPUを捜していると、RXまで使わなくてもRL78でROMサイズの大きなマイコンもあるじゃん!
    というノリで選んだCPUですが、RL78マイコンには慣れていなくて。
    恨み節ですが、エラーやワーニングが出ないなら賢いコンパイラが上手に処理してくれていると思っちゃいます。
    今回の問題が上手く処理できなかったら、敗北を認めて他のマイコンに替える方向に半歩踏み出しているところ。RX110を検討中(笑)
    とはいえRL78シリーズは特徴ある魅力的なハードなので、こんな事で使わなくなるのはもったいない話です。
    CC-RLを使い続けるための裏技って涙ぐましい(笑)ですね!(そこまでやるか)
    某M社などは無償でコードサイズ制限無し(最適化機能で制限)なので、ルネサスも頑張ってほしいところなんですが。

    本題に戻って、27.75Kバイトのミラー領域は効率良いアドレスのための領域なので、 __nearアドレッシングの const領域なら制約されるという意味でアドバイスされたのだろうと思います。
    「RL78のミラー領域について」
    japan.renesasrulz.com/.../rl78
    今回の問題と関係ありそうです。

    CC-RLとGCCのMAPを比較してみました。

    CC-RLでのMAPファイルは次のようになりました。
    まず __farを使わないで const array[8K] を3つ使用した場合、.const セグメントに配置された
    .text 000000ce 00000127 5a 1
    .textf 00000128 000002a0 179 1
    .const 00003000 00008fff 6000 2 (24KB)


    const array[8K] を5つ使うと定数エリアオーバーのエラーを出すので、const __far long Array1[2048] = {1, 1,.... のように__far を付けて アドレッシングモードを変えることで対応しました。
    CC-RL const __far array[8K] を5つ使用し const領域が40KB では .constf セグメントに配置されました
    .constf セグメントはfar領域としてアドレッシングされるのだと思います。
    SECTION START END SIZE ALIGN
    .text 000000ce 00000127 5a 1
    .textf 00000128 00000327 200 1
    .const 00003000 00003000 0 2
    .constf 00003000 0000cfff a000 2 (40KB)


    次はGCCの場合
    GCCの場合は __far を付けなくてもエラーが出ないので、賢いコンパイラが自動時に farアドレッシングのセグメントに割り付けるのでしょうか?
    array[8K] を5つ使って const領域が40KB のMAPを見てみるとCC-RLとおなじアドレスに配置されましたが、.rodataセクションなので far領域ではないようです。
    .rodata 0x00003000 0xa00a ./src/r_main.o
    0x00003000 _Array1
    0x00005000 _Array2
    0x00007000 _Array3
    0x00009000 _Array4
    0x0000b000 _Array5
    *(.rodata.*)
    0x0000d00a _erodata = .
    .frodata 0x0000d00a 0x0


    GCCで __far を明示的に付けた場合のMAPです
    .frodataセクションに配置されました。
    .rodata 0x00003000 0xa
    *(.rodata)
    .rodata 0x00003000 0xa ./src/r_main.o
    *(.rodata.*)
    0x0000300a _erodata = .
    .frodata 0x0000300a 0xa000
    0x0000300a . = ALIGN (0x2)
    *(.frodata)
    .frodata 0x0000300a 0xa000 ./src/r_main.o
    0x0000300a _Array1
    0x0000500a _Array2
    0x0000700a _Array3
    0x0000900a _Array4
    0x0000b00a _Array5
    *(.frodata.*)
    0x0000d00a _efrodata = .


    GCCで __far を明示的に付けると配列定数は .frodata に配置され、定数の参照値も正しく暴走もしなくなりました。
    これで constの値を正しく参照できるようになった説明はできるけど、switch文の暴走まで止まりました。
    これはどういう理由でしょうか?
    そもそも今回、ちいさなプログラムコードが暴走した理由が分かりませんが、 配列定数の他に__near定数域に 0x000a(10バイト) が確保されています。

    ソースには現れない定数がswitch文で使われているようです(NoMayさんの言われた分岐テーブルですね)、そこでswich文の処理内で参照ミスがあって暴走するのかもしれません。
    相変わらず GCCを使うのが怖いです。

    定数を全て farセグメント域に追い込んで、nearの定数域を十分に空けておけばソースコードに現れない定数を安全にアクセスできて、全て解決できるのでしょうか?

    FAQになって当然のような問題なので、FAQで捜せなかったのが残念。というか初めっから敬遠されているコンパイラなの? まだ他にも地雷がありそうで怖いです。

Reply
  • NoMayさん、じま さん、RePlyありがとうございます。
    結論から言うとアドバイスを参考に __far型修飾子を追加することにより暴走を停めることができました。

    自分もメモリマッピング上の問題がありそうと薄々感じていましたが、これ以上頑張って調べるよりアドバイスをもらおうと今回投稿した次第です。
    CPUの選択までさかのぼってお話しすると、コードサイズが大きくなりそうだったのでメモリの大きめのCPUを捜していると、RXまで使わなくてもRL78でROMサイズの大きなマイコンもあるじゃん!
    というノリで選んだCPUですが、RL78マイコンには慣れていなくて。
    恨み節ですが、エラーやワーニングが出ないなら賢いコンパイラが上手に処理してくれていると思っちゃいます。
    今回の問題が上手く処理できなかったら、敗北を認めて他のマイコンに替える方向に半歩踏み出しているところ。RX110を検討中(笑)
    とはいえRL78シリーズは特徴ある魅力的なハードなので、こんな事で使わなくなるのはもったいない話です。
    CC-RLを使い続けるための裏技って涙ぐましい(笑)ですね!(そこまでやるか)
    某M社などは無償でコードサイズ制限無し(最適化機能で制限)なので、ルネサスも頑張ってほしいところなんですが。

    本題に戻って、27.75Kバイトのミラー領域は効率良いアドレスのための領域なので、 __nearアドレッシングの const領域なら制約されるという意味でアドバイスされたのだろうと思います。
    「RL78のミラー領域について」
    japan.renesasrulz.com/.../rl78
    今回の問題と関係ありそうです。

    CC-RLとGCCのMAPを比較してみました。

    CC-RLでのMAPファイルは次のようになりました。
    まず __farを使わないで const array[8K] を3つ使用した場合、.const セグメントに配置された
    .text 000000ce 00000127 5a 1
    .textf 00000128 000002a0 179 1
    .const 00003000 00008fff 6000 2 (24KB)


    const array[8K] を5つ使うと定数エリアオーバーのエラーを出すので、const __far long Array1[2048] = {1, 1,.... のように__far を付けて アドレッシングモードを変えることで対応しました。
    CC-RL const __far array[8K] を5つ使用し const領域が40KB では .constf セグメントに配置されました
    .constf セグメントはfar領域としてアドレッシングされるのだと思います。
    SECTION START END SIZE ALIGN
    .text 000000ce 00000127 5a 1
    .textf 00000128 00000327 200 1
    .const 00003000 00003000 0 2
    .constf 00003000 0000cfff a000 2 (40KB)


    次はGCCの場合
    GCCの場合は __far を付けなくてもエラーが出ないので、賢いコンパイラが自動時に farアドレッシングのセグメントに割り付けるのでしょうか?
    array[8K] を5つ使って const領域が40KB のMAPを見てみるとCC-RLとおなじアドレスに配置されましたが、.rodataセクションなので far領域ではないようです。
    .rodata 0x00003000 0xa00a ./src/r_main.o
    0x00003000 _Array1
    0x00005000 _Array2
    0x00007000 _Array3
    0x00009000 _Array4
    0x0000b000 _Array5
    *(.rodata.*)
    0x0000d00a _erodata = .
    .frodata 0x0000d00a 0x0


    GCCで __far を明示的に付けた場合のMAPです
    .frodataセクションに配置されました。
    .rodata 0x00003000 0xa
    *(.rodata)
    .rodata 0x00003000 0xa ./src/r_main.o
    *(.rodata.*)
    0x0000300a _erodata = .
    .frodata 0x0000300a 0xa000
    0x0000300a . = ALIGN (0x2)
    *(.frodata)
    .frodata 0x0000300a 0xa000 ./src/r_main.o
    0x0000300a _Array1
    0x0000500a _Array2
    0x0000700a _Array3
    0x0000900a _Array4
    0x0000b00a _Array5
    *(.frodata.*)
    0x0000d00a _efrodata = .


    GCCで __far を明示的に付けると配列定数は .frodata に配置され、定数の参照値も正しく暴走もしなくなりました。
    これで constの値を正しく参照できるようになった説明はできるけど、switch文の暴走まで止まりました。
    これはどういう理由でしょうか?
    そもそも今回、ちいさなプログラムコードが暴走した理由が分かりませんが、 配列定数の他に__near定数域に 0x000a(10バイト) が確保されています。

    ソースには現れない定数がswitch文で使われているようです(NoMayさんの言われた分岐テーブルですね)、そこでswich文の処理内で参照ミスがあって暴走するのかもしれません。
    相変わらず GCCを使うのが怖いです。

    定数を全て farセグメント域に追い込んで、nearの定数域を十分に空けておけばソースコードに現れない定数を安全にアクセスできて、全て解決できるのでしょうか?

    FAQになって当然のような問題なので、FAQで捜せなかったのが残念。というか初めっから敬遠されているコンパイラなの? まだ他にも地雷がありそうで怖いです。

Children
No Data